mirror of
https://github.com/SabreTools/MPF.git
synced 2026-02-04 05:35:52 +00:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b1a303fea | ||
|
|
80a0f6da35 | ||
|
|
0c30eb7a60 | ||
|
|
7a8125bb71 | ||
|
|
c4beffd845 | ||
|
|
f97c112a53 | ||
|
|
5ef43ab6be | ||
|
|
2c399f99bf | ||
|
|
42e9eb0b96 | ||
|
|
e67d65f908 | ||
|
|
4ec8954b14 | ||
|
|
1a6abb039c | ||
|
|
bb5d1e5ac8 | ||
|
|
03c4c475eb | ||
|
|
04d7817d28 | ||
|
|
7cd100bc53 | ||
|
|
019924232a | ||
|
|
b5fc9f0275 | ||
|
|
44509b72ed | ||
|
|
d532a63dbd | ||
|
|
227785b079 | ||
|
|
0e364be998 | ||
|
|
7ae1f64ee3 | ||
|
|
92463a103d | ||
|
|
101cdb7b34 | ||
|
|
e924299f85 | ||
|
|
b983f7eb4a | ||
|
|
33b4be8b24 | ||
|
|
71a4edc8ba | ||
|
|
ceb305eb54 | ||
|
|
0b0d13dcf3 | ||
|
|
9f02368622 | ||
|
|
152010ee14 | ||
|
|
c6d5f0aea5 | ||
|
|
8c2ad64bb8 | ||
|
|
fa54d694b6 | ||
|
|
dc35b08624 | ||
|
|
4429515ba2 | ||
|
|
fdbc7b34e5 | ||
|
|
a1ab442cf0 | ||
|
|
9ed5c297f6 | ||
|
|
4ce9b214b0 | ||
|
|
7dcdadda75 | ||
|
|
f87a4d9fe2 | ||
|
|
e4e5d173f0 | ||
|
|
115b242d59 | ||
|
|
706bf8a431 | ||
|
|
87ecf1aa99 | ||
|
|
b5cf274333 | ||
|
|
4f4b6879b6 | ||
|
|
3b19463913 | ||
|
|
37386cd182 | ||
|
|
e04bdad16c | ||
|
|
e37f12705d | ||
|
|
5c8dc2c23a | ||
|
|
e9121f3b03 | ||
|
|
d68175db4e | ||
|
|
9d8181b0e2 | ||
|
|
6d657f268a |
@@ -1,3 +1,62 @@
|
||||
### 2.7.4 (2023-10-31)
|
||||
|
||||
- Move Hash enum and simplify
|
||||
- Add other non-cryptographic hashes
|
||||
- Avoid unncessary allocation in hashing
|
||||
- Make Hasher more consistent
|
||||
- Move all hashing to Hasher
|
||||
- Separate out static hashing
|
||||
- Remove unncessary summary
|
||||
- Version-gate new switch statement
|
||||
- Ignore empty protection results
|
||||
- Remove and Sort Usings
|
||||
- Update Redumper to build 244
|
||||
- Enable Bluray dumping with Redumper
|
||||
- Add Bluray layerbreak support for Redumper
|
||||
- Remove now-obsolete notes
|
||||
- Compile most regex statements
|
||||
- Handle a couple of messages
|
||||
- Remove .manufacturer for Bluray
|
||||
- Fix typo that disables DIC during media check (Deterous)
|
||||
- Fix build
|
||||
- Remove duplicate check
|
||||
- Add PIC output for Redumper
|
||||
- Update Redumper to build 246
|
||||
- Enable HD-DVD for Redumper in UI
|
||||
- Attempt to fix window owner issue
|
||||
|
||||
### 2.7.3 (2023-10-26)
|
||||
|
||||
- Split InfoTool into 2 classes
|
||||
- Add path variants for PlayStation info
|
||||
- Convert Drive to use paths internally
|
||||
- Create skeleton for first-run
|
||||
- Show options window on first run
|
||||
- Fix drive letter issue in UI
|
||||
- Add first-run Options title, fix saving bug
|
||||
- Fix multiple handler invocation
|
||||
- Create method for applying theme
|
||||
- Fix drive letter check
|
||||
- Simply theme application
|
||||
- Add framework for deleteable files
|
||||
- Add deleteable file lists for Redumper and DIC
|
||||
- Add optional file deletion
|
||||
- Rearrange OptionsWindow to be easier to navigate
|
||||
- Fix up DumpEnvironment a bit
|
||||
- Add filename suffix setting (nw)
|
||||
- Wire through filename suffix
|
||||
- Expose suffix setting
|
||||
- Set UDF CD threshold at 800MB (Deterous)
|
||||
- Update package versions
|
||||
- Attempt to parse out PS5 params.json
|
||||
- Fix CRC32 hashing
|
||||
- Update XUnit packages
|
||||
- Update to BurnOutSharp 2.9.0
|
||||
- Update to MMI 3.0.0-preview.4
|
||||
- Fix two small nullability issues
|
||||
- Use Array.Empty in hasher
|
||||
- Always use relative path internally (Deterous)
|
||||
|
||||
### 2.7.2 (2023-10-17)
|
||||
|
||||
- Fix options loading for Check
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<Description>Validator for various dumping programs</Description>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>2.7.2</VersionPrefix>
|
||||
<VersionPrefix>2.7.4</VersionPrefix>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
|
||||
@@ -24,7 +24,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.9.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using BurnOutSharp;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core.Data;
|
||||
|
||||
namespace MPF.Core
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace MPF.Core.Converters
|
||||
case InternalProgram.DiscImageCreator:
|
||||
return "DiscImageCreator";
|
||||
case InternalProgram.Redumper:
|
||||
return "redumper";
|
||||
return "Redumper";
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Linq;
|
||||
using Microsoft.Management.Infrastructure;
|
||||
using Microsoft.Management.Infrastructure.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
@@ -14,7 +13,6 @@ namespace MPF.Core.Data
|
||||
/// Represents information for a single drive
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TODO: This needs to be less Windows-centric. Devices do not always have a single letter that can be used.
|
||||
/// TODO: Can the Aaru models be used instead of the ones I've created here?
|
||||
/// </remarks>
|
||||
public class Drive
|
||||
@@ -96,9 +94,10 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Windows drive letter
|
||||
/// Read-only access to the drive letter
|
||||
/// </summary>
|
||||
public char Letter => this.Name == null || this.Name.Length == 0 ? '\0' : this.Name[0];
|
||||
/// <remarks>Should only be used in UI applications</remarks>
|
||||
public char? Letter => this.Name?[0] ?? '\0';
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -188,7 +187,7 @@ namespace MPF.Core.Data
|
||||
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
|
||||
{
|
||||
var drives = GetDriveList(ignoreFixedDrives);
|
||||
drives = drives.OrderBy(i => i == null ? '\0' : i.Letter).ToList();
|
||||
drives = drives.OrderBy(i => i == null ? "\0" : i.Name).ToList();
|
||||
return drives;
|
||||
}
|
||||
|
||||
@@ -264,13 +263,9 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
// Handle optical media by size and filesystem
|
||||
if (this.TotalSize >= 0 && this.TotalSize < 800_000_000 && this.DriveFormat == "CDFS")
|
||||
if (this.TotalSize >= 0 && this.TotalSize <= 800_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
|
||||
return (MediaType.CDROM, null);
|
||||
else if (this.TotalSize >= 0 && this.TotalSize < 400_000_000 && this.DriveFormat == "UDF")
|
||||
return (MediaType.CDROM, null);
|
||||
else if (this.TotalSize >= 800_000_000 && this.TotalSize <= 8_540_000_000 && this.DriveFormat == "CDFS")
|
||||
return (MediaType.DVD, null);
|
||||
else if (this.TotalSize >= 400_000_000 && this.TotalSize <= 8_540_000_000 && this.DriveFormat == "UDF")
|
||||
else if (this.TotalSize > 800_000_000 && this.TotalSize <= 8_540_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
|
||||
return (MediaType.DVD, null);
|
||||
else if (this.TotalSize > 8_540_000_000)
|
||||
return (MediaType.BluRay, null);
|
||||
@@ -285,14 +280,11 @@ namespace MPF.Core.Data
|
||||
/// <returns></returns>
|
||||
public RedumpSystem? GetRedumpSystem(RedumpSystem? defaultValue)
|
||||
{
|
||||
string drivePath = $"{this.Letter}:\\";
|
||||
|
||||
// If we can't read the media in that drive, we can't do anything
|
||||
if (!Directory.Exists(drivePath))
|
||||
if (!Directory.Exists(this.Name))
|
||||
return defaultValue;
|
||||
|
||||
// We're going to assume for floppies, HDDs, and removable drives
|
||||
// TODO: Try to be smarter about this
|
||||
if (this.InternalDriveType != Data.InternalDriveType.Optical)
|
||||
return RedumpSystem.IBMPCcompatible;
|
||||
|
||||
@@ -307,7 +299,7 @@ namespace MPF.Core.Data
|
||||
// Bandai Playdia Quick Interactive System
|
||||
try
|
||||
{
|
||||
List<string> files = Directory.EnumerateFiles(drivePath, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
List<string> files = Directory.EnumerateFiles(this.Name, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
|
||||
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
|
||||
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
|
||||
@@ -318,7 +310,7 @@ namespace MPF.Core.Data
|
||||
catch { }
|
||||
|
||||
// Mattel Fisher-Price iXL
|
||||
if (File.Exists(Path.Combine(drivePath, "iXL", "iXLUpdater.exe")))
|
||||
if (File.Exists(Path.Combine(this.Name, "iXL", "iXLUpdater.exe")))
|
||||
{
|
||||
return RedumpSystem.MattelFisherPriceiXL;
|
||||
}
|
||||
@@ -326,8 +318,8 @@ namespace MPF.Core.Data
|
||||
// Microsoft Xbox 360
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "$SystemUpdate"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any()
|
||||
if (Directory.Exists(Path.Combine(this.Name, "$SystemUpdate"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
|
||||
&& this.TotalSize <= 500_000_000)
|
||||
{
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
@@ -338,8 +330,8 @@ namespace MPF.Core.Data
|
||||
// Microsoft Xbox One
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "MSXC"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "MSXC")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "MSXC"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "MSXC")).Any())
|
||||
{
|
||||
return RedumpSystem.MicrosoftXboxOne;
|
||||
}
|
||||
@@ -347,35 +339,23 @@ namespace MPF.Core.Data
|
||||
catch { }
|
||||
|
||||
// Sega Dreamcast
|
||||
if (File.Exists(Path.Combine(drivePath, "IP.BIN")))
|
||||
if (File.Exists(Path.Combine(this.Name, "IP.BIN")))
|
||||
{
|
||||
return RedumpSystem.SegaDreamcast;
|
||||
}
|
||||
|
||||
// Sega Mega-CD / Sega-CD
|
||||
if (File.Exists(Path.Combine(drivePath, "_BOOT", "IP.BIN"))
|
||||
|| File.Exists(Path.Combine(drivePath, "_BOOT", "SP.BIN"))
|
||||
|| File.Exists(Path.Combine(drivePath, "_BOOT", "SP_AS.BIN"))
|
||||
|| File.Exists(Path.Combine(drivePath, "FILESYSTEM.BIN")))
|
||||
if (File.Exists(Path.Combine(this.Name, "_BOOT", "IP.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP_AS.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
|
||||
{
|
||||
return RedumpSystem.SegaMegaCDSegaCD;
|
||||
}
|
||||
|
||||
// Sega Saturn
|
||||
try
|
||||
{
|
||||
var sector = ReadSector(0);
|
||||
if (sector != null)
|
||||
{
|
||||
if (sector.StartsWith(Interface.SaturnSectorZeroStart))
|
||||
return RedumpSystem.SegaSaturn;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Sony PlayStation and Sony PlayStation 2
|
||||
string psxExePath = Path.Combine(drivePath, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
|
||||
string psxExePath = Path.Combine(this.Name, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(this.Name, "SYSTEM.CNF");
|
||||
if (File.Exists(systemCnfPath))
|
||||
{
|
||||
// Check for either BOOT or BOOT2
|
||||
@@ -393,9 +373,9 @@ namespace MPF.Core.Data
|
||||
// Sony PlayStation 3
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "PS3_GAME"))
|
||||
|| Directory.Exists(Path.Combine(drivePath, "PS3_UPDATE"))
|
||||
|| File.Exists(Path.Combine(drivePath, "PS3_DISC.SFB")))
|
||||
if (Directory.Exists(Path.Combine(this.Name, "PS3_GAME"))
|
||||
|| Directory.Exists(Path.Combine(this.Name, "PS3_UPDATE"))
|
||||
|| File.Exists(Path.Combine(this.Name, "PS3_DISC.SFB")))
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
}
|
||||
@@ -414,13 +394,13 @@ namespace MPF.Core.Data
|
||||
// Is used as an on-disc update for the base game app without needing to get update from the internet.
|
||||
// "/addcont/GAME_SERIAL/CONTENT_ID/ac.pkg" can be found in Redump entry 97619.
|
||||
// Originally on disc as "/addcont/CUSA00288/FFXIVEXPS400001A/ac.pkg".
|
||||
if (File.Exists(Path.Combine(drivePath, "PS4", "UPDATE", "PS4UPDATE.PUP")))
|
||||
if (File.Exists(Path.Combine(this.Name, "PS4", "UPDATE", "PS4UPDATE.PUP")))
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation4;
|
||||
}
|
||||
|
||||
// V.Tech V.Flash / V.Smile Pro
|
||||
if (File.Exists(Path.Combine(drivePath, "0SYSTEM")))
|
||||
if (File.Exists(Path.Combine(this.Name, "0SYSTEM")))
|
||||
{
|
||||
return RedumpSystem.VTechVFlashVSmilePro;
|
||||
}
|
||||
@@ -430,7 +410,7 @@ namespace MPF.Core.Data
|
||||
#region Computers
|
||||
|
||||
// Sharp X68000
|
||||
if (File.Exists(Path.Combine(drivePath, "COMMAND.X")))
|
||||
if (File.Exists(Path.Combine(this.Name, "COMMAND.X")))
|
||||
{
|
||||
return RedumpSystem.SharpX68000;
|
||||
}
|
||||
@@ -440,7 +420,7 @@ namespace MPF.Core.Data
|
||||
#region Video Formats
|
||||
|
||||
// BD-Video
|
||||
if (Directory.Exists(Path.Combine(drivePath, "BDMV")))
|
||||
if (Directory.Exists(Path.Combine(this.Name, "BDMV")))
|
||||
{
|
||||
// Technically BD-Audio has this as well, but it's hard to split that out right now
|
||||
return RedumpSystem.BDVideo;
|
||||
@@ -449,14 +429,14 @@ namespace MPF.Core.Data
|
||||
// DVD-Audio and DVD-Video
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "AUDIO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "AUDIO_TS")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "AUDIO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
|
||||
{
|
||||
return RedumpSystem.DVDAudio;
|
||||
}
|
||||
|
||||
else if (Directory.Exists(Path.Combine(drivePath, "VIDEO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VIDEO_TS")).Any())
|
||||
else if (Directory.Exists(Path.Combine(this.Name, "VIDEO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
|
||||
{
|
||||
return RedumpSystem.DVDVideo;
|
||||
}
|
||||
@@ -466,8 +446,8 @@ namespace MPF.Core.Data
|
||||
// HD-DVD-Video
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "HVDVD_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "HVDVD_TS")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "HVDVD_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
|
||||
{
|
||||
return RedumpSystem.HDDVDVideo;
|
||||
}
|
||||
@@ -477,8 +457,8 @@ namespace MPF.Core.Data
|
||||
// VCD
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "VCD"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VCD")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "VCD"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VCD")).Any())
|
||||
{
|
||||
return RedumpSystem.VideoCD;
|
||||
}
|
||||
@@ -542,56 +522,6 @@ namespace MPF.Core.Data
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a sector with a specified size from the drive
|
||||
/// </summary>
|
||||
/// <param name="num">Sector number, non-negative</param>
|
||||
/// <param name="size">Size of a sector in bytes</param>
|
||||
/// <returns>Byte array representing the sector, null on error</returns>
|
||||
#if NET48
|
||||
public byte[] ReadSector(long num, int size = 2048)
|
||||
#else
|
||||
public byte[]? ReadSector(long num, int size = 2048)
|
||||
#endif
|
||||
{
|
||||
// Missing drive leter is not supported
|
||||
if (string.IsNullOrEmpty(this.Name))
|
||||
return null;
|
||||
|
||||
// We don't support negative sectors
|
||||
if (num < 0)
|
||||
return null;
|
||||
|
||||
// Wrap the following in case of device access errors
|
||||
#if NET48
|
||||
Stream fs = null;
|
||||
#else
|
||||
Stream? fs = null;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
// Open the drive as a device
|
||||
fs = File.OpenRead($"\\\\?\\{this.Letter}:");
|
||||
|
||||
// Seek to the start of the sector, if possible
|
||||
long start = num * size;
|
||||
fs.Seek(start, SeekOrigin.Begin);
|
||||
|
||||
// Read and return the sector
|
||||
byte[] buffer = new byte[size];
|
||||
fs.Read(buffer, 0, size);
|
||||
return buffer;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
fs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the current drive information based on path
|
||||
/// </summary>
|
||||
@@ -655,7 +585,7 @@ namespace MPF.Core.Data
|
||||
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
|
||||
{
|
||||
char devId = (properties["Caption"].Value as string ?? string.Empty)[0];
|
||||
drives.ForEach(d => { if (d?.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
|
||||
drives.ForEach(d => { if (d?.Name != null && d.Name[0] == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Available hashing types
|
||||
/// </summary>
|
||||
public enum Hash
|
||||
{
|
||||
CRC32,
|
||||
CRC64,
|
||||
MD5,
|
||||
SHA1,
|
||||
SHA256,
|
||||
SHA384,
|
||||
SHA512,
|
||||
XxHash32,
|
||||
XxHash64,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drive type for dumping
|
||||
/// </summary>
|
||||
|
||||
@@ -15,6 +15,15 @@ namespace MPF.Core.Data
|
||||
public Dictionary<string, string?> Settings { get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if the program is being run with a clean configuration
|
||||
/// </summary>
|
||||
public bool FirstRun
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "FirstRun", true); }
|
||||
set { Settings["FirstRun"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#region Internal Program
|
||||
|
||||
/// <summary>
|
||||
@@ -466,6 +475,15 @@ namespace MPF.Core.Data
|
||||
set { Settings["ToolsInSeparateWindow"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the dump filename as a suffix to the auto-generated files
|
||||
/// </summary>
|
||||
public bool AddFilenameSuffix
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "AddFilenameSuffix", false); }
|
||||
set { Settings["AddFilenameSuffix"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output the compressed JSON version of the submission info
|
||||
/// </summary>
|
||||
@@ -493,6 +511,15 @@ namespace MPF.Core.Data
|
||||
set { Settings["CompressLogFiles"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete unnecessary files to reduce space
|
||||
/// </summary>
|
||||
public bool DeleteUnnecessaryFiles
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "DeleteUnnecessaryFiles", false); }
|
||||
set { Settings["DeleteUnnecessaryFiles"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Skip Options
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information specific to an XGD disc
|
||||
/// </summary>
|
||||
public class XgdInfo
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the information in this object is fully instantiated or not
|
||||
/// </summary>
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Raw XMID/XeMID string that all other information is derived from
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string RawXMID { get; private set; }
|
||||
#else
|
||||
public string? RawXMID { get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// XGD1 XMID
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public SabreTools.Models.Xbox.XMID XMID { get; private set; }
|
||||
#else
|
||||
public SabreTools.Models.Xbox.XMID? XMID { get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// XGD2/3 XeMID
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public SabreTools.Models.Xbox.XeMID XeMID { get; private set; }
|
||||
#else
|
||||
public SabreTools.Models.Xbox.XeMID? XeMID { get; private set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Populate a set of XGD information from a Master ID (XMID/XeMID) string
|
||||
/// </summary>
|
||||
/// <param name="xmid">XMID/XeMID string representing the DMI information</param>
|
||||
public XgdInfo(string xmid)
|
||||
{
|
||||
this.Initialized = false;
|
||||
if (string.IsNullOrWhiteSpace(xmid))
|
||||
return;
|
||||
|
||||
this.RawXMID = xmid.TrimEnd('\0');
|
||||
if (string.IsNullOrWhiteSpace(this.RawXMID))
|
||||
return;
|
||||
|
||||
// XGD1 information is 8 characters
|
||||
if (this.RawXMID.Length == 8)
|
||||
this.Initialized = ParseXGD1XMID(this.RawXMID);
|
||||
|
||||
// XGD2/3 information is semi-variable length
|
||||
else if (this.RawXMID.Length == 13 || this.RawXMID.Length == 14 || this.RawXMID.Length == 21 || this.RawXMID.Length == 22)
|
||||
this.Initialized = ParseXGD23XeMID(this.RawXMID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the human-readable serial string
|
||||
/// </summary>
|
||||
/// <returns>Formatted serial string, null on error</returns>
|
||||
#if NET48
|
||||
public string GetSerial()
|
||||
#else
|
||||
public string? GetSerial()
|
||||
#endif
|
||||
{
|
||||
if (!this.Initialized)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// XGD1 doesn't use PlatformIdentifier
|
||||
if (XMID != null)
|
||||
return $"{XMID.PublisherIdentifier}-{XMID.GameID}";
|
||||
|
||||
// XGD2/3 uses a specific identifier
|
||||
else if (XeMID?.PlatformIdentifier == '2')
|
||||
return $"{XeMID.PublisherIdentifier}-{XeMID.PlatformIdentifier}{XeMID.GameID}";
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the human-readable version string
|
||||
/// </summary>
|
||||
/// <returns>Formatted version string, null on error</returns>
|
||||
/// <remarks>This may differ for XGD2/3 in the future</remarks>
|
||||
#if NET48
|
||||
public string GetVersion()
|
||||
#else
|
||||
public string? GetVersion()
|
||||
#endif
|
||||
{
|
||||
if (!this.Initialized)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// XGD1 doesn't use PlatformIdentifier
|
||||
if (XMID != null)
|
||||
return $"1.{XMID.VersionNumber}";
|
||||
|
||||
// XGD2/3 uses a specific identifier
|
||||
else if (XeMID?.PlatformIdentifier == '2')
|
||||
return $"1.{XeMID.SKU}";
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an XGD1 XMID string
|
||||
/// </summary>
|
||||
/// <param name="rawXmid">XMID string to attempt to parse</param>
|
||||
/// <returns>True if the XMID could be parsed, false otherwise</returns>
|
||||
private bool ParseXGD1XMID(string rawXmid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var xmid = new SabreTools.Serialization.Files.XMID().Deserialize(rawXmid);
|
||||
if (xmid == null)
|
||||
return false;
|
||||
|
||||
this.XMID = xmid;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an XGD2/3 XeMID string
|
||||
/// </summary>
|
||||
/// <param name="rawXemid">XeMID string to attempt to parse</param>
|
||||
/// <returns>True if the XeMID could be parsed, false otherwise</returns>
|
||||
private bool ParseXGD23XeMID(string rawXemid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var xemid = new SabreTools.Serialization.Files.XeMID().Deserialize(rawXemid);
|
||||
if (xemid == null)
|
||||
return false;
|
||||
|
||||
this.XeMID = xemid;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Determine the region based on the XGD serial character
|
||||
/// </summary>
|
||||
/// <param name="region">Character denoting the region</param>
|
||||
/// <returns>Region, if possible</returns>
|
||||
public static Region? GetRegion(char? region)
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
case 'W': return Region.World;
|
||||
case 'A': return Region.UnitedStatesOfAmerica;
|
||||
case 'J': return Region.JapanAsia;
|
||||
case 'E': return Region.Europe;
|
||||
case 'K': return Region.USAJapan;
|
||||
case 'L': return Region.USAEurope;
|
||||
case 'H': return Region.JapanEurope;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BurnOutSharp;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.Core.Modules;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core
|
||||
@@ -133,7 +134,7 @@ namespace MPF.Core
|
||||
Options = options;
|
||||
|
||||
// Output paths
|
||||
OutputPath = InfoTool.NormalizeOutputPaths(outputPath, true);
|
||||
OutputPath = InfoTool.NormalizeOutputPaths(outputPath, false);
|
||||
|
||||
// UI information
|
||||
Drive = drive;
|
||||
@@ -147,53 +148,6 @@ namespace MPF.Core
|
||||
|
||||
#region Public Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Adjust output paths if we're using DiscImageCreator
|
||||
/// </summary>
|
||||
public void AdjustPathsForDiscImageCreator()
|
||||
{
|
||||
// Only DiscImageCreator has issues with paths
|
||||
if (Parameters?.InternalProgram != InternalProgram.DiscImageCreator)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// Normalize the output path
|
||||
string outputPath = InfoTool.NormalizeOutputPaths(OutputPath, true);
|
||||
|
||||
// Replace all instances in the output directory
|
||||
var outputDirectory = Path.GetDirectoryName(outputPath);
|
||||
outputDirectory = outputDirectory?.Replace(".", "_");
|
||||
|
||||
// Replace all instances in the output filename
|
||||
string outputFilename = Path.GetFileNameWithoutExtension(outputPath);
|
||||
outputFilename = outputFilename.Replace(".", "_");
|
||||
|
||||
// Get the extension for recreating the path
|
||||
string outputExtension = Path.GetExtension(outputPath).TrimStart('.');
|
||||
|
||||
// Rebuild the output path
|
||||
if (string.IsNullOrWhiteSpace(outputDirectory))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(outputExtension))
|
||||
OutputPath = outputFilename;
|
||||
else
|
||||
OutputPath = $"{outputFilename}.{outputExtension}";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(outputExtension))
|
||||
OutputPath = Path.Combine(outputDirectory, outputFilename);
|
||||
else
|
||||
OutputPath = Path.Combine(outputDirectory, $"{outputFilename}.{outputExtension}");
|
||||
}
|
||||
|
||||
// Assign the path to the filename as well for dumping
|
||||
((Modules.DiscImageCreator.Parameters)Parameters).Filename = OutputPath;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the parameters object based on the internal program and parameters string
|
||||
/// </summary>
|
||||
@@ -287,31 +241,31 @@ namespace MPF.Core
|
||||
switch (InternalProgram)
|
||||
{
|
||||
case InternalProgram.Aaru:
|
||||
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, OutputPath, driveSpeed, Options);
|
||||
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
case InternalProgram.DiscImageCreator:
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, OutputPath, driveSpeed, Options);
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
case InternalProgram.Redumper:
|
||||
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, OutputPath, driveSpeed, Options);
|
||||
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
default:
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, OutputPath, driveSpeed, Options);
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
Parameters = InternalProgram switch
|
||||
{
|
||||
InternalProgram.Aaru => new Modules.Aaru.Parameters(System, Type, Drive.Letter, OutputPath, driveSpeed, Options),
|
||||
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, OutputPath, driveSpeed, Options),
|
||||
InternalProgram.Redumper => new Modules.Redumper.Parameters(System, Type, Drive.Letter, OutputPath, driveSpeed, Options),
|
||||
InternalProgram.Aaru => new Modules.Aaru.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
InternalProgram.Redumper => new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
_ => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, OutputPath, driveSpeed, Options),
|
||||
_ => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -322,7 +276,7 @@ namespace MPF.Core
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Dumping
|
||||
|
||||
@@ -339,7 +293,7 @@ namespace MPF.Core
|
||||
#else
|
||||
public async Task<string?> EjectDisc() =>
|
||||
#endif
|
||||
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
|
||||
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
|
||||
|
||||
/// <summary>
|
||||
/// Reset the current drive using DiscImageCreator
|
||||
@@ -435,7 +389,7 @@ namespace MPF.Core
|
||||
|
||||
// Extract the information from the output files
|
||||
resultProgress?.Report(Result.Success("Extracting output information from output files..."));
|
||||
var submissionInfo = await InfoTool.ExtractOutputInformation(
|
||||
var submissionInfo = await SubmissionInfoTool.ExtractOutputInformation(
|
||||
OutputPath,
|
||||
Drive,
|
||||
System,
|
||||
@@ -450,21 +404,21 @@ namespace MPF.Core
|
||||
if (seedInfo != null)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Injecting user-supplied information..."));
|
||||
InjectSubmissionInformation(submissionInfo, seedInfo);
|
||||
SubmissionInfoTool.InjectSubmissionInformation(submissionInfo, seedInfo);
|
||||
resultProgress?.Report(Result.Success("Information injection complete!"));
|
||||
}
|
||||
|
||||
// Eject the disc automatically if configured to
|
||||
if (Options.EjectAfterDump == true)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive?.Letter}"));
|
||||
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive?.Name}"));
|
||||
await EjectDisc();
|
||||
}
|
||||
|
||||
// Reset the drive automatically if configured to
|
||||
if (InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Resetting drive {Drive?.Letter}"));
|
||||
resultProgress?.Report(Result.Success($"Resetting drive {Drive?.Name}"));
|
||||
await ResetDrive();
|
||||
}
|
||||
|
||||
@@ -495,30 +449,36 @@ namespace MPF.Core
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(formatResult));
|
||||
|
||||
// Get the filename suffix for auto-generated files
|
||||
var filenameSuffix = Options.AddFilenameSuffix ? Path.GetFileNameWithoutExtension(outputFilename) : null;
|
||||
|
||||
// Write the text output
|
||||
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
|
||||
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, formattedValues);
|
||||
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, formattedValues);
|
||||
if (txtSuccess)
|
||||
resultProgress?.Report(Result.Success(txtResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(txtResult));
|
||||
|
||||
// Write the copy protection output
|
||||
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
|
||||
if (submissionInfo?.CopyProtection?.FullProtections != null && submissionInfo.CopyProtection.FullProtections.Any())
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, submissionInfo);
|
||||
if (scanSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Writing could not complete!"));
|
||||
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo);
|
||||
if (scanSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Writing could not complete!"));
|
||||
}
|
||||
}
|
||||
|
||||
// Write the JSON output, if required
|
||||
if (Options.OutputSubmissionJSON)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Writing information to !submissionInfo.json{(Options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
|
||||
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, submissionInfo, Options.IncludeArtifacts);
|
||||
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, submissionInfo, Options.IncludeArtifacts);
|
||||
if (jsonSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
@@ -529,13 +489,24 @@ namespace MPF.Core
|
||||
if (Options.CompressLogFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Compressing log files..."));
|
||||
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, outputFilename, Parameters);
|
||||
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, Parameters);
|
||||
if (compressSuccess)
|
||||
resultProgress?.Report(Result.Success(compressResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(compressResult));
|
||||
}
|
||||
|
||||
// Delete unnecessary files, if required
|
||||
if (Options.DeleteUnnecessaryFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Deleting unnecessary files..."));
|
||||
(bool deleteSuccess, string deleteResult) = InfoTool.DeleteUnnecessaryFiles(outputDirectory, outputFilename, Parameters);
|
||||
if (deleteSuccess)
|
||||
resultProgress?.Report(Result.Success(deleteResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(deleteResult));
|
||||
}
|
||||
|
||||
resultProgress?.Report(Result.Success("Submission information process complete!"));
|
||||
return Result.Success();
|
||||
}
|
||||
@@ -597,79 +568,6 @@ namespace MPF.Core
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inject information from a seed SubmissionInfo into the existing one
|
||||
/// </summary>
|
||||
/// <param name="info">Existing submission information</param>
|
||||
/// <param name="seed">User-supplied submission information</param>
|
||||
#if NET48
|
||||
private static void InjectSubmissionInformation(SubmissionInfo info, SubmissionInfo seed)
|
||||
#else
|
||||
private static void InjectSubmissionInformation(SubmissionInfo? info, SubmissionInfo? seed)
|
||||
#endif
|
||||
{
|
||||
// If we have any invalid info
|
||||
if (seed == null)
|
||||
return;
|
||||
|
||||
// Ensure that required sections exist
|
||||
info = InfoTool.EnsureAllSections(info);
|
||||
|
||||
// Otherwise, inject information as necessary
|
||||
if (info.CommonDiscInfo != null && seed.CommonDiscInfo != null)
|
||||
{
|
||||
// Info that only overwrites if supplied
|
||||
if (!string.IsNullOrWhiteSpace(seed.CommonDiscInfo.Title)) info.CommonDiscInfo.Title = seed.CommonDiscInfo.Title;
|
||||
if (!string.IsNullOrWhiteSpace(seed.CommonDiscInfo.ForeignTitleNonLatin)) info.CommonDiscInfo.ForeignTitleNonLatin = seed.CommonDiscInfo.ForeignTitleNonLatin;
|
||||
if (!string.IsNullOrWhiteSpace(seed.CommonDiscInfo.DiscNumberLetter)) info.CommonDiscInfo.DiscNumberLetter = seed.CommonDiscInfo.DiscNumberLetter;
|
||||
if (!string.IsNullOrWhiteSpace(seed.CommonDiscInfo.DiscTitle)) info.CommonDiscInfo.DiscTitle = seed.CommonDiscInfo.DiscTitle;
|
||||
if (seed.CommonDiscInfo.Category != null) info.CommonDiscInfo.Category = seed.CommonDiscInfo.Category;
|
||||
if (seed.CommonDiscInfo.Region != null) info.CommonDiscInfo.Region = seed.CommonDiscInfo.Region;
|
||||
if (seed.CommonDiscInfo.Languages != null) info.CommonDiscInfo.Languages = seed.CommonDiscInfo.Languages;
|
||||
if (seed.CommonDiscInfo.LanguageSelection != null) info.CommonDiscInfo.LanguageSelection = seed.CommonDiscInfo.LanguageSelection;
|
||||
if (!string.IsNullOrWhiteSpace(seed.CommonDiscInfo.Serial)) info.CommonDiscInfo.Serial = seed.CommonDiscInfo.Serial;
|
||||
if (!string.IsNullOrWhiteSpace(seed.CommonDiscInfo.Barcode)) info.CommonDiscInfo.Barcode = seed.CommonDiscInfo.Barcode;
|
||||
if (!string.IsNullOrWhiteSpace(seed.CommonDiscInfo.Comments)) info.CommonDiscInfo.Comments = seed.CommonDiscInfo.Comments;
|
||||
if (seed.CommonDiscInfo.CommentsSpecialFields != null) info.CommonDiscInfo.CommentsSpecialFields = seed.CommonDiscInfo.CommentsSpecialFields;
|
||||
if (!string.IsNullOrWhiteSpace(seed.CommonDiscInfo.Contents)) info.CommonDiscInfo.Contents = seed.CommonDiscInfo.Contents;
|
||||
if (seed.CommonDiscInfo.ContentsSpecialFields != null) info.CommonDiscInfo.ContentsSpecialFields = seed.CommonDiscInfo.ContentsSpecialFields;
|
||||
|
||||
// Info that always overwrites
|
||||
info.CommonDiscInfo.Layer0MasteringRing = seed.CommonDiscInfo.Layer0MasteringRing;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = seed.CommonDiscInfo.Layer0MasteringSID;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = seed.CommonDiscInfo.Layer0ToolstampMasteringCode;
|
||||
info.CommonDiscInfo.Layer0MouldSID = seed.CommonDiscInfo.Layer0MouldSID;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = seed.CommonDiscInfo.Layer0AdditionalMould;
|
||||
|
||||
info.CommonDiscInfo.Layer1MasteringRing = seed.CommonDiscInfo.Layer1MasteringRing;
|
||||
info.CommonDiscInfo.Layer1MasteringSID = seed.CommonDiscInfo.Layer1MasteringSID;
|
||||
info.CommonDiscInfo.Layer1ToolstampMasteringCode = seed.CommonDiscInfo.Layer1ToolstampMasteringCode;
|
||||
info.CommonDiscInfo.Layer1MouldSID = seed.CommonDiscInfo.Layer1MouldSID;
|
||||
info.CommonDiscInfo.Layer1AdditionalMould = seed.CommonDiscInfo.Layer1AdditionalMould;
|
||||
|
||||
info.CommonDiscInfo.Layer2MasteringRing = seed.CommonDiscInfo.Layer2MasteringRing;
|
||||
info.CommonDiscInfo.Layer2MasteringSID = seed.CommonDiscInfo.Layer2MasteringSID;
|
||||
info.CommonDiscInfo.Layer2ToolstampMasteringCode = seed.CommonDiscInfo.Layer2ToolstampMasteringCode;
|
||||
|
||||
info.CommonDiscInfo.Layer3MasteringRing = seed.CommonDiscInfo.Layer3MasteringRing;
|
||||
info.CommonDiscInfo.Layer3MasteringSID = seed.CommonDiscInfo.Layer3MasteringSID;
|
||||
info.CommonDiscInfo.Layer3ToolstampMasteringCode = seed.CommonDiscInfo.Layer3ToolstampMasteringCode;
|
||||
}
|
||||
|
||||
if (info.VersionAndEditions != null && seed.VersionAndEditions != null)
|
||||
{
|
||||
// Info that only overwrites if supplied
|
||||
if (!string.IsNullOrWhiteSpace(seed.VersionAndEditions.Version)) info.VersionAndEditions.Version = seed.VersionAndEditions.Version;
|
||||
if (!string.IsNullOrWhiteSpace(seed.VersionAndEditions.OtherEditions)) info.VersionAndEditions.OtherEditions = seed.VersionAndEditions.OtherEditions;
|
||||
}
|
||||
|
||||
if (info.CopyProtection != null && seed.CopyProtection != null)
|
||||
{
|
||||
// Info that only overwrites if supplied
|
||||
if (!string.IsNullOrWhiteSpace(seed.CopyProtection.Protection)) info.CopyProtection.Protection = seed.CopyProtection.Protection;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate the current environment is ready for a dump
|
||||
/// </summary>
|
||||
@@ -681,10 +579,10 @@ namespace MPF.Core
|
||||
return Result.Failure("Error! Current configuration is not supported!");
|
||||
|
||||
// Fix the output paths, just in case
|
||||
OutputPath = InfoTool.NormalizeOutputPaths(OutputPath, true);
|
||||
OutputPath = InfoTool.NormalizeOutputPaths(OutputPath, false);
|
||||
|
||||
// Validate that the output path isn't on the dumping drive
|
||||
if (Drive != null && OutputPath[0] == Drive.Letter)
|
||||
if (Drive?.Name != null && OutputPath.StartsWith(Drive.Name))
|
||||
return Result.Failure("Error! Cannot output to same drive that is being dumped!");
|
||||
|
||||
// Validate that the required program exists
|
||||
@@ -693,7 +591,7 @@ namespace MPF.Core
|
||||
|
||||
// Validate that the dumping drive doesn't contain the executable
|
||||
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath);
|
||||
if (Drive != null && fullExecutablePath[0] == Drive.Letter)
|
||||
if (Drive?.Name != null && fullExecutablePath.StartsWith(Drive.Name))
|
||||
return Result.Failure("Error! Cannot dump same drive that executable resides on!");
|
||||
|
||||
// Validate that the current configuration is supported
|
||||
@@ -741,7 +639,7 @@ namespace MPF.Core
|
||||
var parameters = new Modules.DiscImageCreator.Parameters(string.Empty)
|
||||
{
|
||||
BaseCommand = command,
|
||||
DriveLetter = Drive.Letter.ToString(),
|
||||
DrivePath = Drive.Name,
|
||||
ExecutablePath = Options.DiscImageCreatorPath,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,41 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Hashing;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using MPF.Core.Data;
|
||||
|
||||
namespace MPF.Core.Hashing
|
||||
{
|
||||
/// <summary>
|
||||
/// Available hashing types
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Hash
|
||||
public sealed class Hasher : IDisposable
|
||||
{
|
||||
CRC32 = 1 << 0,
|
||||
MD5 = 1 << 1,
|
||||
SHA1 = 1 << 2,
|
||||
SHA256 = 1 << 3,
|
||||
SHA384 = 1 << 4,
|
||||
SHA512 = 1 << 5,
|
||||
#region Properties
|
||||
|
||||
// Special combinations
|
||||
Standard = CRC32 | MD5 | SHA1,
|
||||
All = CRC32 | MD5 | SHA1 | SHA256 | SHA384 | SHA512,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Async hashing class wraper
|
||||
/// </summary>
|
||||
public class Hasher
|
||||
{
|
||||
/// <summary>
|
||||
/// Hash type associated with the current state
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Hash HashType { get; private set; }
|
||||
#else
|
||||
public Hash HashType { get; init; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Current hash in bytes
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public byte[] CurrentHashBytes
|
||||
#else
|
||||
public byte[]? CurrentHashBytes
|
||||
#endif
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET48
|
||||
switch (_hasher)
|
||||
{
|
||||
case HashAlgorithm ha:
|
||||
return ha.Hash;
|
||||
case NonCryptographicHashAlgorithm ncha:
|
||||
return ncha.GetCurrentHash().Reverse().ToArray();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
return (_hasher) switch
|
||||
{
|
||||
HashAlgorithm ha => ha.Hash,
|
||||
NonCryptographicHashAlgorithm ncha => ncha.GetCurrentHash().Reverse().ToArray(),
|
||||
_ => null,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current hash as a string
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string CurrentHashString => ByteArrayToString(CurrentHashBytes);
|
||||
#else
|
||||
public string? CurrentHashString => ByteArrayToString(CurrentHashBytes);
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// Internal hasher being used for processing
|
||||
/// </summary>
|
||||
/// <remarks>May be either a HashAlgorithm or NonCryptographicHashAlgorithm</remarks>
|
||||
#if NET48
|
||||
private object _hasher;
|
||||
#else
|
||||
private object? _hasher;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="hashType">Hash type to instantiate</param>
|
||||
public Hasher(Hash hashType)
|
||||
{
|
||||
this.HashType = hashType;
|
||||
@@ -47,57 +96,256 @@ namespace MPF.Core.Hashing
|
||||
/// </summary>
|
||||
private void GetHasher()
|
||||
{
|
||||
#if NET48
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC32:
|
||||
_hasher = new Crc32();
|
||||
break;
|
||||
|
||||
case Hash.CRC64:
|
||||
_hasher = new Crc64();
|
||||
break;
|
||||
case Hash.MD5:
|
||||
_hasher = MD5.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA1:
|
||||
_hasher = SHA1.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA256:
|
||||
_hasher = SHA256.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA384:
|
||||
_hasher = SHA384.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA512:
|
||||
_hasher = SHA512.Create();
|
||||
break;
|
||||
case Hash.XxHash32:
|
||||
_hasher = new XxHash32();
|
||||
break;
|
||||
case Hash.XxHash64:
|
||||
_hasher = new XxHash64();
|
||||
break;
|
||||
}
|
||||
#else
|
||||
_hasher = HashType switch
|
||||
{
|
||||
Hash.CRC32 => new Crc32(),
|
||||
Hash.CRC64 => new Crc64(),
|
||||
Hash.MD5 => MD5.Create(),
|
||||
Hash.SHA1 => SHA1.Create(),
|
||||
Hash.SHA256 => SHA256.Create(),
|
||||
Hash.SHA384 => SHA384.Create(),
|
||||
Hash.SHA512 => SHA512.Create(),
|
||||
Hash.XxHash32 => new XxHash32(),
|
||||
Hash.XxHash64 => new XxHash64(),
|
||||
_ => null,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_hasher is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Hashing
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input file path
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <returns>True if hashing was successful, false otherwise</returns>
|
||||
#if NET48
|
||||
public static bool GetFileHashes(string filename, out long size, out string crc32, out string md5, out string sha1)
|
||||
#else
|
||||
public static bool GetFileHashes(string filename, out long size, out string? crc32, out string? md5, out string? sha1)
|
||||
#endif
|
||||
{
|
||||
// Set all initial values
|
||||
crc32 = null; md5 = null; sha1 = null;
|
||||
|
||||
// Get all file hashes
|
||||
var fileHashes = GetFileHashes(filename, out size);
|
||||
if (fileHashes == null)
|
||||
return false;
|
||||
|
||||
// Assign the file hashes and return
|
||||
crc32 = fileHashes[Hash.CRC32];
|
||||
md5 = fileHashes[Hash.MD5];
|
||||
sha1 = fileHashes[Hash.SHA1];
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input file path
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
#if NET48
|
||||
public static Dictionary<Hash, string> GetFileHashes(string filename, out long size)
|
||||
#else
|
||||
public static Dictionary<Hash, string?>? GetFileHashes(string filename, out long size)
|
||||
#endif
|
||||
{
|
||||
// If the file doesn't exist, we can't do anything
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
size = -1;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set the file size
|
||||
size = new FileInfo(filename).Length;
|
||||
|
||||
// Create the output dictionary
|
||||
#if NET48
|
||||
var hashDict = new Dictionary<Hash, string>();
|
||||
#else
|
||||
var hashDict = new Dictionary<Hash, string?>();
|
||||
#endif
|
||||
|
||||
// Open the input file
|
||||
var input = File.OpenRead(filename);
|
||||
|
||||
// Return the hashes from the stream
|
||||
return GetStreamHashes(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
#if NET48
|
||||
public static Dictionary<Hash, string> GetStreamHashes(Stream input)
|
||||
#else
|
||||
public static Dictionary<Hash, string?>? GetStreamHashes(Stream input)
|
||||
#endif
|
||||
{
|
||||
// Create the output dictionary
|
||||
#if NET48
|
||||
var hashDict = new Dictionary<Hash, string>();
|
||||
#else
|
||||
var hashDict = new Dictionary<Hash, string?>();
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
// Get a list of hashers to run over the buffer
|
||||
var hashers = new Dictionary<Hash, Hasher>
|
||||
{
|
||||
{ Hash.CRC32, new Hasher(Hash.CRC32) },
|
||||
{ Hash.CRC64, new Hasher(Hash.CRC64) },
|
||||
{ Hash.MD5, new Hasher(Hash.MD5) },
|
||||
{ Hash.SHA1, new Hasher(Hash.SHA1) },
|
||||
{ Hash.SHA256, new Hasher(Hash.SHA256) },
|
||||
{ Hash.SHA384, new Hasher(Hash.SHA384) },
|
||||
{ Hash.SHA512, new Hasher(Hash.SHA512) },
|
||||
{ Hash.XxHash32, new Hasher(Hash.XxHash32) },
|
||||
{ Hash.XxHash64, new Hasher(Hash.XxHash64) },
|
||||
};
|
||||
|
||||
// Initialize the hashing helpers
|
||||
var loadBuffer = new ThreadLoadBuffer(input);
|
||||
int buffersize = 3 * 1024 * 1024;
|
||||
byte[] buffer0 = new byte[buffersize];
|
||||
byte[] buffer1 = new byte[buffersize];
|
||||
|
||||
/*
|
||||
Please note that some of the following code is adapted from
|
||||
RomVault. This is a modified version of how RomVault does
|
||||
threaded hashing. As such, some of the terminology and code
|
||||
is the same, though variable names and comments may have
|
||||
been tweaked to better fit this code base.
|
||||
*/
|
||||
|
||||
// Pre load the first buffer
|
||||
long refsize = input.Length;
|
||||
int next = refsize > buffersize ? buffersize : (int)refsize;
|
||||
input.Read(buffer0, 0, next);
|
||||
int current = next;
|
||||
refsize -= next;
|
||||
bool bufferSelect = true;
|
||||
|
||||
while (current > 0)
|
||||
{
|
||||
// Trigger the buffer load on the second buffer
|
||||
next = refsize > buffersize ? buffersize : (int)refsize;
|
||||
if (next > 0)
|
||||
loadBuffer.Trigger(bufferSelect ? buffer1 : buffer0, next);
|
||||
|
||||
byte[] buffer = bufferSelect ? buffer0 : buffer1;
|
||||
|
||||
// Run hashes in parallel
|
||||
Parallel.ForEach(hashers, h => h.Value.Process(buffer, current));
|
||||
|
||||
// Wait for the load buffer worker, if needed
|
||||
if (next > 0)
|
||||
loadBuffer.Wait();
|
||||
|
||||
// Setup for the next hashing step
|
||||
current = next;
|
||||
refsize -= next;
|
||||
bufferSelect = !bufferSelect;
|
||||
}
|
||||
|
||||
// Finalize all hashing helpers
|
||||
loadBuffer.Finish();
|
||||
Parallel.ForEach(hashers, h => h.Value.Terminate());
|
||||
|
||||
// Get the results
|
||||
hashDict[Hash.CRC32] = hashers[Hash.CRC32].CurrentHashString;
|
||||
hashDict[Hash.CRC64] = hashers[Hash.CRC64].CurrentHashString;
|
||||
hashDict[Hash.MD5] = hashers[Hash.MD5].CurrentHashString;
|
||||
hashDict[Hash.SHA1] = hashers[Hash.SHA1].CurrentHashString;
|
||||
hashDict[Hash.SHA256] = hashers[Hash.SHA256].CurrentHashString;
|
||||
hashDict[Hash.SHA384] = hashers[Hash.SHA384].CurrentHashString;
|
||||
hashDict[Hash.SHA512] = hashers[Hash.SHA512].CurrentHashString;
|
||||
hashDict[Hash.XxHash32] = hashers[Hash.XxHash32].CurrentHashString;
|
||||
hashDict[Hash.XxHash64] = hashers[Hash.XxHash64].CurrentHashString;
|
||||
hashDict[Hash.CRC64] = hashers[Hash.CRC64].CurrentHashString;
|
||||
|
||||
// Dispose of the hashers
|
||||
loadBuffer.Dispose();
|
||||
foreach (var hasher in hashers.Values)
|
||||
{
|
||||
hasher.Dispose();
|
||||
}
|
||||
|
||||
return hashDict;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
input.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hashing
|
||||
|
||||
/// <summary>
|
||||
/// Process a buffer of some length with the internal hash algorithm
|
||||
/// </summary>
|
||||
public void Process(byte[] buffer, int size)
|
||||
{
|
||||
switch (HashType)
|
||||
switch (_hasher)
|
||||
{
|
||||
case Hash.CRC32:
|
||||
(_hasher as NonCryptographicHashAlgorithm)?.Append(buffer);
|
||||
case HashAlgorithm ha:
|
||||
ha.TransformBlock(buffer, 0, size, null, 0);
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
(_hasher as HashAlgorithm)?.TransformBlock(buffer, 0, size, null, 0);
|
||||
case NonCryptographicHashAlgorithm ncha:
|
||||
var bufferSpan = new ReadOnlySpan<byte>(buffer, 0, size);
|
||||
ncha.Append(bufferSpan);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -105,68 +353,21 @@ namespace MPF.Core.Hashing
|
||||
/// <summary>
|
||||
/// Finalize the internal hash algorigthm
|
||||
/// </summary>
|
||||
/// <remarks>NonCryptographicHashAlgorithm implementations do not need finalization</remarks>
|
||||
public void Terminate()
|
||||
{
|
||||
byte[] emptyBuffer = new byte[0];
|
||||
switch (HashType)
|
||||
byte[] emptyBuffer = Array.Empty<byte>();
|
||||
switch (_hasher)
|
||||
{
|
||||
case Hash.CRC32:
|
||||
(_hasher as NonCryptographicHashAlgorithm)?.Append(emptyBuffer);
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
(_hasher as HashAlgorithm)?.TransformFinalBlock(emptyBuffer, 0, 0);
|
||||
case HashAlgorithm ha:
|
||||
ha.TransformFinalBlock(emptyBuffer, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get internal hash as a byte array
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public byte[] GetHash()
|
||||
#else
|
||||
public byte[]? GetHash()
|
||||
#endif
|
||||
{
|
||||
if (_hasher == null)
|
||||
return null;
|
||||
#endregion
|
||||
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC32:
|
||||
return (_hasher as NonCryptographicHashAlgorithm)?.GetCurrentHash();
|
||||
|
||||
case Hash.MD5:
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
return (_hasher as HashAlgorithm)?.Hash;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get internal hash as a string
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string GetHashString()
|
||||
#else
|
||||
public string? GetHashString()
|
||||
#endif
|
||||
{
|
||||
var hash = GetHash();
|
||||
if (hash == null)
|
||||
return null;
|
||||
|
||||
return ByteArrayToString(hash);
|
||||
}
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Convert a byte array to a hex string
|
||||
@@ -194,5 +395,7 @@ namespace MPF.Core.Hashing
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
2985
MPF.Core/InfoTool.cs
2985
MPF.Core/InfoTool.cs
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>2.7.2</VersionPrefix>
|
||||
<VersionPrefix>2.7.4</VersionPrefix>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
|
||||
@@ -17,14 +17,15 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.9.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0-preview.2" />
|
||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0-preview.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.1.4" />
|
||||
<PackageReference Include="psxt001z" Version="0.21.0-beta1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.1.5" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.1.6" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.1.7" />
|
||||
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="7.0.0" />
|
||||
|
||||
@@ -264,8 +264,12 @@ namespace MPF.Core.Modules.Aaru
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, driveLetter, filename, driveSpeed, options)
|
||||
#if NET48
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
: base(system, type, drivePath, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -334,7 +338,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
var outputDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
// Ensure that required sections exist
|
||||
info = InfoTool.EnsureAllSections(info);
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
|
||||
// TODO: Determine if there's an Aaru version anywhere
|
||||
#if NET48
|
||||
@@ -342,7 +346,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
#else
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
#endif
|
||||
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + ".cicm.xml")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + ".cicm.xml")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// Deserialize the sidecar, if possible
|
||||
var sidecar = GenerateSidecar(basePath + ".cicm.xml");
|
||||
@@ -374,9 +378,9 @@ namespace MPF.Core.Modules.Aaru
|
||||
|
||||
// Fill in the hash data
|
||||
#if NET48
|
||||
info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(datafile);
|
||||
info.TracksAndWriteOffsets.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
#else
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = GenerateDatfile(datafile);
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
#endif
|
||||
|
||||
switch (this.Type)
|
||||
@@ -409,7 +413,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
case MediaType.BluRay:
|
||||
|
||||
// Get the individual hash data, as per internal
|
||||
if (GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
|
||||
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
#if NET48
|
||||
info.SizeAndChecksums.Size = size;
|
||||
@@ -480,7 +484,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiPython2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
#if NET48
|
||||
@@ -493,9 +497,9 @@ namespace MPF.Core.Modules.Aaru
|
||||
}
|
||||
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
@@ -563,7 +567,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
#if NET48
|
||||
@@ -578,7 +582,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
#if NET48
|
||||
@@ -591,39 +595,39 @@ namespace MPF.Core.Modules.Aaru
|
||||
}
|
||||
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -1778,11 +1782,15 @@ namespace MPF.Core.Modules.Aaru
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
#if NET48
|
||||
protected override void SetDefaultParameters(string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
protected override void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
{
|
||||
BaseCommand = $"{CommandStrings.MediaPrefixLong} {CommandStrings.MediaDump}";
|
||||
|
||||
InputValue = $"{driveLetter}:";
|
||||
InputValue = drivePath;
|
||||
OutputValue = filename;
|
||||
|
||||
if (driveSpeed != null)
|
||||
@@ -1870,7 +1878,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
// Now split the string into parts for easier validation
|
||||
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
|
||||
parameters = parameters.Trim();
|
||||
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+")
|
||||
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Value)
|
||||
.ToList();
|
||||
|
||||
@@ -2,17 +2,10 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Hashing;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.Models.PIC;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Modules
|
||||
@@ -168,15 +161,19 @@ namespace MPF.Core.Modules
|
||||
/// </summary>
|
||||
/// <param name="system">RedumpSystem value to use</param>
|
||||
/// <param name="type">MediaType value to use</param>
|
||||
/// <param name="driveLetter">Drive letter to use</param>
|
||||
/// <param name="drivePath">Drive path to use</param>
|
||||
/// <param name="filename">Filename to use</param>
|
||||
/// <param name="driveSpeed">Drive speed to use</param>
|
||||
/// <param name="options">Options object containing all settings that may be used for setting parameters</param>
|
||||
public BaseParameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
#if NET48
|
||||
public BaseParameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
public BaseParameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
{
|
||||
this.System = system;
|
||||
this.Type = type;
|
||||
SetDefaultParameters(driveLetter, filename, driveSpeed, options);
|
||||
SetDefaultParameters(drivePath, filename, driveSpeed, options);
|
||||
}
|
||||
|
||||
#region Abstract Methods
|
||||
@@ -238,6 +235,17 @@ namespace MPF.Core.Modules
|
||||
public virtual string? GetDefaultExtension(MediaType? mediaType) => null;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all deleteable files generated
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <returns>List of all deleteable file paths, empty otherwise</returns>
|
||||
#if NET48
|
||||
public virtual List<string> GetDeleteableFilePaths(string basePath) => new List<string>();
|
||||
#else
|
||||
public virtual List<string> GetDeleteableFilePaths(string basePath) => new();
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all log files generated
|
||||
/// </summary>
|
||||
@@ -291,11 +299,15 @@ namespace MPF.Core.Modules
|
||||
/// <summary>
|
||||
/// Set default parameters for a given system and media type
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use</param>
|
||||
/// <param name="drivePath">Drive path to use</param>
|
||||
/// <param name="filename">Filename to use</param>
|
||||
/// <param name="driveSpeed">Drive speed to use</param>
|
||||
/// <param name="options">Options object containing all settings that may be used for setting parameters</param>
|
||||
protected virtual void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options) { }
|
||||
#if NET48
|
||||
protected virtual void SetDefaultParameters(string drivePath, string filename, int? driveSpeed, Options options) { }
|
||||
#else
|
||||
protected virtual void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Options options) { }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Scan a possible parameter string and populate whatever possible
|
||||
@@ -1186,896 +1198,48 @@ namespace MPF.Core.Modules
|
||||
|
||||
#endregion
|
||||
|
||||
#region Common Information Extraction
|
||||
#region Methods to Move
|
||||
|
||||
/// <summary>
|
||||
/// Generate the proper datfile from the input Datafile, if possible
|
||||
/// Get the hex contents of the PIC file
|
||||
/// </summary>
|
||||
/// <param name="datafile">.dat file location</param>
|
||||
/// <returns>Relevant pieces of the datfile, null on error</returns>
|
||||
/// <param name="picPath">Path to the PIC.bin file associated with the dump</param>
|
||||
/// <param name="trimLength">Number of characters to trim the PIC to, if -1, ignored</param>
|
||||
/// <returns>PIC data as a hex string if possible, null on error</returns>
|
||||
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
|
||||
#if NET48
|
||||
protected static string GenerateDatfile(Datafile datafile)
|
||||
protected static string GetPIC(string picPath, int trimLength = -1)
|
||||
#else
|
||||
protected static string? GenerateDatfile(Datafile? datafile)
|
||||
protected static string? GetPIC(string picPath, int trimLength = -1)
|
||||
#endif
|
||||
{
|
||||
// If we don't have a valid datafile, we can't do anything
|
||||
if (datafile?.Games == null || datafile.Games.Length == 0)
|
||||
return null;
|
||||
|
||||
var roms = datafile.Games[0].Roms;
|
||||
if (roms == null || roms.Length == 0)
|
||||
return null;
|
||||
|
||||
// Otherwise, reconstruct the hash data with only the required info
|
||||
try
|
||||
{
|
||||
string datString = string.Empty;
|
||||
for (int i = 0; i < roms.Length; i++)
|
||||
{
|
||||
var rom = roms[i];
|
||||
datString += $"<rom name=\"{rom.Name}\" size=\"{rom.Size}\" crc=\"{rom.Crc}\" md5=\"{rom.Md5}\" sha1=\"{rom.Sha1}\" />\n";
|
||||
}
|
||||
|
||||
return datString.TrimEnd('\n');
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Datafile from a standard DAT
|
||||
/// </summary>
|
||||
/// <param name="dat">Path to the DAT file to parse</param>
|
||||
/// <returns>Filled Datafile on success, null on error</returns>
|
||||
#if NET48
|
||||
protected static Datafile GetDatafile(string dat)
|
||||
#else
|
||||
protected static Datafile? GetDatafile(string? dat)
|
||||
#endif
|
||||
{
|
||||
// If there's no path, we can't read the file
|
||||
if (string.IsNullOrWhiteSpace(dat))
|
||||
return null;
|
||||
|
||||
// If the file doesn't exist, we can't read it
|
||||
if (!File.Exists(dat))
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(picPath))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Open and read in the XML file
|
||||
XmlReader xtr = XmlReader.Create(dat, new XmlReaderSettings
|
||||
{
|
||||
CheckCharacters = false,
|
||||
DtdProcessing = DtdProcessing.Ignore,
|
||||
IgnoreComments = true,
|
||||
IgnoreWhitespace = true,
|
||||
ValidationFlags = XmlSchemaValidationFlags.None,
|
||||
ValidationType = ValidationType.None,
|
||||
});
|
||||
|
||||
// If the reader is null for some reason, we can't do anything
|
||||
if (xtr == null)
|
||||
var hex = GetFullFile(picPath, true);
|
||||
if (hex == null)
|
||||
return null;
|
||||
|
||||
var serializer = new XmlSerializer(typeof(Datafile));
|
||||
return serializer.Deserialize(xtr) as Datafile;
|
||||
if (trimLength > -1)
|
||||
#if NET48
|
||||
hex = hex.Substring(0, trimLength);
|
||||
#else
|
||||
hex = hex[..trimLength];
|
||||
#endif
|
||||
|
||||
return Regex.Replace(hex, ".{32}", "$0\n", RegexOptions.Compiled);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
// We don't care what the error was right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets disc information from a PIC file
|
||||
/// </summary>
|
||||
/// <param name="pic">Path to a PIC.bin file</param>
|
||||
/// <returns>Filled DiscInformation on success, null on error</returns>
|
||||
/// <remarks>This omits the emergency brake information, if it exists</remarks>
|
||||
#if NET48
|
||||
protected static DiscInformation GetDiscInformation(string pic)
|
||||
#else
|
||||
protected static DiscInformation? GetDiscInformation(string pic)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
return new SabreTools.Serialization.Files.PIC().Deserialize(pic);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input file path
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <returns>True if hashing was successful, false otherwise</returns>
|
||||
#if NET48
|
||||
protected static bool GetFileHashes(string filename, out long size, out string crc32, out string md5, out string sha1)
|
||||
#else
|
||||
protected static bool GetFileHashes(string filename, out long size, out string? crc32, out string? md5, out string? sha1)
|
||||
#endif
|
||||
{
|
||||
// Set all initial values
|
||||
size = -1; crc32 = null; md5 = null; sha1 = null;
|
||||
|
||||
// If the file doesn't exist, we can't do anything
|
||||
if (!File.Exists(filename))
|
||||
return false;
|
||||
|
||||
// Set the file size
|
||||
size = new FileInfo(filename).Length;
|
||||
|
||||
// Open the input file
|
||||
var input = File.OpenRead(filename);
|
||||
|
||||
try
|
||||
{
|
||||
// Get a list of hashers to run over the buffer
|
||||
var hashers = new List<Hasher>
|
||||
{
|
||||
new Hasher(Hash.CRC32),
|
||||
new Hasher(Hash.MD5),
|
||||
new Hasher(Hash.SHA1),
|
||||
new Hasher(Hash.SHA256),
|
||||
new Hasher(Hash.SHA384),
|
||||
new Hasher(Hash.SHA512),
|
||||
};
|
||||
|
||||
// Initialize the hashing helpers
|
||||
var loadBuffer = new ThreadLoadBuffer(input);
|
||||
int buffersize = 3 * 1024 * 1024;
|
||||
byte[] buffer0 = new byte[buffersize];
|
||||
byte[] buffer1 = new byte[buffersize];
|
||||
|
||||
/*
|
||||
Please note that some of the following code is adapted from
|
||||
RomVault. This is a modified version of how RomVault does
|
||||
threaded hashing. As such, some of the terminology and code
|
||||
is the same, though variable names and comments may have
|
||||
been tweaked to better fit this code base.
|
||||
*/
|
||||
|
||||
// Pre load the first buffer
|
||||
long refsize = size;
|
||||
int next = refsize > buffersize ? buffersize : (int)refsize;
|
||||
input.Read(buffer0, 0, next);
|
||||
int current = next;
|
||||
refsize -= next;
|
||||
bool bufferSelect = true;
|
||||
|
||||
while (current > 0)
|
||||
{
|
||||
// Trigger the buffer load on the second buffer
|
||||
next = refsize > buffersize ? buffersize : (int)refsize;
|
||||
if (next > 0)
|
||||
loadBuffer.Trigger(bufferSelect ? buffer1 : buffer0, next);
|
||||
|
||||
byte[] buffer = bufferSelect ? buffer0 : buffer1;
|
||||
|
||||
// Run hashes in parallel
|
||||
Parallel.ForEach(hashers, h => h.Process(buffer, current));
|
||||
|
||||
// Wait for the load buffer worker, if needed
|
||||
if (next > 0)
|
||||
loadBuffer.Wait();
|
||||
|
||||
// Setup for the next hashing step
|
||||
current = next;
|
||||
refsize -= next;
|
||||
bufferSelect = !bufferSelect;
|
||||
}
|
||||
|
||||
// Finalize all hashing helpers
|
||||
loadBuffer.Finish();
|
||||
Parallel.ForEach(hashers, h => h.Terminate());
|
||||
|
||||
// Get the results
|
||||
crc32 = hashers.First(h => h.HashType == Hash.CRC32).GetHashString();
|
||||
md5 = hashers.First(h => h.HashType == Hash.MD5).GetHashString();
|
||||
sha1 = hashers.First(h => h.HashType == Hash.SHA1).GetHashString();
|
||||
//sha256 = hashers.First(h => h.HashType == Hash.SHA256).GetHashString();
|
||||
//sha384 = hashers.First(h => h.HashType == Hash.SHA384).GetHashString();
|
||||
//sha512 = hashers.First(h => h.HashType == Hash.SHA512).GetHashString();
|
||||
|
||||
// Dispose of the hashers
|
||||
loadBuffer.Dispose();
|
||||
hashers.ForEach(h => h.Dispose());
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
input.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the last modified date from a file path, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <returns>Filled DateTime on success, null on failure</returns>
|
||||
#if NET48
|
||||
protected static DateTime? GetFileModifiedDate(string filename, bool fallback = false)
|
||||
#else
|
||||
protected static DateTime? GetFileModifiedDate(string? filename, bool fallback = false)
|
||||
#endif
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filename))
|
||||
return fallback ? (DateTime?)DateTime.UtcNow : null;
|
||||
else if (!File.Exists(filename))
|
||||
return fallback ? (DateTime?)DateTime.UtcNow : null;
|
||||
|
||||
var fi = new FileInfo(filename);
|
||||
return fi.LastWriteTimeUtc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the split values for ISO-based media
|
||||
/// </summary>
|
||||
/// <param name="hashData">String representing the combined hash data</param>
|
||||
/// <returns>True if extraction was successful, false otherwise</returns>
|
||||
#if NET48
|
||||
protected static bool GetISOHashValues(string hashData, out long size, out string crc32, out string md5, out string sha1)
|
||||
#else
|
||||
protected static bool GetISOHashValues(string? hashData, out long size, out string? crc32, out string? md5, out string? sha1)
|
||||
#endif
|
||||
{
|
||||
size = -1; crc32 = null; md5 = null; sha1 = null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(hashData))
|
||||
return false;
|
||||
|
||||
// TODO: Use deserialization to Rom instead of Regex
|
||||
|
||||
var hashreg = new Regex(@"<rom name="".*?"" size=""(.*?)"" crc=""(.*?)"" md5=""(.*?)"" sha1=""(.*?)""");
|
||||
Match m = hashreg.Match(hashData);
|
||||
if (m.Success)
|
||||
{
|
||||
_ = Int64.TryParse(m.Groups[1].Value, out size);
|
||||
crc32 = m.Groups[2].Value;
|
||||
md5 = m.Groups[3].Value;
|
||||
sha1 = m.Groups[4].Value;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the split values for ISO-based media
|
||||
/// </summary>
|
||||
/// <param name="datafile">Datafile represenging the hash data</param>
|
||||
/// <returns>True if extraction was successful, false otherwise</returns>
|
||||
#if NET48
|
||||
protected static bool GetISOHashValues(Datafile datafile, out long size, out string crc32, out string md5, out string sha1)
|
||||
#else
|
||||
protected static bool GetISOHashValues(Datafile? datafile, out long size, out string? crc32, out string? md5, out string? sha1)
|
||||
#endif
|
||||
{
|
||||
size = -1; crc32 = null; md5 = null; sha1 = null;
|
||||
|
||||
if (datafile?.Games == null || datafile.Games.Length == 0)
|
||||
return false;
|
||||
|
||||
var roms = datafile.Games[0].Roms;
|
||||
if (roms == null || roms.Length == 0)
|
||||
return false;
|
||||
|
||||
var rom = roms[0];
|
||||
|
||||
_ = Int64.TryParse(rom.Size, out size);
|
||||
crc32 = rom.Crc;
|
||||
md5 = rom.Md5;
|
||||
sha1 = rom.Sha1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the layerbreak info associated from the disc information
|
||||
/// </summary>
|
||||
/// <param name="di">Disc information containing unformatted data</param>
|
||||
/// <returns>True if layerbreak info was set, false otherwise</returns>
|
||||
#if NET48
|
||||
protected static bool GetLayerbreaks(DiscInformation di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)
|
||||
#else
|
||||
protected static bool GetLayerbreaks(DiscInformation? di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)
|
||||
#endif
|
||||
{
|
||||
// Set the default values
|
||||
layerbreak1 = null; layerbreak2 = null; layerbreak3 = null;
|
||||
|
||||
// If we don't have valid disc information, we can't do anything
|
||||
if (di?.Units == null || di.Units.Length <= 1)
|
||||
return false;
|
||||
|
||||
#if NET48
|
||||
int ReadFromArrayBigEndian(byte[] bytes, int offset)
|
||||
#else
|
||||
static int ReadFromArrayBigEndian(byte[]? bytes, int offset)
|
||||
#endif
|
||||
{
|
||||
if (bytes == null)
|
||||
return default;
|
||||
|
||||
var span = new ReadOnlySpan<byte>(bytes, offset, 0x04);
|
||||
byte[] rev = span.ToArray();
|
||||
Array.Reverse(rev);
|
||||
return BitConverter.ToInt32(rev, 0);
|
||||
}
|
||||
|
||||
// Layerbreak 1 (2+ layers)
|
||||
if (di.Units.Length >= 2)
|
||||
{
|
||||
long offset = ReadFromArrayBigEndian(di.Units[0]?.Body?.FormatDependentContents, 0x0C);
|
||||
long value = ReadFromArrayBigEndian(di.Units[0]?.Body?.FormatDependentContents, 0x10);
|
||||
layerbreak1 = value - offset + 2;
|
||||
}
|
||||
|
||||
// Layerbreak 2 (3+ layers)
|
||||
if (di.Units.Length >= 3)
|
||||
{
|
||||
long offset = ReadFromArrayBigEndian(di.Units[1]?.Body?.FormatDependentContents, 0x0C);
|
||||
long value = ReadFromArrayBigEndian(di.Units[1]?.Body?.FormatDependentContents, 0x10);
|
||||
layerbreak2 = layerbreak1 + value - offset + 2;
|
||||
}
|
||||
|
||||
// Layerbreak 3 (4 layers)
|
||||
if (di.Units.Length >= 4)
|
||||
{
|
||||
long offset = ReadFromArrayBigEndian(di.Units[2]?.Body?.FormatDependentContents, 0x0C);
|
||||
long value = ReadFromArrayBigEndian(di.Units[2]?.Body?.FormatDependentContents, 0x10);
|
||||
layerbreak3 = layerbreak2 + value - offset + 2;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the PIC identifier from the first disc information unit, if possible
|
||||
/// </summary>
|
||||
/// <param name="di">Disc information containing the data</param>
|
||||
/// <returns>String representing the PIC identifier, null on error</returns>
|
||||
#if NET48
|
||||
protected static string GetPICIdentifier(DiscInformation di)
|
||||
#else
|
||||
protected static string? GetPICIdentifier(DiscInformation? di)
|
||||
#endif
|
||||
{
|
||||
// If we don't have valid disc information, we can't do anything
|
||||
if (di?.Units == null || di.Units.Length <= 1)
|
||||
return null;
|
||||
|
||||
// We assume the identifier is consistent across all units
|
||||
return di.Units[0]?.Body?.DiscTypeIdentifier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the EXE date from a PlayStation disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <param name="serial">Internal disc serial, if possible</param>
|
||||
/// <param name="region">Output region, if possible</param>
|
||||
/// <param name="date">Output EXE date in "yyyy-mm-dd" format if possible, null on error</param>
|
||||
/// <returns></returns>
|
||||
#if NET48
|
||||
protected static bool GetPlayStationExecutableInfo(char? driveLetter, out string serial, out Region? region, out string date)
|
||||
#else
|
||||
protected static bool GetPlayStationExecutableInfo(char? driveLetter, out string? serial, out Region? region, out string? date)
|
||||
#endif
|
||||
{
|
||||
serial = null; region = null; date = null;
|
||||
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return false;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return false;
|
||||
|
||||
// Get the two paths that we will need to check
|
||||
string psxExePath = Path.Combine(drivePath, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
|
||||
|
||||
// Try both of the common paths that contain information
|
||||
#if NET48
|
||||
string exeName = null;
|
||||
#else
|
||||
string? exeName = null;
|
||||
#endif
|
||||
|
||||
// Read the CNF file as an INI file
|
||||
var systemCnf = new IniFile(systemCnfPath);
|
||||
string bootValue = string.Empty;
|
||||
|
||||
// PlayStation uses "BOOT" as the key
|
||||
if (systemCnf.ContainsKey("BOOT"))
|
||||
bootValue = systemCnf["BOOT"];
|
||||
|
||||
// PlayStation 2 uses "BOOT2" as the key
|
||||
if (systemCnf.ContainsKey("BOOT2"))
|
||||
bootValue = systemCnf["BOOT2"];
|
||||
|
||||
// If we had any boot value, parse it and get the executable name
|
||||
if (!string.IsNullOrEmpty(bootValue))
|
||||
{
|
||||
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)");
|
||||
if (match.Groups.Count > 1)
|
||||
{
|
||||
// EXE name may have a trailing `;` after
|
||||
// EXE name should always be in all caps
|
||||
exeName = match.Groups[1].Value
|
||||
.Split(';')[0]
|
||||
.ToUpperInvariant();
|
||||
|
||||
// Serial is most of the EXE name normalized
|
||||
serial = exeName
|
||||
.Replace('_', '-')
|
||||
.Replace(".", string.Empty);
|
||||
|
||||
// Some games may have the EXE in a subfolder
|
||||
serial = Path.GetFileName(serial);
|
||||
}
|
||||
}
|
||||
|
||||
// If the SYSTEM.CNF value can't be found, try PSX.EXE
|
||||
if (string.IsNullOrWhiteSpace(exeName) && File.Exists(psxExePath))
|
||||
exeName = "PSX.EXE";
|
||||
|
||||
// If neither can be found, we return false
|
||||
if (string.IsNullOrWhiteSpace(exeName))
|
||||
return false;
|
||||
|
||||
// Get the region, if possible
|
||||
region = GetPlayStationRegion(exeName);
|
||||
|
||||
// Now that we have the EXE name, try to get the fileinfo for it
|
||||
string exePath = Path.Combine(drivePath, exeName);
|
||||
if (!File.Exists(exePath))
|
||||
return false;
|
||||
|
||||
// Fix the Y2K timestamp issue
|
||||
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>
|
||||
/// Get the version from a PlayStation 2 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
#if NET48
|
||||
protected static string GetPlayStation2Version(char? driveLetter)
|
||||
#else
|
||||
protected static string? GetPlayStation2Version(char? driveLetter)
|
||||
#endif
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// Get the SYSTEM.CNF path to check
|
||||
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
|
||||
|
||||
// Try to parse the SYSTEM.CNF file
|
||||
var systemCnf = new IniFile(systemCnfPath);
|
||||
if (systemCnf.ContainsKey("VER"))
|
||||
return systemCnf["VER"];
|
||||
|
||||
// If "VER" can't be found, we can't do much
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
#if NET48
|
||||
protected static string GetPlayStation3Serial(char? driveLetter)
|
||||
#else
|
||||
protected static string? GetPlayStation3Serial(char? driveLetter)
|
||||
#endif
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find PARAM.SFO, we don't have a PlayStation 3 disc
|
||||
string paramSfoPath = Path.Combine(drivePath, "PS3_GAME", "PARAM.SFO");
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading PARAM.SFO to find the serial at the end of the file
|
||||
try
|
||||
{
|
||||
using (var br = new BinaryReader(File.OpenRead(paramSfoPath)))
|
||||
{
|
||||
br.BaseStream.Seek(-0x18, SeekOrigin.End);
|
||||
return new string(br.ReadChars(9));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
#if NET48
|
||||
protected static string GetPlayStation3Version(char? driveLetter)
|
||||
#else
|
||||
protected static string? GetPlayStation3Version(char? driveLetter)
|
||||
#endif
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find PARAM.SFO, we don't have a PlayStation 3 disc
|
||||
string paramSfoPath = Path.Combine(drivePath, "PS3_GAME", "PARAM.SFO");
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading PARAM.SFO to find the version at the end of the file
|
||||
try
|
||||
{
|
||||
using (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 4 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
#if NET48
|
||||
protected static string GetPlayStation4Serial(char? driveLetter)
|
||||
#else
|
||||
protected static string? GetPlayStation4Serial(char? driveLetter)
|
||||
#endif
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find param.sfo, we don't have a PlayStation 4 disc
|
||||
string paramSfoPath = Path.Combine(drivePath, "bd", "param.sfo");
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.sfo to find the serial at the end of the file
|
||||
try
|
||||
{
|
||||
using (var br = new BinaryReader(File.OpenRead(paramSfoPath)))
|
||||
{
|
||||
br.BaseStream.Seek(-0x14, SeekOrigin.End);
|
||||
return new string(br.ReadChars(9));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 4 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
#if NET48
|
||||
protected static string GetPlayStation4Version(char? driveLetter)
|
||||
#else
|
||||
protected static string? GetPlayStation4Version(char? driveLetter)
|
||||
#endif
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find param.sfo, we don't have a PlayStation 4 disc
|
||||
string paramSfoPath = Path.Combine(drivePath, "bd", "param.sfo");
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.sfo to find the version at the end of the file
|
||||
try
|
||||
{
|
||||
using (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="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
#if NET48
|
||||
protected static string GetPlayStation5Serial(char? driveLetter)
|
||||
#else
|
||||
protected static string? GetPlayStation5Serial(char? driveLetter)
|
||||
#endif
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find param.json, we don't have a PlayStation 5 disc
|
||||
string paramJsonPath = Path.Combine(drivePath, "bd", "param.json");
|
||||
if (!File.Exists(paramJsonPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.json to find the serial in the unencrypted JSON
|
||||
try
|
||||
{
|
||||
using (var br = new BinaryReader(File.OpenRead(paramJsonPath)))
|
||||
{
|
||||
br.BaseStream.Seek(0x82E, SeekOrigin.Begin);
|
||||
return new string(br.ReadChars(9));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 5 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
#if NET48
|
||||
protected static string GetPlayStation5Version(char? driveLetter)
|
||||
#else
|
||||
protected static string? GetPlayStation5Version(char? driveLetter)
|
||||
#endif
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find param.json, we don't have a PlayStation 5 disc
|
||||
string paramJsonPath = Path.Combine(drivePath, "bd", "param.json");
|
||||
if (!File.Exists(paramJsonPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.json to find the version in the unencrypted JSON
|
||||
try
|
||||
{
|
||||
using (var br = new BinaryReader(File.OpenRead(paramJsonPath)))
|
||||
{
|
||||
br.BaseStream.Seek(0x89E, SeekOrigin.Begin);
|
||||
return new string(br.ReadChars(5));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Category Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Determine the category based on the UMDImageCreator string
|
||||
/// </summary>
|
||||
/// <param name="region">String representing the category</param>
|
||||
/// <returns>Category, if possible</returns>
|
||||
protected static DiscCategory? GetUMDCategory(string category)
|
||||
{
|
||||
#if NET48
|
||||
switch (category)
|
||||
{
|
||||
case "GAME": return DiscCategory.Games;
|
||||
case "VIDEO": return DiscCategory.Video;
|
||||
case "AUDIO": return DiscCategory.Audio;
|
||||
default: return null;
|
||||
}
|
||||
#else
|
||||
return category switch
|
||||
{
|
||||
"GAME" => DiscCategory.Games,
|
||||
"VIDEO" => DiscCategory.Video,
|
||||
"AUDIO" => DiscCategory.Audio,
|
||||
_ => null,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Determine the region based on the PlayStation serial code
|
||||
/// </summary>
|
||||
/// <param name="serial">PlayStation serial code</param>
|
||||
/// <returns>Region mapped from name, if possible</returns>
|
||||
protected static Region? GetPlayStationRegion(string serial)
|
||||
{
|
||||
// Standardized "S" serials
|
||||
if (serial.StartsWith("S"))
|
||||
{
|
||||
// string publisher = serial[0] + serial[1];
|
||||
// char secondRegion = serial[3];
|
||||
switch (serial[2])
|
||||
{
|
||||
case 'A': return Region.Asia;
|
||||
case 'C': return Region.China;
|
||||
case 'E': return Region.Europe;
|
||||
case 'K': return Region.SouthKorea;
|
||||
case 'U': return Region.UnitedStatesOfAmerica;
|
||||
case 'P':
|
||||
// Region of S_P_ serials may be Japan, Asia, or SouthKorea
|
||||
switch (serial[3])
|
||||
{
|
||||
case 'S':
|
||||
// Check first two digits of S_PS serial
|
||||
#if NET48
|
||||
switch (serial.Substring(5, 2))
|
||||
{
|
||||
case "46": return Region.SouthKorea;
|
||||
case "51": return Region.Asia;
|
||||
case "56": return Region.SouthKorea;
|
||||
case "55": return Region.Asia;
|
||||
default: return Region.Japan;
|
||||
}
|
||||
#else
|
||||
return serial.Substring(5, 2) switch
|
||||
{
|
||||
"46" => Region.SouthKorea,
|
||||
"51" => Region.Asia,
|
||||
"56" => Region.SouthKorea,
|
||||
"55" => Region.Asia,
|
||||
_ => Region.Japan,
|
||||
};
|
||||
#endif
|
||||
case 'M':
|
||||
// Check first three digits of S_PM serial
|
||||
#if NET48
|
||||
switch (serial.Substring(5, 3))
|
||||
{
|
||||
case "645": return Region.SouthKorea;
|
||||
case "675": return Region.SouthKorea;
|
||||
case "885": return Region.SouthKorea;
|
||||
default: return Region.Japan; // Remaining S_PM serials may be Japan or Asia
|
||||
}
|
||||
#else
|
||||
return 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
|
||||
};
|
||||
#endif
|
||||
default: return 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;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -30,8 +30,12 @@ namespace MPF.Core.Modules.CleanRip
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, driveLetter, filename, driveSpeed, options)
|
||||
#if NET48
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
: base(system, type, drivePath, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -72,7 +76,7 @@ namespace MPF.Core.Modules.CleanRip
|
||||
#endif
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = InfoTool.EnsureAllSections(info);
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
|
||||
// TODO: Determine if there's a CleanRip version anywhere
|
||||
#if NET48
|
||||
@@ -80,12 +84,12 @@ namespace MPF.Core.Modules.CleanRip
|
||||
#else
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
#endif
|
||||
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
var datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
|
||||
|
||||
// Get the individual hash data, as per internal
|
||||
if (GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
|
||||
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
#if NET48
|
||||
info.SizeAndChecksums.Size = size;
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.Models.PIC;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Modules.DiscImageCreator
|
||||
@@ -20,9 +19,9 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public override string InputPath => DriveLetter;
|
||||
public override string InputPath => DrivePath;
|
||||
#else
|
||||
public override string? InputPath => DriveLetter;
|
||||
public override string? InputPath => DrivePath;
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -55,9 +54,9 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
/// Drive letter or path to pass to DiscImageCreator
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string DriveLetter { get; set; }
|
||||
public string DrivePath { get; set; }
|
||||
#else
|
||||
public string? DriveLetter { get; set; }
|
||||
public string? DrivePath { get; set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
@@ -194,8 +193,12 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, driveLetter, filename, driveSpeed, options)
|
||||
#if NET48
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
: base(system, type, drivePath, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -417,7 +420,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
var outputDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
// Ensure that required sections exist
|
||||
info = InfoTool.EnsureAllSections(info);
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
|
||||
// Get the dumping program and version
|
||||
var (dicCmd, dicVersion) = GetCommandFilePathAndVersion(basePath);
|
||||
@@ -426,7 +429,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
#else
|
||||
info.DumpingInfo!.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {dicVersion ?? "Unknown Version"}";
|
||||
#endif
|
||||
info.DumpingInfo.DumpingDate = GetFileModifiedDate(dicCmd)?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(dicCmd)?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// Fill in the hardware data
|
||||
if (GetHardwareInfo($"{basePath}_drive.txt", out var manufacturer, out var model, out var firmware))
|
||||
@@ -441,13 +444,13 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
info.DumpingInfo.ReportedDiscType = discTypeOrBookType;
|
||||
|
||||
// Get the Datafile information
|
||||
var datafile = GetDatafile($"{basePath}.dat");
|
||||
var datafile = InfoTool.GetDatafile($"{basePath}.dat");
|
||||
|
||||
// Fill in the hash data
|
||||
#if NET48
|
||||
info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(datafile);
|
||||
info.TracksAndWriteOffsets.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
#else
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = GenerateDatfile(datafile);
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
#endif
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
@@ -509,7 +512,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
case MediaType.BluRay:
|
||||
|
||||
// Get the individual hash data, as per internal
|
||||
if (GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
|
||||
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
#if NET48
|
||||
info.SizeAndChecksums.Size = size;
|
||||
@@ -533,13 +536,13 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
}
|
||||
else if (this.Type == MediaType.BluRay)
|
||||
{
|
||||
var di = GetDiscInformation($"{basePath}_PIC.bin");
|
||||
var di = InfoTool.GetDiscInformation($"{basePath}_PIC.bin");
|
||||
#if NET48
|
||||
info.SizeAndChecksums.PICIdentifier = GetPICIdentifier(di);
|
||||
info.SizeAndChecksums.PICIdentifier = InfoTool.GetPICIdentifier(di);
|
||||
#else
|
||||
info.SizeAndChecksums!.PICIdentifier = GetPICIdentifier(di);
|
||||
info.SizeAndChecksums!.PICIdentifier = InfoTool.GetPICIdentifier(di);
|
||||
#endif
|
||||
if (GetLayerbreaks(di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3))
|
||||
if (InfoTool.GetLayerbreaks(di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3))
|
||||
{
|
||||
if (layerbreak1 != null && layerbreak1 * 2048 < info.SizeAndChecksums.Size)
|
||||
info.SizeAndChecksums.Layerbreak = layerbreak1.Value;
|
||||
@@ -614,7 +617,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiPython2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
#if NET48
|
||||
@@ -627,43 +630,43 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
}
|
||||
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXbox:
|
||||
|
||||
string xgd1XMID;
|
||||
string xmidString;
|
||||
if (string.IsNullOrWhiteSpace(outputDirectory))
|
||||
xgd1XMID = GetXGD1XMID($"{basePath}_DMI.bin");
|
||||
xmidString = GetXGD1XMID($"{basePath}_DMI.bin");
|
||||
else
|
||||
xgd1XMID = GetXGD1XMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
|
||||
xmidString = GetXGD1XMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
|
||||
|
||||
var xgd1Info = new XgdInfo(xgd1XMID);
|
||||
if (xgd1Info?.Initialized == true)
|
||||
var xmid = SabreTools.Serialization.Wrappers.XMID.Create(xmidString);
|
||||
if (xmid != null)
|
||||
{
|
||||
#if NET48
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = xgd1Info.RawXMID ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = xmidString?.TrimEnd('\0') ?? string.Empty;
|
||||
#else
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XMID] = xgd1Info.RawXMID ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XMID] = xmidString?.TrimEnd('\0') ?? string.Empty;
|
||||
#endif
|
||||
info.CommonDiscInfo.Serial = xgd1Info.GetSerial() ?? string.Empty;
|
||||
info.CommonDiscInfo.Serial = xmid.Serial ?? string.Empty;
|
||||
if (!options.EnableRedumpCompatibility)
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = xgd1Info.GetVersion() ?? string.Empty;
|
||||
info.VersionAndEditions.Version = xmid.Version ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = xgd1Info.GetVersion() ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = xmid.Version ?? string.Empty;
|
||||
#endif
|
||||
|
||||
info.CommonDiscInfo.Region = XgdInfo.GetRegion(xgd1Info.XMID?.RegionIdentifier);
|
||||
info.CommonDiscInfo.Region = InfoTool.GetXGDRegion(xmid.Model.RegionIdentifier);
|
||||
}
|
||||
|
||||
// If we have the new, external DAT
|
||||
if (File.Exists($"{basePath}_suppl.dat"))
|
||||
{
|
||||
var suppl = GetDatafile($"{basePath}_suppl.dat");
|
||||
var suppl = InfoTool.GetDatafile($"{basePath}_suppl.dat");
|
||||
if (GetXGDAuxHashInfo(suppl, out var xgd1DMIHash, out var xgd1PFIHash, out var xgd1SSHash))
|
||||
{
|
||||
#if NET48
|
||||
@@ -709,35 +712,35 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXbox360:
|
||||
string xgd23XeMID;
|
||||
string xemidString;
|
||||
if (string.IsNullOrWhiteSpace(outputDirectory))
|
||||
xgd23XeMID = GetXGD23XeMID($"{basePath}_DMI.bin");
|
||||
xemidString = GetXGD23XeMID($"{basePath}_DMI.bin");
|
||||
else
|
||||
xgd23XeMID = GetXGD23XeMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
|
||||
xemidString = GetXGD23XeMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
|
||||
|
||||
var xgd23Info = new XgdInfo(xgd23XeMID);
|
||||
if (xgd23Info?.Initialized == true)
|
||||
var xemid = SabreTools.Serialization.Wrappers.XeMID.Create(xemidString);
|
||||
if (xemid != null)
|
||||
{
|
||||
#if NET48
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = xgd23Info.RawXMID ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = xemidString?.TrimEnd('\0') ?? string.Empty;
|
||||
#else
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XeMID] = xgd23Info.RawXMID ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XeMID] = xemidString?.TrimEnd('\0') ?? string.Empty;
|
||||
#endif
|
||||
info.CommonDiscInfo.Serial = xgd23Info.GetSerial() ?? string.Empty;
|
||||
info.CommonDiscInfo.Serial = xemid.Serial ?? string.Empty;
|
||||
if (!options.EnableRedumpCompatibility)
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = xgd23Info.GetVersion() ?? string.Empty;
|
||||
info.VersionAndEditions.Version = xemid.Version ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = xgd23Info.GetVersion() ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = xemid.Version ?? string.Empty;
|
||||
#endif
|
||||
|
||||
info.CommonDiscInfo.Region = XgdInfo.GetRegion(xgd23Info.XeMID?.RegionIdentifier);
|
||||
info.CommonDiscInfo.Region = InfoTool.GetXGDRegion(xemid.Model.RegionIdentifier);
|
||||
}
|
||||
|
||||
// If we have the new, external DAT
|
||||
if (File.Exists($"{basePath}_suppl.dat"))
|
||||
{
|
||||
var suppl = GetDatafile($"{basePath}_suppl.dat");
|
||||
var suppl = InfoTool.GetDatafile($"{basePath}_suppl.dat");
|
||||
if (GetXGDAuxHashInfo(suppl, out var xgd23DMIHash, out var xgd23PFIHash, out var xgd23SSHash))
|
||||
{
|
||||
#if NET48
|
||||
@@ -978,7 +981,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
#if NET48
|
||||
@@ -1006,7 +1009,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
#if NET48
|
||||
@@ -1019,39 +1022,39 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
}
|
||||
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -1098,7 +1101,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
//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["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")) ?? string.Empty;
|
||||
// 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"))
|
||||
@@ -1161,8 +1164,8 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|| BaseCommand == CommandStrings.XGD2Swap
|
||||
|| BaseCommand == CommandStrings.XGD3Swap)
|
||||
{
|
||||
if (DriveLetter != null)
|
||||
parameters.Add(DriveLetter);
|
||||
if (DrivePath != null)
|
||||
parameters.Add(DrivePath);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
@@ -1848,6 +1851,56 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
public override string? GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetDeleteableFilePaths(string basePath)
|
||||
{
|
||||
var deleteableFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
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");
|
||||
|
||||
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;
|
||||
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (File.Exists($"{basePath}.raw"))
|
||||
deleteableFiles.Add($"{basePath}.raw");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return deleteableFiles;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
{
|
||||
@@ -2031,7 +2084,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
DriveLetter = null;
|
||||
DrivePath = null;
|
||||
DriveSpeed = null;
|
||||
|
||||
Filename = null;
|
||||
@@ -2054,11 +2107,15 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
#if NET48
|
||||
protected override void SetDefaultParameters(string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
protected override void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
{
|
||||
SetBaseCommand(this.System, this.Type);
|
||||
|
||||
DriveLetter = driveLetter.ToString();
|
||||
DrivePath = drivePath;
|
||||
DriveSpeed = driveSpeed;
|
||||
Filename = filename;
|
||||
|
||||
@@ -2201,7 +2258,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// Now split the string into parts for easier validation
|
||||
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
|
||||
parameters = parameters.Trim();
|
||||
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+")
|
||||
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Value)
|
||||
.ToList();
|
||||
@@ -2220,7 +2277,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2252,7 +2309,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2274,7 +2331,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -2285,7 +2342,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2307,7 +2364,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2339,7 +2396,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2361,7 +2418,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2377,7 +2434,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -2388,7 +2445,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -2399,7 +2456,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2415,7 +2472,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2464,7 +2521,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -2475,7 +2532,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2497,7 +2554,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -2508,7 +2565,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -2530,7 +2587,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2569,7 +2626,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -2593,7 +2650,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DriveLetter = parts[1];
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -3117,7 +3174,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// No keys
|
||||
if (line.Contains("No TitleKey"))
|
||||
{
|
||||
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), No TitleKey$");
|
||||
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), No TitleKey$", RegexOptions.Compiled);
|
||||
string matchedFilename = match.Groups[1].Value;
|
||||
if (matchedFilename.EndsWith(";1"))
|
||||
#if NET48
|
||||
@@ -3130,7 +3187,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
}
|
||||
else
|
||||
{
|
||||
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), EncryptedTitleKey: .*?, DecryptedTitleKey: (.*?)$");
|
||||
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), EncryptedTitleKey: .*?, DecryptedTitleKey: (.*?)$", RegexOptions.Compiled);
|
||||
string matchedFilename = match.Groups[1].Value;
|
||||
if (matchedFilename.EndsWith(";1"))
|
||||
#if NET48
|
||||
@@ -3447,7 +3504,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
return null;
|
||||
|
||||
// Create the required regex
|
||||
var trackLengthRegex = new Regex(@"^\s*.*?Track\s*([0-9]{1,2}), LBA\s*[0-9]{1,8} - \s*[0-9]{1,8}, Length\s*([0-9]{1,8})$");
|
||||
var trackLengthRegex = new Regex(@"^\s*.*?Track\s*([0-9]{1,2}), LBA\s*[0-9]{1,8} - \s*[0-9]{1,8}, Length\s*([0-9]{1,8})$", RegexOptions.Compiled);
|
||||
|
||||
// Read in the track length data
|
||||
var trackLengthMapping = new Dictionary<string, string>();
|
||||
@@ -3471,7 +3528,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
return null;
|
||||
|
||||
// Create the required regex
|
||||
var trackSessionRegex = new Regex(@"^\s*Session\s*([0-9]{1,2}),.*?,\s*Track\s*([0-9]{1,2}).*?$");
|
||||
var trackSessionRegex = new Regex(@"^\s*Session\s*([0-9]{1,2}),.*?,\s*Track\s*([0-9]{1,2}).*?$", RegexOptions.Compiled);
|
||||
|
||||
// Read in the track session data
|
||||
var trackSessionMapping = new Dictionary<string, string>();
|
||||
@@ -3575,45 +3632,6 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the hex contents of the PIC file
|
||||
/// </summary>
|
||||
/// <param name="picPath">Path to the PIC.bin file associated with the dump</param>
|
||||
/// <param name="trimLength">Number of characters to trim the PIC to, if -1, ignored</param>
|
||||
/// <returns>PIC data as a hex string if possible, null on error</returns>
|
||||
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
|
||||
#if NET48
|
||||
private static string GetPIC(string picPath, int trimLength = -1)
|
||||
#else
|
||||
private static string? GetPIC(string picPath, int trimLength = -1)
|
||||
#endif
|
||||
{
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(picPath))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var hex = GetFullFile(picPath, true);
|
||||
if (hex == null)
|
||||
return null;
|
||||
|
||||
if (trimLength > -1)
|
||||
#if NET48
|
||||
hex = hex.Substring(0, trimLength);
|
||||
#else
|
||||
hex = hex[..trimLength];
|
||||
#endif
|
||||
|
||||
return Regex.Replace(hex, ".{32}", "$0\n");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the existence of an anti-modchip string from the input file, if possible
|
||||
/// </summary>
|
||||
@@ -4141,7 +4159,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// Set the flag so we don't read duplicate data
|
||||
foundSecuritySectors = true;
|
||||
|
||||
var layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)");
|
||||
var layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)", RegexOptions.Compiled);
|
||||
|
||||
line = sr.ReadLine()?.Trim();
|
||||
if (line == null)
|
||||
@@ -4169,7 +4187,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// Special File Hashes
|
||||
else if (line.StartsWith("<rom"))
|
||||
{
|
||||
if (GetISOHashValues(line, out long _, out var crc32, out _, out _))
|
||||
if (InfoTool.GetISOHashValues(line, out long _, out var crc32, out _, out _))
|
||||
{
|
||||
if (line.Contains("SS.bin"))
|
||||
sshash = crc32?.ToUpperInvariant();
|
||||
@@ -4235,7 +4253,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// Set the flag so we don't read duplicate data
|
||||
foundSecuritySectors = true;
|
||||
|
||||
var layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)");
|
||||
var layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)", RegexOptions.Compiled);
|
||||
|
||||
line = sr.ReadLine()?.Trim();
|
||||
if (line == null)
|
||||
|
||||
@@ -15,14 +15,6 @@ namespace MPF.Core.Modules.Redumper
|
||||
/// <summary>
|
||||
/// Represents a generic set of Redumper parameters
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TODO: Redumper natively supports multiple, chained commands but MPF
|
||||
/// doesn't have an easy way of dealing with that right now. Maybe have
|
||||
/// BaseCommand be a space-deliminated list of the commands?
|
||||
/// TODO: With chained commands, how do we tell what parameters are
|
||||
/// actually supported? Do we go one by one and if it doesn't match
|
||||
/// any, then it's incorrect?
|
||||
/// </remarks>
|
||||
public class Parameters : BaseParameters
|
||||
{
|
||||
#region Generic Dumping Information
|
||||
@@ -217,8 +209,12 @@ namespace MPF.Core.Modules.Redumper
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, driveLetter, filename, driveSpeed, options)
|
||||
#if NET48
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
: base(system, type, drivePath, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -271,7 +267,23 @@ namespace MPF.Core.Modules.Redumper
|
||||
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}.physical") && !File.Exists($"{basePath}.1.physical") && !File.Exists($"{basePath}.2.physical"))
|
||||
if (!File.Exists($"{basePath}.physical") && !File.Exists($"{basePath}.1.physical") && !File.Exists($"{basePath}.2.physical"))
|
||||
missingFiles.Add($"{basePath}.physical");
|
||||
if (!File.Exists($"{basePath}.state"))
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
}
|
||||
|
||||
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}.1.physical") && !File.Exists($"{basePath}.2.physical"))
|
||||
missingFiles.Add($"{basePath}.physical");
|
||||
if (!File.Exists($"{basePath}.state"))
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
@@ -295,7 +307,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
#endif
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = InfoTool.EnsureAllSections(info);
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
|
||||
// Get the dumping program and version
|
||||
#if NET48
|
||||
@@ -303,7 +315,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
#else
|
||||
info.DumpingInfo!.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {GetVersion($"{basePath}.log") ?? "Unknown Version"}";
|
||||
#endif
|
||||
info.DumpingInfo.DumpingDate = GetFileModifiedDate($"{basePath}.log")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate($"{basePath}.log")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// Fill in the hardware data
|
||||
if (GetHardwareInfo($"{basePath}.log", out var manufacturer, out var model, out var firmware))
|
||||
@@ -372,6 +384,8 @@ namespace MPF.Core.Modules.Redumper
|
||||
break;
|
||||
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
#if NET48
|
||||
info.Extras.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD";
|
||||
info.TracksAndWriteOffsets.ClrMameProData = GetDatfile($"{basePath}.log");
|
||||
@@ -381,7 +395,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
#endif
|
||||
|
||||
// Get the individual hash data, as per internal
|
||||
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out var crc32, out var md5, out var sha1))
|
||||
if (InfoTool.GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
#if NET48
|
||||
info.SizeAndChecksums.Size = size;
|
||||
@@ -393,12 +407,57 @@ namespace MPF.Core.Modules.Redumper
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
}
|
||||
|
||||
string layerbreak = GetLayerbreak($"{basePath}.log") ?? string.Empty;
|
||||
// Deal with the layerbreaks
|
||||
if (this.Type == MediaType.DVD)
|
||||
{
|
||||
string layerbreak = GetLayerbreak($"{basePath}.log") ?? string.Empty;
|
||||
#if NET48
|
||||
info.SizeAndChecksums.Layerbreak = !string.IsNullOrEmpty(layerbreak) ? Int64.Parse(layerbreak) : default;
|
||||
info.SizeAndChecksums.Layerbreak = !string.IsNullOrEmpty(layerbreak) ? Int64.Parse(layerbreak) : default;
|
||||
#else
|
||||
info.SizeAndChecksums!.Layerbreak = !string.IsNullOrEmpty(layerbreak) ? Int64.Parse(layerbreak) : default;
|
||||
info.SizeAndChecksums!.Layerbreak = !string.IsNullOrEmpty(layerbreak) ? Int64.Parse(layerbreak) : default;
|
||||
#endif
|
||||
}
|
||||
else if (this.Type == MediaType.BluRay)
|
||||
{
|
||||
var di = InfoTool.GetDiscInformation($"{basePath}.physical");
|
||||
#if NET48
|
||||
info.SizeAndChecksums.PICIdentifier = InfoTool.GetPICIdentifier(di);
|
||||
#else
|
||||
info.SizeAndChecksums!.PICIdentifier = InfoTool.GetPICIdentifier(di);
|
||||
#endif
|
||||
if (InfoTool.GetLayerbreaks(di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3))
|
||||
{
|
||||
if (layerbreak1 != null && layerbreak1 * 2048 < info.SizeAndChecksums.Size)
|
||||
info.SizeAndChecksums.Layerbreak = layerbreak1.Value;
|
||||
|
||||
if (layerbreak2 != null && layerbreak2 * 2048 < info.SizeAndChecksums.Size)
|
||||
info.SizeAndChecksums.Layerbreak2 = layerbreak2.Value;
|
||||
|
||||
if (layerbreak3 != null && layerbreak3 * 2048 < info.SizeAndChecksums.Size)
|
||||
info.SizeAndChecksums.Layerbreak3 = layerbreak3.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Bluray-specific options
|
||||
if (this.Type == MediaType.BluRay)
|
||||
{
|
||||
int trimLength = -1;
|
||||
switch (this.System)
|
||||
{
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
trimLength = 264;
|
||||
break;
|
||||
}
|
||||
|
||||
#if NET48
|
||||
info.Extras.PIC = GetPIC($"{basePath}.physical", trimLength) ?? string.Empty;
|
||||
#else
|
||||
info.Extras!.PIC = GetPIC($"{basePath}.physical", trimLength) ?? string.Empty;
|
||||
#endif
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -426,7 +485,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiPython2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
#if NET48
|
||||
@@ -439,9 +498,9 @@ namespace MPF.Core.Modules.Redumper
|
||||
}
|
||||
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
@@ -512,7 +571,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
#if NET48
|
||||
@@ -536,7 +595,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
#if NET48
|
||||
@@ -549,39 +608,39 @@ namespace MPF.Core.Modules.Redumper
|
||||
}
|
||||
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
#if NET48
|
||||
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
|
||||
#else
|
||||
info.VersionAndEditions!.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -928,6 +987,19 @@ namespace MPF.Core.Modules.Redumper
|
||||
public override string? GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetDeleteableFilePaths(string basePath)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -942,10 +1014,6 @@ namespace MPF.Core.Modules.Redumper
|
||||
logFiles.Add($"{basePath}.fulltoc");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
// if (File.Exists($"{basePath}.scram"))
|
||||
// logFiles.Add($"{basePath}.scram");
|
||||
// if (File.Exists($"{basePath}.scrap"))
|
||||
// logFiles.Add($"{basePath}.scrap");
|
||||
if (File.Exists($"{basePath}.state"))
|
||||
logFiles.Add($"{basePath}.state");
|
||||
if (File.Exists($"{basePath}.subcode"))
|
||||
@@ -957,12 +1025,20 @@ namespace MPF.Core.Modules.Redumper
|
||||
case MediaType.DVD:
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
if (File.Exists($"{basePath}.manufacturer"))
|
||||
logFiles.Add($"{basePath}.manufacturer");
|
||||
if (File.Exists($"{basePath}.1.manufacturer"))
|
||||
logFiles.Add($"{basePath}.1.manufacturer");
|
||||
if (File.Exists($"{basePath}.2.manufacturer"))
|
||||
logFiles.Add($"{basePath}.2.manufacturer");
|
||||
if (File.Exists($"{basePath}.physical"))
|
||||
logFiles.Add($"{basePath}.physical");
|
||||
if (File.Exists($"{basePath}.1.physical"))
|
||||
logFiles.Add($"{basePath}.1.physical");
|
||||
if (File.Exists($"{basePath}.2.physical"))
|
||||
logFiles.Add($"{basePath}.2.physical");
|
||||
if (File.Exists($"{basePath}.state"))
|
||||
logFiles.Add($"{basePath}.state");
|
||||
break;
|
||||
|
||||
case MediaType.HDDVD: // TODO: Confirm that this information outputs
|
||||
case MediaType.BluRay:
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
if (File.Exists($"{basePath}.physical"))
|
||||
logFiles.Add($"{basePath}.physical");
|
||||
if (File.Exists($"{basePath}.1.physical"))
|
||||
@@ -1025,11 +1101,20 @@ namespace MPF.Core.Modules.Redumper
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
#if NET48
|
||||
protected override void SetDefaultParameters(string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
protected override void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
{
|
||||
// If we don't have a CD or DVD, we can't dump using redumper
|
||||
if (this.Type != MediaType.CDROM && this.Type != MediaType.DVD)
|
||||
// If we don't have a CD, DVD, HD-DVD, or BD, we can't dump using redumper
|
||||
if (this.Type != MediaType.CDROM
|
||||
&& this.Type != MediaType.DVD
|
||||
&& this.Type != MediaType.HDDVD
|
||||
&& this.Type != MediaType.BluRay)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
switch (this.Type)
|
||||
@@ -1056,6 +1141,9 @@ namespace MPF.Core.Modules.Redumper
|
||||
case MediaType.DVD:
|
||||
ModeValues = new List<string> { CommandStrings.DVD };
|
||||
break;
|
||||
case MediaType.HDDVD: // TODO: Keep in sync if another command string shows up
|
||||
ModeValues = new List<string> { CommandStrings.DVD };
|
||||
break;
|
||||
case MediaType.BluRay:
|
||||
ModeValues = new List<string> { CommandStrings.BluRay };
|
||||
break;
|
||||
@@ -1065,7 +1153,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
}
|
||||
|
||||
this[FlagStrings.Drive] = true;
|
||||
DriveValue = driveLetter.ToString();
|
||||
DriveValue = drivePath;
|
||||
|
||||
this[FlagStrings.Speed] = true;
|
||||
SpeedValue = driveSpeed;
|
||||
@@ -1124,7 +1212,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
// Now split the string into parts for easier validation
|
||||
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
|
||||
parameters = parameters.Trim();
|
||||
List<string> parts = Regex.Matches(parameters, @"([a-zA-Z\-]*=)?[\""].+?[\""]|[^ ]+")
|
||||
List<string> parts = Regex.Matches(parameters, @"([a-zA-Z\-]*=)?[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Value)
|
||||
.ToList();
|
||||
@@ -1517,7 +1605,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
line = sr.ReadLine()?.Trim();
|
||||
while (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
var match = Regex.Match(line, @"^(.*?): (.*?)$");
|
||||
var match = Regex.Match(line, @"^(.*?): (.*?)$", RegexOptions.Compiled);
|
||||
if (match.Success)
|
||||
{
|
||||
string normalizedKey = match.Groups[2].Value.Replace(':', ' ');
|
||||
@@ -1615,6 +1703,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>Layerbreak if possible, null on error</returns>
|
||||
/// <remarks>TODO: Support multiple layerbreaks when added</remarks>
|
||||
#if NET48
|
||||
private static string GetLayerbreak(string log)
|
||||
#else
|
||||
@@ -2387,9 +2476,9 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// Generate regex
|
||||
// Permissive
|
||||
var regex = new Regex(@"^redumper (v.+) \[.+\]");
|
||||
var regex = new Regex(@"^redumper (v.+) \[.+\]", RegexOptions.Compiled);
|
||||
// Strict
|
||||
//var regex = new Regex(@"^redumper (v\d{4}\.\d{2}\.\d{2}(| build_\d+)) \[.+\]");
|
||||
//var regex = new Regex(@"^redumper (v\d{4}\.\d{2}\.\d{2}(| build_\d+)) \[.+\]", RegexOptions.Compiled);
|
||||
|
||||
// Extract the version string
|
||||
var match = regex.Match(sr.ReadLine()?.Trim() ?? string.Empty);
|
||||
@@ -2431,7 +2520,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// If we find the hardware info line, return each value
|
||||
// drive: <vendor_id> - <product_id> (revision level: <product_revision_level>, vendor specific: <vendor_specific>)
|
||||
var regex = new Regex(@"drive: (.+) - (.+) \(revision level: (.+), vendor specific: (.+)\)");
|
||||
var regex = new Regex(@"drive: (.+) - (.+) \(revision level: (.+), vendor specific: (.+)\)", RegexOptions.Compiled);
|
||||
|
||||
#if NET48
|
||||
string line;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Hashing;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Modules.UmdImageCreator
|
||||
@@ -28,8 +29,12 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, driveLetter, filename, driveSpeed, options)
|
||||
#if NET48
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
: base(system, type, drivePath, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -72,7 +77,7 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
#endif
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = InfoTool.EnsureAllSections(info);
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
|
||||
// TODO: Determine if there's a UMDImageCreator version anywhere
|
||||
#if NET48
|
||||
@@ -80,7 +85,7 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
#else
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
#endif
|
||||
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (this.Type)
|
||||
@@ -92,7 +97,7 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
info.Extras!.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
|
||||
#endif
|
||||
|
||||
if (GetFileHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
|
||||
if (Hasher.GetFileHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
#if NET48
|
||||
info.SizeAndChecksums.Size = filesize;
|
||||
@@ -253,7 +258,7 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
|
||||
umdversion = line.Split(' ')[1];
|
||||
else if (line.StartsWith("pspUmdTypes"))
|
||||
umdcat = GetUMDCategory(line.Split(' ')[1]);
|
||||
umdcat = InfoTool.GetUMDCategory(line.Split(' ')[1]);
|
||||
else if (line.StartsWith("L0 length"))
|
||||
umdlayer = line.Split(' ')[2];
|
||||
else if (line.StartsWith("FileSize:"))
|
||||
|
||||
@@ -4,8 +4,8 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using BinaryObjectScanner;
|
||||
using BinaryObjectScanner.Protection;
|
||||
using BurnOutSharp;
|
||||
using psxt001z;
|
||||
|
||||
namespace MPF.Core
|
||||
@@ -95,8 +95,16 @@ namespace MPF.Core
|
||||
/// </summary>
|
||||
/// <param name="path">Path to scan for anti-modchip strings</param>
|
||||
/// <returns>Anti-modchip existence if possible, false on error</returns>
|
||||
#if NET48
|
||||
public static async Task<bool> GetPlayStationAntiModchipDetected(string path)
|
||||
#else
|
||||
public static async Task<bool> GetPlayStationAntiModchipDetected(string? path)
|
||||
#endif
|
||||
{
|
||||
// If there is no valid path
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return false;
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
@@ -107,7 +115,7 @@ namespace MPF.Core
|
||||
try
|
||||
{
|
||||
byte[] fileContent = File.ReadAllBytes(file);
|
||||
string protection = antiModchip.CheckContents(file, fileContent, false);
|
||||
var protection = antiModchip.CheckContents(file, fileContent, false);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
return true;
|
||||
}
|
||||
@@ -154,7 +162,7 @@ namespace MPF.Core
|
||||
foundProtections = foundProtections.Where(p => p != "ActiveMARK");
|
||||
|
||||
// Cactus Data Shield
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"Cactus Data Shield [0-9]{3} .+")) && foundProtections.Any(p => p == "Cactus Data Shield 200"))
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"Cactus Data Shield [0-9]{3} .+", RegexOptions.Compiled)) && foundProtections.Any(p => p == "Cactus Data Shield 200"))
|
||||
foundProtections = foundProtections.Where(p => p != "Cactus Data Shield 200");
|
||||
|
||||
// CD-Check
|
||||
@@ -184,7 +192,7 @@ namespace MPF.Core
|
||||
// JoWood X-Prot
|
||||
if (foundProtections.Any(p => p.StartsWith("JoWood X-Prot")))
|
||||
{
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"JoWood X-Prot [0-9]\.[0-9]\.[0-9]\.[0-9]{2}")))
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"JoWood X-Prot [0-9]\.[0-9]\.[0-9]\.[0-9]{2}", RegexOptions.Compiled)))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
|
||||
.Where(p => p != "JoWood X-Prot v1.0-v1.3")
|
||||
@@ -233,28 +241,28 @@ namespace MPF.Core
|
||||
// SafeDisc
|
||||
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
|
||||
{
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}")))
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
.Where(p => !p.StartsWith("Macrovision Security Driver"))
|
||||
.Where(p => p != "SafeDisc")
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}")))
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
|
||||
.Where(p => p != "SafeDisc 1/Lite")
|
||||
.Where(p => p != "SafeDisc 2+");
|
||||
}
|
||||
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}")))
|
||||
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
.Where(p => !p.StartsWith("Macrovision Security Driver"))
|
||||
.Where(p => p != "SafeDisc")
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
|
||||
.Where(p => p != "SafeDisc 1/Lite")
|
||||
.Where(p => p != "SafeDisc 2+");
|
||||
}
|
||||
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
|
||||
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
@@ -294,7 +302,7 @@ namespace MPF.Core
|
||||
// StarForce
|
||||
if (foundProtections.Any(p => p.StartsWith("StarForce")))
|
||||
{
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"StarForce [0-9]+\..+")))
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"StarForce [0-9]+\..+", RegexOptions.Compiled)))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "StarForce")
|
||||
.Where(p => p != "StarForce 3-5")
|
||||
|
||||
1924
MPF.Core/SubmissionInfoTool.cs
Normal file
1924
MPF.Core/SubmissionInfoTool.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,11 +4,11 @@ using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BurnOutSharp;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.Core.UI.ComboBoxItems;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.UI.ViewModels
|
||||
@@ -618,19 +618,19 @@ namespace MPF.Core.UI.ViewModels
|
||||
this.UpdateVolumeLabelEnabled = true;
|
||||
|
||||
// If we have a selected drive, keep track of it
|
||||
char? lastSelectedDrive = this.CurrentDrive?.Letter;
|
||||
char? lastSelectedDrive = this.CurrentDrive?.Name?[0] ?? null;
|
||||
|
||||
// Populate the list of drives and add it to the combo box
|
||||
Drives = Drive.CreateListOfDrives(this.Options.IgnoreFixedDrives);
|
||||
|
||||
if (Drives.Count > 0)
|
||||
{
|
||||
VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Letter))}");
|
||||
VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Name))}");
|
||||
|
||||
// Check for the last selected drive, if possible
|
||||
int index = -1;
|
||||
if (lastSelectedDrive != null)
|
||||
index = Drives.FindIndex(d => d.MarkedActive && d.Letter == lastSelectedDrive);
|
||||
index = Drives.FindIndex(d => d.MarkedActive && (d.Name?[0] ?? '\0') == lastSelectedDrive);
|
||||
|
||||
// Check for active optical drives
|
||||
if (index == -1)
|
||||
@@ -965,13 +965,13 @@ namespace MPF.Core.UI.ViewModels
|
||||
|
||||
if (_environment != null && _environment.Options.EjectAfterDump)
|
||||
{
|
||||
VerboseLogLn($"Ejecting disc in drive {_environment.Drive?.Letter}");
|
||||
VerboseLogLn($"Ejecting disc in drive {_environment.Drive?.Name}");
|
||||
await _environment.EjectDisc();
|
||||
}
|
||||
|
||||
if (_environment != null && this.Options.DICResetDriveAfterDump)
|
||||
{
|
||||
VerboseLogLn($"Resetting drive {_environment.Drive?.Letter}");
|
||||
VerboseLogLn($"Resetting drive {_environment.Drive?.Name}");
|
||||
await _environment.ResetDrive();
|
||||
}
|
||||
}
|
||||
@@ -988,11 +988,17 @@ namespace MPF.Core.UI.ViewModels
|
||||
public void UpdateOptions(bool savedSettings, Data.Options? newOptions)
|
||||
#endif
|
||||
{
|
||||
// Get which options to save
|
||||
var optionsToSave = savedSettings ? newOptions : Options;
|
||||
|
||||
// Ensure the first run flag is unset
|
||||
var continuingOptions = new Data.Options(optionsToSave);
|
||||
continuingOptions.FirstRun = false;
|
||||
this.Options = continuingOptions;
|
||||
|
||||
// If settings were changed, reinitialize the UI
|
||||
if (savedSettings)
|
||||
{
|
||||
this.Options = new Data.Options(newOptions);
|
||||
InitializeUIValues(removeEventHandlers: true, rescanDrives: true);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1203,7 +1209,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
// If the drive is marked active, try to read from it
|
||||
else if (this.CurrentDrive.MarkedActive)
|
||||
{
|
||||
VerboseLog($"Trying to detect media type for drive {this.CurrentDrive.Letter} [{this.CurrentDrive.DriveFormat}] using size and filesystem.. ");
|
||||
VerboseLog($"Trying to detect media type for drive {this.CurrentDrive.Name} [{this.CurrentDrive.DriveFormat}] using size and filesystem.. ");
|
||||
(MediaType? detectedMediaType, var errorMessage) = this.CurrentDrive.GetMediaType(this.CurrentSystem);
|
||||
|
||||
// If we got an error message, post it to the log
|
||||
@@ -1263,7 +1269,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
}
|
||||
else if (!this.Options.SkipSystemDetection)
|
||||
{
|
||||
VerboseLog($"Trying to detect system for drive {this.CurrentDrive.Letter}.. ");
|
||||
VerboseLog($"Trying to detect system for drive {this.CurrentDrive.Name}.. ");
|
||||
var currentSystem = this.CurrentDrive?.GetRedumpSystem(this.Options.DefaultSystem) ?? this.Options.DefaultSystem;
|
||||
VerboseLogLn(currentSystem == null ? "unable to detect." : ($"detected {currentSystem.LongName()}."));
|
||||
|
||||
@@ -1431,7 +1437,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
// Catch this in case there's an input path issue
|
||||
try
|
||||
{
|
||||
int driveIndex = Drives.Select(d => d.Letter).ToList().IndexOf(_environment.Parameters?.InputPath?[0] ?? default);
|
||||
int driveIndex = Drives.Select(d => d.Name?[0] ?? '\0').ToList().IndexOf(_environment.Parameters?.InputPath?[0] ?? default);
|
||||
this.CurrentDrive = (driveIndex != -1 ? Drives[driveIndex] : Drives[0]);
|
||||
}
|
||||
catch { }
|
||||
@@ -1445,7 +1451,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
// Disable change handling
|
||||
DisableEventHandlers();
|
||||
|
||||
this.OutputPath = InfoTool.NormalizeOutputPaths(_environment.Parameters?.OutputPath, true);
|
||||
this.OutputPath = InfoTool.NormalizeOutputPaths(_environment.Parameters?.OutputPath, false);
|
||||
|
||||
if (MediaTypes != null)
|
||||
{
|
||||
@@ -1476,10 +1482,10 @@ namespace MPF.Core.UI.ViewModels
|
||||
#endif
|
||||
|
||||
// If we don't have a valid drive
|
||||
if (this.CurrentDrive == null || this.CurrentDrive.Letter == default(char))
|
||||
if (this.CurrentDrive?.Name == null)
|
||||
return (null, "No valid drive found!");
|
||||
|
||||
VerboseLogLn($"Scanning for copy protection in {this.CurrentDrive.Letter}");
|
||||
VerboseLogLn($"Scanning for copy protection in {this.CurrentDrive.Name}");
|
||||
|
||||
var tempContent = this.Status;
|
||||
this.Status = "Scanning for copy protection... this might take a while!";
|
||||
@@ -1490,7 +1496,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
|
||||
var progress = new Progress<ProtectionProgress>();
|
||||
progress.ProgressChanged += ProgressUpdated;
|
||||
var (protections, error) = await Protection.RunProtectionScanOnPath(this.CurrentDrive.Letter + ":\\", this.Options, progress);
|
||||
var (protections, error) = await Protection.RunProtectionScanOnPath(this.CurrentDrive.Name, this.Options, progress);
|
||||
var output = Protection.FormatProtections(protections);
|
||||
|
||||
// If SmartE is detected on the current disc, remove `/sf` from the flags for DIC only -- Disabled until further notice
|
||||
@@ -1502,7 +1508,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
//}
|
||||
|
||||
if (string.IsNullOrEmpty(error))
|
||||
LogLn($"Detected the following protections in {this.CurrentDrive.Letter}:\r\n\r\n{output}");
|
||||
LogLn($"Detected the following protections in {this.CurrentDrive.Name}:\r\n\r\n{output}");
|
||||
else
|
||||
ErrorLogLn($"Path could not be scanned! Exception information:\r\n\r\n{error}");
|
||||
|
||||
@@ -1557,28 +1563,51 @@ namespace MPF.Core.UI.ViewModels
|
||||
VerboseLogLn($"Supported media speeds: {string.Join(", ", this.DriveSpeeds)}");
|
||||
|
||||
// Set the selected speed
|
||||
#if NET48
|
||||
int speed;
|
||||
switch (this.CurrentMediaType)
|
||||
switch (CurrentMediaType)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
case MediaType.GDROM:
|
||||
speed = this.Options.PreferredDumpSpeedCD;
|
||||
speed = Options.PreferredDumpSpeedCD;
|
||||
break;
|
||||
case MediaType.DVD:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
speed = this.Options.PreferredDumpSpeedDVD;
|
||||
speed = Options.PreferredDumpSpeedDVD;
|
||||
break;
|
||||
case MediaType.HDDVD:
|
||||
speed = this.Options.PreferredDumpSpeedHDDVD;
|
||||
speed = Options.PreferredDumpSpeedHDDVD;
|
||||
break;
|
||||
case MediaType.BluRay:
|
||||
speed = this.Options.PreferredDumpSpeedBD;
|
||||
speed = Options.PreferredDumpSpeedBD;
|
||||
break;
|
||||
default:
|
||||
speed = this.Options.PreferredDumpSpeedCD;
|
||||
speed = Options.PreferredDumpSpeedCD;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
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,
|
||||
};
|
||||
#endif
|
||||
|
||||
VerboseLogLn($"Setting drive speed to: {speed}");
|
||||
this.DriveSpeed = speed;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MPF.Core.Data;
|
||||
@@ -7,10 +8,32 @@ using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
public class OptionsViewModel
|
||||
public class OptionsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Title for the window
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string Title
|
||||
#else
|
||||
public string? Title
|
||||
#endif
|
||||
{
|
||||
get => _title;
|
||||
set
|
||||
{
|
||||
_title = value;
|
||||
TriggerPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
#if NET48
|
||||
private string _title;
|
||||
#else
|
||||
private string? _title;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Current set of options
|
||||
/// </summary>
|
||||
@@ -21,7 +44,14 @@ namespace MPF.Core.UI.ViewModels
|
||||
/// </summary>
|
||||
public bool SavedSettings { get; set; }
|
||||
|
||||
#endregion
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
#else
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lists
|
||||
|
||||
@@ -77,5 +107,18 @@ namespace MPF.Core.UI.ViewModels
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Property Updates
|
||||
|
||||
/// <summary>
|
||||
/// Trigger a property changed event
|
||||
/// </summary>
|
||||
private void TriggerPropertyChanged(string propertyName)
|
||||
{
|
||||
// If the property change event is initialized
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,8 +101,10 @@ namespace MPF.Core.Utilities
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
AddFilenameSuffix = false,
|
||||
OutputSubmissionJSON = false,
|
||||
CompressLogFiles = false,
|
||||
DeleteUnnecessaryFiles = false,
|
||||
};
|
||||
|
||||
// Create the submission info to return, if necessary
|
||||
@@ -188,15 +190,21 @@ namespace MPF.Core.Utilities
|
||||
else if (args[startIndex].StartsWith("-l=") || args[startIndex].StartsWith("--load-seed="))
|
||||
{
|
||||
string seedInfo = args[startIndex].Split('=')[1];
|
||||
info = InfoTool.CreateFromFile(seedInfo);
|
||||
info = SubmissionInfoTool.CreateFromFile(seedInfo);
|
||||
}
|
||||
else if (args[startIndex] == "-l" || args[startIndex] == "--load-seed")
|
||||
{
|
||||
string seedInfo = args[startIndex + 1];
|
||||
info = InfoTool.CreateFromFile(seedInfo);
|
||||
info = SubmissionInfoTool.CreateFromFile(seedInfo);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Add filename suffix
|
||||
else if (args[startIndex].Equals("-x") || args[startIndex].Equals("--suffix"))
|
||||
{
|
||||
options.AddFilenameSuffix = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
|
||||
{
|
||||
@@ -209,6 +217,12 @@ namespace MPF.Core.Utilities
|
||||
options.CompressLogFiles = true;
|
||||
}
|
||||
|
||||
// Delete unnecessary files files
|
||||
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
|
||||
{
|
||||
options.DeleteUnnecessaryFiles = true;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
@@ -237,8 +251,10 @@ namespace MPF.Core.Utilities
|
||||
"-s, --scan Enable copy protection scan (requires --path)",
|
||||
"-f, --protect-file Output protection to separate file (requires --scan)",
|
||||
"-l, --load-seed <path> Load a seed submission JSON for user information",
|
||||
"-x, --suffix Enable adding filename suffix",
|
||||
"-j, --json Enable submission JSON output",
|
||||
"-z, --zip Enable log file compression"
|
||||
"-z, --zip Enable log file compression",
|
||||
"-d, --delete Enable unnecessary file deletion",
|
||||
};
|
||||
|
||||
return supportedArguments;
|
||||
|
||||
@@ -164,9 +164,11 @@ namespace MPF.Core.Utilities
|
||||
switch (type)
|
||||
{
|
||||
// Formats considered at least partially dumpable by Redumper
|
||||
case MediaType.BluRay:
|
||||
case MediaType.CDROM:
|
||||
case MediaType.DVD:
|
||||
case MediaType.GDROM:
|
||||
case MediaType.HDDVD:
|
||||
return true;
|
||||
|
||||
// All other formats considered unsupported
|
||||
@@ -200,44 +202,46 @@ namespace MPF.Core.Utilities
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
switch (program)
|
||||
return (program) switch
|
||||
{
|
||||
case InternalProgram.Redumper:
|
||||
return type switch
|
||||
{
|
||||
// Formats considered at least partially supported by Redumper
|
||||
MediaType.CDROM
|
||||
or MediaType.DVD
|
||||
or MediaType.GDROM => true,
|
||||
// Aaru
|
||||
InternalProgram.Aaru when type == MediaType.BluRay => true,
|
||||
InternalProgram.Aaru when type == MediaType.CDROM => true,
|
||||
InternalProgram.Aaru when type == MediaType.CompactFlash => true,
|
||||
InternalProgram.Aaru when type == MediaType.DVD => true,
|
||||
InternalProgram.Aaru when type == MediaType.GDROM => true,
|
||||
InternalProgram.Aaru when type == MediaType.FlashDrive => true,
|
||||
InternalProgram.Aaru when type == MediaType.FloppyDisk => true,
|
||||
InternalProgram.Aaru when type == MediaType.HardDisk => true,
|
||||
InternalProgram.Aaru when type == MediaType.HDDVD => true,
|
||||
InternalProgram.Aaru when type == MediaType.NintendoGameCubeGameDisc => true,
|
||||
InternalProgram.Aaru when type == MediaType.NintendoWiiOpticalDisc => true,
|
||||
InternalProgram.Aaru when type == MediaType.SDCard => true,
|
||||
|
||||
// All other formats considered unsupported
|
||||
_ => false,
|
||||
};
|
||||
case InternalProgram.Aaru:
|
||||
case InternalProgram.DiscImageCreator:
|
||||
return type switch
|
||||
{
|
||||
// Formats considered at least partially supported by MPF
|
||||
MediaType.BluRay
|
||||
or MediaType.CDROM
|
||||
or MediaType.DVD
|
||||
or MediaType.GDROM
|
||||
or MediaType.FloppyDisk
|
||||
or MediaType.CompactFlash
|
||||
or MediaType.SDCard
|
||||
or MediaType.FlashDrive
|
||||
or MediaType.HardDisk
|
||||
or MediaType.HDDVD
|
||||
or MediaType.NintendoGameCubeGameDisc
|
||||
or MediaType.NintendoWiiOpticalDisc => true,
|
||||
// DiscImageCreator
|
||||
InternalProgram.DiscImageCreator when type == MediaType.BluRay => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.CDROM => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.CompactFlash => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.DVD => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.GDROM => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.FlashDrive => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.FloppyDisk => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.HardDisk => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.HDDVD => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.NintendoGameCubeGameDisc => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.NintendoWiiOpticalDisc => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.SDCard => true,
|
||||
|
||||
// All other formats considered unsupported
|
||||
_ => false,
|
||||
};
|
||||
// All other InternalPrograms are not supported for dumping
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// Redumper
|
||||
InternalProgram.Redumper when type == MediaType.BluRay => true,
|
||||
InternalProgram.Redumper when type == MediaType.CDROM => true,
|
||||
InternalProgram.Redumper when type == MediaType.DVD => true,
|
||||
InternalProgram.Redumper when type == MediaType.GDROM => true,
|
||||
InternalProgram.Redumper when type == MediaType.HDDVD => true,
|
||||
|
||||
// Default
|
||||
_ => false,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
using MPF.Core.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Core.Data
|
||||
{
|
||||
public class XgdInfoTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData("1234567")]
|
||||
[InlineData("1234567\0")]
|
||||
[InlineData("123456789")]
|
||||
public void UnmatchedStringTests(string invalidString)
|
||||
{
|
||||
var xgdInfo = new XgdInfo(invalidString);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("AV00100W", "AV", "001", "00", 'W')]
|
||||
[InlineData("AV00100W\0", "AV", "001", "00", 'W')]
|
||||
public void XGD1ValidTests(string validString, string publisher, string gameId, string version, char regionIdentifier)
|
||||
{
|
||||
var xgdInfo = new XgdInfo(validString);
|
||||
|
||||
Assert.True(xgdInfo.Initialized);
|
||||
Assert.NotNull(xgdInfo.XMID);
|
||||
Assert.Equal(publisher, xgdInfo.XMID.PublisherIdentifier);
|
||||
Assert.Equal(gameId, xgdInfo.XMID.GameID);
|
||||
Assert.Equal(version, xgdInfo.XMID.VersionNumber);
|
||||
Assert.Equal(regionIdentifier, xgdInfo.XMID.RegionIdentifier);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Invalid publisher identifier
|
||||
[InlineData("ZZ000000")]
|
||||
[InlineData("ZZ000000\0")]
|
||||
// Invalid region identifier
|
||||
[InlineData("AV00000Z")]
|
||||
[InlineData("AV00000Z\0")]
|
||||
public void XGD1InvalidTests(string invalidString)
|
||||
{
|
||||
var xgdInfo = new XgdInfo(invalidString);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("AV200100W0F11", "AV", "001", "00", 'W', "0", 'F', "11", null)]
|
||||
[InlineData("AV200100W0F11\0", "AV", "001", "00", 'W', "0", 'F', "11", null)]
|
||||
[InlineData("AV200100W01F11", "AV", "001", "00", 'W', "01", 'F', "11", null)]
|
||||
[InlineData("AV200100W01F11\0", "AV", "001", "00", 'W', "01", 'F', "11", null)]
|
||||
[InlineData("AV200100W0F11DEADBEEF", "AV", "001", "00", 'W', "0", 'F', "11", "DEADBEEF")]
|
||||
[InlineData("AV200100W0F11DEADBEEF\0", "AV", "001", "00", 'W', "0", 'F', "11", "DEADBEEF")]
|
||||
[InlineData("AV200100W01F11DEADBEEF", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
|
||||
[InlineData("AV200100W01F11DEADBEEF\0", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
|
||||
public void XGD23ValidTests(string validString, string publisher, string gameId, string sku, char regionIdentifier, string baseVersion, char mediaSubtype, string discNumber, string cert)
|
||||
{
|
||||
var xgdInfo = new XgdInfo(validString);
|
||||
|
||||
Assert.True(xgdInfo.Initialized);
|
||||
Assert.NotNull(xgdInfo.XeMID);
|
||||
Assert.Equal(publisher, xgdInfo.XeMID.PublisherIdentifier);
|
||||
Assert.Equal('2', xgdInfo.XeMID.PlatformIdentifier);
|
||||
Assert.Equal(gameId, xgdInfo.XeMID.GameID);
|
||||
Assert.Equal(sku, xgdInfo.XeMID.SKU);
|
||||
Assert.Equal(regionIdentifier, xgdInfo.XeMID.RegionIdentifier);
|
||||
Assert.Equal(baseVersion, xgdInfo.XeMID.BaseVersion);
|
||||
Assert.Equal(mediaSubtype, xgdInfo.XeMID.MediaSubtypeIdentifier);
|
||||
Assert.Equal(discNumber, xgdInfo.XeMID.DiscNumberIdentifier);
|
||||
Assert.Equal(cert, xgdInfo.XeMID.CertificationSubmissionIdentifier);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Invalid publisher identifier
|
||||
[InlineData("ZZ00000000000")]
|
||||
[InlineData("ZZ00000000000\0")]
|
||||
[InlineData("ZZ000000000000")]
|
||||
[InlineData("ZZ000000000000\0")]
|
||||
[InlineData("ZZ0000000000000000000")]
|
||||
[InlineData("ZZ0000000000000000000\0")]
|
||||
[InlineData("ZZ00000000000000000000")]
|
||||
[InlineData("ZZ00000000000000000000\0")]
|
||||
// Invalid platform identifier
|
||||
[InlineData("AV90000000000")]
|
||||
[InlineData("AV90000000000\0")]
|
||||
[InlineData("AV900000000000")]
|
||||
[InlineData("AV900000000000\0")]
|
||||
[InlineData("AV9000000000000000000")]
|
||||
[InlineData("AV9000000000000000000\0")]
|
||||
[InlineData("AV90000000000000000000")]
|
||||
[InlineData("AV90000000000000000000\0")]
|
||||
// Invalid region identifier
|
||||
[InlineData("AV200000Z0000")]
|
||||
[InlineData("AV200000Z0000\0")]
|
||||
[InlineData("AV200000Z00000")]
|
||||
[InlineData("AV200000Z00000\0")]
|
||||
[InlineData("AV200000Z000000000000")]
|
||||
[InlineData("AV200000Z000000000000\0")]
|
||||
[InlineData("AV200000Z0000000000000")]
|
||||
[InlineData("AV200000Z0000000000000\0")]
|
||||
// Invalid media subtype identifier
|
||||
[InlineData("AV200000W0A00")]
|
||||
[InlineData("AV200000W0A00\0")]
|
||||
[InlineData("AV200000W00A00")]
|
||||
[InlineData("AV200000W00A00\0")]
|
||||
[InlineData("AV200000W00A000000000")]
|
||||
[InlineData("AV200000W00A000000000\0")]
|
||||
[InlineData("AV200000W00A0000000000")]
|
||||
[InlineData("AV200000W00A0000000000\0")]
|
||||
public void XGD23InvalidTests(string invalidString)
|
||||
{
|
||||
var xgdInfo = new XgdInfo(invalidString);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,19 +17,20 @@
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.7.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="psxt001z" Version="0.21.0-beta1" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="xunit" Version="2.5.1" />
|
||||
<PackageReference Include="xunit" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="1.3.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.5.1" />
|
||||
<PackageReference Include="xunit.core" Version="2.5.1" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.5.1" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.5.1" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.5.1">
|
||||
<PackageReference Include="xunit.analyzers" Version="1.4.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.core" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.5.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.1">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromSystemAndTypeTest(RedumpSystem? knownSystem, MediaType? mediaType, string expected)
|
||||
{
|
||||
var options = new Options();
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
Assert.Equal(expected, actual.BaseCommand);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsSpecialDefaultTest(RedumpSystem? knownSystem, MediaType? mediaType,string[] expected)
|
||||
{
|
||||
var options = new Options();
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
@@ -45,7 +45,7 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsC2RereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadC2, string[] expected)
|
||||
{
|
||||
var options = new Options { DICRereadCount = rereadC2 };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
@@ -65,7 +65,7 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsDVDRereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadDVDBD, string[] expected)
|
||||
{
|
||||
var options = new Options { DICDVDRereadCount = rereadDVDBD };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
@@ -89,7 +89,7 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsMultiSectorReadTest(RedumpSystem? knownSystem, MediaType? mediaType, bool multiSectorRead, string[] expected)
|
||||
{
|
||||
var options = new Options { DICMultiSectorRead = multiSectorRead };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
@@ -110,7 +110,7 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsParanoidModeTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoidMode, string[] expected)
|
||||
{
|
||||
var options = new Options { DICParanoidMode = paranoidMode };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using psxt001z;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace WPFCustomMessageBox
|
||||
ShowActivated = true;
|
||||
ShowInTaskbar = true;
|
||||
|
||||
if (owner != null)
|
||||
if (owner != null && owner.IsLoaded)
|
||||
{
|
||||
Owner = owner;
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<UseWPF>true</UseWPF>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>2.7.2</VersionPrefix>
|
||||
<VersionPrefix>2.7.4</VersionPrefix>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
|
||||
|
||||
@@ -45,14 +45,18 @@ namespace MPF.UI.Core.Windows
|
||||
MainViewModel.Init(LogOutput.EnqueueLog, DisplayUserMessage, ShowDiscInformationWindow);
|
||||
|
||||
// Set the UI color scheme according to the options
|
||||
if (MainViewModel.Options.EnableDarkMode)
|
||||
EnableDarkMode();
|
||||
else
|
||||
EnableLightMode();
|
||||
ApplyTheme();
|
||||
|
||||
// Check for updates, if necessary
|
||||
if (MainViewModel.Options.CheckForUpdatesOnStartup)
|
||||
CheckForUpdates(showIfSame: false);
|
||||
|
||||
// Handle first-run, if necessary
|
||||
if (MainViewModel.Options.FirstRun)
|
||||
{
|
||||
// Show the options window
|
||||
ShowOptionsWindow("Welcome to MPF, Explore the Options");
|
||||
}
|
||||
}
|
||||
|
||||
#region UI Functionality
|
||||
@@ -243,7 +247,11 @@ namespace MPF.UI.Core.Windows
|
||||
/// <summary>
|
||||
/// Show the Options window
|
||||
/// </summary>
|
||||
public void ShowOptionsWindow()
|
||||
#if NET48
|
||||
public void ShowOptionsWindow(string title = null)
|
||||
#else
|
||||
public void ShowOptionsWindow(string? title = null)
|
||||
#endif
|
||||
{
|
||||
var optionsWindow = new OptionsWindow(MainViewModel.Options)
|
||||
{
|
||||
@@ -251,27 +259,25 @@ namespace MPF.UI.Core.Windows
|
||||
Owner = this,
|
||||
ShowActivated = true,
|
||||
ShowInTaskbar = true,
|
||||
Title = title ?? "Options",
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
optionsWindow.Closed += OnOptionsUpdated;
|
||||
optionsWindow.Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recolor all UI elements for light mode
|
||||
/// Set the UI color scheme according to the options
|
||||
/// </summary>
|
||||
private static void EnableLightMode()
|
||||
private void ApplyTheme()
|
||||
{
|
||||
var theme = new LightModeTheme();
|
||||
theme.Apply();
|
||||
}
|
||||
Theme theme;
|
||||
if (MainViewModel.Options.EnableDarkMode)
|
||||
theme = new DarkModeTheme();
|
||||
else
|
||||
theme = new LightModeTheme();
|
||||
|
||||
/// <summary>
|
||||
/// Recolor all UI elements for dark mode
|
||||
/// </summary>
|
||||
private static void EnableDarkMode()
|
||||
{
|
||||
var theme = new DarkModeTheme();
|
||||
theme.Apply();
|
||||
}
|
||||
|
||||
@@ -288,9 +294,17 @@ namespace MPF.UI.Core.Windows
|
||||
public void OnOptionsUpdated(object? sender, EventArgs e)
|
||||
#endif
|
||||
{
|
||||
bool savedSettings = (sender as OptionsWindow)?.OptionsViewModel?.SavedSettings ?? false;
|
||||
var options = (sender as OptionsWindow)?.OptionsViewModel?.Options;
|
||||
// Get the options window
|
||||
var optionsWindow = (sender as OptionsWindow);
|
||||
if (optionsWindow?.OptionsViewModel == null)
|
||||
return;
|
||||
|
||||
bool savedSettings = optionsWindow.OptionsViewModel.SavedSettings;
|
||||
var options = optionsWindow.OptionsViewModel.Options;
|
||||
MainViewModel.UpdateOptions(savedSettings, options);
|
||||
|
||||
// Set the UI color scheme according to the options
|
||||
ApplyTheme();
|
||||
}
|
||||
|
||||
#region Menu Bar
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
xmlns:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Options" Width="515.132" WindowStyle="None"
|
||||
Width="515.132" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
@@ -32,7 +32,11 @@
|
||||
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
|
||||
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
|
||||
<Label.Content>
|
||||
<TextBlock TextAlignment="Center"><Bold>Options</Bold></TextBlock>
|
||||
<TextBlock>
|
||||
<TextBlock.Inlines>
|
||||
<Run FontWeight="Bold" Text="{Binding Title, Mode=OneWay}"/>
|
||||
</TextBlock.Inlines>
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
<Label.ContextMenu>
|
||||
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
@@ -68,21 +72,77 @@
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Style="{DynamicResource CustomTabControlStyle}">
|
||||
<TabItem Header="User Interface" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
|
||||
IsChecked="{Binding Options.EnableDarkMode}"
|
||||
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
|
||||
/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Check for Updates on Startup"
|
||||
IsChecked="{Binding Options.CheckForUpdatesOnStartup}"
|
||||
ToolTip="Check for updates when the application starts" Margin="0,4"
|
||||
/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Fast Update Label"
|
||||
IsChecked="{Binding Options.FastUpdateLabel}"
|
||||
ToolTip="Bypasses disc checks to quickly update the output path. Use with caution!" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
<TabItem Header="General" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="User Interface">
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
|
||||
IsChecked="{Binding Options.EnableDarkMode}"
|
||||
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Check for Updates on Startup"
|
||||
IsChecked="{Binding Options.CheckForUpdatesOnStartup}"
|
||||
ToolTip="Check for updates when the application starts" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Fast Update Label"
|
||||
IsChecked="{Binding Options.FastUpdateLabel}"
|
||||
ToolTip="Bypasses disc checks to quickly update the output path. Use with caution!" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Detection">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox x:Name="SkipMediaTypeDetectionCheckBox" VerticalAlignment="Center" Content="Skip Type Detect"
|
||||
IsChecked="{Binding Options.SkipMediaTypeDetection}"
|
||||
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
|
||||
IsChecked="{Binding Options.SkipSystemDetection}"
|
||||
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
|
||||
IsChecked="{Binding Options.IgnoreFixedDrives}"
|
||||
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
|
||||
/>
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
|
||||
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
|
||||
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding Systems}" SelectedItem="{Binding Options.DefaultSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ComboBoxItem}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHeader}" Value="True">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Logging">
|
||||
<UniformGrid Columns="2" Rows="1">
|
||||
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
|
||||
IsChecked="{Binding Options.VerboseLogging}"
|
||||
ToolTip="Display all logging statements" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Auto-Open Log"
|
||||
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
|
||||
ToolTip="Open the log panel when the program launches" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Paths" Style="{DynamicResource CustomTabItemStyle}">
|
||||
@@ -135,47 +195,12 @@
|
||||
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
|
||||
<UniformGrid Columns="2" Rows="9">
|
||||
<CheckBox x:Name="SkipMediaTypeDetectionCheckBox" VerticalAlignment="Center" Content="Skip Type Detect"
|
||||
IsChecked="{Binding Options.SkipMediaTypeDetection}"
|
||||
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
|
||||
IsChecked="{Binding Options.SkipSystemDetection}"
|
||||
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
|
||||
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding Systems}" SelectedItem="{Binding Options.DefaultSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ComboBoxItem}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHeader}" Value="True">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
|
||||
IsChecked="{Binding Options.IgnoreFixedDrives}"
|
||||
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
|
||||
/>
|
||||
|
||||
<UniformGrid Columns="2" Rows="7">
|
||||
<CheckBox VerticalAlignment="Center" Content="Show Separate Window"
|
||||
IsChecked="{Binding Options.ToolsInSeparateWindow}"
|
||||
ToolTip="Show program output in separate command window instead of in the log. Enable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Protection Scan"
|
||||
IsChecked="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Output Protection File"
|
||||
IsChecked="{Binding Options.OutputSeparateProtectionFile}" IsEnabled="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Output protection information to a separate file" Margin="0,4,0,0"
|
||||
@@ -216,6 +241,11 @@
|
||||
ToolTip="Enable adding placeholder text in the output for required and optional fields" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Add Filename Suffix"
|
||||
IsChecked="{Binding Options.AddFilenameSuffix}"
|
||||
ToolTip="Enable appending the output filename to the generated files" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Output Submission JSON"
|
||||
IsChecked="{Binding Options.OutputSubmissionJSON}"
|
||||
ToolTip="Enable outputting a compressed JSON version of the submission info" Margin="0,4"
|
||||
@@ -230,6 +260,35 @@
|
||||
IsChecked="{Binding Options.CompressLogFiles}"
|
||||
ToolTip="Compress output log files to reduce space" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Delete Unnecessary Files"
|
||||
IsChecked="{Binding Options.DeleteUnnecessaryFiles}"
|
||||
ToolTip="Delete unnecesary output files to reduce space" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Protection">
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Protection Scan"
|
||||
IsChecked="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Scan Archive Contents"
|
||||
IsChecked="{Binding Options.ScanArchivesForProtection}"
|
||||
ToolTip="Enable scanning archive contents during protection scanning (may drastically increase scanning time but is more accurate)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Executable Packers"
|
||||
IsChecked="{Binding Options.ScanPackersForProtection}"
|
||||
ToolTip="Include executable packers in outputted protections" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
|
||||
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
|
||||
ToolTip="Include debug information during protection scans" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
@@ -280,153 +339,121 @@
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Protection Scanning" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<TabItem Header="Programs" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Scan Archive Contents"
|
||||
IsChecked="{Binding Options.ScanArchivesForProtection}"
|
||||
ToolTip="Enable scanning archive contents during protection scanning (may drastically increase scanning time but is more accurate)" Margin="0,4"
|
||||
/>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Aaru">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.AaruEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Executable Packers"
|
||||
IsChecked="{Binding Options.ScanPackersForProtection}"
|
||||
ToolTip="Include executable packers in outputted protections" Margin="0,4"
|
||||
/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.AaruEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
|
||||
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
|
||||
ToolTip="Include debug information during protection scans" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
<CheckBox VerticalAlignment="Center" Content="Force Dumping"
|
||||
IsChecked="{Binding Options.AaruForceDumping}"
|
||||
ToolTip="Enable forcing dump even if there are issues" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Strip Personal Data"
|
||||
IsChecked="{Binding Options.AaruStripPersonalData}"
|
||||
ToolTip="Enable stripping of personally identifiable information from metadata" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.AaruRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="DiscImageCreator">
|
||||
<UniformGrid Columns="2" Rows="6">
|
||||
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
|
||||
IsChecked="{Binding Options.DICQuietMode}"
|
||||
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Paranoid Mode"
|
||||
IsChecked="{Binding Options.DICParanoidMode}"
|
||||
ToolTip="Enable pedantic and super-safe flags" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Use CMI Flag"
|
||||
IsChecked="{Binding Options.DICUseCMIFlag}"
|
||||
ToolTip="Enable the CMI flag for supported disc types (DVD/HD-DVD only)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Reset After Dump"
|
||||
IsChecked="{Binding Options.DICResetDriveAfterDump}"
|
||||
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Multi-Sector Read"
|
||||
IsChecked="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Enable the /mr flag for BD drive dumping" Margin="0,4"
|
||||
/>
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
|
||||
<Label Content="Multi-Sector Read Value:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICMultiSectorReadValue}" IsEnabled="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Set the default value for the /mr flag"
|
||||
/>
|
||||
|
||||
<Label Content="CD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on C2 error [CD only]"
|
||||
/>
|
||||
|
||||
<Label Content="DVD/HD-DVD/BD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICDVDRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error [DVD/HD-DVD/BD only]"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redumper">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.RedumperEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.RedumperEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable BE Drive Reading"
|
||||
IsChecked="{Binding Options.RedumperUseBEReading}"
|
||||
ToolTip="Enable setting drive read method to BE_CDDA by default" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Set Generic Drive Type"
|
||||
IsChecked="{Binding Options.RedumperUseGenericDriveType}"
|
||||
ToolTip="Enable setting drive type to Generic by default" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.RedumperRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Aaru" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.AaruEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.AaruEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Force Dumping"
|
||||
IsChecked="{Binding Options.AaruForceDumping}"
|
||||
ToolTip="Enable forcing dump even if there are issues" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Strip Personal Data"
|
||||
IsChecked="{Binding Options.AaruStripPersonalData}"
|
||||
ToolTip="Enable stripping of personally identifiable information from metadata" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.AaruRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="DiscImageCreator" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="6">
|
||||
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
|
||||
IsChecked="{Binding Options.DICQuietMode}"
|
||||
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Paranoid Mode"
|
||||
IsChecked="{Binding Options.DICParanoidMode}"
|
||||
ToolTip="Enable pedantic and super-safe flags" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Use CMI Flag"
|
||||
IsChecked="{Binding Options.DICUseCMIFlag}"
|
||||
ToolTip="Enable the CMI flag for supported disc types (DVD/HD-DVD only)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Reset After Dump"
|
||||
IsChecked="{Binding Options.DICResetDriveAfterDump}"
|
||||
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Multi-Sector Read"
|
||||
IsChecked="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Enable the /mr flag for BD drive dumping" Margin="0,4"
|
||||
/>
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
|
||||
<Label Content="Multi-Sector Read Value:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICMultiSectorReadValue}" IsEnabled="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Set the default value for the /mr flag"
|
||||
/>
|
||||
|
||||
<Label Content="CD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on C2 error [CD only]"
|
||||
/>
|
||||
|
||||
<Label Content="DVD/HD-DVD/BD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICDVDRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error [DVD/HD-DVD/BD only]"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Redumper" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.RedumperEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.RedumperEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable BE Drive Reading"
|
||||
IsChecked="{Binding Options.RedumperUseBEReading}"
|
||||
ToolTip="Enable setting drive read method to BE_CDDA by default" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Set Generic Drive Type"
|
||||
IsChecked="{Binding Options.RedumperUseGenericDriveType}"
|
||||
ToolTip="Enable setting drive type to Generic by default" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.RedumperRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Logging" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
|
||||
IsChecked="{Binding Options.VerboseLogging}"
|
||||
ToolTip="Display all logging statements" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Auto-Open Log"
|
||||
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
|
||||
ToolTip="Open the log panel when the program launches" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Login Info" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump Credentials">
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump">
|
||||
<UniformGrid Columns="5">
|
||||
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Username" />
|
||||
<TextBox x:Name="RedumpUsernameTextBox" Height="22" HorizontalAlignment="Stretch"
|
||||
|
||||
@@ -41,6 +41,17 @@ namespace MPF.UI.Core.Windows
|
||||
RedumpLoginTestButton.Click += OnRedumpTestClick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for OptionsWindow OnContentRendered event
|
||||
/// </summary>
|
||||
protected override void OnContentRendered(EventArgs e)
|
||||
{
|
||||
base.OnContentRendered(e);
|
||||
|
||||
// Set the window title
|
||||
OptionsViewModel.Title = this.Title;
|
||||
}
|
||||
|
||||
#region UI Commands
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<Description>Frontend for various dumping programs</Description>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>2.7.2</VersionPrefix>
|
||||
<VersionPrefix>2.7.4</VersionPrefix>
|
||||
<InternalsVisibleTo>MPF.Test</InternalsVisibleTo>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.9.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# version format
|
||||
version: 2.7.2-{build}
|
||||
version: 2.7.4-{build}
|
||||
|
||||
# pull request template
|
||||
pull_requests:
|
||||
@@ -56,8 +56,8 @@ after_build:
|
||||
- 7z e DiscImageCreator_20230606.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\Creator Release_ANSI\*
|
||||
|
||||
# Redumper
|
||||
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_230/redumper-2023.10.14_build230-win64.zip
|
||||
- 7z e redumper-2023.10.14_build230-win64.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\Redumper redumper-2023.10.14_build230-win64\bin\*
|
||||
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_246/redumper-2023.10.31_build246-win64.zip
|
||||
- 7z e redumper-2023.10.31_build246-win64.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\Redumper redumper-2023.10.31_build246-win64\bin\*
|
||||
|
||||
# Create MPF Debug archives
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net6.0-windows\win-x64\publish\
|
||||
|
||||
Reference in New Issue
Block a user