diff --git a/DICUI.Library/DiscImageCreator/Parameters.cs b/DICUI.Library/DiscImageCreator/Parameters.cs index 1290a5e5..819641b4 100644 --- a/DICUI.Library/DiscImageCreator/Parameters.cs +++ b/DICUI.Library/DiscImageCreator/Parameters.cs @@ -2417,7 +2417,7 @@ namespace DICUI.DiscImageCreator /// Output region, if possible /// Output EXE date in "yyyy-mm-dd" format if possible, null on error /// - private static bool GetPlaystationExecutableInfo(char? driveLetter, out Region? region, out string date) + public static bool GetPlaystationExecutableInfo(char? driveLetter, out Region? region, out string date) { region = null; date = null; @@ -2437,31 +2437,26 @@ namespace DICUI.DiscImageCreator // Try both of the common paths that contain information string exeName = null; - // Try reading SYSTEM.CNF to find the "BOOT" value - if (File.Exists(systemCnfPath)) - { - try - { - using (StreamReader sr = File.OpenText(systemCnfPath)) - { - // Not assuming any ordering, just in case - string line = sr.ReadLine(); - while (!line.StartsWith("BOOT")) - line = sr.ReadLine(); + // Read the CNF file as an INI file + var keyValuePairs = IniParse.ParseIniFile(systemCnfPath); + string bootValue = string.Empty; - // Once it finds the "BOOT" line, extract the name, if possible - var match = Regex.Match(line, @"BOOT.*?=\s*cdrom.?:\\?(.*?)"); - if (match != null && match.Groups.Count > 1) - { - exeName = match.Groups[1].Value; - exeName = exeName.Split(';')[0]; - } - } - } - catch + // PlayStation uses "BOOT" as the key + if (keyValuePairs.ContainsKey("boot")) + bootValue = keyValuePairs["boot"]; + + // PlayStation 2 uses "BOOT2" as the key + if (keyValuePairs.ContainsKey("boot2")) + bootValue = keyValuePairs["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 != null && match.Groups.Count > 1) { - // We don't care what the error was - return false; + exeName = match.Groups[1].Value; + exeName = exeName.Split(';')[0]; } } @@ -2555,34 +2550,16 @@ namespace DICUI.DiscImageCreator if (!Directory.Exists(drivePath)) return null; - // If we can't find SYSTEM.CNF, we don't have a PlayStation 2 disc + // Get the SYSTEM.CNF path to check string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF"); - if (!File.Exists(systemCnfPath)) - return null; - // Try reading SYSTEM.CNF to find the "VER" value - try - { - using (StreamReader sr = File.OpenText(systemCnfPath)) - { - // Not assuming proper ordering, just in case - string line = sr.ReadLine(); - while (!line.StartsWith("VER")) - line = sr.ReadLine(); - - // Once it finds the "VER" line, extract the version - var match = Regex.Match(line, @"VER\s*=\s*(.*)"); - if (match != null && match.Groups.Count > 1) - return match.Groups[1].Value; - - return null; - } - } - catch - { - // We don't care what the error was - return null; - } + // Try to parse the SYSTEM.CNF file + var keyValuePairs = IniParse.ParseIniFile(systemCnfPath); + if (keyValuePairs.ContainsKey("ver")) + return keyValuePairs["ver"]; + + // If "VER" can't be found, we can't do much + return null; } /// diff --git a/DICUI.Library/Utilities/IniParse.cs b/DICUI.Library/Utilities/IniParse.cs new file mode 100644 index 00000000..66c69ce5 --- /dev/null +++ b/DICUI.Library/Utilities/IniParse.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace DICUI.Utilities +{ + public static class IniParse + { + /// + /// Read an INI file based on the path + /// + /// + /// + public static Dictionary ParseIniFile(string path) + { + // If we don't have a file, we can't read it + if (!File.Exists(path)) + return new Dictionary(); + + using (var fileStream = File.OpenRead(path)) + { + return ParseIniFile(fileStream); + } + } + + /// + /// Read an INI file from a stream + /// + /// + /// + public static Dictionary ParseIniFile(Stream stream) + { + // If the stream is invalid or unreadable, we can't process it + if (stream == null || !stream.CanRead || stream.Position >= stream.Length - 1) + return new Dictionary(); + + // Keys are case-insensitive by default + var keyValuePairs = new Dictionary(); + try + { + using (StreamReader sr = new StreamReader(stream)) + { + string section = string.Empty; + while (!sr.EndOfStream) + { + string line = sr.ReadLine().Trim(); + + // Comments start with ';' + if (line.StartsWith(";")) + { + // No-op, we don't process comments + } + + // Section titles are surrounded by square brackets + else if (line.StartsWith("[")) + { + section = line.TrimStart('[').TrimEnd(']'); + } + + // Valid INI lines are in the format key=value + else if (line.Contains("=")) + { + // Split the line by '=' for key-value pairs + string[] data = line.Split('='); + + // If the value field contains an '=', we need to put them back in + string key = data[0].Trim(); + string value = string.Join("=", data.Skip(1)).Trim(); + + // Section names are prepended to the key with a '.' separating + if (!string.IsNullOrEmpty(section)) + key = $"{section}.{key}"; + + // Set or overwrite keys in the returned dictionary + keyValuePairs[key.ToLowerInvariant()] = value; + } + + // All other lines are ignored + } + } + } + catch + { + // We don't care what the error was, just catch and return + return new Dictionary(); + } + + return keyValuePairs; + } + } +}