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