Compare commits

...

59 Commits
2.7.2 ... 2.7.4

Author SHA1 Message Date
Matt Nadareski
9b1a303fea Bump version 2023-10-31 14:21:46 -04:00
Matt Nadareski
80a0f6da35 Attempt to fix window owner issue 2023-10-30 21:59:30 -04:00
Matt Nadareski
0c30eb7a60 Enable HD-DVD for Redumper in UI 2023-10-30 21:55:03 -04:00
Matt Nadareski
7a8125bb71 Update Redumper to build 246 2023-10-30 21:52:34 -04:00
Matt Nadareski
c4beffd845 Add PIC output for Redumper 2023-10-30 21:43:43 -04:00
Matt Nadareski
f97c112a53 Remove duplicate check 2023-10-30 18:44:08 -04:00
Matt Nadareski
5ef43ab6be Fix build 2023-10-30 18:40:17 -04:00
Matt Nadareski
2c399f99bf Update changelog 2023-10-30 18:38:03 -04:00
Deterous
42e9eb0b96 Fix typo in ProgramSupportsMeida (#591) 2023-10-30 15:36:51 -07:00
Matt Nadareski
e67d65f908 Remove .manufacturer for Bluray 2023-10-30 18:32:58 -04:00
Matt Nadareski
4ec8954b14 Handle a couple of messages 2023-10-30 16:42:15 -04:00
Matt Nadareski
1a6abb039c Compile most regex statements 2023-10-30 16:31:50 -04:00
Matt Nadareski
bb5d1e5ac8 Remove now-obsolete notes 2023-10-30 13:07:29 -04:00
Matt Nadareski
03c4c475eb Add Bluray layerbreak support for Redumper 2023-10-30 12:56:58 -04:00
Matt Nadareski
04d7817d28 Enable Bluray dumping with Redumper 2023-10-30 12:45:19 -04:00
Matt Nadareski
7cd100bc53 Update Redumper to build 244 2023-10-30 12:09:38 -04:00
Matt Nadareski
019924232a Remove and Sort Usings 2023-10-30 01:55:56 -04:00
Matt Nadareski
b5fc9f0275 Ignore empty protection results (fixes #590) 2023-10-30 01:05:15 -04:00
Matt Nadareski
44509b72ed Fix whitespace 2023-10-26 12:37:36 -04:00
Matt Nadareski
d532a63dbd Version-gate new switch statement 2023-10-26 12:36:44 -04:00
Matt Nadareski
227785b079 Remove unncessary summary 2023-10-26 12:33:28 -04:00
Matt Nadareski
0e364be998 Separate out static hashing 2023-10-26 12:16:42 -04:00
Matt Nadareski
7ae1f64ee3 Move all hashing to Hasher 2023-10-26 11:56:38 -04:00
Matt Nadareski
92463a103d Make Hasher more consistent 2023-10-26 11:54:13 -04:00
Matt Nadareski
101cdb7b34 Avoid unncessary allocation in hashing 2023-10-26 11:42:36 -04:00
Matt Nadareski
e924299f85 Add other non-cryptographic hashes 2023-10-26 11:37:41 -04:00
Matt Nadareski
b983f7eb4a Move Hash enum and simplify 2023-10-26 11:30:00 -04:00
Matt Nadareski
33b4be8b24 Bump version 2023-10-26 09:35:25 -04:00
Matt Nadareski
71a4edc8ba Update changelog 2023-10-26 09:18:13 -04:00
Deterous
ceb305eb54 Use relative path internally (#589) 2023-10-26 06:17:17 -07:00
Matt Nadareski
0b0d13dcf3 Use Array.Empty in hasher 2023-10-26 01:25:34 -04:00
Matt Nadareski
9f02368622 Fix two small nullability issues 2023-10-26 01:00:09 -04:00
Matt Nadareski
152010ee14 Update to MMI 3.0.0-preview.4 2023-10-26 00:58:44 -04:00
Matt Nadareski
c6d5f0aea5 Update to BurnOutSharp 2.9.0 2023-10-26 00:57:23 -04:00
Matt Nadareski
8c2ad64bb8 Update XUnit packages 2023-10-25 23:19:44 -04:00
Matt Nadareski
fa54d694b6 Fix CRC32 hashing (fixes #587) 2023-10-25 22:38:30 -04:00
Matt Nadareski
dc35b08624 Attempt to parse out PS5 params.json 2023-10-25 16:56:13 -04:00
Matt Nadareski
4429515ba2 Update package versions (fixes #586) 2023-10-25 16:22:32 -04:00
Matt Nadareski
fdbc7b34e5 Update changelog 2023-10-19 20:58:15 -04:00
Deterous
a1ab442cf0 Set UDF CD threshold at 800MB (#585)
* Set UDF CD threshold at 800MB

* Condense DriveFormat checks
2023-10-19 17:56:58 -07:00
Matt Nadareski
9ed5c297f6 Expose suffix setting (fixes #428) 2023-10-18 22:23:40 -04:00
Matt Nadareski
4ce9b214b0 Wire through filename suffix 2023-10-18 22:19:25 -04:00
Matt Nadareski
7dcdadda75 Add filename suffix setting (nw) 2023-10-18 22:04:15 -04:00
Matt Nadareski
f87a4d9fe2 Fix up DumpEnvironment a bit 2023-10-18 21:54:04 -04:00
Matt Nadareski
e4e5d173f0 Rearrange OptionsWindow to be easier to navigate 2023-10-18 16:35:34 -04:00
Matt Nadareski
115b242d59 Add optional file deletion (fixes #126) 2023-10-18 15:49:57 -04:00
Matt Nadareski
706bf8a431 Add deleteable file lists for Redumper and DIC 2023-10-18 15:41:51 -04:00
Matt Nadareski
87ecf1aa99 Add framework for deleteable files 2023-10-18 15:33:32 -04:00
Matt Nadareski
b5cf274333 Simply theme application 2023-10-18 10:31:58 -04:00
Matt Nadareski
4f4b6879b6 Fix drive letter check (fixes #584) 2023-10-18 10:22:05 -04:00
Matt Nadareski
3b19463913 Create method for applying theme 2023-10-18 10:21:40 -04:00
Matt Nadareski
37386cd182 Fix multiple handler invocation 2023-10-18 03:28:41 -04:00
Matt Nadareski
e04bdad16c Add first-run Options title, fix saving bug 2023-10-18 02:59:41 -04:00
Matt Nadareski
e37f12705d Fix drive letter issue in UI 2023-10-18 02:30:38 -04:00
Matt Nadareski
5c8dc2c23a Show options window on first run (fixes #467) 2023-10-18 02:10:29 -04:00
Matt Nadareski
e9121f3b03 Create skeleton for first-run 2023-10-18 01:59:55 -04:00
Matt Nadareski
d68175db4e Convert Drive to use paths internally 2023-10-18 01:47:42 -04:00
Matt Nadareski
9d8181b0e2 Add path variants for PlayStation info 2023-10-18 01:29:26 -04:00
Matt Nadareski
6d657f268a Split InfoTool into 2 classes 2023-10-18 01:17:12 -04:00
36 changed files with 4384 additions and 3958 deletions

View File

@@ -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

View File

@@ -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" />

View File

@@ -1,6 +1,6 @@
using System;
using System.IO;
using BurnOutSharp;
using BinaryObjectScanner;
using MPF.Core;
using MPF.Core.Data;
using MPF.Core.Utilities;

View File

@@ -1,5 +1,5 @@
using System;
using BurnOutSharp;
using BinaryObjectScanner;
using MPF.Core.Data;
namespace MPF.Core

View File

@@ -93,7 +93,7 @@ namespace MPF.Core.Converters
case InternalProgram.DiscImageCreator:
return "DiscImageCreator";
case InternalProgram.Redumper:
return "redumper";
return "Redumper";
#endregion

View File

@@ -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; } });
}
}
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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,
};

View File

@@ -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
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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" />

View File

@@ -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();

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;

View File

@@ -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:"))

View File

@@ -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")

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -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
}
}

View File

@@ -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;

View File

@@ -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
}

View File

@@ -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);
}
}
}

View File

@@ -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>

View File

@@ -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);

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using psxt001z;
using SabreTools.RedumpLib.Data;
using Xunit;

View File

@@ -123,7 +123,7 @@ namespace WPFCustomMessageBox
ShowActivated = true;
ShowInTaskbar = true;
if (owner != null)
if (owner != null && owner.IsLoaded)
{
Owner = owner;
WindowStartupLocation = WindowStartupLocation.CenterOwner;

View File

@@ -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'">

View File

@@ -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

View File

@@ -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"

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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\