diff --git a/SabreTools.Serialization/Models/README.MD b/SabreTools.Serialization/Models/README.MD
index f9bb537c..309e62ab 100644
--- a/SabreTools.Serialization/Models/README.MD
+++ b/SabreTools.Serialization/Models/README.MD
@@ -14,6 +14,7 @@ Not all of this information was able to be gathered directly from the files in q
| [Atari 7800 Development Wiki](https://7800.8bitdev.org/index.php/Main_Page) | Atari7800Cart |
| [BYTE*](https://web.archive.org/web/20240703222951/https://bytepointer.com/index.htm) | NewExecutable |
| [cabextract/libmspack](https://www.cabextract.org.uk/) | LZ |
+| [cxbx](https://www.caustik.com/cxbx/) | XboxExecutable |
| [DBox](https://dbox.tools/) | Xbox |
| [DebugInfo.com](https://www.debuginfo.com/index.html) | PortableExecutable |
| [Devilsclaw](https://devilsclaws.net/) | PFF |
diff --git a/SabreTools.Serialization/Models/Xbox/Constants.cs b/SabreTools.Serialization/Models/Xbox/Constants.cs
index 0a437f5d..e258b0e4 100644
--- a/SabreTools.Serialization/Models/Xbox/Constants.cs
+++ b/SabreTools.Serialization/Models/Xbox/Constants.cs
@@ -198,20 +198,5 @@ namespace SabreTools.Data.Models.Xbox
{ 'L', "USA / Europe" },
{ 'W', "World" },
};
-
- ///
- /// XBox Executable magic number ("XBEH")
- ///
- public static readonly byte[] XbeMagicBytes = [0x58, 0x42, 0x45, 0x48];
-
- ///
- /// XBox Executable magic number ("XBEH")
- ///
- public const string XbeMagicString = "XBEH";
-
- ///
- /// XBox Executable magic number ("XBEH")
- ///
- public const uint XbeMagicUInt32 = 0x48454258;
}
}
diff --git a/SabreTools.Serialization/Models/Xbox/XBE.cs b/SabreTools.Serialization/Models/Xbox/XBE.cs
deleted file mode 100644
index bd1ea48d..00000000
--- a/SabreTools.Serialization/Models/Xbox/XBE.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-namespace SabreTools.Data.Models.Xbox
-{
- ///
- /// XBox Executable format
- ///
- ///
- public class XBE
- {
- ///
- /// XBE header
- ///
- public XBEHeader? Header { get; set; }
-
- ///
- /// Certificate structure pointed to by
- ///
- /// TODO: Determine if address is real or virtual
- public XBECertificate? Certificate { get; set; }
-
- ///
- /// Section headers pointed to by
- ///
- /// TODO: Determine if address is real or virtual
- public XBESectionHeader[] SectionHeaders { get; set; } = [];
-
- ///
- /// Thread Local Storage (TLS) structure pointed to by
- ///
- /// TODO: Determine if address is real or virtual
- public XBEThreadLocalStorage? ThreadLocalStorage { get; set; }
-
- ///
- /// Library versions pointed to by
- ///
- /// TODO: Determine if address is real or virtual
- public XBELibraryVersion[] LibraryVersions { get; set; } = [];
-
- ///
- /// Kernel library version pointed to by
- ///
- /// TODO: Determine if address is real or virtual
- public XBELibraryVersion? KernelLibraryVersion { get; set; }
-
- ///
- /// XAPI library version pointed to by
- ///
- /// TODO: Determine if address is real or virtual
- public XBELibraryVersion? XAPILibraryVersion { get; set; }
- }
-}
diff --git a/SabreTools.Serialization/Models/Xbox/XBECertificate.cs b/SabreTools.Serialization/Models/XboxExecutable/Certificate.cs
similarity index 89%
rename from SabreTools.Serialization/Models/Xbox/XBECertificate.cs
rename to SabreTools.Serialization/Models/XboxExecutable/Certificate.cs
index d3ce5998..b58f0185 100644
--- a/SabreTools.Serialization/Models/Xbox/XBECertificate.cs
+++ b/SabreTools.Serialization/Models/XboxExecutable/Certificate.cs
@@ -1,10 +1,10 @@
-namespace SabreTools.Data.Models.Xbox
+namespace SabreTools.Data.Models.XboxExecutable
{
///
/// XBox Executable certificate
///
///
- public class XBECertificate
+ public class Certificate
{
///
/// Number of bytes that should be reserved for this certificate.
@@ -38,12 +38,12 @@ namespace SabreTools.Data.Models.Xbox
///
/// Allowed media types for this .XBE.
///
- public XbeAllowedMediaTypes AllowedMediaTypes { get; set; }
+ public AllowedMediaTypes AllowedMediaTypes { get; set; }
///
/// Game region for this .XBE.
///
- public XbeGameRegion GameRegion { get; set; }
+ public GameRegion GameRegion { get; set; }
///
/// Game ratings for this .XBE. It is typically safe to set this to 0xFFFFFFFF.
@@ -73,6 +73,6 @@ namespace SabreTools.Data.Models.Xbox
///
/// 16 x 16-byte Signature Keys. An unsigned .XBE can just zero these out.
///
- public byte[,] AlternateSignatureKeys { get; set; } = new byte[16, 16];
+ public byte[][] AlternateSignatureKeys { get; set; } = new byte[16][];
}
}
diff --git a/SabreTools.Serialization/Models/XboxExecutable/Constants.cs b/SabreTools.Serialization/Models/XboxExecutable/Constants.cs
new file mode 100644
index 00000000..0fb39cad
--- /dev/null
+++ b/SabreTools.Serialization/Models/XboxExecutable/Constants.cs
@@ -0,0 +1,21 @@
+namespace SabreTools.Data.Models.XboxExecutable
+{
+ ///
+ public static class Constants
+ {
+ ///
+ /// XBox Executable magic number ("XBEH")
+ ///
+ public static readonly byte[] MagicBytes = [0x58, 0x42, 0x45, 0x48];
+
+ ///
+ /// XBox Executable magic number ("XBEH")
+ ///
+ public const string MagicString = "XBEH";
+
+ ///
+ /// XBox Executable magic number ("XBEH")
+ ///
+ public const uint MagicUInt32 = 0x48454258;
+ }
+}
diff --git a/SabreTools.Serialization/Models/Xbox/Enums.cs b/SabreTools.Serialization/Models/XboxExecutable/Enums.cs
similarity index 90%
rename from SabreTools.Serialization/Models/Xbox/Enums.cs
rename to SabreTools.Serialization/Models/XboxExecutable/Enums.cs
index 6b4f9c46..758b6bb6 100644
--- a/SabreTools.Serialization/Models/Xbox/Enums.cs
+++ b/SabreTools.Serialization/Models/XboxExecutable/Enums.cs
@@ -1,13 +1,13 @@
using System;
-namespace SabreTools.Data.Models.Xbox
+namespace SabreTools.Data.Models.XboxExecutable
{
///
/// Allowed media types for this .XBE
///
///
[Flags]
- public enum XbeAllowedMediaTypes : uint
+ public enum AllowedMediaTypes : uint
{
XBEIMAGE_MEDIA_TYPE_HARD_DISK = 0x00000001,
XBEIMAGE_MEDIA_TYPE_DVD_X2 = 0x00000002,
@@ -29,7 +29,7 @@ namespace SabreTools.Data.Models.Xbox
///
///
[Flags]
- public enum XbeGameRegion : uint
+ public enum GameRegion : uint
{
XBEIMAGE_GAME_REGION_NA = 0x00000001,
XBEIMAGE_GAME_REGION_JAPAN = 0x00000002,
@@ -42,7 +42,7 @@ namespace SabreTools.Data.Models.Xbox
///
///
[Flags]
- public enum XbeInitializationFlags : uint
+ public enum InitializationFlags : uint
{
MountUtilityDrive = 0x00000001,
FormatUtilityDrive = 0x00000002,
@@ -55,7 +55,7 @@ namespace SabreTools.Data.Models.Xbox
///
///
[Flags]
- public enum XbeLibraryFlags : uint
+ public enum LibraryFlags : uint
{
/// 13-Bit Mask
QFEVersion = 0x1FFF,
@@ -72,7 +72,7 @@ namespace SabreTools.Data.Models.Xbox
///
///
[Flags]
- public enum XbeSectionFlags : uint
+ public enum SectionFlags : uint
{
Writable = 0x00000001,
Preload = 0x00000002,
diff --git a/SabreTools.Serialization/Models/XboxExecutable/Executable.cs b/SabreTools.Serialization/Models/XboxExecutable/Executable.cs
new file mode 100644
index 00000000..a83a9b58
--- /dev/null
+++ b/SabreTools.Serialization/Models/XboxExecutable/Executable.cs
@@ -0,0 +1,50 @@
+namespace SabreTools.Data.Models.XboxExecutable
+{
+ ///
+ /// XBox Executable format
+ ///
+ ///
+ public class Executable
+ {
+ ///
+ /// XBE header
+ ///
+ public Header? Header { get; set; }
+
+ ///
+ /// Certificate structure pointed to by
+ ///
+ /// TODO: Determine if address is real or virtual
+ public Certificate? Certificate { get; set; }
+
+ ///
+ /// Section headers pointed to by
+ ///
+ /// TODO: Determine if address is real or virtual
+ public SectionHeader[] SectionHeaders { get; set; } = [];
+
+ ///
+ /// Thread Local Storage (TLS) structure pointed to by
+ ///
+ /// TODO: Determine if address is real or virtual
+ public ThreadLocalStorage? ThreadLocalStorage { get; set; }
+
+ ///
+ /// Library versions pointed to by
+ ///
+ /// TODO: Determine if address is real or virtual
+ public LibraryVersion[] LibraryVersions { get; set; } = [];
+
+ ///
+ /// Kernel library version pointed to by
+ ///
+ /// TODO: Determine if address is real or virtual
+ public LibraryVersion? KernelLibraryVersion { get; set; }
+
+ ///
+ /// XAPI library version pointed to by
+ ///
+ /// TODO: Determine if address is real or virtual
+ public LibraryVersion? XAPILibraryVersion { get; set; }
+ }
+}
diff --git a/SabreTools.Serialization/Models/Xbox/XBEHeader.cs b/SabreTools.Serialization/Models/XboxExecutable/Header.cs
similarity index 98%
rename from SabreTools.Serialization/Models/Xbox/XBEHeader.cs
rename to SabreTools.Serialization/Models/XboxExecutable/Header.cs
index cf6cfb79..f88a875a 100644
--- a/SabreTools.Serialization/Models/Xbox/XBEHeader.cs
+++ b/SabreTools.Serialization/Models/XboxExecutable/Header.cs
@@ -1,10 +1,10 @@
-namespace SabreTools.Data.Models.Xbox
+namespace SabreTools.Data.Models.XboxExecutable
{
///
/// XBox Executable format header
///
///
- public class XBEHeader
+ public class Header
{
///
/// "XBEH"
@@ -60,7 +60,7 @@ namespace SabreTools.Data.Models.Xbox
///
/// Various flags for this .XBE file.
///
- public XbeInitializationFlags InitializationFlags { get; set; }
+ public InitializationFlags InitializationFlags { get; set; }
///
/// Address to the Image entry point, after the .XBE is loaded into memory. This is where execution starts.
diff --git a/SabreTools.Serialization/Models/Xbox/XBELibraryVersion.cs b/SabreTools.Serialization/Models/XboxExecutable/LibraryVersion.cs
similarity index 86%
rename from SabreTools.Serialization/Models/Xbox/XBELibraryVersion.cs
rename to SabreTools.Serialization/Models/XboxExecutable/LibraryVersion.cs
index d06457b0..ddcba94c 100644
--- a/SabreTools.Serialization/Models/Xbox/XBELibraryVersion.cs
+++ b/SabreTools.Serialization/Models/XboxExecutable/LibraryVersion.cs
@@ -1,10 +1,10 @@
-namespace SabreTools.Data.Models.Xbox
+namespace SabreTools.Data.Models.XboxExecutable
{
///
/// XBox Executable library version
///
///
- public class XBELibraryVersion
+ public class LibraryVersion
{
///
/// 8-byte name of this library. (i.e. "XAPILIB")
@@ -29,6 +29,6 @@ namespace SabreTools.Data.Models.Xbox
///
/// Various flags for this library.
///
- public XbeLibraryFlags LibraryFlags { get; set; }
+ public LibraryFlags LibraryFlags { get; set; }
}
}
diff --git a/SabreTools.Serialization/Models/Xbox/XBESectionHeader.cs b/SabreTools.Serialization/Models/XboxExecutable/SectionHeader.cs
similarity index 93%
rename from SabreTools.Serialization/Models/Xbox/XBESectionHeader.cs
rename to SabreTools.Serialization/Models/XboxExecutable/SectionHeader.cs
index 72738dbf..42a8e105 100644
--- a/SabreTools.Serialization/Models/Xbox/XBESectionHeader.cs
+++ b/SabreTools.Serialization/Models/XboxExecutable/SectionHeader.cs
@@ -1,16 +1,16 @@
-namespace SabreTools.Data.Models.Xbox
+namespace SabreTools.Data.Models.XboxExecutable
{
///
/// XBox Executable format section header
///
///
///
- public class XBESectionHeader
+ public class SectionHeader
{
///
/// Various flags for this .XBE section.
///
- public XbeSectionFlags SectionFlags { get; set; }
+ public SectionFlags SectionFlags { get; set; }
///
/// Address of memory to load this section at.
diff --git a/SabreTools.Serialization/Models/Xbox/XBEThreadLocalStorage.cs b/SabreTools.Serialization/Models/XboxExecutable/ThreadLocalStorage.cs
similarity index 89%
rename from SabreTools.Serialization/Models/Xbox/XBEThreadLocalStorage.cs
rename to SabreTools.Serialization/Models/XboxExecutable/ThreadLocalStorage.cs
index 49c31feb..8264d7e0 100644
--- a/SabreTools.Serialization/Models/Xbox/XBEThreadLocalStorage.cs
+++ b/SabreTools.Serialization/Models/XboxExecutable/ThreadLocalStorage.cs
@@ -1,10 +1,10 @@
-namespace SabreTools.Data.Models.Xbox
+namespace SabreTools.Data.Models.XboxExecutable
{
///
- /// XBox Executable TLS
+ /// XBox Executable thread-local storage
///
///
- public class XBEThreadLocalStorage
+ public class ThreadLocalStorage
{
///
/// Address, after the .XBE is loaded into memory, of this .XBE's TLS Data.
diff --git a/SabreTools.Serialization/Readers/XboxExecutable.cs b/SabreTools.Serialization/Readers/XboxExecutable.cs
new file mode 100644
index 00000000..e745d00b
--- /dev/null
+++ b/SabreTools.Serialization/Readers/XboxExecutable.cs
@@ -0,0 +1,224 @@
+using System.IO;
+using SabreTools.Data.Models.XboxExecutable;
+using SabreTools.IO.Extensions;
+using static SabreTools.Data.Models.XboxExecutable.Constants;
+
+#pragma warning disable IDE0017 // Simplify object initialization
+namespace SabreTools.Serialization.Readers
+{
+ public class XboxExecutable : BaseBinaryReader
+ {
+ ///
+ public override Executable? Deserialize(Stream? data)
+ {
+ // If the data is invalid
+ if (data is null || !data.CanRead)
+ return null;
+
+ try
+ {
+ // Cache the current offset
+ long initialOffset = data.Position;
+
+ // Create a new executable to fill
+ var xbe = new Executable();
+
+ #region ParseHeader
+
+ // Parse the file header
+ var header = ParseHeader(data);
+ if (!header.MagicNumber.EqualsExactly(MagicBytes))
+ return null;
+
+ // Set the file header
+ xbe.Header = header;
+
+ #endregion
+
+ #region Certificate
+
+ // TODO: Seek to and parse certificate
+
+ #endregion
+
+ #region Section Headers
+
+ // TODO: Seek to and parse section headers
+
+ #endregion
+
+ #region TLS
+
+ // TODO: Seek to and parse TLS
+
+ #endregion
+
+ #region Library Versions
+
+ // TODO: Seek to and parse library versions
+
+ #endregion
+
+ #region Kernel Library Version
+
+ // TODO: Seek to and parse kernel library version
+
+ #endregion
+
+ #region XAPI Library Version
+
+ // TODO: Seek to and parse XAPI library version
+
+ #endregion
+
+ return xbe;
+ }
+ catch
+ {
+ // Ignore the actual error
+ return null;
+ }
+ }
+
+ ///
+ /// Parse a Stream into a Certificate
+ ///
+ /// Stream to parse
+ /// Filled Certificate on success, null on error
+ public static Certificate ParseCertificate(Stream data)
+ {
+ var obj = new Certificate();
+
+ obj.SizeOfCertificate = data.ReadUInt32LittleEndian();
+ obj.TimeDate = data.ReadUInt32LittleEndian();
+ obj.TitleID = data.ReadUInt32LittleEndian();
+ obj.TitleName = data.ReadBytes(0x50);
+
+ obj.AlternativeTitleIDs = new uint[16];
+ for (int i = 0; i < obj.AlternativeTitleIDs.Length; i++)
+ {
+ obj.AlternativeTitleIDs[i] = data.ReadUInt32LittleEndian();
+ }
+
+ obj.AllowedMediaTypes = (AllowedMediaTypes)data.ReadUInt32LittleEndian();
+ obj.GameRegion = (GameRegion)data.ReadUInt32LittleEndian();
+ obj.GameRatings = data.ReadUInt32LittleEndian();
+ obj.DiskNumber = data.ReadUInt32LittleEndian();
+ obj.Version = data.ReadUInt32LittleEndian();
+ obj.LANKey = data.ReadBytes(16);
+ obj.SignatureKey = data.ReadBytes(16);
+
+ obj.AlternateSignatureKeys = new byte[16][];
+ for (int i = 0; i < obj.AlternateSignatureKeys.Length; i++)
+ {
+ obj.AlternateSignatureKeys[i] = data.ReadBytes(16);
+ }
+
+ return obj;
+ }
+
+ ///
+ /// Parse a Stream into a Header
+ ///
+ /// Stream to parse
+ /// Filled Header on success, null on error
+ public static Header ParseHeader(Stream data)
+ {
+ var obj = new Header();
+
+ obj.MagicNumber = data.ReadBytes(4);
+ obj.DigitalSignature = data.ReadBytes(256);
+ obj.BaseAddress = data.ReadUInt32LittleEndian();
+ obj.SizeOfHeaders = data.ReadUInt32LittleEndian();
+ obj.SizeOfImage = data.ReadUInt32LittleEndian();
+ obj.SizeOfImageHeader = data.ReadUInt32LittleEndian();
+ obj.TimeDate = data.ReadUInt32LittleEndian();
+ obj.CertificateAddress = data.ReadUInt32LittleEndian();
+ obj.NumberOfSections = data.ReadUInt32LittleEndian();
+ obj.SectionHeadersAddress = data.ReadUInt32LittleEndian();
+ obj.InitializationFlags = (InitializationFlags)data.ReadUInt32LittleEndian();
+ obj.EntryPoint = data.ReadUInt32LittleEndian();
+ obj.TLSAddress = data.ReadUInt32LittleEndian();
+ obj.PEStackCommit = data.ReadUInt32LittleEndian();
+ obj.PEHeapReserve = data.ReadUInt32LittleEndian();
+ obj.PEHeapCommit = data.ReadUInt32LittleEndian();
+ obj.PEBaseAddress = data.ReadUInt32LittleEndian();
+ obj.PESizeOfImage = data.ReadUInt32LittleEndian();
+ obj.PEChecksum = data.ReadUInt32LittleEndian();
+ obj.PETimeDate = data.ReadUInt32LittleEndian();
+ obj.DebugPathNameAddress = data.ReadUInt32LittleEndian();
+ obj.DebugFileNameAddress = data.ReadUInt32LittleEndian();
+ obj.DebugUnicodeFileNameAddress = data.ReadUInt32LittleEndian();
+ obj.KernelImageThunkAddress = data.ReadUInt32LittleEndian();
+ obj.NonKernelImportDirectoryAddress = data.ReadUInt32LittleEndian();
+ obj.NumberOfLibraryVersions = data.ReadUInt32LittleEndian();
+ obj.LibraryVersionsAddress = data.ReadUInt32LittleEndian();
+ obj.KernelLibraryVersionAddress = data.ReadUInt32LittleEndian();
+ obj.XAPILibraryVersionAddress = data.ReadUInt32LittleEndian();
+ obj.LogoBitmapAddress = data.ReadUInt32LittleEndian();
+ obj.LogoBitmapSize = data.ReadUInt32LittleEndian();
+
+ return obj;
+ }
+
+ ///
+ /// Parse a Stream into a LibraryVersion
+ ///
+ /// Stream to parse
+ /// Filled LibraryVersion on success, null on error
+ public static LibraryVersion ParseLibraryVersion(Stream data)
+ {
+ var obj = new LibraryVersion();
+
+ obj.LibraryName = data.ReadBytes(8);
+ obj.MajorVersion = data.ReadUInt16LittleEndian();
+ obj.MinorVersion = data.ReadUInt16LittleEndian();
+ obj.BuildVersion = data.ReadUInt16LittleEndian();
+ obj.LibraryFlags = (LibraryFlags)data.ReadUInt32LittleEndian();
+
+ return obj;
+ }
+
+ ///
+ /// Parse a Stream into a SectionHeader
+ ///
+ /// Stream to parse
+ /// Filled SectionHeader on success, null on error
+ public static SectionHeader ParseSectionHeader(Stream data)
+ {
+ var obj = new SectionHeader();
+
+ obj.SectionFlags = (SectionFlags)data.ReadUInt32LittleEndian();
+ obj.VirtualAddress = data.ReadUInt32LittleEndian();
+ obj.VirtualSize = data.ReadUInt32LittleEndian();
+ obj.RawAddress = data.ReadUInt32LittleEndian();
+ obj.RawSize = data.ReadUInt32LittleEndian();
+ obj.SectionNameAddress = data.ReadUInt32LittleEndian();
+ obj.SectionNameReferenceCount = data.ReadUInt32LittleEndian();
+ obj.HeadSharedPageReferenceCountAddress = data.ReadUInt32LittleEndian();
+ obj.TailSharedPageReferenceCountAddress = data.ReadUInt32LittleEndian();
+ obj.SectionDigest = data.ReadBytes(20);
+
+ return obj;
+ }
+
+ ///
+ /// Parse a Stream into a ThreadLocalStorage
+ ///
+ /// Stream to parse
+ /// Filled ThreadLocalStorage on success, null on error
+ public static ThreadLocalStorage ParseThreadLocalStorage(Stream data)
+ {
+ var obj = new ThreadLocalStorage();
+
+ obj.DataStartAddress = data.ReadUInt32LittleEndian();
+ obj.DataEndAddress = data.ReadUInt32LittleEndian();
+ obj.TLSIndexAddress = data.ReadUInt32LittleEndian();
+ obj.TLSCallbackAddress = data.ReadUInt32LittleEndian();
+ obj.SizeOfZeroFill = data.ReadUInt32LittleEndian();
+ obj.Characteristics = data.ReadUInt32LittleEndian();
+
+ return obj;
+ }
+ }
+}