From 285e94ca6950a9e277e021e16645a86b5a64f00e Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 31 Jan 2024 12:00:08 +0900
Subject: [PATCH] Parse PSX/PS2/KP2 exe date from logs (#639)
* Parse EXE date from Redumper
* Parse EXE date from DIC logs
* Fix DIC exe date parsing
* Split PS EXE name from EXE info
* Remove redundant path splitting
---
CHANGELIST.md | 1 +
MPF.Core/InfoTool.cs | 121 +++++++++++-------
.../Modules/DiscImageCreator/Parameters.cs | 78 ++++++++++-
MPF.Core/Modules/Redumper/Parameters.cs | 48 ++++++-
4 files changed, 196 insertions(+), 52 deletions(-)
diff --git a/CHANGELIST.md b/CHANGELIST.md
index 2eba6dbd7..fcdc46fcc 100644
--- a/CHANGELIST.md
+++ b/CHANGELIST.md
@@ -22,6 +22,7 @@
- Fix information pulling for CleanRip and UIC
- Add UMD handling for the disc info window
- Detect Photo CD
+- Parse PSX/PS2/KP2 exe date from logs (Deterous)
### 3.0.3 (2023-12-04)
diff --git a/MPF.Core/InfoTool.cs b/MPF.Core/InfoTool.cs
index 893ebeeac..bcb3d2738 100644
--- a/MPF.Core/InfoTool.cs
+++ b/MPF.Core/InfoTool.cs
@@ -384,6 +384,66 @@ internal static void GetLibCryptDetected(SubmissionInfo info, string basePath)
return di.Units[0]?.Body?.DiscTypeIdentifier;
}
+ internal static string? GetPlayStationExecutableName(char? driveLetter)
+ {
+ // If there's no drive letter, we can't get exe name
+ if (driveLetter == null)
+ return null;
+
+ // Convert drive letter to drive path
+ string drivePath = driveLetter + ":\\";
+ return GetPlayStationExecutableName(drivePath);
+ }
+
+ internal static string? GetPlayStationExecutableName(string? drivePath)
+ {
+ // If there's no drive path, we can't get exe name
+ if (string.IsNullOrEmpty(drivePath))
+ return null;
+
+ // If the folder no longer exists, we can't get exe name
+ if (!Directory.Exists(drivePath))
+ return null;
+
+ // Get the two paths that we will need to check
+ string psxExePath = Path.Combine(drivePath, "PSX.EXE");
+ string systemCnfPath = Path.Combine(drivePath, "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
///
@@ -400,7 +460,7 @@ internal static bool GetPlayStationExecutableInfo(char? driveLetter, out string?
if (driveLetter == null)
return false;
- // If the folder no longer exists, we can't do this part
+ // Convert drive letter to drive path
string drivePath = driveLetter + ":\\";
return GetPlayStationExecutableInfo(drivePath, out serial, out region, out date);
}
@@ -425,54 +485,23 @@ internal static bool GetPlayStationExecutableInfo(string? drivePath, out string?
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
- string? exeName = null;
-
- // 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)
- {
- // 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);
+ // Get the executable name
+ string? exeName = GetPlayStationExecutableName(drivePath);
- // Some games may have the EXE in a subfolder
- serial = Path.GetFileName(serial);
- }
- }
+ // If no executable found, we can't do this part
+ if (exeName == null)
+ return false;
- // If the SYSTEM.CNF value can't be found, try PSX.EXE
- if (string.IsNullOrEmpty(exeName) && File.Exists(psxExePath))
- exeName = "PSX.EXE";
+ // EXE name may have a trailing `;` after
+ // EXE name should always be in all caps
+ exeName = exeName
+ .Split(';')[0]
+ .ToUpperInvariant();
- // If neither can be found, we return false
- if (string.IsNullOrEmpty(exeName))
- return false;
+ // Serial is most of the EXE name normalized
+ serial = exeName
+ .Replace('_', '-')
+ .Replace(".", string.Empty);
// Get the region, if possible
region = GetPlayStationRegion(exeName);
diff --git a/MPF.Core/Modules/DiscImageCreator/Parameters.cs b/MPF.Core/Modules/DiscImageCreator/Parameters.cs
index 628849760..59b35d387 100644
--- a/MPF.Core/Modules/DiscImageCreator/Parameters.cs
+++ b/MPF.Core/Modules/DiscImageCreator/Parameters.cs
@@ -541,12 +541,14 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, Options options
break;
case RedumpSystem.KonamiPython2:
+ info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name));
+
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
- info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
+ info.CommonDiscInfo.EXEDateBuildDate ??= pythonTwoDate;
}
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
@@ -785,12 +787,14 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, Options options
break;
case RedumpSystem.SonyPlayStation:
+ info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name), true);
+
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
- info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
+ info.CommonDiscInfo.EXEDateBuildDate ??= playstationDate;
}
bool? psEdcStatus = null;
@@ -804,12 +808,14 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, Options options
break;
case RedumpSystem.SonyPlayStation2:
+ info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name));
+
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
- info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
+ info.CommonDiscInfo.EXEDateBuildDate ??= playstationTwoDate;
}
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
@@ -2971,6 +2977,72 @@ private static long GetErrorCount(string edcecc)
}
}
+ ///
+ /// Get the PSX/PS2/KP2 EXE Date from the log, if possible
+ ///
+ /// Log file location
+ /// Internal serial
+ /// True if PSX disc, false otherwise
+ /// EXE date if possible, null otherwise
+ public static string? GetPlayStationEXEDate(string log, string? exeName, bool psx = false)
+ {
+ // If the file doesn't exist, we can't get the info
+ if (!File.Exists(log))
+ return null;
+
+ // If the EXE name is not valid, we can't get the info
+ if (string.IsNullOrEmpty(exeName))
+ return null;
+
+ try
+ {
+ string? exeDate = null;
+ using var sr = File.OpenText(log);
+ var line = sr.ReadLine();
+ while (line != null)
+ {
+ // Trim the line for later use
+ line = line.Trim();
+
+ // The exe date is listed in a single line, File Identifier: ABCD_123.45;1
+ if (line.Length >= "File Identifier: ".Length + 11 &&
+ line.StartsWith("File Identifier:") &&
+ line.Substring("File Identifier: ".Length) == exeName)
+ {
+ // Account for Y2K date problem
+ if (exeDate != null && exeDate!.Substring(0, 2) == "19")
+ {
+ string decade = exeDate!.Substring(2, 1);
+ // Does only PSX need to account for 1920s-60s?
+ if (decade == "0" || decade == "1" ||
+ psx && (decade == "2" || decade == "3" || decade == "4" || decade == "5" || decade == "6"))
+ exeDate = $"20{exeDate!.Substring(2)}";
+ }
+
+ // Currently stored date is the EXE date, return it
+ return exeDate;
+ }
+
+ // The exe datetime is listed in a single line
+ if (line.Length >= "Recording Date and Time: ".Length + 10 &&
+ line.StartsWith("Recording Date and Time:"))
+ {
+ // exe date: ISO datetime (yyyy-MM-ddT.....)
+ exeDate = line.Substring("Recording Date and Time: ".Length, 10);
+ }
+
+ line = sr.ReadLine();
+ }
+
+ return null;
+ }
+ catch
+ {
+ // We don't care what the exception is right now
+ return null;
+ }
+ }
+
///
/// Get the build info from a GD-ROM LD area, if possible
///
diff --git a/MPF.Core/Modules/Redumper/Parameters.cs b/MPF.Core/Modules/Redumper/Parameters.cs
index af3d1ec03..5a25194e4 100644
--- a/MPF.Core/Modules/Redumper/Parameters.cs
+++ b/MPF.Core/Modules/Redumper/Parameters.cs
@@ -413,12 +413,13 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, Options options
break;
case RedumpSystem.KonamiPython2:
+ info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
- info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
+ info.CommonDiscInfo.EXEDateBuildDate ??= pythonTwoDate;
}
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
@@ -477,12 +478,13 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, Options options
break;
case RedumpSystem.SonyPlayStation:
+ info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
- info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
+ info.CommonDiscInfo.EXEDateBuildDate ??= playstationDate;
}
info.CopyProtection!.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}.log").ToYesNo();
@@ -492,12 +494,13 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, Options options
break;
case RedumpSystem.SonyPlayStation2:
+ info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
- info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
+ info.CommonDiscInfo.EXEDateBuildDate ??= playstationTwoDate;
}
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
@@ -1686,6 +1689,45 @@ public static long GetErrorCount(string log)
}
}
+ ///
+ /// Get the EXE Date from the log, if possible
+ ///
+ /// Log file location
+ /// EXE date if possible, null otherwise
+ public static string? GetEXEDate(string log)
+ {
+ // If the file doesn't exist, we can't get the info
+ if (!File.Exists(log))
+ return null;
+
+ try
+ {
+ using var sr = File.OpenText(log);
+ var line = sr.ReadLine();
+ while (line != null)
+ {
+ // Trim the line for later use
+ line = line.Trim();
+
+ // The exe date is listed in a single line
+ if (line.StartsWith("EXE date:"))
+ {
+ // exe date: yyyy-MM-dd
+ return line.Substring("EXE date: ".Length);
+ }
+
+ line = sr.ReadLine();
+ }
+
+ return null;
+ }
+ catch
+ {
+ // We don't care what the exception is right now
+ return null;
+ }
+ }
+
///
/// Get hardware information from the input file, if possible
///