using System; using System.IO; using System.Text; using SabreTools.Data.Extensions; using SabreTools.Data.Models.XboxExecutable; namespace SabreTools.Wrappers { public partial class XboxExecutable : WrapperBase { #region Descriptive Properties /// public override string DescriptionString => "XBox Executable"; #endregion #region Extension Properties /// public uint[]? AlternativeTitleIDs => Certificate?.AlternativeTitleIDs; /// public string[]? AlternativeTitleIDsStrings { get { // Ignore invalid alternative title IDs if (AlternativeTitleIDs is null) return null; return Array.ConvertAll(AlternativeTitleIDs, ba => ba.ToFormattedXBETitleID()); } } /// public uint BaseAddress => Model.Header?.BaseAddress ?? 0; /// public Certificate? Certificate => Model.Certificate; /// public byte[] DigitalSignature => Model.Header?.DigitalSignature ?? []; /// /// Adjusted with debug shift public uint EntryPointDebug => (Model.Header?.EntryPoint ?? 0) ^ 0x94859D4B; /// /// Adjusted with debug shift public uint EntryPointRetail => (Model.Header?.EntryPoint ?? 0) ^ 0xA8FC57AB; /// public LibraryVersion? KernelLibraryVersion => Model.KernelLibraryVersion; /// /// Adjusted with debug shift public uint KernelImageThunkAddressDebug => (Model.Header?.KernelImageThunkAddress ?? 0) ^ 0xEFB1F152; /// /// Adjusted with debug shift public uint KernelImageThunkAddressRetail => (Model.Header?.KernelImageThunkAddress ?? 0) ^ 0x5B6D40B6; /// public LibraryVersion[] LibraryVersions => Model.LibraryVersions; /// public SectionHeader[] SectionHeaders => Model.SectionHeaders; /// public ThreadLocalStorage? ThreadLocalStorage => Model.ThreadLocalStorage; /// public uint TitleID => Certificate?.TitleID ?? 0; /// public string TitleIDString => TitleID.ToFormattedXBETitleID(); /// public string? TitleName { get { // Ignore invalid certificates if (Certificate is null) return null; return Encoding.Unicode.GetString(Certificate.TitleName); } } /// public LibraryVersion? XAPILibraryVersion => Model.XAPILibraryVersion; #endregion #region Constructors /// public XboxExecutable(Executable model, byte[] data) : base(model, data) { } /// public XboxExecutable(Executable model, byte[] data, int offset) : base(model, data, offset) { } /// public XboxExecutable(Executable model, byte[] data, int offset, int length) : base(model, data, offset, length) { } /// public XboxExecutable(Executable model, Stream data) : base(model, data) { } /// public XboxExecutable(Executable model, Stream data, long offset) : base(model, data, offset) { } /// public XboxExecutable(Executable model, Stream data, long offset, long length) : base(model, data, offset, length) { } #endregion #region Static Constructors /// /// Create an XBox Executable from a byte array and offset /// /// Byte array representing the archive /// Offset within the array to parse /// An XBox Executable wrapper on success, null on failure public static XboxExecutable? Create(byte[]? data, int offset) { // If the data is invalid if (data is null || data.Length == 0) return null; // If the offset is out of bounds if (offset < 0 || offset >= data.Length) return null; // Create a memory stream and use that var dataStream = new MemoryStream(data, offset, data.Length - offset); return Create(dataStream); } /// /// Create an XBox Executable from a Stream /// /// Stream representing the archive /// An XBox Executable wrapper on success, null on failure public static XboxExecutable? Create(Stream? data) { // If the data is invalid if (data is null || !data.CanRead) return null; try { // Cache the current offset long currentOffset = data.Position; var model = new Serialization.Readers.XboxExecutable().Deserialize(data); if (model is null) return null; return new XboxExecutable(model, data, currentOffset); } catch { return null; } } #endregion } }