From 07d8a5d38d9fe18203e87704e3ba3fdabfbee4d5 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 28 Jan 2026 16:01:25 +0900
Subject: [PATCH 01/17] Cleanup SS handling
---
MPF.Processors/ProcessingTool.cs | 186 +++++++++++++---------------
MPF.Processors/Redumper.cs | 13 +-
MPF.Processors/XboxBackupCreator.cs | 49 ++++----
3 files changed, 120 insertions(+), 128 deletions(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index ed9b51a12..dfb79a91d 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1095,108 +1095,61 @@ public static bool IsValidSS(byte[] ss)
// Must be a valid XGD type
if (!GetXGDType(ss, out int xgdType))
return false;
+
+ // Drive entry table must be duplicated exactly
+ if (!data.Skip(0x661).Take(0x730 - 0x661).SequenceEqual(data.Skip(0x730).Take(0x7FF - 0x730)))
+ return false;
- // Only continue to check SSv2 for XGD3
- if (xgdType != 3)
+ // Remaining checks are only for Xbox360 SS
+ if (xgdType == 1)
return true;
- // Determine if XGD3 SS.bin is SSv1 (Original Kreon) or SSv2 (0800 / Repaired Kreon)
+ // Determine if XGD3 SS is invalid SSv1 (Original Kreon) or valid SSv2 (0800 / Custom Kreon)
+ if (xgdType != 3)
+ {
+ bool bad_xgd3 = false;
#if NET20
- var checkArr = new byte[72];
- Array.Copy(ss, 32, checkArr, 0, 72);
- return Array.Exists(checkArr, x => x != 0);
+ var checkArr = new byte[72];
+ Array.Copy(ss, 32, checkArr, 0, 72);
+ bad_xgd3 = Array.Exists(checkArr, x => x != 0);
#else
- return ss.Skip(32).Take(72).Any(x => x != 0);
+ bad_xgd3 = ss.Skip(32).Take(72).Any(x => x != 0);
#endif
- }
-
- ///
- /// Determine if a given SS.bin is valid but contains zeroed challenge responses
- ///
- /// Path to the SS file to check
- /// True if valid but partial SS.bin, false otherwise
- public static bool IsValidPartialSS(string ssPath)
- {
- if (!File.Exists(ssPath))
- return false;
+ if (bad_xgd3)
+ return false;
+ }
- byte[] ss = File.ReadAllBytes(ssPath);
- if (ss.Length != 2048)
+ // Must have correct version and number of CCRT entries
+ if (ss[0x300] != 2 || ss[0x301] != 21 || ss[0x65F] != 0x02 || ss[0x49E != 0x04])
return false;
-
- return IsValidPartialSS(ss);
+
+ return true;
}
///
- /// Determine if a given SS is valid but contains zeroed challenge responses
+ /// Determine if a given SS has already been repaired and cleaned
///
/// Byte array of SS sector
- /// True if SS is a valid but partial SS, false otherwise
- public static bool IsValidPartialSS(byte[] ss)
+ /// True if SS is repaired and cleaned, false otherwise
+ public static bool IsFixedSS(byte[] ss)
{
- // Check 1 sector long
if (ss.Length != 2048)
return false;
- // Must be a valid XGD type
- if (!GetXGDType(ss, out int xgdType))
- return false;
-
- // Determine challenge table offset, XGD1 is never partial
- int ccrt_offset = 0;
- if (xgdType == 1)
- return false;
- else if (xgdType == 2)
- ccrt_offset = 0x200;
- else if (xgdType == 3)
- ccrt_offset = 0x20;
-
- int[] entry_offsets = [0, 9, 18, 27, 36, 45, 54, 63];
- int[] entry_lengths = [8, 8, 8, 8, 4, 4, 4, 4];
- for (int i = 0; i < entry_offsets.Length; i++)
- {
- bool emptyResponse = true;
- for (int b = 0; b < entry_lengths[i]; b++)
- {
- if (ss[ccrt_offset + entry_offsets[i] + b] != 0x00)
- {
- emptyResponse = false;
- break;
- }
- }
-
- if (emptyResponse)
- return true;
- }
-
- return false;
- }
-
- ///
- /// Determine if a given SS has already been cleaned
- ///
- /// Byte array of SS sector
- /// True if SS is clean, false otherwise
- public static bool IsCleanSS(byte[] ss)
- {
- if (ss.Length != 2048)
+ if (!IsValid(ss))
return false;
if (!GetXGDType(ss, out int xgdType))
return false;
+
+ // Valid XGD1 is always fixed
+ if (xgdType == 1)
+ return true;
-#if NET20
- var checkArr = new byte[72];
- Array.Copy(ss, 32, checkArr, 0, 72);
- if (xgdType == 3 && Array.Exists(checkArr, x => x != 0))
-#else
- if (xgdType == 3 && ss.Skip(32).Take(72).Any(x => x != 0))
-#endif
+ if (xgdType == 3)
{
// Check for a cleaned SSv2
-
int rtOffset = 0x24;
-
if (ss[rtOffset + 36] != 0x01)
return false;
if (ss[rtOffset + 37] != 0x00)
@@ -1232,55 +1185,80 @@ public static bool IsCleanSS(byte[] ss)
}
else
{
- // Check for a cleaned SSv1
-
+ // Check for a cleaned XGD2
int rtOffset = 0x204;
-
if (ss[rtOffset + 36] != 0x01)
return false;
if (ss[rtOffset + 37] != 0x00)
return false;
- if (xgdType == 2 && ss[rtOffset + 39] != 0x00)
+ if (ss[rtOffset + 39] != 0x00)
return false;
- if (xgdType == 2 && ss[rtOffset + 40] != 0x00)
+ if (ss[rtOffset + 40] != 0x00)
return false;
if (ss[rtOffset + 45] != 0x5B)
return false;
if (ss[rtOffset + 46] != 0x00)
return false;
- if (xgdType == 2 && ss[rtOffset + 48] != 0x00)
+ if (ss[rtOffset + 48] != 0x00)
return false;
- if (xgdType == 2 && ss[rtOffset + 49] != 0x00)
+ if (ss[rtOffset + 49] != 0x00)
return false;
if (ss[rtOffset + 54] != 0xB5)
return false;
if (ss[rtOffset + 55] != 0x00)
return false;
- if (xgdType == 2 && ss[rtOffset + 57] != 0x00)
+ if (ss[rtOffset + 57] != 0x00)
return false;
- if (xgdType == 2 && ss[rtOffset + 58] != 0x00)
+ if (ss[rtOffset + 58] != 0x00)
return false;
if (ss[rtOffset + 63] != 0x0F)
return false;
if (ss[rtOffset + 64] != 0x01)
return false;
- if (xgdType == 2 && ss[rtOffset + 66] != 0x00)
+ if (ss[rtOffset + 66] != 0x00)
return false;
- if (xgdType == 2 && ss[rtOffset + 67] != 0x00)
+ if (ss[rtOffset + 67] != 0x00)
return false;
}
- // All angles are as expected, it is clean
+ // Determine challenge table offset
+ int ccrt_offset = 0;
+ if (xgdType == 2)
+ ccrt_offset = 0x200;
+ else if (xgdType == 3)
+ ccrt_offset = 0x20;
+
+ // Check for empty challenge responses
+ int[] entry_offsets = [0, 9, 18, 27, 36, 45, 54, 63];
+ int[] entry_lengths = [8, 8, 8, 8, 4, 4, 4, 4];
+ for (int i = 0; i < entry_offsets.Length; i++)
+ {
+ bool emptyResponse = true;
+ for (int b = 0; b < entry_lengths[i]; b++)
+ {
+ if (ss[ccrt_offset + entry_offsets[i] + b] != 0x00)
+ {
+ emptyResponse = false;
+ break;
+ }
+ }
+
+ if (emptyResponse)
+ return false;
+ }
+
+ // TODO: Check for correct challenge responses
+
return true;
}
///
- /// Clean a rawSS.bin file and write it to a file
+ /// Repair and clean a rawSS.bin file and write it to a file
///
/// Path to the raw SS file to read from
- /// Path to the clean SS file to write to
+ /// Path to the fixed SS file to write to
/// True if successful, false otherwise
- public static bool CleanSS(string rawSS, string cleanSS)
+ public static bool FixSS(string rawSS, string fixedSS)
{
if (!File.Exists(rawSS))
return false;
@@ -1289,20 +1267,20 @@ public static bool CleanSS(string rawSS, string cleanSS)
if (ss.Length != 2048)
return false;
- if (!CleanSS(ss))
+ if (!FixSS(ss))
return false;
- File.WriteAllBytes(cleanSS, ss);
+ File.WriteAllBytes(fixedSS, ss);
return true;
}
///
- /// Fix a SS sector to its predictable clean form.
- /// With help from ss_sector_range
+ /// Repair and clean a SS sector to its valid, predictable clean form.
+ /// Note: Also see ss_sector_range and abgx360
///
/// Byte array of raw SS sector
/// True if successful, false otherwise
- public static bool CleanSS(byte[] ss)
+ public static bool FixSS(byte[] ss)
{
// Must be entire sector
if (ss.Length != 2048)
@@ -1329,11 +1307,12 @@ public static bool CleanSS(byte[] ss)
if (xgdType == 3 && !ssv2)
return false;
+ // Clean SS (set fixed angles)
switch (xgdType)
{
case 1:
- // Leave Original Xbox SS.bin unchanged
- return true;
+ // Cannot clean XGD1 SS.bin
+ break;
case 2:
// Fix standard SSv1 ss.bin
@@ -1356,7 +1335,7 @@ public static bool CleanSS(byte[] ss)
ss[580] = 1; // 0x01
ss[582] = 0; // 0x00
ss[583] = 0; // 0x00
- return true;
+ break;
case 3:
if (ssv2)
@@ -1395,13 +1374,16 @@ public static bool CleanSS(byte[] ss)
ss[579] = 15; // 0x0F
ss[580] = 1; // 0x01
}
-
- return true;
+ break;
default:
// Unknown XGD type
return false;
}
+
+ // TODO: Repair challenge responses
+
+ return true;
}
///
diff --git a/MPF.Processors/Redumper.cs b/MPF.Processors/Redumper.cs
index ca995c71e..621c39344 100644
--- a/MPF.Processors/Redumper.cs
+++ b/MPF.Processors/Redumper.cs
@@ -282,7 +282,7 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
if (!File.Exists($"{basePath}.pfi"))
RemoveHeaderAndTrim($"{basePath}.physical", $"{basePath}.pfi");
if (!File.Exists($"{basePath}.ss"))
- ProcessingTool.CleanSS($"{basePath}.security", $"{basePath}.ss");
+ ProcessingTool.FixSS($"{basePath}.security", $"{basePath}.ss");
string xmidString = ProcessingTool.GetXMID($"{basePath}.dmi").Trim('\0');
if (!string.IsNullOrEmpty(xmidString))
@@ -316,12 +316,21 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
string? pfiCrc = HashTool.GetFileHash($"{basePath}.pfi", HashType.CRC32);
if (pfiCrc is not null)
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = pfiCrc.ToUpperInvariant();
- if (ProcessingTool.IsValidSS($"{basePath}.ss") && !ProcessingTool.IsValidPartialSS($"{basePath}.ss"))
+
+ // Only record SS hash if it is valid
+ if (ProcessingTool.IsFixedSS($"{basePath}.ss"))
{
string? ssCrc = HashTool.GetFileHash($"{basePath}.ss", HashType.CRC32);
if (ssCrc is not null)
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = ssCrc.ToUpperInvariant();
}
+ else if (ProcessingTool.FixSS($"{basePath}.ss", $"{basePath}.ss"))
+ {
+ // Attempt to repair bad .ss file succeeded, hash it
+ string? ssCrc = HashTool.GetFileHash($"{basePath}.ss", HashType.CRC32);
+ if (ssCrc is not null)
+ info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = ssCrc.ToUpperInvariant();
+ }
string? ssRanges = ProcessingTool.GetSSRanges($"{basePath}.ss");
if (!string.IsNullOrEmpty(ssRanges))
diff --git a/MPF.Processors/XboxBackupCreator.cs b/MPF.Processors/XboxBackupCreator.cs
index 3d7213202..0a07e0b47 100644
--- a/MPF.Processors/XboxBackupCreator.cs
+++ b/MPF.Processors/XboxBackupCreator.cs
@@ -105,33 +105,34 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
}
#pragma warning restore IDE0010
- // Get the output file paths
+ // Hash DMI/PFI
string dmiPath = Path.Combine(outputDirectory, "DMI.bin");
+ if (File.Exists(dmiPath))
+ info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = HashTool.GetFileHash(dmiPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
string pfiPath = Path.Combine(outputDirectory, "PFI.bin");
- string ssPath = Path.Combine(outputDirectory, "SS.bin");
+ if (File.Exists(pfiPath))
+ info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = HashTool.GetFileHash(pfiPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
// Deal with SS.bin
+ string ssPath = Path.Combine(outputDirectory, "SS.bin");
if (File.Exists(ssPath))
{
- // Save security sector ranges
- string? ranges = ProcessingTool.GetSSRanges(ssPath);
- if (!string.IsNullOrEmpty(ranges))
- info.Extras.SecuritySectorRanges = ranges;
+ // Ensure a raw SS is saved (recreate from log if needed)
+ RecreateSS(logPath!, ssPath, Path.Combine(outputDirectory, "RawSS.bin"));.
- // Recreate RawSS.bin
- RecreateSS(logPath!, ssPath, Path.Combine(outputDirectory, "RawSS.bin"));
+ if (ProcessingTool.IsValidSS(ssPath))
+ {
+ // Save security sector ranges
+ string? ranges = ProcessingTool.GetSSRanges(ssPath);
+ if (!string.IsNullOrEmpty(ranges))
+ info.Extras.SecuritySectorRanges = ranges;
+ }
- // Run ss_sector_range to get repeatable SS hash
- ProcessingTool.CleanSS(ssPath, ssPath);
- }
+ // Repair and clean SS, only hash SS if successful
+ if (ProcessingTool.FixSS(ssPath, ssPath))
+ info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = HashTool.GetFileHash(ssPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
- // DMI/PFI/SS CRC32 hashes
- if (File.Exists(dmiPath))
- info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = HashTool.GetFileHash(dmiPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
- if (File.Exists(pfiPath))
- info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = HashTool.GetFileHash(pfiPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
- if (File.Exists(ssPath))
- info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = HashTool.GetFileHash(ssPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
+ }
}
///
@@ -507,18 +508,18 @@ internal bool GetReadErrors(string? log, out long readErrors)
}
///
- /// Recreate an SS.bin file from XBC log and write it to a file
+ /// Recreate a RawSS.bin file from XBC log and write it to a file
///
/// Path to XBC log
- /// Path to the clean SS file to read from
- /// Path to the raw SS file to write to
+ /// Path to the provided SS file to read from
+ /// Path to the recreated SS file to write to
/// True if successful, false otherwise
- private static bool RecreateSS(string log, string cleanSS, string rawSS)
+ private static bool RecreateSS(string log, string currentSS, string rawSS)
{
- if (!File.Exists(log) || !File.Exists(cleanSS))
+ if (!File.Exists(log) || !File.Exists(currentSS) || File.Exists(rawSS))
return false;
- byte[] ss = File.ReadAllBytes(cleanSS);
+ byte[] ss = File.ReadAllBytes(currentSS);
if (ss.Length != 2048)
return false;
From fbaf6674a9018b11c89731a16fb9a7aae21e8f77 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 28 Jan 2026 16:09:06 +0900
Subject: [PATCH 02/17] Fix SS validity check
---
MPF.Processors/ProcessingTool.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index dfb79a91d..57f0c1bc8 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1105,7 +1105,7 @@ public static bool IsValidSS(byte[] ss)
return true;
// Determine if XGD3 SS is invalid SSv1 (Original Kreon) or valid SSv2 (0800 / Custom Kreon)
- if (xgdType != 3)
+ if (xgdType == 3)
{
bool bad_xgd3 = false;
#if NET20
@@ -1119,8 +1119,8 @@ public static bool IsValidSS(byte[] ss)
return false;
}
- // Must have correct version and number of CCRT entries
- if (ss[0x300] != 2 || ss[0x301] != 21 || ss[0x65F] != 0x02 || ss[0x49E != 0x04])
+ // XGD2 must have correct version (2) and number of CCRT entries (21)
+ if (ss[0x300] != 2 || ss[0x301] != 21 || ss[0x65F] != 2)
return false;
return true;
From 0e9dc60bcc93d1bdb1fe8e793a8afbe3103dc3c7 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 28 Jan 2026 17:18:11 +0900
Subject: [PATCH 03/17] Clean SS for DIC dumps too
---
MPF.Processors/DiscImageCreator.cs | 51 ++++++++++++++++++++++++++---
MPF.Processors/XboxBackupCreator.cs | 8 +++--
2 files changed, 53 insertions(+), 6 deletions(-)
diff --git a/MPF.Processors/DiscImageCreator.cs b/MPF.Processors/DiscImageCreator.cs
index f4bfe440c..a761faad0 100644
--- a/MPF.Processors/DiscImageCreator.cs
+++ b/MPF.Processors/DiscImageCreator.cs
@@ -345,7 +345,8 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
{
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd1DMIHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash ?? string.Empty;
- info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash ?? string.Empty;
+ // Don't put raw SS hash from _suppl.dat / _disc.txt in submission info
+ //info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash ?? string.Empty;
}
if (GetXGDAuxInfo($"{basePath}_disc.txt", out _, out _, out _, out var xgd1SS))
@@ -359,11 +360,30 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
{
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd1DMIHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash ?? string.Empty;
- info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash ?? string.Empty;
+ // Don't put raw SS hash from _suppl.dat / _disc.txt in submission info
+ //info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash ?? string.Empty;
info.Extras.SecuritySectorRanges = xgd1SS ?? string.Empty;
}
}
+ string xgd1SSPath = $"{basePath}_SS.bin";
+ string xgd1RawSSPath = $"{basePath}_RawSS.bin";
+ if (File.Exists(xgd1SSPath) && ProcessingTool.IsValidSS(xgd1SSPath))
+ {
+ // Save untouched SS
+ if (!File.Exists(xgd1RawSSPath))
+ File.Move(xgd1SSPath, xgd1RawSSPath);
+
+
+ // Repair, clean, and validate SS before adding hash to submission info
+ if (ProcessingTool.FixSS(xgd1SSPath, xgd1SSPath))
+ {
+ string? xgd1SSCrc = HashTool.GetFileHash(xgd1SSPath, HashType.CRC32);
+ if (xgd1SSCrc is not null)
+ info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSCrc.ToUpperInvariant();
+ }
+ }
+
break;
case RedumpSystem.MicrosoftXbox360:
@@ -387,7 +407,8 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
{
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd23DMIHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash ?? string.Empty;
- info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash ?? string.Empty;
+ // Don't put raw SS hash from _suppl.dat / _disc.txt in submission info
+ //info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash ?? string.Empty;
}
if (GetXGDAuxInfo($"{basePath}_disc.txt", out _, out _, out _, out var xgd23SS))
@@ -401,11 +422,30 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
{
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd23DMIHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash ?? string.Empty;
- info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash ?? string.Empty;
+ // Don't put raw SS hash from _suppl.dat / _disc.txt in submission info
+ //info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash ?? string.Empty;
info.Extras.SecuritySectorRanges = xgd23SS ?? string.Empty;
}
}
+ string xgd2SSPath = $"{basePath}_SS.bin";
+ string xgd2RawSSPath = $"{basePath}_RawSS.bin";
+ if (File.Exists(xgd2SSPath) && ProcessingTool.IsValidSS(xgd2SSPath))
+ {
+ // Save untouched SS
+ if (!File.Exists(xgd2RawSSPath))
+ File.Move(xgd2SSPath, xgd2RawSSPath);
+
+
+ // Repair, clean, and validate SS before adding hash to submission info
+ if (ProcessingTool.FixSS(xgd2SSPath, xgd2SSPath))
+ {
+ string? xgd2SSCrc = HashTool.GetFileHash(xgd2SSPath, HashType.CRC32);
+ if (xgd2SSCrc is not null)
+ info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd2SSCrc.ToUpperInvariant();
+ }
+ }
+
break;
case RedumpSystem.NamcoSegaNintendoTriforce:
@@ -787,6 +827,9 @@ internal override List GetOutputFiles(MediaType? mediaType, string?
? OutputFileFlags.Required | OutputFileFlags.Binary | OutputFileFlags.Zippable
: OutputFileFlags.Binary | OutputFileFlags.Zippable,
"ss"),
+ new($"{outputFilename}_RawSS.bin",
+ OutputFileFlags.Binary | OutputFileFlags.Zippable,
+ "raw_ss"),
];
case MediaType.HDDVD:
diff --git a/MPF.Processors/XboxBackupCreator.cs b/MPF.Processors/XboxBackupCreator.cs
index 0a07e0b47..a04e79c23 100644
--- a/MPF.Processors/XboxBackupCreator.cs
+++ b/MPF.Processors/XboxBackupCreator.cs
@@ -118,7 +118,7 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
if (File.Exists(ssPath))
{
// Ensure a raw SS is saved (recreate from log if needed)
- RecreateSS(logPath!, ssPath, Path.Combine(outputDirectory, "RawSS.bin"));.
+ RecreateSS(logPath!, ssPath, Path.Combine(outputDirectory, "RawSS.bin"));
if (ProcessingTool.IsValidSS(ssPath))
{
@@ -130,7 +130,11 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
// Repair and clean SS, only hash SS if successful
if (ProcessingTool.FixSS(ssPath, ssPath))
- info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = HashTool.GetFileHash(ssPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
+ {
+ string? ssCrc = HashTool.GetFileHash(ssPath, HashType.CRC32);
+ if (ssCrc is not null)
+ info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = ssCrc.ToUpperInvariant();
+ }
}
}
From 67e5401a7c1bc44350be910384248dcdde226e2b Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 28 Jan 2026 17:27:33 +0900
Subject: [PATCH 04/17] Fix build
---
MPF.Processors/DiscImageCreator.cs | 1 +
MPF.Processors/ProcessingTool.cs | 21 +++++++++++++++++++--
MPF.Processors/XboxBackupCreator.cs | 5 -----
3 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/MPF.Processors/DiscImageCreator.cs b/MPF.Processors/DiscImageCreator.cs
index a761faad0..ed270532e 100644
--- a/MPF.Processors/DiscImageCreator.cs
+++ b/MPF.Processors/DiscImageCreator.cs
@@ -7,6 +7,7 @@
using System.Text.RegularExpressions;
using MPF.Processors.OutputFiles;
using SabreTools.Data.Models.Logiqx;
+using SabreTools.Hashing;
using SabreTools.RedumpLib.Data;
#if NET462_OR_GREATER || NETCOREAPP
using SharpCompress.Archives;
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index 57f0c1bc8..e28f215f1 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1097,7 +1097,7 @@ public static bool IsValidSS(byte[] ss)
return false;
// Drive entry table must be duplicated exactly
- if (!data.Skip(0x661).Take(0x730 - 0x661).SequenceEqual(data.Skip(0x730).Take(0x7FF - 0x730)))
+ if (!ss.Skip(0x661).Take(0x730 - 0x661).SequenceEqual(ss.Skip(0x730).Take(0x7FF - 0x730)))
return false;
// Remaining checks are only for Xbox360 SS
@@ -1126,6 +1126,23 @@ public static bool IsValidSS(byte[] ss)
return true;
}
+ ///
+ /// Determine if a given SS file has already been repaired and cleaned
+ ///
+ /// Path to the SS to check
+ /// True if SS is repaired and cleaned, false otherwise
+ public static bool IsFixedSS(string ssPath)
+ {
+ if (!File.Exists(ssPath))
+ return false;
+
+ byte[] ss = File.ReadAllBytes(ssPath);
+ if (ss.Length != 2048)
+ return false;
+
+ return IsFixedSS(ss);
+ }
+
///
/// Determine if a given SS has already been repaired and cleaned
///
@@ -1136,7 +1153,7 @@ public static bool IsFixedSS(byte[] ss)
if (ss.Length != 2048)
return false;
- if (!IsValid(ss))
+ if (!IsValidSS(ss))
return false;
if (!GetXGDType(ss, out int xgdType))
diff --git a/MPF.Processors/XboxBackupCreator.cs b/MPF.Processors/XboxBackupCreator.cs
index a04e79c23..6d23a5798 100644
--- a/MPF.Processors/XboxBackupCreator.cs
+++ b/MPF.Processors/XboxBackupCreator.cs
@@ -557,11 +557,6 @@ private static bool RecreateSS(string log, byte[] ss)
if (xgdType == 0)
return false;
- // Don't recreate an already raw SS
- // (but do save to file, so return true)
- if (!ProcessingTool.IsCleanSS(ss))
- return true;
-
// Example replay table:
/*
----------------------------------------
From 1cef42aa92565c04279ff7462eb549bad64e470f Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 28 Jan 2026 17:38:19 +0900
Subject: [PATCH 05/17] Account for net20
---
MPF.Processors/ProcessingTool.cs | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index e28f215f1..0305d6b74 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1097,7 +1097,16 @@ public static bool IsValidSS(byte[] ss)
return false;
// Drive entry table must be duplicated exactly
- if (!ss.Skip(0x661).Take(0x730 - 0x661).SequenceEqual(ss.Skip(0x730).Take(0x7FF - 0x730)))
+
+#if NET20
+ var table1 = new byte[207];
+ Array.Copy(ss, 0x661, table1, 0, 207);
+ var table2 = new byte[207];
+ Array.Copy(ss, 0x730, table2, 0, 207);
+ if (Array.Exists(Enumerable.Range(0, a.Length).ToArray(), i => table1[i] != table2[i]))
+#else
+ if (!ss.AsSpan(0x661, 0x730 - 0x661).SequenceEqual(ss.AsSpan(0x730, 0x7FF - 0x730)))
+#endif
return false;
// Remaining checks are only for Xbox360 SS
From 47db8ae41441da8448a52e4ea2ec793fb0f3922d Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 28 Jan 2026 17:49:26 +0900
Subject: [PATCH 06/17] Account for old dotnet
---
MPF.Processors/ProcessingTool.cs | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index 0305d6b74..3e04da8d5 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1097,16 +1097,16 @@ public static bool IsValidSS(byte[] ss)
return false;
// Drive entry table must be duplicated exactly
-
-#if NET20
- var table1 = new byte[207];
- Array.Copy(ss, 0x661, table1, 0, 207);
- var table2 = new byte[207];
- Array.Copy(ss, 0x730, table2, 0, 207);
- if (Array.Exists(Enumerable.Range(0, a.Length).ToArray(), i => table1[i] != table2[i]))
-#else
- if (!ss.AsSpan(0x661, 0x730 - 0x661).SequenceEqual(ss.AsSpan(0x730, 0x7FF - 0x730)))
-#endif
+ bool mismatch = false;
+ for (int i = 0; i < 207; i++)
+ {
+ if (ss[0x661 + i] != ss[0x730 + i])
+ {
+ mismatch = true;
+ break;
+ }
+ }
+ if (mismatch)
return false;
// Remaining checks are only for Xbox360 SS
From 9fb4f20a386a7f30992d1fa137fa081e8f5479e0 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 28 Jan 2026 17:56:28 +0900
Subject: [PATCH 07/17] Fix tests
---
MPF.Processors.Test/DiscImageCreatorTests.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/MPF.Processors.Test/DiscImageCreatorTests.cs b/MPF.Processors.Test/DiscImageCreatorTests.cs
index abc766cc3..46e1db688 100644
--- a/MPF.Processors.Test/DiscImageCreatorTests.cs
+++ b/MPF.Processors.Test/DiscImageCreatorTests.cs
@@ -117,7 +117,7 @@ public void GetOutputFiles_DVD_Populated()
var processor = new DiscImageCreator(RedumpSystem.IBMPCcompatible);
var actual = processor.GetOutputFiles(MediaType.DVD, outputDirectory, outputFilename);
- Assert.Equal(16, actual.Count);
+ Assert.Equal(17, actual.Count);
}
[Fact]
@@ -128,7 +128,7 @@ public void GetOutputFiles_NintendoGameCubeGameDisc_Populated()
var processor = new DiscImageCreator(RedumpSystem.IBMPCcompatible);
var actual = processor.GetOutputFiles(MediaType.NintendoGameCubeGameDisc, outputDirectory, outputFilename);
- Assert.Equal(16, actual.Count);
+ Assert.Equal(17, actual.Count);
}
[Fact]
@@ -139,7 +139,7 @@ public void GetOutputFiles_NintendoWiiOpticalDisc_Populated()
var processor = new DiscImageCreator(RedumpSystem.IBMPCcompatible);
var actual = processor.GetOutputFiles(MediaType.NintendoWiiOpticalDisc, outputDirectory, outputFilename);
- Assert.Equal(16, actual.Count);
+ Assert.Equal(17, actual.Count);
}
[Fact]
From dc9f81df8dff71cfd79f831a904f011dde2cd2d2 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Thu, 29 Jan 2026 15:08:51 +0900
Subject: [PATCH 08/17] decrypt response table
---
MPF.Processors/ProcessingTool.cs | 153 ++++++++++++++++++++-----------
1 file changed, 100 insertions(+), 53 deletions(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index 3e04da8d5..055bedf0f 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -3,6 +3,7 @@
#if NET35_OR_GREATER || NETCOREAPP
using System.Linq;
#endif
+using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
@@ -1168,7 +1169,7 @@ public static bool IsFixedSS(byte[] ss)
if (!GetXGDType(ss, out int xgdType))
return false;
- // Valid XGD1 is always fixed
+ // XGD1 can't be fixed
if (xgdType == 1)
return true;
@@ -1209,7 +1210,7 @@ public static bool IsFixedSS(byte[] ss)
if (ss[rtOffset + 67] != 0x01)
return false;
}
- else
+ else if (xgdType == 2)
{
// Check for a cleaned XGD2
int rtOffset = 0x204;
@@ -1247,33 +1248,8 @@ public static bool IsFixedSS(byte[] ss)
return false;
}
- // Determine challenge table offset
- int ccrt_offset = 0;
- if (xgdType == 2)
- ccrt_offset = 0x200;
- else if (xgdType == 3)
- ccrt_offset = 0x20;
-
- // Check for empty challenge responses
- int[] entry_offsets = [0, 9, 18, 27, 36, 45, 54, 63];
- int[] entry_lengths = [8, 8, 8, 8, 4, 4, 4, 4];
- for (int i = 0; i < entry_offsets.Length; i++)
- {
- bool emptyResponse = true;
- for (int b = 0; b < entry_lengths[i]; b++)
- {
- if (ss[ccrt_offset + entry_offsets[i] + b] != 0x00)
- {
- emptyResponse = false;
- break;
- }
- }
-
- if (emptyResponse)
- return false;
- }
-
- // TODO: Check for correct challenge responses
+ // Check challenge responses
+ FixSS(ss, false);
return true;
}
@@ -1306,7 +1282,7 @@ public static bool FixSS(string rawSS, string fixedSS)
///
/// Byte array of raw SS sector
/// True if successful, false otherwise
- public static bool FixSS(byte[] ss)
+ public static bool FixSS(byte[] ss, bool write = true)
{
// Must be entire sector
if (ss.Length != 2048)
@@ -1337,34 +1313,37 @@ public static bool FixSS(byte[] ss)
switch (xgdType)
{
case 1:
- // Cannot clean XGD1 SS.bin
- break;
+ // Cannot clean or fix XGD1 SS
+ return true;
case 2:
// Fix standard SSv1 ss.bin
- ss[552] = 1; // 0x01
- ss[553] = 0; // 0x00
- ss[555] = 0; // 0x00
- ss[556] = 0; // 0x00
-
- ss[561] = 91; // 0x5B
- ss[562] = 0; // 0x00
- ss[564] = 0; // 0x00
- ss[565] = 0; // 0x00
-
- ss[570] = 181; // 0xB5
- ss[571] = 0; // 0x00
- ss[573] = 0; // 0x00
- ss[574] = 0; // 0x00
-
- ss[579] = 15; // 0x0F
- ss[580] = 1; // 0x01
- ss[582] = 0; // 0x00
- ss[583] = 0; // 0x00
+ if (write)
+ {
+ ss[552] = 1; // 0x01
+ ss[553] = 0; // 0x00
+ ss[555] = 0; // 0x00
+ ss[556] = 0; // 0x00
+
+ ss[561] = 91; // 0x5B
+ ss[562] = 0; // 0x00
+ ss[564] = 0; // 0x00
+ ss[565] = 0; // 0x00
+
+ ss[570] = 181; // 0xB5
+ ss[571] = 0; // 0x00
+ ss[573] = 0; // 0x00
+ ss[574] = 0; // 0x00
+
+ ss[579] = 15; // 0x0F
+ ss[580] = 1; // 0x01
+ ss[582] = 0; // 0x00
+ ss[583] = 0; // 0x00
+ }
break;
case 3:
- if (ssv2)
+ if (write && ssv2)
{
ss[72] = 1; // 0x01
ss[73] = 0; // 0x00
@@ -1386,7 +1365,7 @@ public static bool FixSS(byte[] ss)
ss[102] = 15; // 0x0F
ss[103] = 1; // 0x01
}
- else
+ else if (write)
{
ss[552] = 1; // 0x01
ss[553] = 0; // 0x00
@@ -1407,6 +1386,74 @@ public static bool FixSS(byte[] ss)
return false;
}
+ // Determine challenge table offset
+ int ccrt_offset = 0;
+ if (xgdType == 2)
+ ccrt_offset = 0x200;
+ else if (xgdType == 3)
+ ccrt_offset = 0x20;
+
+ // Setup decryptor
+#if NET20
+ using RijndaelManaged aes = new RijndaelManaged();
+ aes.BlockSize = 128;
+#else
+ using Aes aes = Aes.Create();
+#endif
+ aes.Key = new byte[] { 0xD1, 0xE3, 0xB3, 0x3A, 0x6C, 0x1E, 0xF7, 0x70, 0x5F, 0x6D, 0xE9, 0x3B, 0xB6, 0xC0, 0xDC, 0x71 };
+ aes.Mode = CipherMode.ECB;
+ aes.Padding = PaddingMode.None;
+ using ICryptoTransform decryptor = aes.CreateDecryptor();
+
+ // Perform decryption
+ byte[] iv = new byte[16];
+ byte[] dcrt = new byte[252];
+ bool ct01_found = false;
+ for (int i = 0; i < 240; i+=16)
+ {
+ decryptor.TransformBlock(ss, 0x304 + i, 16, dcrt, i);
+ for (int j = 0; j < 16; j++)
+ {
+ dcrt[i + j] ^= iv[j];
+ iv[j] = ss[0x304 + i + j];
+ }
+ // Validate challenge type 1
+ if (dcrt[i] == 1)
+ {
+ // Cannot fix SS with two type 1 challenges
+ if (ct01_found)
+ return false;
+ ct01_found = true;
+ // Challenge type 1 must match CPR_MAI
+ int cpr_mai_offset = (xgdType == 3) ? 0xF0 : 0x2D0;
+ if (dcrt[i + 4] != ss[cpr_mai_offset] || dcrt[i + 5] != ss[cpr_mai_offset + 1] || dcrt[i + 6] != ss[cpr_mai_offset + 2] || dcrt[i + 7] != ss[cpr_mai_offset + 3])
+ return false;
+ }
+ // Cannot fix unknown challenge types
+ else if (dcrt[i] != 0xE0 && dcrt[i] != 0x14 && dcrt[i] != 0x15 && dcrt[i] != 0x24 && dcrt[i] != 0x25 && (dcrt[i] & 0xF) != 0xF0)
+ return false;
+ }
+ Array.Copy(ss, 0x304 + 240, dcrt, 240, 12);
+
+ // Check for empty challenge responses
+ int[] entryOffsets = [0, 9, 18, 27, 36, 45, 54, 63];
+ int[] entryLengths = [8, 8, 8, 8, 4, 4, 4, 4];
+ for (int i = 0; i < entryOffsets.Length; i++)
+ {
+ bool emptyResponse = true;
+ for (int b = 0; b < entryLengths[i]; b++)
+ {
+ if (ss[ccrt_offset + entryOffsets[i] + b] != 0x00)
+ {
+ emptyResponse = false;
+ break;
+ }
+ }
+
+ if (emptyResponse)
+ return false;
+ }
+
// TODO: Repair challenge responses
return true;
From 28d63a1282566f62e9bfbe3b10894fac0e81b363 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Fri, 30 Jan 2026 11:41:52 +0900
Subject: [PATCH 09/17] Update ProcessingTool.cs
---
MPF.Processors/ProcessingTool.cs | 28 ++++++++--------------------
1 file changed, 8 insertions(+), 20 deletions(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index 055bedf0f..334ab9cf1 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -966,7 +966,7 @@ public static bool ParseGetKeyLog(string? logPath, out byte[]? key, out byte[]?
#region Xbox and Xbox 360
///
- /// Get the XGD1 Master ID (XMID) information
+ /// Get the XGD1 Manufacturing ID (XMID) information
///
/// DMI.bin file location
/// String representation of the XGD1 DMI information, empty string on error
@@ -1248,10 +1248,8 @@ public static bool IsFixedSS(byte[] ss)
return false;
}
- // Check challenge responses
- FixSS(ss, false);
-
- return true;
+ // Check challenge responses (don't write)
+ return FixSS(ss, false);
}
///
@@ -1365,20 +1363,6 @@ public static bool FixSS(byte[] ss, bool write = true)
ss[102] = 15; // 0x0F
ss[103] = 1; // 0x01
}
- else if (write)
- {
- ss[552] = 1; // 0x01
- ss[553] = 0; // 0x00
-
- ss[561] = 91; // 0x5B
- ss[562] = 0; // 0x00
-
- ss[570] = 181; // 0xB5
- ss[571] = 0; // 0x00
-
- ss[579] = 15; // 0x0F
- ss[580] = 1; // 0x01
- }
break;
default:
@@ -1386,6 +1370,10 @@ public static bool FixSS(byte[] ss, bool write = true)
return false;
}
+ // Must be 21 challenge entries
+ if (ss[0x660] != 21)
+ return false;
+
// Determine challenge table offset
int ccrt_offset = 0;
if (xgdType == 2)
@@ -1417,6 +1405,7 @@ public static bool FixSS(byte[] ss, bool write = true)
dcrt[i + j] ^= iv[j];
iv[j] = ss[0x304 + i + j];
}
+
// Validate challenge type 1
if (dcrt[i] == 1)
{
@@ -1435,7 +1424,6 @@ public static bool FixSS(byte[] ss, bool write = true)
}
Array.Copy(ss, 0x304 + 240, dcrt, 240, 12);
- // Check for empty challenge responses
int[] entryOffsets = [0, 9, 18, 27, 36, 45, 54, 63];
int[] entryLengths = [8, 8, 8, 8, 4, 4, 4, 4];
for (int i = 0; i < entryOffsets.Length; i++)
From 7dcb946525953a979e6323f7b48c4857b6a19fa7 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Fri, 30 Jan 2026 18:09:11 +0900
Subject: [PATCH 10/17] Repair CCRT
---
MPF.Processors/ProcessingTool.cs | 244 +++++++++++++++++++++----------
1 file changed, 166 insertions(+), 78 deletions(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index 334ab9cf1..16c4fa42e 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1294,6 +1294,10 @@ public static bool FixSS(byte[] ss, bool write = true)
if (!GetXGDType(ss, out int xgdType))
return false;
+ // Cannot fix XGD1
+ if (xgdType == 1)
+ return true;
+
// Determine if XGD3 SS.bin is SSv1 (Original Kreon) or SSv2 (0800 / Repaired Kreon)
#if NET20
var checkArr = new byte[72];
@@ -1307,13 +1311,171 @@ public static bool FixSS(byte[] ss, bool write = true)
if (xgdType == 3 && !ssv2)
return false;
+ // Must be 21 challenge entries
+ if (ss[0x660] != 21)
+ return false;
+
+ // Setup decryptor
+#if NET20
+ using RijndaelManaged aes = new RijndaelManaged();
+ aes.BlockSize = 128;
+#else
+ using Aes aes = Aes.Create();
+#endif
+ aes.Key = new byte[] { 0xD1, 0xE3, 0xB3, 0x3A, 0x6C, 0x1E, 0xF7, 0x70, 0x5F, 0x6D, 0xE9, 0x3B, 0xB6, 0xC0, 0xDC, 0x71 };
+ aes.Mode = CipherMode.CBC;
+ aes.Padding = PaddingMode.None;
+ using ICryptoTransform decryptor = aes.CreateDecryptor();
+
+ // Perform decryption
+ byte[] iv = new byte[16];
+ byte[] dcrt = new byte[252];
+ bool ct01_found = false;
+ for (int i = 0; i < 240; i+=16)
+ decryptor.TransformBlock(ss, 0x304 + i, 16, dcrt, i);
+ Array.Copy(ss, 0x304 + 240, dcrt, 240, 12);
+
+ // Rebuild challenge response table
+ var cids = new Dictionary();
+ for (int i = 0; i < dcrt.Length; i+=12)
+ {
+ // Validate challenge type 1
+ if (dcrt[i] == 1)
+ {
+ // Cannot fix SS with two type 1 challenges
+ if (ct01_found)
+ return false;
+ ct01_found = true;
+ // Challenge type 1 must match CPR_MAI
+ int cpr_mai_offset = (xgdType == 3) ? 0xF0 : 0x2D0;
+ if (dcrt[i + 4] != ss[cpr_mai_offset] || dcrt[i + 5] != ss[cpr_mai_offset + 1] || dcrt[i + 6] != ss[cpr_mai_offset + 2] || dcrt[i + 7] != ss[cpr_mai_offset + 3])
+ return false;
+ }
+ // Check CIDs of known challenges
+ else if (dcrt[i] == 0x14 || dcrt[i] == 0x15 || dcrt[i] == 0x24 || dcrt[i] == 0x25 || dcrt[i] != 0xE0 || (dcrt[i] & 0xF) != 0xF0)
+ {
+ // Cannot fix SS with duplicate Challenge IDs
+ if (cids.ContainsKey(dcrt[i + 1]))
+ return false;
+ cids.Add(dcrt[i + 1], i);
+ }
+ // Cannot fix SS with unknown challenge types
+ else
+ return false;
+ }
+
+ // Determine challenge table offset
+ int ccrt_offset = 0;
+ if (xgdType == 2)
+ ccrt_offset = 0x200;
+ else if (xgdType == 3)
+ ccrt_offset = 0x20;
+
+ // Repair challenge table
+ for (int i = 0; i < 23; i++)
+ {
+ // Cannot rebuild SS with orphan challenge ID
+ if (!cids.TryGetValue(ss[0x730 + i * 9 + 1], out byte cOffset))
+ return;
+
+ // Validate challenge type with response type
+ byte rOffset = 0x730 + i * 9;
+ bool angle_challenge = false;
+ bool other_challenge = false;
+ int challenge_count = 0;
+ switch (ss[cOffset])
+ {
+ case 0x14:
+ if (ss[rOffset] != 3)
+ return false;
+ challenge_count += 1;
+ // Challenge must be in expected order
+ if (challenge_count > 5)
+ return false;
+ break;
+ case 0x15:
+ if (ss[rOffset] != 1)
+ return false;
+ challenge_count += 1;
+ // Challenge must be in expected order
+ if (challenge_count < 5)
+ return false;
+ break;
+ case 0x24:
+ if (ss[rOffset] != 7)
+ return false;
+ challenge_count += 1;
+ // Challenge must be in expected order
+ if (challenge_count < 5 || challenge_count > 8)
+ return false;
+ angle_challenge = true;
+ break;
+ case 0x25:
+ if (ss[rOffset] != 9)
+ return false;
+ challenge_count += 1;
+ // Challenge must be in expected order
+ if (challenge_count < 5 || challenge_count > 8)
+ return false;
+ angle_challenge = true;
+ break;
+ default:
+ other_challenge = true;
+ break;
+ }
+
+ // Skip other challenges
+ if (other_challenge)
+ continue;
+
+ // Set/check challenge data
+ if (!write && ss[ccrt_offset + i * 9] != ss[cOffset + 4])
+ return false;
+ else
+ ss[ccrt_offset + i * 9] = ss[cOffset + 4];
+ if (!write && ss[ccrt_offset + i * 9 + 1] != ss[cOffset + 5])
+ return false
+ else
+ ss[ccrt_offset + i * 9 + 1] = ss[cOffset + 5];
+ if (!write && ss[ccrt_offset + i * 9 + 2] != ss[cOffset + 6])
+ return false
+ else
+ ss[ccrt_offset + i * 9 + 2] = ss[cOffset + 6];
+ if (!write && ss[ccrt_offset + i * 9 + 3] != ss[cOffset + 7])
+ return false
+ else
+ ss[ccrt_offset + i * 9 + 2] = ss[cOffset + 7];
+
+ // Set challenge response for non-angle challenges
+ if (!angle_challenge)
+ {
+ if(!write && ss[ccrt_offset + i * 9 + 4] != ss[cOffset + 8])
+ return false;
+ else
+ ss[ccrt_offset + i * 9 + 4] = ss[cOffset + 8];
+ if(!write && ss[ccrt_offset + i * 9 + 5] != ss[cOffset + 9])
+ return false;
+ else
+ ss[ccrt_offset + i * 9 + 5] = ss[cOffset + 9];
+ if(!write && ss[ccrt_offset + i * 9 + 6] != ss[cOffset + 10])
+ return false;
+ else
+ ss[ccrt_offset + i * 9 + 6] = ss[cOffset + 10];
+ if(!write && ss[ccrt_offset + i * 9 + 7] != ss[cOffset + 11])
+ return false;
+ else
+ ss[ccrt_offset + i * 9 + 7] = ss[cOffset + 11];
+ if(!write && ss[ccrt_offset + i * 9 + 8] != 0)
+ return false;
+ else
+ ss[ccrt_offset + i * 9 + 8] = 0;
+ }
+
+ }
+
// Clean SS (set fixed angles)
switch (xgdType)
{
- case 1:
- // Cannot clean or fix XGD1 SS
- return true;
-
case 2:
// Fix standard SSv1 ss.bin
if (write)
@@ -1370,80 +1532,6 @@ public static bool FixSS(byte[] ss, bool write = true)
return false;
}
- // Must be 21 challenge entries
- if (ss[0x660] != 21)
- return false;
-
- // Determine challenge table offset
- int ccrt_offset = 0;
- if (xgdType == 2)
- ccrt_offset = 0x200;
- else if (xgdType == 3)
- ccrt_offset = 0x20;
-
- // Setup decryptor
-#if NET20
- using RijndaelManaged aes = new RijndaelManaged();
- aes.BlockSize = 128;
-#else
- using Aes aes = Aes.Create();
-#endif
- aes.Key = new byte[] { 0xD1, 0xE3, 0xB3, 0x3A, 0x6C, 0x1E, 0xF7, 0x70, 0x5F, 0x6D, 0xE9, 0x3B, 0xB6, 0xC0, 0xDC, 0x71 };
- aes.Mode = CipherMode.ECB;
- aes.Padding = PaddingMode.None;
- using ICryptoTransform decryptor = aes.CreateDecryptor();
-
- // Perform decryption
- byte[] iv = new byte[16];
- byte[] dcrt = new byte[252];
- bool ct01_found = false;
- for (int i = 0; i < 240; i+=16)
- {
- decryptor.TransformBlock(ss, 0x304 + i, 16, dcrt, i);
- for (int j = 0; j < 16; j++)
- {
- dcrt[i + j] ^= iv[j];
- iv[j] = ss[0x304 + i + j];
- }
-
- // Validate challenge type 1
- if (dcrt[i] == 1)
- {
- // Cannot fix SS with two type 1 challenges
- if (ct01_found)
- return false;
- ct01_found = true;
- // Challenge type 1 must match CPR_MAI
- int cpr_mai_offset = (xgdType == 3) ? 0xF0 : 0x2D0;
- if (dcrt[i + 4] != ss[cpr_mai_offset] || dcrt[i + 5] != ss[cpr_mai_offset + 1] || dcrt[i + 6] != ss[cpr_mai_offset + 2] || dcrt[i + 7] != ss[cpr_mai_offset + 3])
- return false;
- }
- // Cannot fix unknown challenge types
- else if (dcrt[i] != 0xE0 && dcrt[i] != 0x14 && dcrt[i] != 0x15 && dcrt[i] != 0x24 && dcrt[i] != 0x25 && (dcrt[i] & 0xF) != 0xF0)
- return false;
- }
- Array.Copy(ss, 0x304 + 240, dcrt, 240, 12);
-
- int[] entryOffsets = [0, 9, 18, 27, 36, 45, 54, 63];
- int[] entryLengths = [8, 8, 8, 8, 4, 4, 4, 4];
- for (int i = 0; i < entryOffsets.Length; i++)
- {
- bool emptyResponse = true;
- for (int b = 0; b < entryLengths[i]; b++)
- {
- if (ss[ccrt_offset + entryOffsets[i] + b] != 0x00)
- {
- emptyResponse = false;
- break;
- }
- }
-
- if (emptyResponse)
- return false;
- }
-
- // TODO: Repair challenge responses
-
return true;
}
From 9b184a931bd8eb75574207e6f17fbde230c2a2b0 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Sun, 8 Feb 2026 20:43:04 +0900
Subject: [PATCH 11/17] fix
---
MPF.Processors/ProcessingTool.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index 16c4fa42e..daf23cb14 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1434,15 +1434,15 @@ public static bool FixSS(byte[] ss, bool write = true)
else
ss[ccrt_offset + i * 9] = ss[cOffset + 4];
if (!write && ss[ccrt_offset + i * 9 + 1] != ss[cOffset + 5])
- return false
+ return false;
else
ss[ccrt_offset + i * 9 + 1] = ss[cOffset + 5];
if (!write && ss[ccrt_offset + i * 9 + 2] != ss[cOffset + 6])
- return false
+ return false;
else
ss[ccrt_offset + i * 9 + 2] = ss[cOffset + 6];
if (!write && ss[ccrt_offset + i * 9 + 3] != ss[cOffset + 7])
- return false
+ return false;
else
ss[ccrt_offset + i * 9 + 2] = ss[cOffset + 7];
From 7afd56890422a8ad2a1fb08961308a5d31054fc9 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Tue, 24 Feb 2026 14:33:17 +0900
Subject: [PATCH 12/17] fix build
---
MPF.Processors/ProcessingTool.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index daf23cb14..1bf5ce006 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
#if NET35_OR_GREATER || NETCOREAPP
using System.Linq;
@@ -1376,10 +1377,10 @@ public static bool FixSS(byte[] ss, bool write = true)
{
// Cannot rebuild SS with orphan challenge ID
if (!cids.TryGetValue(ss[0x730 + i * 9 + 1], out byte cOffset))
- return;
+ return false;
// Validate challenge type with response type
- byte rOffset = 0x730 + i * 9;
+ int rOffset = 0x730 + i * 9);
bool angle_challenge = false;
bool other_challenge = false;
int challenge_count = 0;
From 624590f50516838c3d5872a7cecf60c8a6a9dbde Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Tue, 24 Feb 2026 14:38:50 +0900
Subject: [PATCH 13/17] fix
---
MPF.Processors/ProcessingTool.cs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index 1bf5ce006..93c213791 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1380,7 +1380,7 @@ public static bool FixSS(byte[] ss, bool write = true)
return false;
// Validate challenge type with response type
- int rOffset = 0x730 + i * 9);
+ int rOffset = 0x730 + i * 9;
bool angle_challenge = false;
bool other_challenge = false;
int challenge_count = 0;
@@ -1471,7 +1471,6 @@ public static bool FixSS(byte[] ss, bool write = true)
else
ss[ccrt_offset + i * 9 + 8] = 0;
}
-
}
// Clean SS (set fixed angles)
From d13f4976b1be7032761c55dd66735596d8f0286c Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Tue, 24 Feb 2026 14:44:43 +0900
Subject: [PATCH 14/17] cid value is int
---
MPF.Processors/ProcessingTool.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index 93c213791..e6745054e 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1337,7 +1337,7 @@ public static bool FixSS(byte[] ss, bool write = true)
Array.Copy(ss, 0x304 + 240, dcrt, 240, 12);
// Rebuild challenge response table
- var cids = new Dictionary();
+ var cids = new Dictionary();
for (int i = 0; i < dcrt.Length; i+=12)
{
// Validate challenge type 1
@@ -1376,7 +1376,7 @@ public static bool FixSS(byte[] ss, bool write = true)
for (int i = 0; i < 23; i++)
{
// Cannot rebuild SS with orphan challenge ID
- if (!cids.TryGetValue(ss[0x730 + i * 9 + 1], out byte cOffset))
+ if (!cids.TryGetValue(ss[0x730 + i * 9 + 1], out int cOffset))
return false;
// Validate challenge type with response type
From 53d2201b8fc65155ce773e73f95570796cb04d5b Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 25 Feb 2026 00:33:37 +0900
Subject: [PATCH 15/17] code review
---
MPF.Processors/DiscImageCreator.cs | 16 +++---
MPF.Processors/ProcessingTool.cs | 82 ++++++++++++++++-------------
MPF.Processors/XboxBackupCreator.cs | 2 +-
3 files changed, 57 insertions(+), 43 deletions(-)
diff --git a/MPF.Processors/DiscImageCreator.cs b/MPF.Processors/DiscImageCreator.cs
index ed270532e..58d507d2f 100644
--- a/MPF.Processors/DiscImageCreator.cs
+++ b/MPF.Processors/DiscImageCreator.cs
@@ -372,9 +372,11 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
if (File.Exists(xgd1SSPath) && ProcessingTool.IsValidSS(xgd1SSPath))
{
// Save untouched SS
- if (!File.Exists(xgd1RawSSPath))
- File.Move(xgd1SSPath, xgd1RawSSPath);
-
+ try
+ {
+ if (!File.Exists(xgd1RawSSPath))
+ File.Move(xgd1SSPath, xgd1RawSSPath);
+ }
// Repair, clean, and validate SS before adding hash to submission info
if (ProcessingTool.FixSS(xgd1SSPath, xgd1SSPath))
@@ -434,9 +436,11 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
if (File.Exists(xgd2SSPath) && ProcessingTool.IsValidSS(xgd2SSPath))
{
// Save untouched SS
- if (!File.Exists(xgd2RawSSPath))
- File.Move(xgd2SSPath, xgd2RawSSPath);
-
+ try
+ {
+ if (!File.Exists(xgd2RawSSPath))
+ File.Move(xgd2SSPath, xgd2RawSSPath);
+ }
// Repair, clean, and validate SS before adding hash to submission info
if (ProcessingTool.FixSS(xgd2SSPath, xgd2SSPath))
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index e6745054e..d57af7e90 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1099,17 +1099,11 @@ public static bool IsValidSS(byte[] ss)
return false;
// Drive entry table must be duplicated exactly
- bool mismatch = false;
for (int i = 0; i < 207; i++)
{
if (ss[0x661 + i] != ss[0x730 + i])
- {
- mismatch = true;
- break;
- }
+ return false;
}
- if (mismatch)
- return false;
// Remaining checks are only for Xbox360 SS
if (xgdType == 1)
@@ -1118,13 +1112,12 @@ public static bool IsValidSS(byte[] ss)
// Determine if XGD3 SS is invalid SSv1 (Original Kreon) or valid SSv2 (0800 / Custom Kreon)
if (xgdType == 3)
{
- bool bad_xgd3 = false;
#if NET20
var checkArr = new byte[72];
Array.Copy(ss, 32, checkArr, 0, 72);
- bad_xgd3 = Array.Exists(checkArr, x => x != 0);
+ if(Array.Exists(checkArr, x => x != 0))
#else
- bad_xgd3 = ss.Skip(32).Take(72).Any(x => x != 0);
+ if(ss.Skip(32).Take(72).Any(x => x != 0))
#endif
if (bad_xgd3)
return false;
@@ -1172,17 +1165,18 @@ public static bool IsFixedSS(byte[] ss)
// XGD1 can't be fixed
if (xgdType == 1)
+ {
return true;
-
- if (xgdType == 3)
+ }
+ else if (xgdType == 2)
{
- // Check for a cleaned SSv2
- int rtOffset = 0x24;
+ // Check for a cleaned XGD2
+ int rtOffset = 0x204;
if (ss[rtOffset + 36] != 0x01)
return false;
if (ss[rtOffset + 37] != 0x00)
return false;
- if (ss[rtOffset + 39] != 0x01)
+ if (ss[rtOffset + 39] != 0x00)
return false;
if (ss[rtOffset + 40] != 0x00)
return false;
@@ -1190,7 +1184,7 @@ public static bool IsFixedSS(byte[] ss)
return false;
if (ss[rtOffset + 46] != 0x00)
return false;
- if (ss[rtOffset + 48] != 0x5B)
+ if (ss[rtOffset + 48] != 0x00)
return false;
if (ss[rtOffset + 49] != 0x00)
return false;
@@ -1198,7 +1192,7 @@ public static bool IsFixedSS(byte[] ss)
return false;
if (ss[rtOffset + 55] != 0x00)
return false;
- if (ss[rtOffset + 57] != 0xB5)
+ if (ss[rtOffset + 57] != 0x00)
return false;
if (ss[rtOffset + 58] != 0x00)
return false;
@@ -1206,20 +1200,20 @@ public static bool IsFixedSS(byte[] ss)
return false;
if (ss[rtOffset + 64] != 0x01)
return false;
- if (ss[rtOffset + 66] != 0x0F)
+ if (ss[rtOffset + 66] != 0x00)
return false;
- if (ss[rtOffset + 67] != 0x01)
+ if (ss[rtOffset + 67] != 0x00)
return false;
}
- else if (xgdType == 2)
+ else if (xgdType == 3)
{
- // Check for a cleaned XGD2
- int rtOffset = 0x204;
+ // Check for a cleaned SSv2
+ int rtOffset = 0x24;
if (ss[rtOffset + 36] != 0x01)
return false;
if (ss[rtOffset + 37] != 0x00)
return false;
- if (ss[rtOffset + 39] != 0x00)
+ if (ss[rtOffset + 39] != 0x01)
return false;
if (ss[rtOffset + 40] != 0x00)
return false;
@@ -1227,7 +1221,7 @@ public static bool IsFixedSS(byte[] ss)
return false;
if (ss[rtOffset + 46] != 0x00)
return false;
- if (ss[rtOffset + 48] != 0x00)
+ if (ss[rtOffset + 48] != 0x5B)
return false;
if (ss[rtOffset + 49] != 0x00)
return false;
@@ -1235,7 +1229,7 @@ public static bool IsFixedSS(byte[] ss)
return false;
if (ss[rtOffset + 55] != 0x00)
return false;
- if (ss[rtOffset + 57] != 0x00)
+ if (ss[rtOffset + 57] != 0xB5)
return false;
if (ss[rtOffset + 58] != 0x00)
return false;
@@ -1243,11 +1237,15 @@ public static bool IsFixedSS(byte[] ss)
return false;
if (ss[rtOffset + 64] != 0x01)
return false;
- if (ss[rtOffset + 66] != 0x00)
+ if (ss[rtOffset + 66] != 0x0F)
return false;
- if (ss[rtOffset + 67] != 0x00)
+ if (ss[rtOffset + 67] != 0x01)
return false;
}
+ else
+ {
+ return false;
+ }
// Check challenge responses (don't write)
return FixSS(ss, false);
@@ -1277,10 +1275,10 @@ public static bool FixSS(string rawSS, string fixedSS)
///
/// Repair and clean a SS sector to its valid, predictable clean form.
- /// Note: Also see ss_sector_range and abgx360
///
/// Byte array of raw SS sector
/// True if successful, false otherwise
+ /// Also see ss_sector_range and abgx360
public static bool FixSS(byte[] ss, bool write = true)
{
// Must be entire sector
@@ -1318,26 +1316,27 @@ public static bool FixSS(byte[] ss, bool write = true)
// Setup decryptor
#if NET20
- using RijndaelManaged aes = new RijndaelManaged();
+ using var aes = new RijndaelManaged();
aes.BlockSize = 128;
#else
- using Aes aes = Aes.Create();
+ using var aes = Aes.Create();
#endif
- aes.Key = new byte[] { 0xD1, 0xE3, 0xB3, 0x3A, 0x6C, 0x1E, 0xF7, 0x70, 0x5F, 0x6D, 0xE9, 0x3B, 0xB6, 0xC0, 0xDC, 0x71 };
+ aes.Key = [0xD1, 0xE3, 0xB3, 0x3A, 0x6C, 0x1E, 0xF7, 0x70, 0x5F, 0x6D, 0xE9, 0x3B, 0xB6, 0xC0, 0xDC, 0x71];
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
- using ICryptoTransform decryptor = aes.CreateDecryptor();
+ using var decryptor = aes.CreateDecryptor();
// Perform decryption
- byte[] iv = new byte[16];
byte[] dcrt = new byte[252];
bool ct01_found = false;
for (int i = 0; i < 240; i+=16)
+ {
decryptor.TransformBlock(ss, 0x304 + i, 16, dcrt, i);
+ }
Array.Copy(ss, 0x304 + 240, dcrt, 240, 12);
// Rebuild challenge response table
- var cids = new Dictionary();
+ Dictionary cids = [];
for (int i = 0; i < dcrt.Length; i+=12)
{
// Validate challenge type 1
@@ -1346,6 +1345,7 @@ public static bool FixSS(byte[] ss, bool write = true)
// Cannot fix SS with two type 1 challenges
if (ct01_found)
return false;
+
ct01_found = true;
// Challenge type 1 must match CPR_MAI
int cpr_mai_offset = (xgdType == 3) ? 0xF0 : 0x2D0;
@@ -1362,7 +1362,9 @@ public static bool FixSS(byte[] ss, bool write = true)
}
// Cannot fix SS with unknown challenge types
else
+ {
return false;
+ }
}
// Determine challenge table offset
@@ -1373,6 +1375,7 @@ public static bool FixSS(byte[] ss, bool write = true)
ccrt_offset = 0x20;
// Repair challenge table
+ int challenge_count = 0;
for (int i = 0; i < 23; i++)
{
// Cannot rebuild SS with orphan challenge ID
@@ -1383,41 +1386,48 @@ public static bool FixSS(byte[] ss, bool write = true)
int rOffset = 0x730 + i * 9;
bool angle_challenge = false;
bool other_challenge = false;
- int challenge_count = 0;
switch (ss[cOffset])
{
case 0x14:
if (ss[rOffset] != 3)
return false;
+
challenge_count += 1;
// Challenge must be in expected order
if (challenge_count > 5)
return false;
+
break;
case 0x15:
if (ss[rOffset] != 1)
return false;
+
challenge_count += 1;
// Challenge must be in expected order
if (challenge_count < 5)
return false;
+
break;
case 0x24:
if (ss[rOffset] != 7)
return false;
+
challenge_count += 1;
// Challenge must be in expected order
if (challenge_count < 5 || challenge_count > 8)
return false;
+
angle_challenge = true;
break;
case 0x25:
if (ss[rOffset] != 9)
return false;
+
challenge_count += 1;
// Challenge must be in expected order
if (challenge_count < 5 || challenge_count > 8)
return false;
+
angle_challenge = true;
break;
default:
@@ -1445,7 +1455,7 @@ public static bool FixSS(byte[] ss, bool write = true)
if (!write && ss[ccrt_offset + i * 9 + 3] != ss[cOffset + 7])
return false;
else
- ss[ccrt_offset + i * 9 + 2] = ss[cOffset + 7];
+ ss[ccrt_offset + i * 9 + 3] = ss[cOffset + 7];
// Set challenge response for non-angle challenges
if (!angle_challenge)
diff --git a/MPF.Processors/XboxBackupCreator.cs b/MPF.Processors/XboxBackupCreator.cs
index 6d23a5798..22885a2d7 100644
--- a/MPF.Processors/XboxBackupCreator.cs
+++ b/MPF.Processors/XboxBackupCreator.cs
@@ -109,6 +109,7 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
string dmiPath = Path.Combine(outputDirectory, "DMI.bin");
if (File.Exists(dmiPath))
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = HashTool.GetFileHash(dmiPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
+
string pfiPath = Path.Combine(outputDirectory, "PFI.bin");
if (File.Exists(pfiPath))
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = HashTool.GetFileHash(pfiPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
@@ -135,7 +136,6 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
if (ssCrc is not null)
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = ssCrc.ToUpperInvariant();
}
-
}
}
From 364716262a21142931edf21f1af2d04c336309d6 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 25 Feb 2026 00:39:28 +0900
Subject: [PATCH 16/17] fix
---
MPF.Processors/DiscImageCreator.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/MPF.Processors/DiscImageCreator.cs b/MPF.Processors/DiscImageCreator.cs
index 58d507d2f..dd68ca6db 100644
--- a/MPF.Processors/DiscImageCreator.cs
+++ b/MPF.Processors/DiscImageCreator.cs
@@ -377,6 +377,7 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
if (!File.Exists(xgd1RawSSPath))
File.Move(xgd1SSPath, xgd1RawSSPath);
}
+ catch { }
// Repair, clean, and validate SS before adding hash to submission info
if (ProcessingTool.FixSS(xgd1SSPath, xgd1SSPath))
@@ -441,6 +442,7 @@ public override void GenerateSubmissionInfo(SubmissionInfo info, MediaType? medi
if (!File.Exists(xgd2RawSSPath))
File.Move(xgd2SSPath, xgd2RawSSPath);
}
+ catch { }
// Repair, clean, and validate SS before adding hash to submission info
if (ProcessingTool.FixSS(xgd2SSPath, xgd2SSPath))
From 79cb6ca3bebc94e930de231f3ed8bc31a862ada2 Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Wed, 25 Feb 2026 00:44:43 +0900
Subject: [PATCH 17/17] fix
---
MPF.Processors/ProcessingTool.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/MPF.Processors/ProcessingTool.cs b/MPF.Processors/ProcessingTool.cs
index d57af7e90..dbdc7a78f 100644
--- a/MPF.Processors/ProcessingTool.cs
+++ b/MPF.Processors/ProcessingTool.cs
@@ -1119,7 +1119,6 @@ public static bool IsValidSS(byte[] ss)
#else
if(ss.Skip(32).Take(72).Any(x => x != 0))
#endif
- if (bad_xgd3)
return false;
}