diff --git a/CHANGELIST.md b/CHANGELIST.md
index 183ba2ee..62fc5009 100644
--- a/CHANGELIST.md
+++ b/CHANGELIST.md
@@ -4,6 +4,7 @@
- Add Check flags for protection scan extras
- Add comments around default options object
- Fix usings ordering in ItemHelper
+- Add physical drive extensions to new tool
### 3.2.1 (2024-08-05)
diff --git a/MPF.Frontend/Drive.cs b/MPF.Frontend/Drive.cs
index ff2dbe8b..d7747e7c 100644
--- a/MPF.Frontend/Drive.cs
+++ b/MPF.Frontend/Drive.cs
@@ -1,17 +1,10 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
#if NET462_OR_GREATER || NETCOREAPP
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Generic;
#endif
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using MPF.Processors;
-using SabreTools.IO;
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend
@@ -236,463 +229,6 @@ namespace MPF.Frontend
#endregion
- #region Information Extraction
-
- ///
- /// Get the EXE name from a PlayStation disc, if possible
- ///
- /// Executable name on success, null otherwise
- public string? GetPlayStationExecutableName()
- {
- // If there's no drive path, we can't get exe name
- if (string.IsNullOrEmpty(Name))
- return null;
-
- // If the folder no longer exists, we can't get exe name
- if (!Directory.Exists(Name))
- return null;
-
- // Get the two paths that we will need to check
- string psxExePath = Path.Combine(Name, "PSX.EXE");
- string systemCnfPath = Path.Combine(Name, "SYSTEM.CNF");
-
- // Read the CNF file as an INI file
- var systemCnf = new IniFile(systemCnfPath);
- string? bootValue = string.Empty;
-
- // PlayStation uses "BOOT" as the key
- if (systemCnf.ContainsKey("BOOT"))
- bootValue = systemCnf["BOOT"];
-
- // PlayStation 2 uses "BOOT2" as the key
- if (systemCnf.ContainsKey("BOOT2"))
- bootValue = systemCnf["BOOT2"];
-
- // If we had any boot value, parse it and get the executable name
- if (!string.IsNullOrEmpty(bootValue))
- {
- var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)", RegexOptions.Compiled);
- if (match.Groups.Count > 1)
- {
- string? serial = match.Groups[1].Value;
-
- // Some games may have the EXE in a subfolder
- serial = Path.GetFileName(serial);
-
- return serial;
- }
- }
-
- // If the SYSTEM.CNF value can't be found, try PSX.EXE
- if (File.Exists(psxExePath))
- return "PSX.EXE";
-
- // If neither can be found, we return null
- return null;
- }
-
- ///
- /// Get the EXE date from a PlayStation disc, if possible
- ///
- /// Internal disc serial, if possible
- /// Output region, if possible
- /// Output EXE date in "yyyy-mm-dd" format if possible, null on error
- /// True if information could be determined, false otherwise
- public bool GetPlayStationExecutableInfo(out string? serial, out Region? region, out string? date)
- {
- serial = null; region = null; date = null;
-
- // If there's no drive path, we can't do this part
- if (string.IsNullOrEmpty(Name))
- return false;
-
- // If the folder no longer exists, we can't do this part
- if (!Directory.Exists(Name))
- return false;
-
- // Get the executable name
- string? exeName = GetPlayStationExecutableName();
-
- // If no executable found, we can't do this part
- if (exeName == null)
- return false;
-
- // EXE name may have a trailing `;` after
- // EXE name should always be in all caps
- exeName = exeName
- .Split(';')[0]
- .ToUpperInvariant();
-
- // Serial is most of the EXE name normalized
- serial = exeName
- .Replace('_', '-')
- .Replace(".", string.Empty);
-
- // Get the region, if possible
- region = ProcessingTool.GetPlayStationRegion(exeName);
-
- // Now that we have the EXE name, try to get the fileinfo for it
- string exePath = Path.Combine(Name, exeName);
- if (!File.Exists(exePath))
- return false;
-
- // Fix the Y2K timestamp issue
- var fi = new FileInfo(exePath);
- var dt = new DateTime(fi.LastWriteTimeUtc.Year >= 1900 && fi.LastWriteTimeUtc.Year < 1920 ? 2000 + fi.LastWriteTimeUtc.Year % 100 : fi.LastWriteTimeUtc.Year,
- fi.LastWriteTimeUtc.Month, fi.LastWriteTimeUtc.Day);
- date = dt.ToString("yyyy-MM-dd");
-
- return true;
- }
-
- ///
- /// Get the version from a PlayStation 2 disc, if possible
- ///
- /// Game version if possible, null on error
- public string? GetPlayStation2Version()
- {
- // If there's no drive path, we can't do this part
- if (string.IsNullOrEmpty(Name))
- return null;
-
- // If the folder no longer exists, we can't do this part
- if (!Directory.Exists(Name))
- return null;
-
- // Get the SYSTEM.CNF path to check
- string systemCnfPath = Path.Combine(Name, "SYSTEM.CNF");
-
- // Try to parse the SYSTEM.CNF file
- var systemCnf = new IniFile(systemCnfPath);
- if (systemCnf.ContainsKey("VER"))
- return systemCnf["VER"];
-
- // If "VER" can't be found, we can't do much
- return null;
- }
-
- ///
- /// Get the internal serial from a PlayStation 3 disc, if possible
- ///
- /// Internal disc serial if possible, null on error
- public string? GetPlayStation3Serial()
- {
- // If there's no drive path, we can't do this part
- if (string.IsNullOrEmpty(Name))
- return null;
-
- // If the folder no longer exists, we can't do this part
- if (!Directory.Exists(Name))
- return null;
-
- // Attempt to use PS3_DISC.SFB
- string sfbPath = Path.Combine(Name, "PS3_DISC.SFB");
- if (File.Exists(sfbPath))
- {
- try
- {
- using var br = new BinaryReader(File.OpenRead(sfbPath));
- br.BaseStream.Seek(0x220, SeekOrigin.Begin);
- return new string(br.ReadChars(0x10)).TrimEnd('\0');
- }
- catch
- {
- // We don't care what the error was
- return null;
- }
- }
-
- // Attempt to use PARAM.SFO
-#if NET20 || NET35
- string sfoPath = Path.Combine(Path.Combine(Name, "PS3_GAME"), "PARAM.SFO");
-#else
- string sfoPath = Path.Combine(Name, "PS3_GAME", "PARAM.SFO");
-#endif
- if (File.Exists(sfoPath))
- {
- try
- {
- using var br = new BinaryReader(File.OpenRead(sfoPath));
- br.BaseStream.Seek(-0x18, SeekOrigin.End);
- return new string(br.ReadChars(9)).TrimEnd('\0').Insert(4, "-");
- }
- catch
- {
- // We don't care what the error was
- return null;
- }
- }
-
- return null;
- }
-
- ///
- /// Get the version from a PlayStation 3 disc, if possible
- ///
- /// Game version if possible, null on error
- public string? GetPlayStation3Version()
- {
- // If there's no drive path, we can't do this part
- if (string.IsNullOrEmpty(Name))
- return null;
-
- // If the folder no longer exists, we can't do this part
- if (!Directory.Exists(Name))
- return null;
-
- // Attempt to use PS3_DISC.SFB
- string sfbPath = Path.Combine(Name, "PS3_DISC.SFB");
- if (File.Exists(sfbPath))
- {
- try
- {
- using var br = new BinaryReader(File.OpenRead(sfbPath));
- br.BaseStream.Seek(0x230, SeekOrigin.Begin);
- var discVersion = new string(br.ReadChars(0x10)).TrimEnd('\0');
- if (!string.IsNullOrEmpty(discVersion))
- return discVersion;
- }
- catch
- {
- // We don't care what the error was
- return null;
- }
- }
-
- // Attempt to use PARAM.SFO
-#if NET20 || NET35
- string sfoPath = Path.Combine(Path.Combine(Name, "PS3_GAME"), "PARAM.SFO");
-#else
- string sfoPath = Path.Combine(Name, "PS3_GAME", "PARAM.SFO");
-#endif
- if (File.Exists(sfoPath))
- {
- try
- {
- using var br = new BinaryReader(File.OpenRead(sfoPath));
- br.BaseStream.Seek(-0x08, SeekOrigin.End);
- return new string(br.ReadChars(5)).TrimEnd('\0');
- }
- catch
- {
- // We don't care what the error was
- return null;
- }
- }
-
- return null;
- }
-
- ///
- /// Get the firmware version from a PlayStation 3 disc, if possible
- ///
- /// Firmware version if possible, null on error
- public string? GetPlayStation3FirmwareVersion()
- {
- // If there's no drive path, we can't do this part
- if (string.IsNullOrEmpty(Name))
- return null;
-
- // If the folder no longer exists, we can't do this part
- if (!Directory.Exists(Name))
- return null;
-
- // Attempt to read from /PS3_UPDATE/PS3UPDAT.PUP
-#if NET20 || NET35
- string pupPath = Path.Combine(Path.Combine(Name, "PS3_UPDATE"), "PS3UPDAT.PUP");
-#else
- string pupPath = Path.Combine(Name, "PS3_UPDATE", "PS3UPDAT.PUP");
-#endif
- if (!File.Exists(pupPath))
- return null;
-
- try
- {
- using var br = new BinaryReader(File.OpenRead(pupPath));
- br.BaseStream.Seek(0x3E, SeekOrigin.Begin);
- byte[] buf = new byte[2];
- br.Read(buf, 0, 2);
- Array.Reverse(buf);
- short location = BitConverter.ToInt16(buf, 0);
- br.BaseStream.Seek(location, SeekOrigin.Begin);
- return new string(br.ReadChars(4));
- }
- catch
- {
- // We don't care what the error was
- return null;
- }
- }
-
- ///
- /// Get the internal serial from a PlayStation 4 disc, if possible
- ///
- /// Internal disc serial if possible, null on error
- public string? GetPlayStation4Serial()
- {
- // If there's no drive path, we can't do this part
- if (string.IsNullOrEmpty(Name))
- return null;
-
- // If the folder no longer exists, we can't do this part
- if (!Directory.Exists(Name))
- return null;
-
- // If we can't find param.sfo, we don't have a PlayStation 4 disc
-#if NET20 || NET35
- string paramSfoPath = Path.Combine(Path.Combine(Name, "bd"), "param.sfo");
-#else
- string paramSfoPath = Path.Combine(Name, "bd", "param.sfo");
-#endif
- if (!File.Exists(paramSfoPath))
- return null;
-
- // Let's try reading param.sfo to find the serial at the end of the file
- try
- {
- using var br = new BinaryReader(File.OpenRead(paramSfoPath));
- br.BaseStream.Seek(-0x14, SeekOrigin.End);
- return new string(br.ReadChars(9)).Insert(4, "-");
- }
- catch
- {
- // We don't care what the error was
- return null;
- }
- }
-
- ///
- /// Get the version from a PlayStation 4 disc, if possible
- ///
- /// Game version if possible, null on error
- public string? GetPlayStation4Version()
- {
- // If there's no drive path, we can't do this part
- if (string.IsNullOrEmpty(Name))
- return null;
-
- // If the folder no longer exists, we can't do this part
- if (!Directory.Exists(Name))
- return null;
-
- // If we can't find param.sfo, we don't have a PlayStation 4 disc
-#if NET20 || NET35
- string paramSfoPath = Path.Combine(Path.Combine(Name, "bd"), "param.sfo");
-#else
- string paramSfoPath = Path.Combine(Name, "bd", "param.sfo");
-#endif
- if (!File.Exists(paramSfoPath))
- return null;
-
- // Let's try reading param.sfo to find the version at the end of the file
- try
- {
- using var br = new BinaryReader(File.OpenRead(paramSfoPath));
- br.BaseStream.Seek(-0x08, SeekOrigin.End);
- return new string(br.ReadChars(5));
- }
- catch
- {
- // We don't care what the error was
- return null;
- }
- }
-
- ///
- /// Get the internal serial from a PlayStation 5 disc, if possible
- ///
- /// Internal disc serial if possible, null on error
- public string? GetPlayStation5Serial()
- {
- // Attempt to get the param.json file
- var json = GetPlayStation5ParamsJsonFromDrive();
- if (json == null)
- return null;
-
- try
- {
- return json["disc"]?[0]?["masterDataId"]?.Value()?.Insert(4, "-");
- }
- catch
- {
- // We don't care what the error was
- return null;
- }
- }
-
- //
- /// Get the version from a PlayStation 5 disc, if possible
- ///
- /// Game version if possible, null on error
- public string? GetPlayStation5Version()
- {
- // Attempt to get the param.json file
- var json = GetPlayStation5ParamsJsonFromDrive();
- if (json == null)
- return null;
-
- try
- {
- return json["masterVersion"]?.Value();
- }
- catch
- {
- // We don't care what the error was
- return null;
- }
- }
-
- ///
- /// Get the params.json file from a drive path, if possible
- ///
- /// JObject representing the JSON on success, null on error
- private JObject? GetPlayStation5ParamsJsonFromDrive()
- {
- // If there's no drive path, we can't do this part
- if (string.IsNullOrEmpty(Name))
- return null;
-
- // If the folder no longer exists, we can't do this part
- if (!Directory.Exists(Name))
- return null;
-
- // If we can't find param.json, we don't have a PlayStation 5 disc
-#if NET20 || NET35
- string paramJsonPath = Path.Combine(Path.Combine(Name, "bd"), "param.json");
-#else
- string paramJsonPath = Path.Combine(Name, "bd", "param.json");
-#endif
- return GetPlayStation5ParamsJsonFromFile(paramJsonPath);
- }
-
- ///
- /// Get the params.json file from a filename, if possible
- ///
- /// Filename to check
- /// JObject representing the JSON on success, null on error
- private static JObject? GetPlayStation5ParamsJsonFromFile(string? filename)
- {
- // If the file doesn't exist
- if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
- return null;
-
- // Let's try reading param.json to find the version in the unencrypted JSON
- try
- {
- using var br = new BinaryReader(File.OpenRead(filename));
- br.BaseStream.Seek(0x800, SeekOrigin.Begin);
- byte[] jsonBytes = br.ReadBytes((int)(br.BaseStream.Length - 0x800));
- return JsonConvert.DeserializeObject(Encoding.ASCII.GetString(jsonBytes)) as JObject;
- }
- catch
- {
- // We don't care what the error was
- return null;
- }
- }
-
- #endregion
-
#region Helpers
///
diff --git a/MPF.Frontend/Tools/PhysicalTool.cs b/MPF.Frontend/Tools/PhysicalTool.cs
new file mode 100644
index 00000000..772ae110
--- /dev/null
+++ b/MPF.Frontend/Tools/PhysicalTool.cs
@@ -0,0 +1,483 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using MPF.Processors;
+using SabreTools.IO;
+using SabreTools.RedumpLib.Data;
+
+namespace MPF.Frontend.Tools
+{
+ public static class PhysicalTool
+ {
+ #region Information Extraction
+
+ ///
+ /// Get the EXE name from a PlayStation disc, if possible
+ ///
+ /// Drive to extract information from
+ /// Executable name on success, null otherwise
+ public static string? GetPlayStationExecutableName(Drive? drive)
+ {
+ // If there's no drive path, we can't get exe name
+ if (string.IsNullOrEmpty(drive?.Name))
+ return null;
+
+ // If the folder no longer exists, we can't get exe name
+ if (!Directory.Exists(drive.Name))
+ return null;
+
+ // Get the two paths that we will need to check
+ string psxExePath = Path.Combine(drive.Name, "PSX.EXE");
+ string systemCnfPath = Path.Combine(drive.Name, "SYSTEM.CNF");
+
+ // Read the CNF file as an INI file
+ var systemCnf = new IniFile(systemCnfPath);
+ string? bootValue = string.Empty;
+
+ // PlayStation uses "BOOT" as the key
+ if (systemCnf.ContainsKey("BOOT"))
+ bootValue = systemCnf["BOOT"];
+
+ // PlayStation 2 uses "BOOT2" as the key
+ if (systemCnf.ContainsKey("BOOT2"))
+ bootValue = systemCnf["BOOT2"];
+
+ // If we had any boot value, parse it and get the executable name
+ if (!string.IsNullOrEmpty(bootValue))
+ {
+ var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)", RegexOptions.Compiled);
+ if (match.Groups.Count > 1)
+ {
+ string? serial = match.Groups[1].Value;
+
+ // Some games may have the EXE in a subfolder
+ serial = Path.GetFileName(serial);
+
+ return serial;
+ }
+ }
+
+ // If the SYSTEM.CNF value can't be found, try PSX.EXE
+ if (File.Exists(psxExePath))
+ return "PSX.EXE";
+
+ // If neither can be found, we return null
+ return null;
+ }
+
+ ///
+ /// Get the EXE date from a PlayStation disc, if possible
+ ///
+ /// Drive to extract information from
+ /// Internal disc serial, if possible
+ /// Output region, if possible
+ /// Output EXE date in "yyyy-mm-dd" format if possible, null on error
+ /// True if information could be determined, false otherwise
+ public static bool GetPlayStationExecutableInfo(Drive? drive, out string? serial, out Region? region, out string? date)
+ {
+ serial = null; region = null; date = null;
+
+ // If there's no drive path, we can't do this part
+ if (string.IsNullOrEmpty(drive?.Name))
+ return false;
+
+ // If the folder no longer exists, we can't do this part
+ if (!Directory.Exists(drive.Name))
+ return false;
+
+ // Get the executable name
+ string? exeName = GetPlayStationExecutableName(drive);
+
+ // If no executable found, we can't do this part
+ if (exeName == null)
+ return false;
+
+ // EXE name may have a trailing `;` after
+ // EXE name should always be in all caps
+ exeName = exeName
+ .Split(';')[0]
+ .ToUpperInvariant();
+
+ // Serial is most of the EXE name normalized
+ serial = exeName
+ .Replace('_', '-')
+ .Replace(".", string.Empty);
+
+ // Get the region, if possible
+ region = ProcessingTool.GetPlayStationRegion(exeName);
+
+ // Now that we have the EXE name, try to get the fileinfo for it
+ string exePath = Path.Combine(drive.Name, exeName);
+ if (!File.Exists(exePath))
+ return false;
+
+ // Fix the Y2K timestamp issue
+ var fi = new FileInfo(exePath);
+ var dt = new DateTime(fi.LastWriteTimeUtc.Year >= 1900 && fi.LastWriteTimeUtc.Year < 1920 ? 2000 + fi.LastWriteTimeUtc.Year % 100 : fi.LastWriteTimeUtc.Year,
+ fi.LastWriteTimeUtc.Month, fi.LastWriteTimeUtc.Day);
+ date = dt.ToString("yyyy-MM-dd");
+
+ return true;
+ }
+
+ ///
+ /// Get the version from a PlayStation 2 disc, if possible
+ ///
+ /// Drive to extract information from
+ /// Game version if possible, null on error
+ public static string? GetPlayStation2Version(Drive? drive)
+ {
+ // If there's no drive path, we can't do this part
+ if (string.IsNullOrEmpty(drive?.Name))
+ return null;
+
+ // If the folder no longer exists, we can't do this part
+ if (!Directory.Exists(drive.Name))
+ return null;
+
+ // Get the SYSTEM.CNF path to check
+ string systemCnfPath = Path.Combine(drive.Name, "SYSTEM.CNF");
+
+ // Try to parse the SYSTEM.CNF file
+ var systemCnf = new IniFile(systemCnfPath);
+ if (systemCnf.ContainsKey("VER"))
+ return systemCnf["VER"];
+
+ // If "VER" can't be found, we can't do much
+ return null;
+ }
+
+ ///
+ /// Get the internal serial from a PlayStation 3 disc, if possible
+ ///
+ /// Drive to extract information from
+ /// Internal disc serial if possible, null on error
+ public static string? GetPlayStation3Serial(Drive? drive)
+ {
+ // If there's no drive path, we can't do this part
+ if (string.IsNullOrEmpty(drive?.Name))
+ return null;
+
+ // If the folder no longer exists, we can't do this part
+ if (!Directory.Exists(drive.Name))
+ return null;
+
+ // Attempt to use PS3_DISC.SFB
+ string sfbPath = Path.Combine(drive.Name, "PS3_DISC.SFB");
+ if (File.Exists(sfbPath))
+ {
+ try
+ {
+ using var br = new BinaryReader(File.OpenRead(sfbPath));
+ br.BaseStream.Seek(0x220, SeekOrigin.Begin);
+ return new string(br.ReadChars(0x10)).TrimEnd('\0');
+ }
+ catch
+ {
+ // We don't care what the error was
+ return null;
+ }
+ }
+
+ // Attempt to use PARAM.SFO
+#if NET20 || NET35
+ string sfoPath = Path.Combine(Path.Combine(drive.Name, "PS3_GAME"), "PARAM.SFO");
+#else
+ string sfoPath = Path.Combine(drive.Name, "PS3_GAME", "PARAM.SFO");
+#endif
+ if (File.Exists(sfoPath))
+ {
+ try
+ {
+ using var br = new BinaryReader(File.OpenRead(sfoPath));
+ br.BaseStream.Seek(-0x18, SeekOrigin.End);
+ return new string(br.ReadChars(9)).TrimEnd('\0').Insert(4, "-");
+ }
+ catch
+ {
+ // We don't care what the error was
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Get the version from a PlayStation 3 disc, if possible
+ ///
+ /// Drive to extract information from
+ /// Game version if possible, null on error
+ public static string? GetPlayStation3Version(Drive? drive)
+ {
+ // If there's no drive path, we can't do this part
+ if (string.IsNullOrEmpty(drive?.Name))
+ return null;
+
+ // If the folder no longer exists, we can't do this part
+ if (!Directory.Exists(drive.Name))
+ return null;
+
+ // Attempt to use PS3_DISC.SFB
+ string sfbPath = Path.Combine(drive.Name, "PS3_DISC.SFB");
+ if (File.Exists(sfbPath))
+ {
+ try
+ {
+ using var br = new BinaryReader(File.OpenRead(sfbPath));
+ br.BaseStream.Seek(0x230, SeekOrigin.Begin);
+ var discVersion = new string(br.ReadChars(0x10)).TrimEnd('\0');
+ if (!string.IsNullOrEmpty(discVersion))
+ return discVersion;
+ }
+ catch
+ {
+ // We don't care what the error was
+ return null;
+ }
+ }
+
+ // Attempt to use PARAM.SFO
+#if NET20 || NET35
+ string sfoPath = Path.Combine(Path.Combine(drive.Name, "PS3_GAME"), "PARAM.SFO");
+#else
+ string sfoPath = Path.Combine(drive.Name, "PS3_GAME", "PARAM.SFO");
+#endif
+ if (File.Exists(sfoPath))
+ {
+ try
+ {
+ using var br = new BinaryReader(File.OpenRead(sfoPath));
+ br.BaseStream.Seek(-0x08, SeekOrigin.End);
+ return new string(br.ReadChars(5)).TrimEnd('\0');
+ }
+ catch
+ {
+ // We don't care what the error was
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Get the firmware version from a PlayStation 3 disc, if possible
+ ///
+ /// Drive to extract information from
+ /// Firmware version if possible, null on error
+ public static string? GetPlayStation3FirmwareVersion(Drive? drive)
+ {
+ // If there's no drive path, we can't do this part
+ if (string.IsNullOrEmpty(drive?.Name))
+ return null;
+
+ // If the folder no longer exists, we can't do this part
+ if (!Directory.Exists(drive.Name))
+ return null;
+
+ // Attempt to read from /PS3_UPDATE/PS3UPDAT.PUP
+#if NET20 || NET35
+ string pupPath = Path.Combine(Path.Combine(drive.Name, "PS3_UPDATE"), "PS3UPDAT.PUP");
+#else
+ string pupPath = Path.Combine(drive.Name, "PS3_UPDATE", "PS3UPDAT.PUP");
+#endif
+ if (!File.Exists(pupPath))
+ return null;
+
+ try
+ {
+ using var br = new BinaryReader(File.OpenRead(pupPath));
+ br.BaseStream.Seek(0x3E, SeekOrigin.Begin);
+ byte[] buf = new byte[2];
+ br.Read(buf, 0, 2);
+ Array.Reverse(buf);
+ short location = BitConverter.ToInt16(buf, 0);
+ br.BaseStream.Seek(location, SeekOrigin.Begin);
+ return new string(br.ReadChars(4));
+ }
+ catch
+ {
+ // We don't care what the error was
+ return null;
+ }
+ }
+
+ ///
+ /// Get the internal serial from a PlayStation 4 disc, if possible
+ ///
+ /// Drive to extract information from
+ /// Internal disc serial if possible, null on error
+ public static string? GetPlayStation4Serial(Drive? drive)
+ {
+ // If there's no drive path, we can't do this part
+ if (string.IsNullOrEmpty(drive?.Name))
+ return null;
+
+ // If the folder no longer exists, we can't do this part
+ if (!Directory.Exists(drive.Name))
+ return null;
+
+ // If we can't find param.sfo, we don't have a PlayStation 4 disc
+#if NET20 || NET35
+ string paramSfoPath = Path.Combine(Path.Combine(drive.Name, "bd"), "param.sfo");
+#else
+ string paramSfoPath = Path.Combine(drive.Name, "bd", "param.sfo");
+#endif
+ if (!File.Exists(paramSfoPath))
+ return null;
+
+ // Let's try reading param.sfo to find the serial at the end of the file
+ try
+ {
+ using var br = new BinaryReader(File.OpenRead(paramSfoPath));
+ br.BaseStream.Seek(-0x14, SeekOrigin.End);
+ return new string(br.ReadChars(9)).Insert(4, "-");
+ }
+ catch
+ {
+ // We don't care what the error was
+ return null;
+ }
+ }
+
+ ///
+ /// Get the version from a PlayStation 4 disc, if possible
+ ///
+ /// Drive to extract information from
+ /// Game version if possible, null on error
+ public static string? GetPlayStation4Version(Drive? drive)
+ {
+ // If there's no drive path, we can't do this part
+ if (string.IsNullOrEmpty(drive?.Name))
+ return null;
+
+ // If the folder no longer exists, we can't do this part
+ if (!Directory.Exists(drive.Name))
+ return null;
+
+ // If we can't find param.sfo, we don't have a PlayStation 4 disc
+#if NET20 || NET35
+ string paramSfoPath = Path.Combine(Path.Combine(drive.Name, "bd"), "param.sfo");
+#else
+ string paramSfoPath = Path.Combine(drive.Name, "bd", "param.sfo");
+#endif
+ if (!File.Exists(paramSfoPath))
+ return null;
+
+ // Let's try reading param.sfo to find the version at the end of the file
+ try
+ {
+ using var br = new BinaryReader(File.OpenRead(paramSfoPath));
+ br.BaseStream.Seek(-0x08, SeekOrigin.End);
+ return new string(br.ReadChars(5));
+ }
+ catch
+ {
+ // We don't care what the error was
+ return null;
+ }
+ }
+
+ ///
+ /// Get the internal serial from a PlayStation 5 disc, if possible
+ ///
+ /// Drive to extract information from
+ /// Internal disc serial if possible, null on error
+ public static string? GetPlayStation5Serial(Drive? drive)
+ {
+ // Attempt to get the param.json file
+ var json = GetPlayStation5ParamsJsonFromDrive(drive);
+ if (json == null)
+ return null;
+
+ try
+ {
+ return json["disc"]?[0]?["masterDataId"]?.Value()?.Insert(4, "-");
+ }
+ catch
+ {
+ // We don't care what the error was
+ return null;
+ }
+ }
+
+ //
+ /// Get the version from a PlayStation 5 disc, if possible
+ ///
+ /// Drive to extract information from
+ /// Game version if possible, null on error
+ public static string? GetPlayStation5Version(Drive? drive)
+ {
+ // Attempt to get the param.json file
+ var json = GetPlayStation5ParamsJsonFromDrive(drive);
+ if (json == null)
+ return null;
+
+ try
+ {
+ return json["masterVersion"]?.Value();
+ }
+ catch
+ {
+ // We don't care what the error was
+ return null;
+ }
+ }
+
+ ///
+ /// Get the params.json file from a drive path, if possible
+ ///
+ /// Drive to extract information from
+ /// JObject representing the JSON on success, null on error
+ private static JObject? GetPlayStation5ParamsJsonFromDrive(Drive? drive)
+ {
+ // If there's no drive path, we can't do this part
+ if (string.IsNullOrEmpty(drive?.Name))
+ return null;
+
+ // If the folder no longer exists, we can't do this part
+ if (!Directory.Exists(drive.Name))
+ return null;
+
+ // If we can't find param.json, we don't have a PlayStation 5 disc
+#if NET20 || NET35
+ string paramJsonPath = Path.Combine(Path.Combine(drive.Name, "bd"), "param.json");
+#else
+ string paramJsonPath = Path.Combine(drive.Name, "bd", "param.json");
+#endif
+ return GetPlayStation5ParamsJsonFromFile(paramJsonPath);
+ }
+
+ ///
+ /// Get the params.json file from a filename, if possible
+ ///
+ /// Filename to check
+ /// JObject representing the JSON on success, null on error
+ private static JObject? GetPlayStation5ParamsJsonFromFile(string? filename)
+ {
+ // If the file doesn't exist
+ if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
+ return null;
+
+ // Let's try reading param.json to find the version in the unencrypted JSON
+ try
+ {
+ using var br = new BinaryReader(File.OpenRead(filename));
+ br.BaseStream.Seek(0x800, SeekOrigin.Begin);
+ byte[] jsonBytes = br.ReadBytes((int)(br.BaseStream.Length - 0x800));
+ return JsonConvert.DeserializeObject(Encoding.ASCII.GetString(jsonBytes)) as JObject;
+ }
+ catch
+ {
+ // We don't care what the error was
+ return null;
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/MPF.Frontend/Tools/SubmissionGenerator.cs b/MPF.Frontend/Tools/SubmissionGenerator.cs
index 14f18e18..7a6204e4 100644
--- a/MPF.Frontend/Tools/SubmissionGenerator.cs
+++ b/MPF.Frontend/Tools/SubmissionGenerator.cs
@@ -681,12 +681,12 @@ namespace MPF.Frontend.Tools
case RedumpSystem.KonamiPython2:
// TODO: Remove this hack when DIC supports build date output
if (isDiscImageCreator)
- info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", drive?.GetPlayStationExecutableName());
+ info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", PhysicalTool.GetPlayStationExecutableName(drive));
if (info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? kp2Exe) && !string.IsNullOrEmpty(kp2Exe))
info.CommonDiscInfo.Region = ProcessingTool.GetPlayStationRegion(kp2Exe);
- if (drive?.GetPlayStationExecutableInfo(out var kp2Serial, out Region? kp2Region, out var kp2Date) == true)
+ if (PhysicalTool.GetPlayStationExecutableInfo(drive, out var kp2Serial, out Region? kp2Region, out var kp2Date) == true)
{
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? value) || string.IsNullOrEmpty(value))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = kp2Serial ?? string.Empty;
@@ -696,7 +696,7 @@ namespace MPF.Frontend.Tools
}
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
- info.VersionAndEditions!.Version = drive?.GetPlayStation2Version() ?? string.Empty;
+ info.VersionAndEditions!.Version = PhysicalTool.GetPlayStation2Version(drive) ?? string.Empty;
break;
@@ -795,12 +795,12 @@ namespace MPF.Frontend.Tools
case RedumpSystem.SonyPlayStation:
// TODO: Remove this hack when DIC supports build date output
if (isDiscImageCreator)
- info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", drive?.GetPlayStationExecutableName(), psx: true);
+ info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", PhysicalTool.GetPlayStationExecutableName(drive), psx: true);
if (info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? psxExe) && !string.IsNullOrEmpty(psxExe))
info.CommonDiscInfo.Region = ProcessingTool.GetPlayStationRegion(psxExe);
- if (drive?.GetPlayStationExecutableInfo(out var psxSerial, out Region? psxRegion, out var psxDate) == true)
+ if (PhysicalTool.GetPlayStationExecutableInfo(drive, out var psxSerial, out Region? psxRegion, out var psxDate) == true)
{
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? value) || string.IsNullOrEmpty(value))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = psxSerial ?? string.Empty;
@@ -816,12 +816,12 @@ namespace MPF.Frontend.Tools
// TODO: Remove this hack when DIC supports build date output
if (isDiscImageCreator)
- info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", drive?.GetPlayStationExecutableName());
+ info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", PhysicalTool.GetPlayStationExecutableName(drive));
if (info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps2Exe) && !string.IsNullOrEmpty(ps2Exe))
info.CommonDiscInfo.Region = ProcessingTool.GetPlayStationRegion(ps2Exe);
- if (drive?.GetPlayStationExecutableInfo(out var ps2Serial, out Region? ps2Region, out var ps2Date) == true)
+ if (PhysicalTool.GetPlayStationExecutableInfo(drive, out var ps2Serial, out Region? ps2Region, out var ps2Date) == true)
{
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? value) || string.IsNullOrEmpty(value))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = ps2Serial ?? string.Empty;
@@ -831,7 +831,7 @@ namespace MPF.Frontend.Tools
}
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
- info.VersionAndEditions!.Version = drive?.GetPlayStation2Version() ?? string.Empty;
+ info.VersionAndEditions!.Version = PhysicalTool.GetPlayStation2Version(drive) ?? string.Empty;
break;
@@ -840,14 +840,14 @@ namespace MPF.Frontend.Tools
info.Extras.DiscID ??= addPlaceholders ? RequiredValue : string.Empty;
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps3Serial) || string.IsNullOrEmpty(ps3Serial))
- info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = drive?.GetPlayStation3Serial() ?? string.Empty;
+ info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = PhysicalTool.GetPlayStation3Serial(drive) ?? string.Empty;
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
- info.VersionAndEditions!.Version = drive?.GetPlayStation3Version() ?? string.Empty;
+ info.VersionAndEditions!.Version = PhysicalTool.GetPlayStation3Version(drive) ?? string.Empty;
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.Patches, out string? ps3Firmware) || string.IsNullOrEmpty(ps3Firmware))
{
- string? firmwareVersion = drive?.GetPlayStation3FirmwareVersion();
+ string? firmwareVersion = PhysicalTool.GetPlayStation3FirmwareVersion(drive);
if (firmwareVersion != null)
info.CommonDiscInfo!.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}";
}
@@ -856,19 +856,19 @@ namespace MPF.Frontend.Tools
case RedumpSystem.SonyPlayStation4:
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps4Serial) || string.IsNullOrEmpty(ps4Serial))
- info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = drive?.GetPlayStation4Serial() ?? string.Empty;
+ info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = PhysicalTool.GetPlayStation4Serial(drive) ?? string.Empty;
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
- info.VersionAndEditions!.Version = drive?.GetPlayStation4Version() ?? string.Empty;
+ info.VersionAndEditions!.Version = PhysicalTool.GetPlayStation4Version(drive) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation5:
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps5Serial) || string.IsNullOrEmpty(ps5Serial))
- info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = drive?.GetPlayStation5Serial() ?? string.Empty;
+ info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = PhysicalTool.GetPlayStation5Serial(drive) ?? string.Empty;
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
- info.VersionAndEditions!.Version = drive?.GetPlayStation5Version() ?? string.Empty;
+ info.VersionAndEditions!.Version = PhysicalTool.GetPlayStation5Version(drive) ?? string.Empty;
break;
diff --git a/MPF.Frontend/ViewModels/MainViewModel.cs b/MPF.Frontend/ViewModels/MainViewModel.cs
index 1390fedd..2e09e97e 100644
--- a/MPF.Frontend/ViewModels/MainViewModel.cs
+++ b/MPF.Frontend/ViewModels/MainViewModel.cs
@@ -1917,7 +1917,7 @@ namespace MPF.Frontend.ViewModels
{
case RedumpSystem.SonyPlayStation:
case RedumpSystem.SonyPlayStation2:
- drive.GetPlayStationExecutableInfo(out string? serial, out _, out _);
+ PhysicalTool.GetPlayStationExecutableInfo(drive, out string? serial, out _, out _);
volumeLabel = serial ?? "track";
break;