From e8d1567d07c59ca2a54842e5f8996d2f9630cd4f Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Mon, 13 Oct 2025 09:14:50 +0900
Subject: [PATCH] Parse XboxOne/SX Title IDs (#897)
* Parse XboxOne/SX Title IDs
* Bump RedumpLib
* Fix build
* Don't use lists because net20
---
CHANGELIST.md | 1 +
MPF.CLI/MPF.CLI.csproj | 2 +-
MPF.Check/MPF.Check.csproj | 2 +-
.../MPF.ExecutionContexts.Test.csproj | 2 +-
.../MPF.ExecutionContexts.csproj | 2 +-
MPF.Frontend.Test/MPF.Frontend.Test.csproj | 2 +-
MPF.Frontend/MPF.Frontend.csproj | 2 +-
MPF.Frontend/Tools/PhysicalTool.cs | 57 ++++++++++++++++++-
MPF.Frontend/Tools/SubmissionGenerator.cs | 2 +
.../MPF.Processors.Test.csproj | 2 +-
MPF.Processors/MPF.Processors.csproj | 2 +-
MPF.UI/MPF.UI.csproj | 2 +-
12 files changed, 67 insertions(+), 11 deletions(-)
diff --git a/CHANGELIST.md b/CHANGELIST.md
index 830701a5..d50fdb57 100644
--- a/CHANGELIST.md
+++ b/CHANGELIST.md
@@ -9,6 +9,7 @@
- Move output file implementations to separate namespace
- Conditionally require state
- Replace "We" comments
+- Parse XboxOne/SX Title IDs
### 3.5.0 (2025-10-10)
diff --git a/MPF.CLI/MPF.CLI.csproj b/MPF.CLI/MPF.CLI.csproj
index ae267cad..b6c3bbe4 100644
--- a/MPF.CLI/MPF.CLI.csproj
+++ b/MPF.CLI/MPF.CLI.csproj
@@ -44,7 +44,7 @@
-
+
\ No newline at end of file
diff --git a/MPF.Check/MPF.Check.csproj b/MPF.Check/MPF.Check.csproj
index 79a5d1ea..750c277a 100644
--- a/MPF.Check/MPF.Check.csproj
+++ b/MPF.Check/MPF.Check.csproj
@@ -44,7 +44,7 @@
-
+
\ No newline at end of file
diff --git a/MPF.ExecutionContexts.Test/MPF.ExecutionContexts.Test.csproj b/MPF.ExecutionContexts.Test/MPF.ExecutionContexts.Test.csproj
index c22e76ce..8cfe93f2 100644
--- a/MPF.ExecutionContexts.Test/MPF.ExecutionContexts.Test.csproj
+++ b/MPF.ExecutionContexts.Test/MPF.ExecutionContexts.Test.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/MPF.ExecutionContexts/MPF.ExecutionContexts.csproj b/MPF.ExecutionContexts/MPF.ExecutionContexts.csproj
index dccb9be0..0ba0c079 100644
--- a/MPF.ExecutionContexts/MPF.ExecutionContexts.csproj
+++ b/MPF.ExecutionContexts/MPF.ExecutionContexts.csproj
@@ -32,7 +32,7 @@
-
+
\ No newline at end of file
diff --git a/MPF.Frontend.Test/MPF.Frontend.Test.csproj b/MPF.Frontend.Test/MPF.Frontend.Test.csproj
index f1d87a54..9d80431f 100644
--- a/MPF.Frontend.Test/MPF.Frontend.Test.csproj
+++ b/MPF.Frontend.Test/MPF.Frontend.Test.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/MPF.Frontend/MPF.Frontend.csproj b/MPF.Frontend/MPF.Frontend.csproj
index 94123cd1..458104a7 100644
--- a/MPF.Frontend/MPF.Frontend.csproj
+++ b/MPF.Frontend/MPF.Frontend.csproj
@@ -38,7 +38,7 @@
-
+
diff --git a/MPF.Frontend/Tools/PhysicalTool.cs b/MPF.Frontend/Tools/PhysicalTool.cs
index 4d8464af..b8b31dbd 100644
--- a/MPF.Frontend/Tools/PhysicalTool.cs
+++ b/MPF.Frontend/Tools/PhysicalTool.cs
@@ -726,11 +726,11 @@ namespace MPF.Frontend.Tools
/// Filenames if possible, null on error
public static string? GetXboxFilenames(Drive? drive)
{
- // If there's no drive path, we can't get BEE flag
+ // If there's no drive path, can't do anything
if (string.IsNullOrEmpty(drive?.Name))
return null;
- // If the folder no longer exists, we can't get exe name
+ // If the folder no longer exists, can't do anything
if (!Directory.Exists(drive!.Name))
return null;
@@ -752,6 +752,59 @@ namespace MPF.Frontend.Tools
}
}
+ ///
+ /// Get Title ID(s) for Xbox One and Xbox Series X
+ ///
+ /// Drive to extract information from
+ /// Title ID(s) if possible, null on error
+ public static string? GetXboxTitleID(Drive? drive)
+ {
+ // If there's no drive path, can't do anything
+ if (string.IsNullOrEmpty(drive?.Name))
+ return null;
+
+ // If the folder no longer exists, can't do anything
+ if (!Directory.Exists(drive!.Name))
+ return null;
+
+ // Get the catalog.js path
+#if NET20 || NET35
+ string catalogjs = Path.Combine(drive.Name, Path.Combine("MSXC", Path.Combine("Metadata", "catalog.js")));
+#else
+ string catalogjs = Path.Combine(drive.Name, "MSXC", "Metadata", "catalog.js");
+#endif
+ // Check catalog.js exists
+ if (!File.Exists(catalogjs))
+ return null;
+
+ // Deserialize catalog.js and extract Title ID(s)
+ try
+ {
+ SabreTools.Data.Models.Xbox.Catalog? catalog = new SabreTools.Serialization.Readers.Catalog().Deserialize(catalogjs);
+ if (catalog == null)
+ return null;
+ if (!string.IsNullOrEmpty(catalog.TitleID))
+ return catalog.TitleID;
+ if (catalog.Packages == null)
+ return null;
+
+ string[] titleIDs = new string[catalog.Packages.Length];
+ int i = 0;
+ foreach (var package in catalog.Packages)
+ {
+ if (package != null && package.TitleID != null)
+ titleIDs[i] = package.TitleID;
+ i++;
+ }
+ return string.Join(", ", titleIDs);
+ }
+ catch
+ {
+ // Absorb the exception
+ return null;
+ }
+ }
+
#endregion
#region Sega
diff --git a/MPF.Frontend/Tools/SubmissionGenerator.cs b/MPF.Frontend/Tools/SubmissionGenerator.cs
index 2fc0deee..103d5c97 100644
--- a/MPF.Frontend/Tools/SubmissionGenerator.cs
+++ b/MPF.Frontend/Tools/SubmissionGenerator.cs
@@ -846,10 +846,12 @@ namespace MPF.Frontend.Tools
case RedumpSystem.MicrosoftXboxOne:
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = PhysicalTool.GetXboxFilenames(drive) ?? string.Empty;
+ info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.TitleID] = PhysicalTool.GetXboxTitleID(drive) ?? string.Empty;
break;
case RedumpSystem.MicrosoftXboxSeriesXS:
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = PhysicalTool.GetXboxFilenames(drive) ?? string.Empty;
+ info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.TitleID] = PhysicalTool.GetXboxTitleID(drive) ?? string.Empty;
break;
case RedumpSystem.NamcoSegaNintendoTriforce:
diff --git a/MPF.Processors.Test/MPF.Processors.Test.csproj b/MPF.Processors.Test/MPF.Processors.Test.csproj
index 2c39b63f..389b43b3 100644
--- a/MPF.Processors.Test/MPF.Processors.Test.csproj
+++ b/MPF.Processors.Test/MPF.Processors.Test.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/MPF.Processors/MPF.Processors.csproj b/MPF.Processors/MPF.Processors.csproj
index e0bf1439..52ee7152 100644
--- a/MPF.Processors/MPF.Processors.csproj
+++ b/MPF.Processors/MPF.Processors.csproj
@@ -36,7 +36,7 @@
-
+
diff --git a/MPF.UI/MPF.UI.csproj b/MPF.UI/MPF.UI.csproj
index fffd8424..4c0620f5 100644
--- a/MPF.UI/MPF.UI.csproj
+++ b/MPF.UI/MPF.UI.csproj
@@ -70,7 +70,7 @@
-
+