diff --git a/SabreTools.Serialization/Extensions/StringBuilderExtensions.cs b/SabreTools.Serialization/Extensions/StringBuilderExtensions.cs
index a29f7833..9f339e68 100644
--- a/SabreTools.Serialization/Extensions/StringBuilderExtensions.cs
+++ b/SabreTools.Serialization/Extensions/StringBuilderExtensions.cs
@@ -455,6 +455,24 @@ namespace SabreTools.Data.Extensions
return sb.AppendLine($"{prefixString}: {valueString}");
}
+ ///
+ /// Append a line containing a String[] value to a StringBuilder
+ ///
+ public static StringBuilder AppendLine(this StringBuilder sb, string?[]? value, string prefixString)
+ {
+ string valueString = "[NULL]";
+ if (value is not null)
+ {
+ var valueArr = Array.ConvertAll(value, s => s is null ? "[NULL]" : s);
+ valueString = string.Join(", ", valueArr);
+ }
+
+ if (valueString.Length == 0)
+ return sb.AppendLine($"{prefixString}: [EMPTY]");
+
+ return sb.AppendLine($"{prefixString}: {valueString}");
+ }
+
///
/// Append a line containing a UInt64[] value to a StringBuilder
///
diff --git a/SabreTools.Serialization/Models/XboxExecutable/Certificate.cs b/SabreTools.Serialization/Models/XboxExecutable/Certificate.cs
index e27cb131..fbfa7d78 100644
--- a/SabreTools.Serialization/Models/XboxExecutable/Certificate.cs
+++ b/SabreTools.Serialization/Models/XboxExecutable/Certificate.cs
@@ -35,7 +35,7 @@ namespace SabreTools.Data.Models.XboxExecutable
/// to matter with unsigned code (or signed code, for that matter), so they can all
/// be set to zero.
///
- public uint[] AlternativeTitleIDs { get; set; } = new uint[16];
+ public byte[][] AlternativeTitleIDs { get; set; } = new byte[16][];
///
/// Allowed media types for this .XBE.
diff --git a/SabreTools.Serialization/Readers/XboxExecutable.cs b/SabreTools.Serialization/Readers/XboxExecutable.cs
index 24f0a471..a27b40fe 100644
--- a/SabreTools.Serialization/Readers/XboxExecutable.cs
+++ b/SabreTools.Serialization/Readers/XboxExecutable.cs
@@ -156,10 +156,10 @@ namespace SabreTools.Serialization.Readers
obj.TitleID = data.ReadBytes(4);
obj.TitleName = data.ReadBytes(0x50);
- obj.AlternativeTitleIDs = new uint[16];
+ obj.AlternativeTitleIDs = new byte[16][];
for (int i = 0; i < obj.AlternativeTitleIDs.Length; i++)
{
- obj.AlternativeTitleIDs[i] = data.ReadUInt32LittleEndian();
+ obj.AlternativeTitleIDs[i] = data.ReadBytes(4);
}
obj.AllowedMediaTypes = (AllowedMediaTypes)data.ReadUInt32LittleEndian();
diff --git a/SabreTools.Serialization/Wrappers/XboxExecutable.Printing.cs b/SabreTools.Serialization/Wrappers/XboxExecutable.Printing.cs
index 1a8a5ed4..8fe3c454 100644
--- a/SabreTools.Serialization/Wrappers/XboxExecutable.Printing.cs
+++ b/SabreTools.Serialization/Wrappers/XboxExecutable.Printing.cs
@@ -1,3 +1,4 @@
+using System;
using System.Text;
using SabreTools.Data.Extensions;
using SabreTools.Data.Models.XboxExecutable;
@@ -45,6 +46,7 @@ namespace SabreTools.Serialization.Wrappers
builder.AppendLine(certificate.TitleName, " Title name");
builder.AppendLine(Encoding.Unicode.GetString(certificate.TitleName).TrimEnd('\0'), " Title name (Unicode)");
builder.AppendLine(certificate.AlternativeTitleIDs, " Alternative title IDs");
+ builder.AppendLine(Array.ConvertAll(certificate.AlternativeTitleIDs, b => b.ToFormattedXBETitleID()), " Alternative title IDs (Formatted)");
builder.AppendLine($" Allowed media types: {certificate.AllowedMediaTypes} (0x{certificate.AllowedMediaTypes:X})");
builder.AppendLine($" Game region: {certificate.GameRegion} (0x{certificate.GameRegion:X})");
builder.AppendLine(certificate.GameRatings, " Game ratings");
diff --git a/SabreTools.Serialization/Wrappers/XboxExecutable.cs b/SabreTools.Serialization/Wrappers/XboxExecutable.cs
index 425c96e7..729d67fd 100644
--- a/SabreTools.Serialization/Wrappers/XboxExecutable.cs
+++ b/SabreTools.Serialization/Wrappers/XboxExecutable.cs
@@ -1,3 +1,4 @@
+using System;
using System.IO;
using System.Text;
using SabreTools.Data.Extensions;
@@ -16,6 +17,22 @@ namespace SabreTools.Serialization.Wrappers
#region Extension Properties
+ ///
+ public byte[][]? AlternativeTitleIDsBytes => Certificate?.AlternativeTitleIDs;
+
+ ///
+ public string[]? AlternativeTitleIDsStrings
+ {
+ get
+ {
+ // Ignore invalid alternative title IDs
+ if (AlternativeTitleIDsBytes is null)
+ return null;
+
+ return Array.ConvertAll(AlternativeTitleIDsBytes, ba => ba.ToFormattedXBETitleID() ?? "[NULL]");
+ }
+ }
+
///
public uint BaseAddress => Model.Header?.BaseAddress ?? 0;