diff --git a/DICUI.Library/Aaru/Parameters.cs b/DICUI.Library/Aaru/Parameters.cs
index 7a9d3129..f79148bf 100644
--- a/DICUI.Library/Aaru/Parameters.cs
+++ b/DICUI.Library/Aaru/Parameters.cs
@@ -1396,7 +1396,38 @@ namespace DICUI.Aaru
switch (system)
{
+ case KnownSystem.KonamiPython2:
+ if (GetPlaystationExecutableInfo(drive?.Letter, out Region? pythonTwoRegion, out string pythonTwoDate))
+ {
+ info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
+ info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
+ }
+ info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
+ break;
+
+ case KnownSystem.SonyPlayStation:
+ if (GetPlaystationExecutableInfo(drive?.Letter, out Region? playstationRegion, out string playstationDate))
+ {
+ info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
+ info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
+ }
+
+ break;
+
+ case KnownSystem.SonyPlayStation2:
+ if (GetPlaystationExecutableInfo(drive?.Letter, out Region? playstationTwoRegion, out string playstationTwoDate))
+ {
+ info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
+ info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
+ }
+
+ info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
+ break;
+
+ case KnownSystem.SonyPlayStation4:
+ info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
+ break;
}
}
diff --git a/DICUI.Library/Data/BaseParameters.cs b/DICUI.Library/Data/BaseParameters.cs
index 4c5178e5..5cda0967 100644
--- a/DICUI.Library/Data/BaseParameters.cs
+++ b/DICUI.Library/Data/BaseParameters.cs
@@ -224,6 +224,8 @@ namespace DICUI.Data
{ }
}
+ #region Parameter Parsing
+
///
/// Returns whether or not the selected item exists
///
@@ -378,5 +380,261 @@ namespace DICUI.Data
return true;
}
+
+ #endregion
+
+ #region Common Information Extraction
+
+ ///
+ /// Get the EXE date from a PlayStation disc, if possible
+ ///
+ /// Drive letter to use to check
+ /// Output region, if possible
+ /// Output EXE date in "yyyy-mm-dd" format if possible, null on error
+ ///
+ protected static bool GetPlaystationExecutableInfo(char? driveLetter, out Region? region, out string date)
+ {
+ 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
+ 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.?:\\?(.*)");
+ if (match != null && match.Groups.Count > 1)
+ {
+ exeName = match.Groups[1].Value;
+ exeName = exeName.Split(';')[0];
+ }
+ }
+
+ // 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
+ FileInfo fi = new FileInfo(exePath);
+ DateTime 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 letter to use to check
+ /// Game version if possible, null on error
+ protected static string GetPlayStation2Version(char? driveLetter)
+ {
+ // 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;
+ }
+
+ ///
+ /// Get the version from a PlayStation 4 disc, if possible
+ ///
+ /// Drive letter to use to check
+ /// Game version if possible, null on error
+ protected static string GetPlayStation4Version(char? driveLetter)
+ {
+ // 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 (BinaryReader 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;
+ }
+ }
+
+ #endregion
+
+ #region Category Extraction
+
+ ///
+ /// Determine the category based on the UMDImageCreator string
+ ///
+ /// String representing the category
+ /// Category, if possible
+ protected static Category? GetUMDCategory(string category)
+ {
+ switch (category)
+ {
+ case "GAME":
+ return Category.Games;
+ case "VIDEO":
+ return Category.Video;
+ case "AUDIO":
+ return Category.Audio;
+ default:
+ return null;
+ }
+ }
+
+ #endregion
+
+ #region Region Extraction
+
+ ///
+ /// Determine the region based on the PlayStation serial code
+ ///
+ /// PlayStation serial code
+ /// Region mapped from name, if possible
+ 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 'J':
+ return Region.JapanKorea;
+ case 'K':
+ return Region.Korea;
+ case 'P':
+ return Region.Japan;
+ case 'U':
+ return Region.USA;
+ }
+ }
+
+ // Japan-only special serial
+ else if (serial.StartsWith("PAPX"))
+ return Region.Japan;
+
+ // Region appears entirely random
+ else if (serial.StartsWith("PABX"))
+ return null;
+
+ // Japan-only special serial
+ else if (serial.StartsWith("PCBX"))
+ 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;
+
+ return null;
+ }
+
+ ///
+ /// Determine the region based on the XGD serial character
+ ///
+ /// Character denoting the region
+ /// Region, if possible
+ protected static Region? GetXgdRegion(char region)
+ {
+ switch (region)
+ {
+ case 'W':
+ return Region.World;
+ case 'A':
+ return Region.USA;
+ 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
}
}
\ No newline at end of file
diff --git a/DICUI.Library/DiscImageCreator/Parameters.cs b/DICUI.Library/DiscImageCreator/Parameters.cs
index 27e0a733..ed03e1aa 100644
--- a/DICUI.Library/DiscImageCreator/Parameters.cs
+++ b/DICUI.Library/DiscImageCreator/Parameters.cs
@@ -1653,7 +1653,7 @@ namespace DICUI.DiscImageCreator
break;
case KnownSystem.MicrosoftXBOX:
- if (GetXBOXAuxInfo(basePath + "_disc.txt", out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver))
+ if (GetXgdAuxInfo(basePath + "_disc.txt", out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver))
{
info.CommonDiscInfo.Comments += $"{Template.XBOXDMIHash}: {dmihash ?? ""}\n" +
$"{Template.XBOXPFIHash}: {pfihash ?? ""}\n" +
@@ -1662,7 +1662,7 @@ namespace DICUI.DiscImageCreator
info.Extras.SecuritySectorRanges = ss ?? "";
}
- if (GetXBOXDMIInfo(Path.Combine(outputDirectory, "DMI.bin"), out string serial, out string version, out Region? region))
+ if (GetXboxDMIInfo(Path.Combine(outputDirectory, "DMI.bin"), out string serial, out string version, out Region? region))
{
info.CommonDiscInfo.Serial = serial ?? "";
info.VersionAndEditions.Version = version ?? "";
@@ -1672,7 +1672,7 @@ namespace DICUI.DiscImageCreator
break;
case KnownSystem.MicrosoftXBOX360:
- if (GetXBOXAuxInfo(basePath + "_disc.txt", out string dmi360hash, out string pfi360hash, out string ss360hash, out string ss360, out string ssver360))
+ if (GetXgdAuxInfo(basePath + "_disc.txt", out string dmi360hash, out string pfi360hash, out string ss360hash, out string ss360, out string ssver360))
{
info.CommonDiscInfo.Comments += $"{Template.XBOXDMIHash}: {dmi360hash ?? ""}\n" +
$"{Template.XBOXPFIHash}: {pfi360hash ?? ""}\n" +
@@ -1681,7 +1681,7 @@ namespace DICUI.DiscImageCreator
info.Extras.SecuritySectorRanges = ss360 ?? "";
}
- if (GetXBOX360DMIInfo(Path.Combine(outputDirectory, "DMI.bin"), out string serial360, out string version360, out Region? region360))
+ if (GetXbox360DMIInfo(Path.Combine(outputDirectory, "DMI.bin"), out string serial360, out string version360, out Region? region360))
{
info.CommonDiscInfo.Serial = serial360 ?? "";
info.VersionAndEditions.Version = version360 ?? "";
@@ -1970,7 +1970,7 @@ namespace DICUI.DiscImageCreator
private void SetBaseCommand(KnownSystem? system, MediaType? type)
{
// If we have an invalid combination, we should BaseCommand = null
- if (!Utilities.Validators.GetValidMediaTypes(system).Contains(type))
+ if (!Validators.GetValidMediaTypes(system).Contains(type))
{
BaseCommand = Command.NONE;
return;
@@ -2410,195 +2410,6 @@ namespace DICUI.DiscImageCreator
}
}
- ///
- /// Get the EXE date from a PlayStation disc, if possible
- ///
- /// Drive letter to use to check
- /// 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)
- {
- 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
- string exeName = null;
-
- // Read the CNF file as an INI file
- var keyValuePairs = IniParse.ParseIniFile(systemCnfPath);
- string bootValue = string.Empty;
-
- // 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)
- {
- exeName = match.Groups[1].Value;
- exeName = exeName.Split(';')[0];
- }
- }
-
- // 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;
-
- // Standardized "S" serials
- if (exeName.StartsWith("S"))
- {
- // string publisher = exeName[0] + exeName[1];
- // char secondRegion = exeName[3];
- switch (exeName[2])
- {
- case 'A':
- region = Region.Asia;
- break;
- case 'C':
- region = Region.China;
- break;
- case 'E':
- region = Region.Europe;
- break;
- case 'J':
- region = Region.JapanKorea;
- break;
- case 'K':
- region = Region.Korea;
- break;
- case 'P':
- region = Region.Japan;
- break;
- case 'U':
- region = Region.USA;
- break;
- }
- }
-
- // Special cases
- else if (exeName.StartsWith("PAPX"))
- {
- region = Region.Japan;
- }
- else if (exeName.StartsWith("PABX"))
- {
- // Region appears entirely random
- }
- else if (exeName.StartsWith("PCBX"))
- {
- region = Region.Japan;
- }
- else if (exeName.StartsWith("PDBX"))
- {
- // Single disc known, Japan
- }
- else if (exeName.StartsWith("PEBX"))
- {
- // Single disc known, Europe
- }
-
- // 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
- FileInfo fi = new FileInfo(exePath);
- DateTime 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 letter to use to check
- /// Game version if possible, null on error
- private static string GetPlayStation2Version(char? driveLetter)
- {
- // 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 keyValuePairs = IniParse.ParseIniFile(systemCnfPath);
- if (keyValuePairs.ContainsKey("ver"))
- return keyValuePairs["ver"];
-
- // If "VER" can't be found, we can't do much
- return null;
- }
-
- ///
- /// Get the version from a PlayStation 4 disc, if possible
- ///
- /// Drive letter to use to check
- /// Game version if possible, null on error
- private static string GetPlayStation4Version(char? driveLetter)
- {
- // 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 (BinaryReader 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 PVD from the input file, if possible
///
@@ -2835,26 +2646,6 @@ namespace DICUI.DiscImageCreator
}
}
- ///
- /// Determine the category based on the UMDImageCreator string
- ///
- /// String representing the category
- /// Category, if possible
- private static Category? GetUMDCategory(string category)
- {
- switch (category)
- {
- case "GAME":
- return Category.Games;
- case "VIDEO":
- return Category.Video;
- case "AUDIO":
- return Category.Audio;
- default:
- return null;
- }
- }
-
///
/// Get the write offset from the input file, if possible
///
@@ -2864,9 +2655,7 @@ namespace DICUI.DiscImageCreator
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(disc))
- {
return null;
- }
using (StreamReader sr = File.OpenText(disc))
{
@@ -2890,11 +2679,11 @@ namespace DICUI.DiscImageCreator
}
///
- /// Get the XBOX/360 auxiliary info from the outputted files, if possible
+ /// Get the XGD auxiliary info from the outputted files, if possible
///
/// _disc.txt file location
/// True on successful extraction of info, false otherwise
- private static bool GetXBOXAuxInfo(string disc, out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver)
+ private static bool GetXgdAuxInfo(string disc, out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver)
{
dmihash = null; pfihash = null; sshash = null; ss = null; ssver = null;
@@ -2906,46 +2695,45 @@ namespace DICUI.DiscImageCreator
{
try
{
- // Fast forward to the Security Sector version and read it
- while (!sr.ReadLine().Trim().StartsWith("CPR_MAI Key")) ;
- ssver = sr.ReadLine().Trim().Split(' ')[4]; // "Version of challenge table: "
-
- // Fast forward to the Security Sector Ranges
- while (!sr.ReadLine().Trim().StartsWith("Number of security sector ranges:")) ;
-
- // Now that we're at the ranges, read each line in and concatenate
- Regex layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)");
- string line = sr.ReadLine().Trim();
- while (!line.StartsWith("========== Unlock 2 state(wxripper) =========="))
+ while(!sr.EndOfStream)
{
- // If we have a recognized line format, parse it
- if (line.StartsWith("Layer "))
+ string line = sr.ReadLine().Trim();
+
+ // Security Sector version
+ if (line.StartsWith("Version of challenge table"))
{
- var match = layerRegex.Match(line);
- ss += $"{match.Groups[1]}-{match.Groups[2]}\n";
+ ssver = line.Split(' ')[4]; // "Version of challenge table: "
}
- line = sr.ReadLine().Trim();
- }
+ // Security Sector ranges
+ else if (line.StartsWith("Number of security sector ranges:"))
+ {
+ Regex layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)");
- // Fast forward to the aux hashes
- while (!line.StartsWith("
- /// Get the XOX serial info from the DMI.bin file, if possible
+ /// Get the Xbox serial info from the DMI.bin file, if possible
///
/// DMI.bin file location
/// True on successful extraction of info, false otherwise
- private static bool GetXBOXDMIInfo(string dmi, out string serial, out string version, out Region? region)
+ private static bool GetXboxDMIInfo(string dmi, out string serial, out string version, out Region? region)
{
serial = null; version = null; region = Region.World;
@@ -2979,7 +2767,7 @@ namespace DICUI.DiscImageCreator
serial = $"{str[0]}{str[1]}-{str[2]}{str[3]}{str[4]}";
version = $"1.{str[5]}{str[6]}";
- region = GetXBOXRegion(str[7]);
+ region = GetXgdRegion(str[7]);
return true;
}
catch
@@ -2990,13 +2778,13 @@ namespace DICUI.DiscImageCreator
}
///
- /// Get the XBOX 360 serial info from the DMI.bin file, if possible
+ /// Get the Xbox 360 serial info from the DMI.bin file, if possible
///
/// DMI.bin file location
/// True on successful extraction of info, false otherwise
- private static bool GetXBOX360DMIInfo(string dmi, out string serial, out string version, out Region? region)
+ private static bool GetXbox360DMIInfo(string dmi, out string serial, out string version, out Region? region)
{
- serial = null; version = null; region = null;
+ serial = null; version = null; region = Region.World;
if (!File.Exists(dmi))
return false;
@@ -3010,7 +2798,7 @@ namespace DICUI.DiscImageCreator
serial = $"{str[0]}{str[1]}-{str[2]}{str[3]}{str[4]}{str[5]}";
version = $"1.{str[6]}{str[7]}";
- region = GetXBOXRegion(str[8]);
+ region = GetXgdRegion(str[8]);
// str[9], str[10], str[11] - unknown purpose
// str[12], str[13] - disc <12> of <13>
return true;
@@ -3022,34 +2810,6 @@ namespace DICUI.DiscImageCreator
}
}
- ///
- /// Determine the region based on the XBOX serial character
- ///
- /// Character denoting the region
- /// Region, if possible
- private static Region? GetXBOXRegion(char region)
- {
- switch (region)
- {
- case 'W':
- return Region.World;
- case 'A':
- return Region.USA;
- 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
}
}
diff --git a/DICUI.Library/Utilities/IniFile.cs b/DICUI.Library/Utilities/IniFile.cs
new file mode 100644
index 00000000..ac2f4120
--- /dev/null
+++ b/DICUI.Library/Utilities/IniFile.cs
@@ -0,0 +1,234 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace DICUI.Utilities
+{
+ public class IniFile
+ {
+ private Dictionary _keyValuePairs;
+
+ public string this[string key]
+ {
+ get
+ {
+ if (_keyValuePairs == null)
+ _keyValuePairs = new Dictionary();
+
+ key = key.ToLowerInvariant();
+ if (_keyValuePairs.ContainsKey(key))
+ return _keyValuePairs[key];
+
+ return null;
+ }
+ set
+ {
+ if (_keyValuePairs == null)
+ _keyValuePairs = new Dictionary();
+
+ key = key.ToLowerInvariant();
+ _keyValuePairs[key] = value;
+ }
+ }
+
+ ///
+ /// Create an empty INI file
+ ///
+ public IniFile()
+ {
+ _keyValuePairs = new Dictionary();
+ }
+
+ ///
+ /// Populate an INI file from path
+ ///
+ public IniFile(string path)
+ {
+ this.Parse(path);
+ }
+
+ ///
+ /// Populate an INI file from stream
+ ///
+ public IniFile(Stream stream)
+ {
+ this.Parse(stream);
+ }
+
+ ///
+ /// Check if the key exists in the INI file
+ ///
+ public bool ContainsKey(string key)
+ {
+ return _keyValuePairs.ContainsKey(key.ToLowerInvariant());
+ }
+
+ ///
+ /// Add or update a key and value to the INI file
+ ///
+ public void AddOrUpdate(string key, string value)
+ {
+ _keyValuePairs[key.ToLowerInvariant()] = value;
+ }
+
+ ///
+ /// Remove a key from the INI file
+ ///
+ public void Remove(string key)
+ {
+ _keyValuePairs.Remove(key.ToLowerInvariant());
+ }
+
+ ///
+ /// Read an INI file based on the path
+ ///
+ public bool Parse(string path)
+ {
+ // If we don't have a file, we can't read it
+ if (!File.Exists(path))
+ return false;
+
+ using (var fileStream = File.OpenRead(path))
+ {
+ return Parse(fileStream);
+ }
+ }
+
+ ///
+ /// Read an INI file from a stream
+ ///
+ public bool Parse(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 false;
+
+ // Keys are case-insensitive by default
+ 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 false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Write an INI file to a path
+ ///
+ public bool Write(string path)
+ {
+ // If we don't have a valid dictionary with values, we can't write out
+ if (_keyValuePairs == null || _keyValuePairs.Count == 0)
+ return false;
+
+ using (var fileStream = File.OpenWrite(path))
+ {
+ return Write(fileStream);
+ }
+ }
+
+ ///
+ /// Write an INI file to a stream
+ ///
+ public bool Write(Stream stream)
+ {
+ // If we don't have a valid dictionary with values, we can't write out
+ if (_keyValuePairs == null || _keyValuePairs.Count == 0)
+ return false;
+
+ // If the stream is invalid or unwritable, we can't output to it
+ if (stream == null || !stream.CanWrite || stream.Position >= stream.Length - 1)
+ return false;
+
+ try
+ {
+ using (StreamWriter sw = new StreamWriter(stream))
+ {
+ // Order the dictionary by keys to link sections together
+ var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
+
+ string section = string.Empty;
+ foreach (var keyValuePair in orderedKeyValuePairs)
+ {
+ // Extract the key and value
+ string key = keyValuePair.Key;
+ string value = keyValuePair.Value;
+
+ // We assume '.' is a section name separator
+ if (key.Contains('.'))
+ {
+ // Split the key by '.'
+ string[] data = keyValuePair.Key.Split('.');
+
+ // If the key contains an '.', we need to put them back in
+ string newSection = data[0].Trim();
+ key = string.Join(".", data.Skip(1)).Trim();
+
+ // If we have a new section, write it out
+ if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
+ {
+ sw.WriteLine($"[{newSection}]");
+ section = newSection;
+ }
+ }
+
+ // Now write out the key and value in a standardized way
+ sw.WriteLine($"{key}={value}");
+ }
+ }
+ }
+ catch
+ {
+ // We don't care what the error was, just catch and return
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/DICUI.Library/Utilities/IniParse.cs b/DICUI.Library/Utilities/IniParse.cs
deleted file mode 100644
index 66c69ce5..00000000
--- a/DICUI.Library/Utilities/IniParse.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-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;
- }
- }
-}
diff --git a/DICUI.Library/Utilities/Validators.cs b/DICUI.Library/Utilities/Validators.cs
index 3c728e87..7f98dd3d 100644
--- a/DICUI.Library/Utilities/Validators.cs
+++ b/DICUI.Library/Utilities/Validators.cs
@@ -918,22 +918,19 @@ namespace DICUI.Utilities
}
// Sony PlayStation and Sony PlayStation 2
- if (File.Exists(Path.Combine(drivePath, "SYSTEM.CNF")))
+ string psxExePath = Path.Combine(drivePath, "PSX.EXE");
+ string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
+ if (File.Exists(systemCnfPath))
{
// Check for either BOOT or BOOT2
- using (StreamReader reader = File.OpenText(Path.Combine(drivePath, "SYSTEM.CNF")))
- {
- while (!reader.EndOfStream)
- {
- string line = reader.ReadLine();
- if (line.Contains("BOOT2"))
- return KnownSystem.SonyPlayStation2;
- else if (line.Contains("BOOT"))
- return KnownSystem.SonyPlayStation;
- }
- }
-
- // If we have a weird disc, just assume PS1
+ var systemCnf = new IniFile(systemCnfPath);
+ if (systemCnf.ContainsKey("BOOT"))
+ return KnownSystem.SonyPlayStation;
+ else if (systemCnf.ContainsKey("BOOT2"))
+ return KnownSystem.SonyPlayStation2;
+ }
+ else if (File.Exists(psxExePath))
+ {
return KnownSystem.SonyPlayStation;
}