From 12a7936665a8197f69ba90e2e2806110c0aa2b07 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sat, 16 Sep 2023 16:06:48 -0400 Subject: [PATCH] Add code from BOS --- AACSMediaKeyBlock.cs | 329 ++++++ BDPlusSVM.cs | 23 + BFPK.cs | 71 ++ BSP.cs | 162 +++ CFB.cs | 133 +++ CIA.cs | 473 +++++++++ Extensions.cs | 332 ++++++ GCF.cs | 613 +++++++++++ InstallShieldCabinet.cs | 457 ++++++++ LinearExecutable.cs | 762 ++++++++++++++ MSDOS.cs | 81 ++ MicrosoftCabinet.cs | 168 +++ N3DS.cs | 669 ++++++++++++ NCF.cs | 406 +++++++ NewExecutable.cs | 416 ++++++++ Nitro.cs | 282 +++++ PAK.cs | 71 ++ PFF.cs | 99 ++ PlayJAudioFile.cs | 200 ++++ PortableExecutable.cs | 2038 ++++++++++++++++++++++++++++++++++++ Quantum.cs | 82 ++ SGA.cs | 449 ++++++++ SabreTools.Printing.csproj | 66 +- VBSP.cs | 85 ++ VPK.cs | 155 +++ WAD.cs | 114 ++ XZP.cs | 164 +++ 27 files changed, 8868 insertions(+), 32 deletions(-) create mode 100644 AACSMediaKeyBlock.cs create mode 100644 BDPlusSVM.cs create mode 100644 BFPK.cs create mode 100644 BSP.cs create mode 100644 CFB.cs create mode 100644 CIA.cs create mode 100644 Extensions.cs create mode 100644 GCF.cs create mode 100644 InstallShieldCabinet.cs create mode 100644 LinearExecutable.cs create mode 100644 MSDOS.cs create mode 100644 MicrosoftCabinet.cs create mode 100644 N3DS.cs create mode 100644 NCF.cs create mode 100644 NewExecutable.cs create mode 100644 Nitro.cs create mode 100644 PAK.cs create mode 100644 PFF.cs create mode 100644 PlayJAudioFile.cs create mode 100644 PortableExecutable.cs create mode 100644 Quantum.cs create mode 100644 SGA.cs create mode 100644 VBSP.cs create mode 100644 VPK.cs create mode 100644 WAD.cs create mode 100644 XZP.cs diff --git a/AACSMediaKeyBlock.cs b/AACSMediaKeyBlock.cs new file mode 100644 index 0000000..c9caa9c --- /dev/null +++ b/AACSMediaKeyBlock.cs @@ -0,0 +1,329 @@ +using System.Text; +using SabreTools.Models.AACS; + +namespace SabreTools.Printing +{ + public static class AACSMediaKeyBlock + { + public static void Print(StringBuilder builder, MediaKeyBlock mediaKeyBlock) + { + builder.AppendLine("AACS Media Key Block Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, mediaKeyBlock.Records); + } + +#if NET48 + private static void Print(StringBuilder builder, Record[] records) +#else + private static void Print(StringBuilder builder, Record?[]? records) +#endif + { + builder.AppendLine(" Records Information:"); + builder.AppendLine(" -------------------------"); + if (records == null || records.Length == 0) + { + builder.AppendLine(" No records"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < records.Length; i++) + { + var record = records[i]; + Print(builder, record, i); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Record record, int index) +#else + private static void Print(StringBuilder builder, Record? record, int index) +#endif + { + builder.AppendLine($" Record Entry {index}"); + if (record == null) + { + builder.AppendLine(" [NULL]"); + return; + } + + builder.AppendLine($" Record type: {record.RecordType} (0x{record.RecordType:X})"); + builder.AppendLine(record.RecordLength, " Record length"); + + switch (record) + { + case EndOfMediaKeyBlockRecord eomkb: + Print(builder, eomkb); + break; + case ExplicitSubsetDifferenceRecord esd: + Print(builder, esd); + break; + case MediaKeyDataRecord mkd: + Print(builder, mkd); + break; + case SubsetDifferenceIndexRecord sdi: + Print(builder, sdi); + break; + case TypeAndVersionRecord tav: + Print(builder, tav); + break; + case DriveRevocationListRecord drl: + Print(builder, drl); + break; + case HostRevocationListRecord hrl: + Print(builder, hrl); + break; + case VerifyMediaKeyRecord vmk: + Print(builder, vmk); + break; + case CopyrightRecord c: + Print(builder, c); + break; + } + } + +#if NET48 + private static void Print(StringBuilder builder, EndOfMediaKeyBlockRecord record) +#else + private static void Print(StringBuilder builder, EndOfMediaKeyBlockRecord record) +#endif + { + if (record == null) + return; + + builder.AppendLine(record.SignatureData, " Signature data"); + } + +#if NET48 + private static void Print(StringBuilder builder, ExplicitSubsetDifferenceRecord record) +#else + private static void Print(StringBuilder builder, ExplicitSubsetDifferenceRecord? record) +#endif + { + if (record == null) + return; + + builder.AppendLine(" Subset Differences:"); + builder.AppendLine(" -------------------------"); + if (record?.SubsetDifferences == null || record.SubsetDifferences.Length == 0) + { + builder.AppendLine(" No subset differences"); + return; + } + + for (int j = 0; j < record.SubsetDifferences.Length; j++) + { + var sd = record.SubsetDifferences[j]; + builder.AppendLine($" Subset Difference {j}"); + if (sd == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(sd.Mask, " Mask"); + builder.AppendLine(sd.Number, " Number"); + } + } + } + +#if NET48 + private static void Print(StringBuilder builder, MediaKeyDataRecord record) +#else + private static void Print(StringBuilder builder, MediaKeyDataRecord? record) +#endif + { + if (record == null) + return; + + builder.AppendLine(" Media Keys:"); + builder.AppendLine(" -------------------------"); + if (record?.MediaKeyData == null || record.MediaKeyData.Length == 0) + { + builder.AppendLine(" No media keys"); + return; + } + + for (int j = 0; j < record.MediaKeyData.Length; j++) + { + var mk = record.MediaKeyData[j]; + builder.AppendLine(mk, $" Media key {j}"); + } + } + +#if NET48 + private static void Print(StringBuilder builder, SubsetDifferenceIndexRecord record) +#else + private static void Print(StringBuilder builder, SubsetDifferenceIndexRecord? record) +#endif + { + if (record == null) + return; + + builder.AppendLine($" Span: {record.Span} (0x{record.Span:X})"); + builder.AppendLine(" Offsets:"); + builder.AppendLine(" -------------------------"); + if (record.Offsets == null || record.Offsets.Length == 0) + { + builder.AppendLine(" No offsets"); + return; + } + + for (int j = 0; j < record.Offsets.Length; j++) + { + var offset = record.Offsets[j]; + builder.AppendLine(offset, $" Offset {j}"); + } + } + +#if NET48 + private static void Print(StringBuilder builder, TypeAndVersionRecord record) +#else + private static void Print(StringBuilder builder, TypeAndVersionRecord? record) +#endif + { + if (record == null) + return; + + builder.AppendLine($" Media key block type: {record.MediaKeyBlockType} (0x{record.MediaKeyBlockType:X})"); + builder.AppendLine(record.VersionNumber, " Version number"); + } + +#if NET48 + private static void Print(StringBuilder builder, DriveRevocationListRecord record) +#else + private static void Print(StringBuilder builder, DriveRevocationListRecord? record) +#endif + { + if (record == null) + return; + + builder.AppendLine(record.TotalNumberOfEntries, " Total number of entries"); + builder.AppendLine(" Signature Blocks:"); + builder.AppendLine(" -------------------------"); + if (record.SignatureBlocks == null || record.SignatureBlocks.Length == 0) + { + builder.AppendLine(" No signature blocks"); + return; + } + + for (int j = 0; j < record.SignatureBlocks.Length; j++) + { + var block = record.SignatureBlocks[j]; + builder.AppendLine($" Signature Block {j}"); + if (block == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(block.NumberOfEntries, " Number of entries"); + builder.AppendLine(" Entry Fields:"); + builder.AppendLine(" -------------------------"); + if (block.EntryFields == null || block.EntryFields.Length == 0) + { + builder.AppendLine(" No entry fields"); + } + else + { + for (int k = 0; k < block.EntryFields.Length; k++) + { + var ef = block.EntryFields[k]; + builder.AppendLine($" Entry {k}"); + if (ef == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(ef.Range, " Range"); + builder.AppendLine(ef.DriveID, " Drive ID"); + } + } + } + } + } + +#if NET48 + private static void Print(StringBuilder builder, HostRevocationListRecord record) +#else + private static void Print(StringBuilder builder, HostRevocationListRecord? record) +#endif + { + if (record == null) + return; + + builder.AppendLine($" Total number of entries: {record.TotalNumberOfEntries} (0x{record.TotalNumberOfEntries:X})"); + builder.AppendLine(" Signature Blocks:"); + builder.AppendLine(" -------------------------"); + if (record.SignatureBlocks == null || record.SignatureBlocks.Length == 0) + { + builder.AppendLine(" No signature blocks"); + return; + } + + for (int j = 0; j < record.SignatureBlocks.Length; j++) + { + builder.AppendLine($" Signature Block {j}"); + var block = record.SignatureBlocks[j]; + if (block == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(block.NumberOfEntries, " Number of entries"); + builder.AppendLine(" Entry Fields:"); + builder.AppendLine(" -------------------------"); + if (block.EntryFields == null || block.EntryFields.Length == 0) + { + builder.AppendLine(" No entry fields"); + continue; + } + + for (int k = 0; k < block.EntryFields.Length; k++) + { + var ef = block.EntryFields[k]; + builder.AppendLine($" Entry {k}"); + if (ef == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + + builder.AppendLine(ef.Range, " Range"); + builder.AppendLine(ef.HostID, " Host ID"); + } + } + } + } + +#if NET48 + private static void Print(StringBuilder builder, VerifyMediaKeyRecord record) +#else + private static void Print(StringBuilder builder, VerifyMediaKeyRecord? record) +#endif + { + if (record == null) + return; + + builder.AppendLine(record.CiphertextValue, " Ciphertext value"); + } + +#if NET48 + private static void Print(StringBuilder builder, CopyrightRecord record) +#else + private static void Print(StringBuilder builder, CopyrightRecord? record) +#endif + { + if (record == null) + return; + + builder.AppendLine(record.Copyright, " Copyright"); + } + } +} \ No newline at end of file diff --git a/BDPlusSVM.cs b/BDPlusSVM.cs new file mode 100644 index 0000000..1a09c03 --- /dev/null +++ b/BDPlusSVM.cs @@ -0,0 +1,23 @@ +using System.Text; +using SabreTools.Models.BDPlus; + +namespace SabreTools.Printing +{ + public static class BDPlusSVM + { + public static void Print(StringBuilder builder, SVM svm) + { + builder.AppendLine("BD+ SVM Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(svm.Signature, "Signature"); + builder.AppendLine(svm.Unknown1, "Unknown 1"); + builder.AppendLine(svm.Year, "Year"); + builder.AppendLine(svm.Month, "Month"); + builder.AppendLine(svm.Day, "Day"); + builder.AppendLine(svm.Unknown2, "Unknown 2"); + builder.AppendLine(svm.Length, "Length"); + //builder.AppendLine(svm.Data, "Data"); + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/BFPK.cs b/BFPK.cs new file mode 100644 index 0000000..f813de6 --- /dev/null +++ b/BFPK.cs @@ -0,0 +1,71 @@ +using System.Text; +using SabreTools.Models.BFPK; + +namespace SabreTools.Printing +{ + public static class BFPK + { + public static void Print(StringBuilder builder, Archive archive) + { + builder.AppendLine("BFPK Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, archive.Header); + Print(builder, archive.Files); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Magic, " Magic"); + builder.AppendLine(header.Version, " Version"); + builder.AppendLine(header.Files, " Files"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, FileEntry[] files) +#else + private static void Print(StringBuilder builder, FileEntry?[]? files) +#endif + { + builder.AppendLine(" File Table Information:"); + builder.AppendLine(" -------------------------"); + if (files == null || files.Length == 0) + { + builder.AppendLine(" No file table items"); + return; + } + + for (int i = 0; i < files.Length; i++) + { + var entry = files[i]; + builder.AppendLine($" File Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.NameSize, " Name size"); + builder.AppendLine(entry.Name, " Name"); + builder.AppendLine(entry.UncompressedSize, " Uncompressed size"); + builder.AppendLine(entry.Offset, " Offset"); + builder.AppendLine(entry.CompressedSize, " Compressed size"); + } + } + } +} \ No newline at end of file diff --git a/BSP.cs b/BSP.cs new file mode 100644 index 0000000..5651d99 --- /dev/null +++ b/BSP.cs @@ -0,0 +1,162 @@ +using System.Text; +using SabreTools.Models.BSP; +using static SabreTools.Models.BSP.Constants; + +namespace SabreTools.Printing +{ + public static class BSP + { + public static void Print(StringBuilder builder, File file) + { + builder.AppendLine("BSP Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, file.Header); + Print(builder, file.Lumps); + Print(builder, file.TextureHeader); + Print(builder, file.Textures); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Version, " Version"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Lump[] lumps) +#else + private static void Print(StringBuilder builder, Lump?[]? lumps) +#endif + { + builder.AppendLine(" Lumps Information:"); + builder.AppendLine(" -------------------------"); + if (lumps == null || lumps.Length == 0) + { + builder.AppendLine(" No lumps"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < lumps.Length; i++) + { + var lump = lumps[i]; + string specialLumpName = string.Empty; + switch (i) + { + case HL_BSP_LUMP_ENTITIES: + specialLumpName = " (entities)"; + break; + case HL_BSP_LUMP_TEXTUREDATA: + specialLumpName = " (texture data)"; + break; + } + + builder.AppendLine($" Lump {i}{specialLumpName}"); + if (lump == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(lump.Offset, " Offset"); + builder.AppendLine(lump.Length, " Length"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, TextureHeader header) +#else + private static void Print(StringBuilder builder, TextureHeader? header) +#endif + { + builder.AppendLine(" Texture Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No texture header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.TextureCount, " Texture count"); + builder.AppendLine(" Offsets:"); + if (header.Offsets == null || header.Offsets.Length == 0) + { + builder.AppendLine(" No offsets"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < header.Offsets.Length; i++) + { + builder.AppendLine(header.Offsets[i], $" Offset {i}"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Texture[] textures) +#else + private static void Print(StringBuilder builder, Texture?[]? textures) +#endif + { + builder.AppendLine(" Textures Information:"); + builder.AppendLine(" -------------------------"); + if (textures == null || textures.Length == 0) + { + builder.AppendLine(" No textures"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < textures.Length; i++) + { + var texture = textures[i]; + builder.AppendLine($" Texture {i}"); + if (texture == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(texture.Name, " Name"); + builder.AppendLine(texture.Width, " Width"); + builder.AppendLine(texture.Height, " Height"); + builder.AppendLine(" Offsets:"); + if (texture.Offsets == null || texture.Offsets.Length == 0) + { + builder.AppendLine(" No offsets"); + continue; + } + else + { + for (int j = 0; j < texture.Offsets.Length; j++) + { + builder.AppendLine(texture.Offsets[i], $" Offset {j}"); + } + } + // Skip texture data + builder.AppendLine(texture.PaletteSize, " Palette size"); + // Skip palette data + } + builder.AppendLine(); + } + + } +} \ No newline at end of file diff --git a/CFB.cs b/CFB.cs new file mode 100644 index 0000000..269103c --- /dev/null +++ b/CFB.cs @@ -0,0 +1,133 @@ +using System; +using System.Text; +using SabreTools.Models.CFB; + +namespace SabreTools.Printing +{ + public static class CFB + { + public static void Print(StringBuilder builder, Binary binary) + { + builder.AppendLine("Compound File Binary Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, binary.Header); + Print(builder, binary.FATSectorNumbers, "FAT"); + Print(builder, binary.MiniFATSectorNumbers, "Mini FAT"); + Print(builder, binary.DIFATSectorNumbers, "DIFAT"); + Print(builder, binary.DirectoryEntries); + } + +#if NET48 + private static void Print(StringBuilder builder, FileHeader header) +#else + private static void Print(StringBuilder builder, FileHeader? header) +#endif + { + builder.AppendLine(" File Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No file header"); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.CLSID, " CLSID"); + builder.AppendLine(header.MinorVersion, " Minor version"); + builder.AppendLine(header.MajorVersion, " Major version"); + builder.AppendLine(header.ByteOrder, " Byte order"); + builder.AppendLine($" Sector shift: {header.SectorShift} (0x{header.SectorShift:X}) => {Math.Pow(2, header.SectorShift)}"); + builder.AppendLine($" Mini sector shift: {header.MiniSectorShift} (0x{header.MiniSectorShift:X}) => {Math.Pow(2, header.MiniSectorShift)}"); + builder.AppendLine(header.Reserved, " Reserved"); + builder.AppendLine(header.NumberOfDirectorySectors, " Number of directory sectors"); + builder.AppendLine(header.NumberOfFATSectors, " Number of FAT sectors"); + builder.AppendLine(header.FirstDirectorySectorLocation, " First directory sector location"); + builder.AppendLine(header.TransactionSignatureNumber, " Transaction signature number"); + builder.AppendLine(header.MiniStreamCutoffSize, " Mini stream cutoff size"); + builder.AppendLine(header.FirstMiniFATSectorLocation, " First mini FAT sector location"); + builder.AppendLine(header.NumberOfMiniFATSectors, " Number of mini FAT sectors"); + builder.AppendLine(header.FirstDIFATSectorLocation, " First DIFAT sector location"); + builder.AppendLine(header.NumberOfDIFATSectors, " Number of DIFAT sectors"); + builder.AppendLine(" DIFAT:"); + if (header.DIFAT == null || header.DIFAT.Length == 0) + { + builder.AppendLine(" No DIFAT entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < header.DIFAT.Length; i++) + { + builder.AppendLine($" DIFAT Entry {i}: {header.DIFAT[i]} (0x{header.DIFAT[i]:X})"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, SectorNumber[] sectorNumbers, string name) +#else + private static void Print(StringBuilder builder, SectorNumber?[]? sectorNumbers, string name) +#endif + { + builder.AppendLine($" {name} Sectors Information:"); + builder.AppendLine(" -------------------------"); + if (sectorNumbers == null || sectorNumbers.Length == 0) + { + builder.AppendLine($" No {name} sectors"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < sectorNumbers.Length; i++) + { + builder.AppendLine($" {name} Sector Entry {i}: {sectorNumbers[i]} (0x{sectorNumbers[i]:X})"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryEntry[] directoryEntries) +#else + private static void Print(StringBuilder builder, DirectoryEntry?[]? directoryEntries) +#endif + { + builder.AppendLine(" Directory Entries Information:"); + builder.AppendLine(" -------------------------"); + if (directoryEntries == null || directoryEntries.Length == 0) + { + builder.AppendLine(" No directory entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < directoryEntries.Length; i++) + { + var directoryEntry = directoryEntries[i]; + builder.AppendLine($" Directory Entry {i}"); + if (directoryEntry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(directoryEntry.Name, " Name"); + builder.AppendLine(directoryEntry.NameLength, " Name length"); + builder.AppendLine($" Object type: {directoryEntry.ObjectType} (0x{directoryEntry.ObjectType:X})"); + builder.AppendLine($" Color flag: {directoryEntry.ColorFlag} (0x{directoryEntry.ColorFlag:X})"); + builder.AppendLine($" Left sibling ID: {directoryEntry.LeftSiblingID} (0x{directoryEntry.LeftSiblingID:X})"); + builder.AppendLine($" Right sibling ID: {directoryEntry.RightSiblingID} (0x{directoryEntry.RightSiblingID:X})"); + builder.AppendLine($" Child ID: {directoryEntry.ChildID} (0x{directoryEntry.ChildID:X})"); + builder.AppendLine(directoryEntry.CLSID, " CLSID"); + builder.AppendLine(directoryEntry.StateBits, " State bits"); + builder.AppendLine(directoryEntry.CreationTime, " Creation time"); + builder.AppendLine(directoryEntry.ModifiedTime, " Modification time"); + builder.AppendLine(directoryEntry.StartingSectorLocation, " Staring sector location"); + builder.AppendLine(directoryEntry.StreamSize, " Stream size"); + } + builder.AppendLine(); + } + + } +} \ No newline at end of file diff --git a/CIA.cs b/CIA.cs new file mode 100644 index 0000000..3b6f822 --- /dev/null +++ b/CIA.cs @@ -0,0 +1,473 @@ +using System.Text; +using SabreTools.Models.N3DS; + +namespace SabreTools.Printing +{ + public static class CIA + { + public static void Print(StringBuilder builder, SabreTools.Models.N3DS.CIA cia) + { + builder.AppendLine("CIA Archive Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, cia.Header); + Print(builder, cia.CertificateChain); + Print(builder, cia.Ticket); + Print(builder, cia.TMDFileData); + Print(builder, cia.Partitions); + Print(builder, cia.MetaData); + } + +#if NET48 + private static void Print(StringBuilder builder, CIAHeader header) +#else + private static void Print(StringBuilder builder, CIAHeader? header) +#endif + { + builder.AppendLine(" CIA Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No CIA header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.HeaderSize, " Header size"); + builder.AppendLine(header.Type, " Type"); + builder.AppendLine(header.Version, " Version"); + builder.AppendLine(header.CertificateChainSize, " Certificate chain size"); + builder.AppendLine(header.TicketSize, " Ticket size"); + builder.AppendLine(header.TMDFileSize, " TMD file size"); + builder.AppendLine(header.MetaSize, " Meta size"); + builder.AppendLine(header.ContentSize, " Content size"); + builder.AppendLine(header.ContentIndex, " Content index"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Certificate[] certificateChain) +#else + private static void Print(StringBuilder builder, Certificate?[]? certificateChain) +#endif + { + builder.AppendLine(" Certificate Chain Information:"); + builder.AppendLine(" -------------------------"); + if (certificateChain == null || certificateChain.Length == 0) + { + builder.AppendLine(" No certificates, expected 3"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < certificateChain.Length; i++) + { + var certificate = certificateChain[i]; + + string certificateName = string.Empty; + switch (i) + { + case 0: certificateName = " (CA)"; break; + case 1: certificateName = " (Ticket)"; break; + case 2: certificateName = " (TMD)"; break; + } + + builder.AppendLine($" Certificate {i}{certificateName}"); + if (certificate == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine($" Signature type: {certificate.SignatureType} (0x{certificate.SignatureType:X})"); + builder.AppendLine(certificate.SignatureSize, " Signature size"); + builder.AppendLine(certificate.PaddingSize, " Padding size"); + builder.AppendLine(certificate.Signature, " Signature"); + builder.AppendLine(certificate.Padding, " Padding"); + builder.AppendLine(certificate.Issuer, " Issuer"); + builder.AppendLine($" Key type: {certificate.KeyType} (0x{certificate.KeyType:X})"); + builder.AppendLine(certificate.Name, " Name"); + builder.AppendLine(certificate.ExpirationTime, " Expiration time"); + switch (certificate.KeyType) + { + case PublicKeyType.RSA_4096: + case PublicKeyType.RSA_2048: + builder.AppendLine(certificate.RSAModulus, " Modulus"); + builder.AppendLine(certificate.RSAPublicExponent, " Public exponent"); + builder.AppendLine(certificate.RSAPadding, " Padding"); + break; + case PublicKeyType.EllipticCurve: + builder.AppendLine(certificate.ECCPublicKey, " Public key"); + builder.AppendLine(certificate.ECCPadding, " Padding"); + break; + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Ticket ticket) +#else + private static void Print(StringBuilder builder, Ticket? ticket) +#endif + { + builder.AppendLine(" Ticket Information:"); + builder.AppendLine(" -------------------------"); + if (ticket == null) + { + builder.AppendLine(" No ticket"); + builder.AppendLine(); + return; + } + + builder.AppendLine($" Signature type: {ticket.SignatureType} (0x{ticket.SignatureType:X})"); + builder.AppendLine(ticket.SignatureSize, " Signature size"); + builder.AppendLine(ticket.PaddingSize, " Padding size"); + builder.AppendLine(ticket.Signature, " Signature"); + builder.AppendLine(ticket.Padding, " Padding"); + builder.AppendLine(ticket.Issuer, " Issuer"); + builder.AppendLine(ticket.ECCPublicKey, " ECC public key"); + builder.AppendLine(ticket.Version, " Version"); + builder.AppendLine(ticket.CaCrlVersion, " CaCrlVersion"); + builder.AppendLine(ticket.SignerCrlVersion, " SignerCrlVersion"); + builder.AppendLine(ticket.TitleKey, " Title key"); + builder.AppendLine(ticket.Reserved1, " Reserved 1"); + builder.AppendLine(ticket.TicketID, " Ticket ID"); + builder.AppendLine(ticket.ConsoleID, " Console ID"); + builder.AppendLine(ticket.TitleID, " Title ID"); + builder.AppendLine(ticket.Reserved2, " Reserved 2"); + builder.AppendLine(ticket.TicketTitleVersion, " Ticket title version"); + builder.AppendLine(ticket.Reserved3, " Reserved 3"); + builder.AppendLine(ticket.LicenseType, " License type"); + builder.AppendLine(ticket.CommonKeyYIndex, " Common key Y index"); + builder.AppendLine(ticket.Reserved4, " Reserved 4"); + builder.AppendLine(ticket.eShopAccountID, " eShop Account ID"); + builder.AppendLine(ticket.Reserved5, " Reserved 5"); + builder.AppendLine(ticket.Audit, " Audit"); + builder.AppendLine(ticket.Reserved6, " Reserved 6"); + builder.AppendLine(" Limits:"); + if (ticket.Limits == null || ticket.Limits.Length == 0) + { + builder.AppendLine(" No limits"); + } + else + { + for (int i = 0; i < ticket.Limits.Length; i++) + { + builder.AppendLine(ticket.Limits[i], $" Limit {i}"); + } + } + builder.AppendLine(ticket.ContentIndexSize, " Content index size"); + builder.AppendLine(ticket.ContentIndex, " Content index"); + builder.AppendLine(); + + builder.AppendLine(" Ticket Certificate Chain Information:"); + builder.AppendLine(" -------------------------"); + if (ticket.CertificateChain == null || ticket.CertificateChain.Length == 0) + { + builder.AppendLine(" No certificates, expected 2"); + } + else + { + for (int i = 0; i < ticket.CertificateChain.Length; i++) + { + var certificate = ticket.CertificateChain[i]; + + string certificateName = string.Empty; + switch (i) + { + case 0: certificateName = " (Ticket)"; break; + case 1: certificateName = " (CA)"; break; + } + + builder.AppendLine($" Certificate {i}{certificateName}"); + if (certificate == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine($" Signature type: {certificate.SignatureType} (0x{certificate.SignatureType:X})"); + builder.AppendLine(certificate.SignatureSize, " Signature size"); + builder.AppendLine(certificate.PaddingSize, " Padding size"); + builder.AppendLine(certificate.Signature, " Signature"); + builder.AppendLine(certificate.Padding, " Padding"); + builder.AppendLine(certificate.Issuer, " Issuer"); + builder.AppendLine($" Key type: {certificate.KeyType} (0x{certificate.KeyType:X})"); + builder.AppendLine(certificate.Name, " Name"); + builder.AppendLine(certificate.ExpirationTime, " Expiration time"); + switch (certificate.KeyType) + { + case PublicKeyType.RSA_4096: + case PublicKeyType.RSA_2048: + builder.AppendLine(certificate.RSAModulus, " Modulus"); + builder.AppendLine(certificate.RSAPublicExponent, " Public exponent"); + builder.AppendLine(certificate.RSAPadding, " Padding"); + break; + case PublicKeyType.EllipticCurve: + builder.AppendLine(certificate.ECCPublicKey, " Public key"); + builder.AppendLine(certificate.ECCPadding, " Padding"); + break; + } + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, TitleMetadata tmd) +#else + private static void Print(StringBuilder builder, TitleMetadata? tmd) +#endif + { + builder.AppendLine(" Title Metadata Information:"); + builder.AppendLine(" -------------------------"); + if (tmd == null) + { + builder.AppendLine(" No title metadata"); + builder.AppendLine(); + return; + } + + builder.AppendLine($" Signature type: {tmd.SignatureType} (0x{tmd.SignatureType:X})"); + builder.AppendLine(tmd.SignatureSize, " Signature size"); + builder.AppendLine(tmd.PaddingSize, " Padding size"); + builder.AppendLine(tmd.Signature, " Signature"); + builder.AppendLine(tmd.Padding1, " Padding 1"); + builder.AppendLine(tmd.Issuer, " Issuer"); + builder.AppendLine(tmd.Version, " Version"); + builder.AppendLine(tmd.CaCrlVersion, " CaCrlVersion"); + builder.AppendLine(tmd.SignerCrlVersion, " SignerCrlVersion"); + builder.AppendLine(tmd.Reserved1, " Reserved 1"); + builder.AppendLine(tmd.SystemVersion, " System version"); + builder.AppendLine(tmd.TitleID, " Title ID"); + builder.AppendLine(tmd.TitleType, " Title type"); + builder.AppendLine(tmd.GroupID, " Group ID"); + builder.AppendLine(tmd.SaveDataSize, " Save data size"); + builder.AppendLine(tmd.SRLPrivateSaveDataSize, " SRL private save data size"); + builder.AppendLine(tmd.Reserved2, " Reserved 2"); + builder.AppendLine(tmd.SRLFlag, " SRL flag"); + builder.AppendLine(tmd.Reserved3, " Reserved 3"); + builder.AppendLine(tmd.AccessRights, " Access rights"); + builder.AppendLine(tmd.TitleVersion, " Title version"); + builder.AppendLine(tmd.ContentCount, " Content count"); + builder.AppendLine(tmd.BootContent, " Boot content"); + builder.AppendLine(tmd.Padding2, " Padding 2"); + builder.AppendLine(tmd.SHA256HashContentInfoRecords, " SHA-256 hash of the content info records"); + builder.AppendLine(); + + builder.AppendLine(" Ticket Content Info Records Information:"); + builder.AppendLine(" -------------------------"); + if (tmd.ContentInfoRecords == null || tmd.ContentInfoRecords.Length == 0) + { + builder.AppendLine(" No content info records, expected 64"); + } + else + { + for (int i = 0; i < tmd.ContentInfoRecords.Length; i++) + { + var contentInfoRecord = tmd.ContentInfoRecords[i]; + builder.AppendLine($" Content Info Record {i}"); + if (contentInfoRecord == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(contentInfoRecord.ContentIndexOffset, " Content index offset"); + builder.AppendLine(contentInfoRecord.ContentCommandCount, " Content command count"); + builder.AppendLine(contentInfoRecord.UnhashedContentRecordsSHA256Hash, $" SHA-256 hash of the next {contentInfoRecord.ContentCommandCount} records not hashed"); + } + } + builder.AppendLine(); + + builder.AppendLine(" Ticket Content Chunk Records Information:"); + builder.AppendLine(" -------------------------"); + if (tmd.ContentChunkRecords == null || tmd.ContentChunkRecords.Length == 0) + { + builder.AppendLine($" No content chunk records, expected {tmd.ContentCount}"); + } + else + { + for (int i = 0; i < tmd.ContentChunkRecords.Length; i++) + { + var contentChunkRecord = tmd.ContentChunkRecords[i]; + builder.AppendLine($" Content Chunk Record {i}"); + if (contentChunkRecord == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(contentChunkRecord.ContentId, " Content ID"); + builder.AppendLine($" Content index: {contentChunkRecord.ContentIndex} (0x{contentChunkRecord.ContentIndex:X})"); + builder.AppendLine($" Content type: {contentChunkRecord.ContentType} (0x{contentChunkRecord.ContentType:X})"); + builder.AppendLine(contentChunkRecord.ContentSize, " Content size"); + builder.AppendLine(contentChunkRecord.SHA256Hash, " SHA-256 hash"); + } + } + builder.AppendLine(); + + builder.AppendLine(" Ticket Certificate Chain Information:"); + builder.AppendLine(" -------------------------"); + if (tmd.CertificateChain == null || tmd.CertificateChain.Length == 0) + { + builder.AppendLine(" No certificates, expected 2"); + } + else + { + for (int i = 0; i < tmd.CertificateChain.Length; i++) + { + var certificate = tmd.CertificateChain[i]; + + string certificateName = string.Empty; + switch (i) + { + case 0: certificateName = " (TMD)"; break; + case 1: certificateName = " (CA)"; break; + } + + builder.AppendLine($" Certificate {i}{certificateName}"); + if (certificate == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine($" Signature type: {certificate.SignatureType} (0x{certificate.SignatureType:X})"); + builder.AppendLine(certificate.SignatureSize, " Signature size"); + builder.AppendLine(certificate.PaddingSize, " Padding size"); + builder.AppendLine(certificate.Signature, " Signature"); + builder.AppendLine(certificate.Padding, " Padding"); + builder.AppendLine(certificate.Issuer, " Issuer"); + builder.AppendLine($" Key type: {certificate.KeyType} (0x{certificate.KeyType:X})"); + builder.AppendLine(certificate.Name, " Name"); + builder.AppendLine(certificate.ExpirationTime, " Expiration time"); + switch (certificate.KeyType) + { + case PublicKeyType.RSA_4096: + case PublicKeyType.RSA_2048: + builder.AppendLine(certificate.RSAModulus, " Modulus"); + builder.AppendLine(certificate.RSAPublicExponent, " Public exponent"); + builder.AppendLine(certificate.RSAPadding, " Padding"); + break; + case PublicKeyType.EllipticCurve: + builder.AppendLine(certificate.ECCPublicKey, " Public key"); + builder.AppendLine(certificate.ECCPadding, " Padding"); + break; + } + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, NCCHHeader[] partitions) +#else + private static void Print(StringBuilder builder, NCCHHeader?[]? partitions) +#endif + { + builder.AppendLine(" NCCH Partition Header Information:"); + builder.AppendLine(" -------------------------"); + if (partitions == null || partitions.Length == 0) + { + builder.AppendLine(" No NCCH partition headers"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < partitions.Length; i++) + { + var partitionHeader = partitions[i]; + builder.AppendLine($" NCCH Partition Header {i}"); + if (partitionHeader == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + if (partitionHeader.MagicID == string.Empty) + { + builder.AppendLine(" Empty partition, no data can be parsed"); + continue; + } + else if (partitionHeader.MagicID != Constants.NCCHMagicNumber) + { + builder.AppendLine(" Unrecognized partition data, no data can be parsed"); + continue; + } + + builder.AppendLine(partitionHeader.RSA2048Signature, " RSA-2048 SHA-256 signature"); + builder.AppendLine(partitionHeader.MagicID, " Magic ID"); + builder.AppendLine(partitionHeader.ContentSizeInMediaUnits, " Content size in media units"); + builder.AppendLine(partitionHeader.PartitionId, " Partition ID"); + builder.AppendLine(partitionHeader.MakerCode, " Maker code"); + builder.AppendLine(partitionHeader.Version, " Version"); + builder.AppendLine(partitionHeader.VerificationHash, " Verification hash"); + builder.AppendLine(partitionHeader.ProgramId, " Program ID"); + builder.AppendLine(partitionHeader.Reserved1, " Reserved 1"); + builder.AppendLine(partitionHeader.LogoRegionHash, " Logo region SHA-256 hash"); + builder.AppendLine(partitionHeader.ProductCode, " Product code"); + builder.AppendLine(partitionHeader.ExtendedHeaderHash, " Extended header SHA-256 hash"); + builder.AppendLine(partitionHeader.ExtendedHeaderSizeInBytes, " Extended header size in bytes"); + builder.AppendLine(partitionHeader.Reserved2, " Reserved 2"); + builder.AppendLine(" Flags:"); + if (partitionHeader.Flags == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(partitionHeader.Flags.Reserved0, " Reserved 0"); + builder.AppendLine(partitionHeader.Flags.Reserved1, " Reserved 1"); + builder.AppendLine(partitionHeader.Flags.Reserved2, " Reserved 2"); + builder.AppendLine($" Crypto method: {partitionHeader.Flags.CryptoMethod} (0x{partitionHeader.Flags.CryptoMethod:X})"); + builder.AppendLine($" Content platform: {partitionHeader.Flags.ContentPlatform} (0x{partitionHeader.Flags.ContentPlatform:X})"); + builder.AppendLine($" Content type: {partitionHeader.Flags.MediaPlatformIndex} (0x{partitionHeader.Flags.MediaPlatformIndex:X})"); + builder.AppendLine(partitionHeader.Flags.ContentUnitSize, " Content unit size"); + builder.AppendLine($" Bitmasks: {partitionHeader.Flags.BitMasks} (0x{partitionHeader.Flags.BitMasks:X})"); + } + builder.AppendLine(partitionHeader.PlainRegionOffsetInMediaUnits, " Plain region offset, in media units"); + builder.AppendLine(partitionHeader.PlainRegionSizeInMediaUnits, " Plain region size, in media units"); + builder.AppendLine(partitionHeader.LogoRegionOffsetInMediaUnits, " Logo region offset, in media units"); + builder.AppendLine(partitionHeader.LogoRegionSizeInMediaUnits, " Logo region size, in media units"); + builder.AppendLine(partitionHeader.ExeFSOffsetInMediaUnits, " ExeFS offset, in media units"); + builder.AppendLine(partitionHeader.ExeFSSizeInMediaUnits, " ExeFS size, in media units"); + builder.AppendLine(partitionHeader.ExeFSHashRegionSizeInMediaUnits, " ExeFS hash region offset, in media units"); + builder.AppendLine(partitionHeader.Reserved3, " Reserved 3"); + builder.AppendLine(partitionHeader.RomFSOffsetInMediaUnits, " RomFS offset, in media units"); + builder.AppendLine(partitionHeader.RomFSSizeInMediaUnits, " RomFS size, in media units"); + builder.AppendLine(partitionHeader.RomFSHashRegionSizeInMediaUnits, " RomFS hash region offset, in media units"); + builder.AppendLine(partitionHeader.Reserved4, " Reserved 4"); + builder.AppendLine(partitionHeader.ExeFSSuperblockHash, " ExeFS superblock SHA-256 hash"); + builder.AppendLine(partitionHeader.RomFSSuperblockHash, " RomFS superblock SHA-256 hash"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, MetaData metaData) +#else + private static void Print(StringBuilder builder, MetaData? metaData) +#endif + { + builder.AppendLine(" Meta Data Information:"); + builder.AppendLine(" -------------------------"); + if (metaData == null) + { + builder.AppendLine(" No meta file data"); + builder.AppendLine(); + return; + } + + builder.AppendLine(metaData.TitleIDDependencyList, " Title ID dependency list"); + builder.AppendLine(metaData.Reserved1, " Reserved 1"); + builder.AppendLine(metaData.CoreVersion, " Core version"); + builder.AppendLine(metaData.Reserved2, " Reserved 2"); + builder.AppendLine(metaData.IconData, " Icon data"); + builder.AppendLine(); + } + + } +} \ No newline at end of file diff --git a/Extensions.cs b/Extensions.cs new file mode 100644 index 0000000..63f9432 --- /dev/null +++ b/Extensions.cs @@ -0,0 +1,332 @@ +using System; +using System.Text; + +namespace SabreTools.Printing +{ + // TODO: Add extension for printing enums, if possible + internal static class Extensions + { + /// + /// Append a line containing a boolean to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, bool value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, bool? value, string prefixString) +#endif + { +#if NET6_0_OR_GREATER + value ??= false; +#endif + + return sb.AppendLine($"{prefixString}: {value.ToString()}"); + } + + /// + /// Append a line containing a Char to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, char value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, char? value, string prefixString) +#endif + { +#if NET48 + string valueString = value.ToString(); +#else + string valueString = (value == null ? "[NULL]" : value.ToString()); +#endif + + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a Int8 to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, sbyte value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, sbyte? value, string prefixString) +#endif + { +#if NET6_0_OR_GREATER + value ??= 0; +#endif + + string valueString = $"{value} (0x{value:X})"; + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a UInt8 to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, byte value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, byte? value, string prefixString) +#endif + { +#if NET6_0_OR_GREATER + value ??= 0; +#endif + + string valueString = $"{value} (0x{value:X})"; + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a Int16 to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, short value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, short? value, string prefixString) +#endif + { +#if NET6_0_OR_GREATER + value ??= 0; +#endif + + string valueString = $"{value} (0x{value:X})"; + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a UInt16 to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, ushort value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, ushort? value, string prefixString) +#endif + { +#if NET6_0_OR_GREATER + value ??= 0; +#endif + + string valueString = $"{value} (0x{value:X})"; + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a Int32 to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, int value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, int? value, string prefixString) +#endif + { +#if NET6_0_OR_GREATER + value ??= 0; +#endif + + string valueString = $"{value} (0x{value:X})"; + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a UInt32 to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, uint value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, uint? value, string prefixString) +#endif + { +#if NET6_0_OR_GREATER + value ??= 0; +#endif + + string valueString = $"{value} (0x{value:X})"; + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a Int64 to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, long value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, long? value, string prefixString) +#endif + { +#if NET6_0_OR_GREATER + value ??= 0; +#endif + + string valueString = $"{value} (0x{value:X})"; + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a UInt64 to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, ulong value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, ulong? value, string prefixString) +#endif + { +#if NET6_0_OR_GREATER + value ??= 0; +#endif + + string valueString = $"{value} (0x{value:X})"; + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a string to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, string value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, string? value, string prefixString) +#endif + { +#if NET6_0_OR_GREATER + value ??= string.Empty; +#endif + + string valueString = value ?? "[NULL]"; + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a Guid to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, Guid value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, Guid? value, string prefixString) +#endif + { +#if NET48 + string valueString = value.ToString(); +#else + value ??= Guid.Empty; + string valueString = value.Value.ToString(); +#endif + + return sb.AppendLine($"{prefixString}: {value}"); + } + + /// + /// Append a line containing a UInt8[] value to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, byte[] value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, byte[]? value, string prefixString) +#endif + { + string valueString = (value == null ? "[NULL]" : BitConverter.ToString(value).Replace('-', ' ')); + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a UInt8[] value as a string to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, byte[] value, string prefixString, Encoding encoding) +#else + public static StringBuilder AppendLine(this StringBuilder sb, byte[]? value, string prefixString, Encoding encoding) +#endif + { + string valueString = (value == null ? "[NULL]" : encoding.GetString(value).Replace("\0", string.Empty)); + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a Char[] value to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, char[] value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, char[]? value, string prefixString) +#endif + { + string valueString = (value == null ? "[NULL]" : string.Join(", ", value)); + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a Int16[] value to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, short[] value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, short[]? value, string prefixString) +#endif + { + string valueString = (value == null ? "[NULL]" : string.Join(", ", value)); + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a UInt16[] value to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, ushort[] value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, ushort[]? value, string prefixString) +#endif + { + string valueString = (value == null ? "[NULL]" : string.Join(", ", value)); + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a Int32[] value to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, int[] value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, int[]? value, string prefixString) +#endif + { + string valueString = (value == null ? "[NULL]" : string.Join(", ", value)); + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a UInt32[] value to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, uint[] value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, uint[]? value, string prefixString) +#endif + { + string valueString = (value == null ? "[NULL]" : string.Join(", ", value)); + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a Int64[] value to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, long[] value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, long[]? value, string prefixString) +#endif + { + string valueString = (value == null ? "[NULL]" : string.Join(", ", value)); + return sb.AppendLine($"{prefixString}: {valueString}"); + } + + /// + /// Append a line containing a UInt64[] value to a StringBuilder + /// +#if NET48 + public static StringBuilder AppendLine(this StringBuilder sb, ulong[] value, string prefixString) +#else + public static StringBuilder AppendLine(this StringBuilder sb, ulong[]? value, string prefixString) +#endif + { + string valueString = (value == null ? "[NULL]" : string.Join(", ", value)); + return sb.AppendLine($"{prefixString}: {valueString}"); + } + } +} \ No newline at end of file diff --git a/GCF.cs b/GCF.cs new file mode 100644 index 0000000..d29ed64 --- /dev/null +++ b/GCF.cs @@ -0,0 +1,613 @@ +using System.Text; +using SabreTools.Models.GCF; + +namespace SabreTools.Printing +{ + public static class GCF + { + public static void Print(StringBuilder builder, File file) + { + builder.AppendLine("GCF Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + // Header + Print(builder, file.Header); + + // Block Entries + Print(builder, file.BlockEntryHeader); + Print(builder, file.BlockEntries); + + // Fragmentation Maps + Print(builder, file.FragmentationMapHeader); + Print(builder, file.FragmentationMaps); + + // Block Entry Maps + Print(builder, file.BlockEntryMapHeader); + Print(builder, file.BlockEntryMaps); + + // Directory and Directory Maps + Print(builder, file.DirectoryHeader); + Print(builder, file.DirectoryEntries); + // TODO: Should we print out the entire string table? + Print(builder, file.DirectoryInfo1Entries); + Print(builder, file.DirectoryInfo2Entries); + Print(builder, file.DirectoryCopyEntries); + Print(builder, file.DirectoryLocalEntries); + Print(builder, file.DirectoryMapHeader); + Print(builder, file.DirectoryMapEntries); + + // Checksums and Checksum Maps + Print(builder, file.ChecksumHeader); + Print(builder, file.ChecksumMapHeader); + Print(builder, file.ChecksumMapEntries); + Print(builder, file.ChecksumEntries); + + // Data Blocks + Print(builder, file.DataBlockHeader); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.MajorVersion, " Major version"); + builder.AppendLine(header.MinorVersion, " Minor version"); + builder.AppendLine(header.CacheID, " Cache ID"); + builder.AppendLine(header.LastVersionPlayed, " Last version played"); + builder.AppendLine(header.Dummy1, " Dummy 1"); + builder.AppendLine(header.Dummy2, " Dummy 2"); + builder.AppendLine(header.FileSize, " File size"); + builder.AppendLine(header.BlockSize, " Block size"); + builder.AppendLine(header.BlockCount, " Block count"); + builder.AppendLine(header.Dummy3, " Dummy 3"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, BlockEntryHeader header) +#else + private static void Print(StringBuilder builder, BlockEntryHeader? header) +#endif + { + builder.AppendLine(" Block Entry Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No block entry header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.BlockCount, " Block count"); + builder.AppendLine(header.BlocksUsed, " Blocks used"); + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.Dummy1, " Dummy 1"); + builder.AppendLine(header.Dummy2, " Dummy 2"); + builder.AppendLine(header.Dummy3, " Dummy 3"); + builder.AppendLine(header.Dummy4, " Dummy 4"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, BlockEntry[] entries) +#else + private static void Print(StringBuilder builder, BlockEntry?[]? entries) +#endif + { + builder.AppendLine(" Block Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No block entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Block Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.EntryFlags, " Entry flags"); + builder.AppendLine(entry.FileDataOffset, " File data offset"); + builder.AppendLine(entry.FileDataSize, " File data size"); + builder.AppendLine(entry.FirstDataBlockIndex, " First data block index"); + builder.AppendLine(entry.NextBlockEntryIndex, " Next block entry index"); + builder.AppendLine(entry.PreviousBlockEntryIndex, " Previous block entry index"); + builder.AppendLine(entry.DirectoryIndex, " Directory index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, FragmentationMapHeader header) +#else + private static void Print(StringBuilder builder, FragmentationMapHeader? header) +#endif + { + builder.AppendLine(" Fragmentation Map Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No fragmentation map header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.BlockCount, " Block count"); + builder.AppendLine(header.FirstUnusedEntry, " First unused entry"); + builder.AppendLine(header.Terminator, " Terminator"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, FragmentationMap[] entries) +#else + private static void Print(StringBuilder builder, FragmentationMap?[]? entries) +#endif + { + builder.AppendLine(" Fragmentation Maps Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No fragmentation maps"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Fragmentation Map {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.NextDataBlockIndex, " Next data block index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, BlockEntryMapHeader header) +#else + private static void Print(StringBuilder builder, BlockEntryMapHeader? header) +#endif + { + builder.AppendLine(" Block Entry Map Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No block entry map header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.BlockCount, " Block count"); + builder.AppendLine(header.FirstBlockEntryIndex, " First block entry index"); + builder.AppendLine(header.LastBlockEntryIndex, " Last block entry index"); + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, BlockEntryMap[] entries) +#else + private static void Print(StringBuilder builder, BlockEntryMap?[]? entries) +#endif + { + builder.AppendLine(" Block Entry Maps Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No block entry maps"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Block Entry Map {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.PreviousBlockEntryIndex, " Previous data block index"); + builder.AppendLine(entry.NextBlockEntryIndex, " Next data block index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryHeader header) +#else + private static void Print(StringBuilder builder, DirectoryHeader? header) +#endif + { + builder.AppendLine(" Directory Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No directory header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.CacheID, " Cache ID"); + builder.AppendLine(header.LastVersionPlayed, " Last version played"); + builder.AppendLine(header.ItemCount, " Item count"); + builder.AppendLine(header.FileCount, " File count"); + builder.AppendLine(header.Dummy1, " Dummy 1"); + builder.AppendLine(header.DirectorySize, " Directory size"); + builder.AppendLine(header.NameSize, " Name size"); + builder.AppendLine(header.Info1Count, " Info 1 count"); + builder.AppendLine(header.CopyCount, " Copy count"); + builder.AppendLine(header.LocalCount, " Local count"); + builder.AppendLine(header.Dummy2, " Dummy 2"); + builder.AppendLine(header.Dummy3, " Dummy 3"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryEntry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryEntry?[]? entries) +#endif + { + builder.AppendLine(" Directory Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.NameOffset, " Name offset"); + builder.AppendLine(entry.Name, " Name"); + builder.AppendLine(entry.ItemSize, " Item size"); + builder.AppendLine(entry.ChecksumIndex, " Checksum index"); + builder.AppendLine($" Directory flags: {entry.DirectoryFlags} (0x{entry.DirectoryFlags:X})"); + builder.AppendLine(entry.ParentIndex, " Parent index"); + builder.AppendLine(entry.NextIndex, " Next index"); + builder.AppendLine(entry.FirstIndex, " First index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryInfo1Entry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryInfo1Entry?[]? entries) +#endif + { + builder.AppendLine(" Directory Info 1 Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory info 1 entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Info 1 Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Dummy0, " Dummy 0"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryInfo2Entry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryInfo2Entry?[]? entries) +#endif + { + builder.AppendLine(" Directory Info 2 Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory info 2 entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Info 2 Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Dummy0, " Dummy 0"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryCopyEntry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryCopyEntry?[]? entries) +#endif + { + builder.AppendLine(" Directory Copy Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory copy entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Copy Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.DirectoryIndex, " Directory index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryLocalEntry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryLocalEntry?[]? entries) +#endif + { + builder.AppendLine(" Directory Local Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory local entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Local Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.DirectoryIndex, " Directory index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryMapHeader header) +#else + private static void Print(StringBuilder builder, DirectoryMapHeader? header) +#endif + { + builder.AppendLine(" Directory Map Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No directory map header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.Dummy1, " Dummy 1"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryMapEntry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryMapEntry?[]? entries) +#endif + { + builder.AppendLine(" Directory Map Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory map entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Map Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.FirstBlockIndex, " First block index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ChecksumHeader header) +#else + private static void Print(StringBuilder builder, ChecksumHeader? header) +#endif + { + builder.AppendLine(" Checksum Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No checksum header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.ChecksumSize, " Checksum size"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ChecksumMapHeader header) +#else + private static void Print(StringBuilder builder, ChecksumMapHeader? header) +#endif + { + builder.AppendLine(" Checksum Map Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No checksum map header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.Dummy1, " Dummy 1"); + builder.AppendLine(header.ItemCount, " Item count"); + builder.AppendLine(header.ChecksumCount, " Checksum count"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ChecksumMapEntry[] entries) +#else + private static void Print(StringBuilder builder, ChecksumMapEntry?[]? entries) +#endif + { + builder.AppendLine(" Checksum Map Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No checksum map entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Checksum Map Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.ChecksumCount, " Checksum count"); + builder.AppendLine(entry.FirstChecksumIndex, " First checksum index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ChecksumEntry[] entries) +#else + private static void Print(StringBuilder builder, ChecksumEntry?[]? entries) +#endif + { + builder.AppendLine(" Checksum Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No checksum entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Checksum Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Checksum, " Checksum"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DataBlockHeader header) +#else + private static void Print(StringBuilder builder, DataBlockHeader? header) +#endif + { + builder.AppendLine(" Data Block Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No data block header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.LastVersionPlayed, " Last version played"); + builder.AppendLine(header.BlockCount, " Block count"); + builder.AppendLine(header.BlockSize, " Block size"); + builder.AppendLine(header.FirstBlockOffset, " First block offset"); + builder.AppendLine(header.BlocksUsed, " Blocks used"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/InstallShieldCabinet.cs b/InstallShieldCabinet.cs new file mode 100644 index 0000000..47a52cc --- /dev/null +++ b/InstallShieldCabinet.cs @@ -0,0 +1,457 @@ +using System; +using System.Collections.Generic; +using System.Text; +using SabreTools.Models.InstallShieldCabinet; + +namespace SabreTools.Printing +{ + public static class InstallShieldCabinet + { + public static void Print(StringBuilder builder, Cabinet cabinet) + { + builder.AppendLine("InstallShield Cabinet Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + // Major Version + int majorVersion = GetMajorVersion(cabinet.CommonHeader); + + // Headers + Print(builder, cabinet.CommonHeader, majorVersion); + Print(builder, cabinet.VolumeHeader, majorVersion); + Print(builder, cabinet.Descriptor); + + // File Descriptors + Print(builder, cabinet.FileDescriptorOffsets); + Print(builder, cabinet.DirectoryNames); + Print(builder, cabinet.FileDescriptors); + + // File Groups + Print(builder, cabinet.FileGroupOffsets, "File Group"); + Print(builder, cabinet.FileGroups); + + // Components + Print(builder, cabinet.ComponentOffsets, "Component"); + Print(builder, cabinet.Components); + } + +#if NET48 + private static int GetMajorVersion(CommonHeader header) +#else + private static int GetMajorVersion(CommonHeader? header) +#endif + { +#if NET48 + uint majorVersion = header.Version; +#else + uint majorVersion = header?.Version ?? 0; +#endif + if (majorVersion >> 24 == 1) + { + majorVersion = (majorVersion >> 12) & 0x0F; + } + else if (majorVersion >> 24 == 2 || majorVersion >> 24 == 4) + { + majorVersion = majorVersion & 0xFFFF; + if (majorVersion != 0) + majorVersion /= 100; + } + + return (int)majorVersion; + } + +#if NET48 + private static void Print(StringBuilder builder, CommonHeader header, int majorVersion) +#else + private static void Print(StringBuilder builder, CommonHeader? header, int majorVersion) +#endif + { + builder.AppendLine(" Common Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(value: " No common header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine($" Version: {header.Version} (0x{header.Version:X}) [{majorVersion}]"); + builder.AppendLine(header.VolumeInfo, " Volume info"); + builder.AppendLine(header.DescriptorOffset, " Descriptor offset"); + builder.AppendLine(header.DescriptorSize, " Descriptor size"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, VolumeHeader header, int majorVersion) +#else + private static void Print(StringBuilder builder, VolumeHeader? header, int majorVersion) +#endif + { + builder.AppendLine(" Volume Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(value: " No volume header"); + builder.AppendLine(); + return; + } + + if (majorVersion <= 5) + { + builder.AppendLine(header.DataOffset, " Data offset"); + builder.AppendLine(header.FirstFileIndex, " First file index"); + builder.AppendLine(header.LastFileIndex, " Last file index"); + builder.AppendLine(header.FirstFileOffset, " First file offset"); + builder.AppendLine(header.FirstFileSizeExpanded, " First file size expanded"); + builder.AppendLine(header.FirstFileSizeCompressed, " First file size compressed"); + builder.AppendLine(header.LastFileOffset, " Last file offset"); + builder.AppendLine(header.LastFileSizeExpanded, " Last file size expanded"); + builder.AppendLine(header.LastFileSizeCompressed, " Last file size compressed"); + } + else + { + builder.AppendLine(header.DataOffset, " Data offset"); + builder.AppendLine(header.DataOffsetHigh, " Data offset high"); + builder.AppendLine(header.FirstFileIndex, " First file index"); + builder.AppendLine(header.LastFileIndex, " Last file index"); + builder.AppendLine(header.FirstFileOffset, " First file offset"); + builder.AppendLine(header.FirstFileOffsetHigh, " First file offset high"); + builder.AppendLine(header.FirstFileSizeExpanded, " First file size expanded"); + builder.AppendLine(header.FirstFileSizeExpandedHigh, " First file size expanded high"); + builder.AppendLine(header.FirstFileSizeCompressed, " First file size compressed"); + builder.AppendLine(header.FirstFileSizeCompressedHigh, " First file size compressed high"); + builder.AppendLine(header.LastFileOffset, " Last file offset"); + builder.AppendLine(header.LastFileOffsetHigh, " Last file offset high"); + builder.AppendLine(header.LastFileSizeExpanded, " Last file size expanded"); + builder.AppendLine(header.LastFileSizeExpandedHigh, " Last file size expanded high"); + builder.AppendLine(header.LastFileSizeCompressed, " Last file size compressed"); + builder.AppendLine(header.LastFileSizeCompressedHigh, " Last file size compressed high"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Descriptor descriptor) +#else + private static void Print(StringBuilder builder, Descriptor? descriptor) +#endif + { + builder.AppendLine(" Descriptor Information:"); + builder.AppendLine(" -------------------------"); + if (descriptor == null) + { + builder.AppendLine(" No descriptor"); + builder.AppendLine(); + return; + } + + builder.AppendLine(descriptor.StringsOffset, " Strings offset"); + builder.AppendLine(descriptor.Reserved0, " Reserved 0"); + builder.AppendLine(descriptor.ComponentListOffset, " Component list offset"); + builder.AppendLine(descriptor.FileTableOffset, " File table offset"); + builder.AppendLine(descriptor.Reserved1, " Reserved 1"); + builder.AppendLine(descriptor.FileTableSize, " File table size"); + builder.AppendLine(descriptor.FileTableSize2, " File table size 2"); + builder.AppendLine(descriptor.DirectoryCount, " Directory count"); + builder.AppendLine(descriptor.Reserved2, " Reserved 2"); + builder.AppendLine(descriptor.Reserved3, " Reserved 3"); + builder.AppendLine(descriptor.Reserved4, " Reserved 4"); + builder.AppendLine(descriptor.FileCount, " File count"); + builder.AppendLine(descriptor.FileTableOffset2, " File table offset 2"); + builder.AppendLine(descriptor.ComponentTableInfoCount, " Component table info count"); + builder.AppendLine(descriptor.ComponentTableOffset, " Component table offset"); + builder.AppendLine(descriptor.Reserved5, " Reserved 5"); + builder.AppendLine(descriptor.Reserved6, " Reserved 6"); + builder.AppendLine(); + + builder.AppendLine(" File group offsets:"); + builder.AppendLine(" -------------------------"); + if (descriptor.FileGroupOffsets == null || descriptor.FileGroupOffsets.Length == 0) + { + builder.AppendLine(" No file group offsets"); + } + else + { + for (int i = 0; i < descriptor.FileGroupOffsets.Length; i++) + { + builder.AppendLine(descriptor.FileGroupOffsets[i], $" File Group Offset {i}"); + } + } + builder.AppendLine(); + + builder.AppendLine(" Component offsets:"); + builder.AppendLine(" -------------------------"); + if (descriptor.ComponentOffsets == null || descriptor.ComponentOffsets.Length == 0) + { + builder.AppendLine(" No component offsets"); + } + else + { + for (int i = 0; i < descriptor.ComponentOffsets.Length; i++) + { + builder.AppendLine(descriptor.ComponentOffsets[i], $" Component Offset {i}"); + } + } + builder.AppendLine(); + + builder.AppendLine(descriptor.SetupTypesOffset, " Setup types offset"); + builder.AppendLine(descriptor.SetupTableOffset, " Setup table offset"); + builder.AppendLine(descriptor.Reserved7, " Reserved 7"); + builder.AppendLine(descriptor.Reserved8, " Reserved 8"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, uint[] entries) +#else + private static void Print(StringBuilder builder, uint[]? entries) +#endif + { + builder.AppendLine(" File Descriptor Offsets:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No file descriptor offsets"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + builder.AppendLine(entries[i], $" File Descriptor Offset {i}"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, string[] entries) +#else + private static void Print(StringBuilder builder, string?[]? entries) +#endif + { + builder.AppendLine(" Directory Names:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory names"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + builder.AppendLine(entries[i], $" Directory Name {i}"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, FileDescriptor[] entries) +#else + private static void Print(StringBuilder builder, FileDescriptor?[]? entries) +#endif + { + builder.AppendLine(" File Descriptors:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No file descriptors"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" File Descriptor {i}:"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.NameOffset, " Name offset"); + builder.AppendLine(entry.Name, " Name"); + builder.AppendLine(entry.DirectoryIndex, " Directory index"); + builder.AppendLine($" Flags: {entry.Flags} (0x{entry.Flags:X})"); + builder.AppendLine(entry.ExpandedSize, " Expanded size"); + builder.AppendLine(entry.CompressedSize, " Compressed size"); + builder.AppendLine(entry.DataOffset, " Data offset"); + builder.AppendLine(entry.MD5, " MD5"); + builder.AppendLine(entry.Volume, " Volume"); + builder.AppendLine(entry.LinkPrevious, " Link previous"); + builder.AppendLine(entry.LinkNext, " Link next"); + builder.AppendLine($" Link flags: {entry.LinkFlags} (0x{entry.LinkFlags:X})"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Dictionary entries, string name) +#else + private static void Print(StringBuilder builder, Dictionary? entries, string name) +#endif + { + builder.AppendLine($" {name} Offsets:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Count == 0) + { + builder.AppendLine($" No {name.ToLowerInvariant()} offsets"); + builder.AppendLine(); + return; + } + + foreach (var kvp in entries) + { + long offset = kvp.Key; + var value = kvp.Value; + + builder.AppendLine($" {name} Offset {offset}:"); + if (value == null) + { + builder.AppendLine($" Unassigned {name.ToLowerInvariant()}"); + continue; + } + + builder.AppendLine(value.NameOffset, " Name offset"); + builder.AppendLine(value.Name, " Name"); + builder.AppendLine(value.DescriptorOffset, " Descriptor offset"); + builder.AppendLine(value.NextOffset, " Next offset"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, FileGroup[] entries) +#else + private static void Print(StringBuilder builder, FileGroup?[]? entries) +#endif + { + builder.AppendLine(" File Groups:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No file groups"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var fileGroup = entries[i]; + builder.AppendLine($" File Group {i}:"); + if (fileGroup == null) + { + builder.AppendLine(" Unassigned file group"); + continue; + } + + builder.AppendLine(fileGroup.NameOffset, " Name offset"); + builder.AppendLine(fileGroup.Name, " Name"); + builder.AppendLine(fileGroup.ExpandedSize, " Expanded size"); + builder.AppendLine(fileGroup.Reserved0, " Reserved 0"); + builder.AppendLine(fileGroup.CompressedSize, " Compressed size"); + builder.AppendLine(fileGroup.Reserved1, " Reserved 1"); + builder.AppendLine(fileGroup.Reserved2, " Reserved 2"); + builder.AppendLine(fileGroup.Attribute1, " Attribute 1"); + builder.AppendLine(fileGroup.Attribute2, " Attribute 2"); + builder.AppendLine(fileGroup.FirstFile, " First file"); + builder.AppendLine(fileGroup.LastFile, " Last file"); + builder.AppendLine(fileGroup.UnknownOffset, " Unknown offset"); + builder.AppendLine(fileGroup.Var4Offset, " Var 4 offset"); + builder.AppendLine(fileGroup.Var1Offset, " Var 1 offset"); + builder.AppendLine(fileGroup.HTTPLocationOffset, " HTTP location offset"); + builder.AppendLine(fileGroup.FTPLocationOffset, " FTP location offset"); + builder.AppendLine(fileGroup.MiscOffset, " Misc. offset"); + builder.AppendLine(fileGroup.Var2Offset, " Var 2 offset"); + builder.AppendLine(fileGroup.TargetDirectoryOffset, " Target directory offset"); + builder.AppendLine(fileGroup.Reserved3, " Reserved 3"); + builder.AppendLine(fileGroup.Reserved4, " Reserved 4"); + builder.AppendLine(fileGroup.Reserved5, " Reserved 5"); + builder.AppendLine(fileGroup.Reserved6, " Reserved 6"); + builder.AppendLine(fileGroup.Reserved7, " Reserved 7"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Component[] entries) +#else + private static void Print(StringBuilder builder, Component?[]? entries) +#endif + { + builder.AppendLine(" Components:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No components"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var component = entries[i]; + builder.AppendLine($" Component {i}:"); + if (component == null) + { + builder.AppendLine(" Unassigned component"); + continue; + } + + builder.AppendLine(component.IdentifierOffset, " Identifier offset"); + builder.AppendLine(component.Identifier, " Identifier"); + builder.AppendLine(component.DescriptorOffset, " Descriptor offset"); + builder.AppendLine(component.DisplayNameOffset, " Display name offset"); + builder.AppendLine(component.DisplayName, " Display name"); + builder.AppendLine(component.Reserved0, " Reserved 0"); + builder.AppendLine(component.ReservedOffset0, " Reserved offset 0"); + builder.AppendLine(component.ReservedOffset1, " Reserved offset 1"); + builder.AppendLine(component.ComponentIndex, " Component index"); + builder.AppendLine(component.NameOffset, " Name offset"); + builder.AppendLine(component.Name, " Name"); + builder.AppendLine(component.ReservedOffset2, " Reserved offset 2"); + builder.AppendLine(component.ReservedOffset3, " Reserved offset 3"); + builder.AppendLine(component.ReservedOffset4, " Reserved offset 4"); + builder.AppendLine(component.Reserved1, " Reserved 1"); + builder.AppendLine(component.CLSIDOffset, " CLSID offset"); + builder.AppendLine(component.CLSID, " CLSID"); + builder.AppendLine(component.Reserved2, " Reserved 2"); + builder.AppendLine(component.Reserved3, " Reserved 3"); + builder.AppendLine(component.DependsCount, " Depends count"); + builder.AppendLine(component.DependsOffset, " Depends offset"); + builder.AppendLine(component.FileGroupCount, " File group count"); + builder.AppendLine(component.FileGroupNamesOffset, " File group names offset"); + builder.AppendLine(); + + builder.AppendLine(" File group names:"); + builder.AppendLine(" -------------------------"); + if (component.FileGroupNames == null || component.FileGroupNames.Length == 0) + { + builder.AppendLine(" No file group names"); + } + else + { + for (int j = 0; j < component.FileGroupNames.Length; j++) + { + builder.AppendLine(component.FileGroupNames[j], $" File Group Name {j}"); + } + } + builder.AppendLine(); + + builder.AppendLine(component.X3Count, " X3 count"); + builder.AppendLine(component.X3Offset, " X3 offset"); + builder.AppendLine(component.SubComponentsCount, " Sub-components count"); + builder.AppendLine(component.SubComponentsOffset, " Sub-components offset"); + builder.AppendLine(component.NextComponentOffset, " Next component offset"); + builder.AppendLine(component.ReservedOffset5, " Reserved offset 5"); + builder.AppendLine(component.ReservedOffset6, " Reserved offset 6"); + builder.AppendLine(component.ReservedOffset7, " Reserved offset 7"); + builder.AppendLine(component.ReservedOffset8, " Reserved offset 8"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/LinearExecutable.cs b/LinearExecutable.cs new file mode 100644 index 0000000..131be4a --- /dev/null +++ b/LinearExecutable.cs @@ -0,0 +1,762 @@ +using System.Text; +using SabreTools.Models.LinearExecutable; + +namespace SabreTools.Printing +{ + public static class LinearExecutable + { + public static void Print(StringBuilder builder, Executable executable) + { + builder.AppendLine("New Executable Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + // Stub + Print(builder, executable.Stub?.Header); + + // Information Block + Print(builder, executable.InformationBlock); + + // Tables + Print(builder, executable.ObjectTable); + Print(builder, executable.ObjectPageMap); + Print(builder, executable.ResourceTable); + Print(builder, executable.ResidentNamesTable); + Print(builder, executable.EntryTable); + Print(builder, executable.ModuleFormatDirectivesTable); + Print(builder, executable.VerifyRecordDirectiveTable); + Print(builder, executable.FixupPageTable); + Print(builder, executable.FixupRecordTable); + Print(builder, executable.ImportModuleNameTable); + Print(builder, executable.ImportModuleProcedureNameTable); + Print(builder, executable.PerPageChecksumTable); + Print(builder, executable.NonResidentNamesTable); + + // Debug + Print(builder, executable.DebugInformation); + } + +#if NET48 + private static void Print(StringBuilder builder, SabreTools.Models.MSDOS.ExecutableHeader header) +#else + private static void Print(StringBuilder builder, SabreTools.Models.MSDOS.ExecutableHeader? header) +#endif + { + builder.AppendLine(" MS-DOS Stub Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No MS-DOS stub header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Magic, " Magic number"); + builder.AppendLine(header.LastPageBytes, " Last page bytes"); + builder.AppendLine(header.Pages, " Pages"); + builder.AppendLine(header.RelocationItems, " Relocation items"); + builder.AppendLine(header.HeaderParagraphSize, " Header paragraph size"); + builder.AppendLine(header.MinimumExtraParagraphs, " Minimum extra paragraphs"); + builder.AppendLine(header.MaximumExtraParagraphs, " Maximum extra paragraphs"); + builder.AppendLine(header.InitialSSValue, " Initial SS value"); + builder.AppendLine(header.InitialSPValue, " Initial SP value"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine(header.InitialIPValue, " Initial IP value"); + builder.AppendLine(header.InitialCSValue, " Initial CS value"); + builder.AppendLine(header.RelocationTableAddr, " Relocation table address"); + builder.AppendLine(header.OverlayNumber, " Overlay number"); + builder.AppendLine(); + + builder.AppendLine(" MS-DOS Stub Extended Header Information:"); + builder.AppendLine(" -------------------------"); + builder.AppendLine(header.Reserved1, " Reserved words"); + builder.AppendLine(header.OEMIdentifier, " OEM identifier"); + builder.AppendLine(header.OEMInformation, " OEM information"); + builder.AppendLine(header.Reserved2, " Reserved words"); + builder.AppendLine(header.NewExeHeaderAddr, " New EXE header address"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, InformationBlock block) +#else + private static void Print(StringBuilder builder, InformationBlock? block) +#endif + { + builder.AppendLine(" Information Block Information:"); + builder.AppendLine(" -------------------------"); + if (block == null) + { + builder.AppendLine(" No information block"); + builder.AppendLine(); + return; + } + + builder.AppendLine(block.Signature, " Signature"); + builder.AppendLine($" Byte order: {block.ByteOrder} (0x{block.ByteOrder:X})"); + builder.AppendLine($" Word order: {block.WordOrder} (0x{block.WordOrder:X})"); + builder.AppendLine(block.ExecutableFormatLevel, " Executable format level"); + builder.AppendLine($" CPU type: {block.CPUType} (0x{block.CPUType:X})"); + builder.AppendLine($" Module OS: {block.ModuleOS} (0x{block.ModuleOS:X})"); + builder.AppendLine(block.ModuleVersion, " Module version"); + builder.AppendLine($" Module type flags: {block.ModuleTypeFlags} (0x{block.ModuleTypeFlags:X})"); + builder.AppendLine(block.ModuleNumberPages, " Module number pages"); + builder.AppendLine(block.InitialObjectCS, " Initial object CS"); + builder.AppendLine(block.InitialEIP, " Initial EIP"); + builder.AppendLine(block.InitialObjectSS, " Initial object SS"); + builder.AppendLine(block.InitialESP, " Initial ESP"); + builder.AppendLine(block.MemoryPageSize, " Memory page size"); + builder.AppendLine(block.BytesOnLastPage, " Bytes on last page"); + builder.AppendLine(block.FixupSectionSize, " Fix-up section size"); + builder.AppendLine(block.FixupSectionChecksum, " Fix-up section checksum"); + builder.AppendLine(block.LoaderSectionSize, " Loader section size"); + builder.AppendLine(block.LoaderSectionChecksum, " Loader section checksum"); + builder.AppendLine(block.ObjectTableOffset, " Object table offset"); + builder.AppendLine(block.ObjectTableCount, " Object table count"); + builder.AppendLine(block.ObjectPageMapOffset, " Object page map offset"); + builder.AppendLine(block.ObjectIterateDataMapOffset, " Object iterate data map offset"); + builder.AppendLine(block.ResourceTableOffset, " Resource table offset"); + builder.AppendLine(block.ResourceTableCount, " Resource table count"); + builder.AppendLine(block.ResidentNamesTableOffset, " Resident names table offset"); + builder.AppendLine(block.EntryTableOffset, " Entry table offset"); + builder.AppendLine(block.ModuleDirectivesTableOffset, " Module directives table offset"); + builder.AppendLine(block.ModuleDirectivesCount, " Module directives table count"); + builder.AppendLine(block.FixupPageTableOffset, " Fix-up page table offset"); + builder.AppendLine(block.FixupRecordTableOffset, " Fix-up record table offset"); + builder.AppendLine(block.ImportedModulesNameTableOffset, " Imported modules name table offset"); + builder.AppendLine(block.ImportedModulesCount, " Imported modules count"); + builder.AppendLine(block.ImportProcedureNameTableOffset, " Imported procedure name table count"); + builder.AppendLine(block.PerPageChecksumTableOffset, " Per-page checksum table offset"); + builder.AppendLine(block.DataPagesOffset, " Data pages offset"); + builder.AppendLine(block.PreloadPageCount, " Preload page count"); + builder.AppendLine(block.NonResidentNamesTableOffset, " Non-resident names table offset"); + builder.AppendLine(block.NonResidentNamesTableLength, " Non-resident names table length"); + builder.AppendLine(block.NonResidentNamesTableChecksum, " Non-resident names table checksum"); + builder.AppendLine(block.AutomaticDataObject, " Automatic data object"); + builder.AppendLine(block.DebugInformationOffset, " Debug information offset"); + builder.AppendLine(block.DebugInformationLength, " Debug information length"); + builder.AppendLine(block.PreloadInstancePagesNumber, " Preload instance pages number"); + builder.AppendLine(block.DemandInstancePagesNumber, " Demand instance pages number"); + builder.AppendLine(block.ExtraHeapAllocation, " Extra heap allocation"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ObjectTableEntry[] entries) +#else + private static void Print(StringBuilder builder, ObjectTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Object Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No object table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Object Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.VirtualSegmentSize, " Virtual segment size"); + builder.AppendLine(entry.RelocationBaseAddress, " Relocation base address"); + builder.AppendLine($" Object flags: {entry.ObjectFlags} (0x{entry.ObjectFlags:X})"); + builder.AppendLine(entry.PageTableIndex, " Page table index"); + builder.AppendLine(entry.PageTableEntries, " Page table entries"); + builder.AppendLine(entry.Reserved, " Reserved"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ObjectPageMapEntry[] entries) +#else + private static void Print(StringBuilder builder, ObjectPageMapEntry?[]? entries) +#endif + { + builder.AppendLine(" Object Page Map Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No object page map entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Object Page Map Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.PageDataOffset, " Page data offset"); + builder.AppendLine(entry.DataSize, " Data size"); + builder.AppendLine($" Flags: {entry.Flags} (0x{entry.Flags:X})"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ResourceTableEntry[] entries) +#else + private static void Print(StringBuilder builder, ResourceTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Resource Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No resource table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Resource Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine($" Type ID: {entry.TypeID} (0x{entry.TypeID:X})"); + builder.AppendLine(entry.NameID, " Name ID"); + builder.AppendLine(entry.ResourceSize, " Resource size"); + builder.AppendLine(entry.ObjectNumber, " Object number"); + builder.AppendLine(entry.Offset, " Offset"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ResidentNamesTableEntry[] entries) +#else + private static void Print(StringBuilder builder, ResidentNamesTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Resident Names Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No resident names table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Resident Names Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Length, " Length"); + builder.AppendLine(entry.Name, " Name"); + builder.AppendLine(entry.OrdinalNumber, " Ordinal number"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, EntryTableBundle[] bundles) +#else + private static void Print(StringBuilder builder, EntryTableBundle?[]? bundles) +#endif + { + builder.AppendLine(" Entry Table Information:"); + builder.AppendLine(" -------------------------"); + if (bundles == null || bundles.Length == 0) + { + builder.AppendLine(" No entry table bundles"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < bundles.Length; i++) + { + var bundle = bundles[i]; + builder.AppendLine($" Entry Table Bundle {i}"); + if (bundle == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(bundle.Entries, " Entries"); + builder.AppendLine($" Bundle type: {bundle.BundleType} (0x{bundle.BundleType:X})"); + builder.AppendLine(); + + builder.AppendLine(" Entry Table Entries:"); + builder.AppendLine(" -------------------------"); + if (bundle.TableEntries == null || bundle.TableEntries.Length == 0) + { + builder.AppendLine(" No entry table entries"); + builder.AppendLine(); + continue; + } + + for (int j = 0; j < bundle.TableEntries.Length; j++) + { + var entry = bundle.TableEntries[j]; + builder.AppendLine($" Entry Table Entry {j}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + switch (bundle.BundleType & ~BundleType.ParameterTypingInformationPresent) + { + case BundleType.UnusedEntry: + builder.AppendLine(" Unused, empty entry"); + break; + + case BundleType.SixteenBitEntry: + builder.AppendLine(entry.SixteenBitObjectNumber, " Object number"); + builder.AppendLine($" Entry flags: {entry.SixteenBitEntryFlags} (0x{entry.SixteenBitEntryFlags:X})"); + builder.AppendLine(entry.SixteenBitOffset, " Offset"); + break; + + case BundleType.TwoEightySixCallGateEntry: + builder.AppendLine(entry.TwoEightySixObjectNumber, " Object number"); + builder.AppendLine($" Entry flags: {entry.TwoEightySixEntryFlags} (0x{entry.TwoEightySixEntryFlags:X})"); + builder.AppendLine(entry.TwoEightySixOffset, " Offset"); + builder.AppendLine(entry.TwoEightySixCallgate, " Callgate"); + break; + + case BundleType.ThirtyTwoBitEntry: + builder.AppendLine(entry.ThirtyTwoBitObjectNumber, " Object number"); + builder.AppendLine($" Entry flags: {entry.ThirtyTwoBitEntryFlags} (0x{entry.ThirtyTwoBitEntryFlags:X})"); + builder.AppendLine(entry.ThirtyTwoBitOffset, " Offset"); + break; + + case BundleType.ForwarderEntry: + builder.AppendLine(entry.ForwarderReserved, " Reserved"); + builder.AppendLine($" Forwarder flags: {entry.ForwarderFlags} (0x{entry.ForwarderFlags:X})"); + builder.AppendLine(entry.ForwarderModuleOrdinalNumber, " Module ordinal number"); + builder.AppendLine(entry.ProcedureNameOffset, " Procedure name offset"); + builder.AppendLine(entry.ImportOrdinalNumber, " Import ordinal number"); + break; + + default: + builder.AppendLine($" Unknown entry type {bundle.BundleType}"); + break; + } + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ModuleFormatDirectivesTableEntry[] entries) +#else + private static void Print(StringBuilder builder, ModuleFormatDirectivesTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Module Format Directives Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No module format directives table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Moduile Format Directives Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine($" Directive number: {entry.DirectiveNumber} (0x{entry.DirectiveNumber:X})"); + builder.AppendLine(entry.DirectiveDataLength, " Directive data length"); + builder.AppendLine(entry.DirectiveDataOffset, " Directive data offset"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, VerifyRecordDirectiveTableEntry[] entries) +#else + private static void Print(StringBuilder builder, VerifyRecordDirectiveTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Verify Record Directive Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No verify record directive table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Verify Record Directive Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.EntryCount, " Entry count"); + builder.AppendLine(entry.OrdinalIndex, " Ordinal index"); + builder.AppendLine(entry.Version, " Version"); + builder.AppendLine(entry.ObjectEntriesCount, " Object entries count"); + builder.AppendLine(entry.ObjectNumberInModule, " Object number in module"); + builder.AppendLine(entry.ObjectLoadBaseAddress, " Object load base address"); + builder.AppendLine(entry.ObjectVirtualAddressSize, " Object virtual address size"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, FixupPageTableEntry[] entries) +#else + private static void Print(StringBuilder builder, FixupPageTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Fix-up Page Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No fix-up page table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Fix-up Page Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Offset, " Offset"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, FixupRecordTableEntry[] entries) +#else + private static void Print(StringBuilder builder, FixupRecordTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Fix-up Record Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No fix-up record table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Fix-up Record Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine($" Source type: {entry.SourceType} (0x{entry.SourceType:X})"); + builder.AppendLine($" Target flags: {entry.TargetFlags} (0x{entry.TargetFlags:X})"); + + // Source list flag + if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag)) + builder.AppendLine(entry.SourceOffsetListCount, " Source offset list count"); + else + builder.AppendLine(entry.SourceOffset, " Source offset"); + + // OBJECT / TRGOFF + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReference)) + { + // 16-bit Object Number/Module Ordinal Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + builder.AppendLine(entry.TargetObjectNumberWORD, " Target object number"); + else + builder.AppendLine(entry.TargetObjectNumberByte, " Target object number"); + + // 16-bit Selector fixup + if (!entry.SourceType.HasFlag(FixupRecordSourceType.SixteenBitSelectorFixup)) + { + // 32-bit Target Offset Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) + builder.AppendLine(entry.TargetOffsetDWORD, " Target offset"); + else + builder.AppendLine(entry.TargetOffsetWORD, " Target offset"); + } + } + + // MOD ORD# / IMPORT ORD / ADDITIVE + else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByOrdinal)) + { + // 16-bit Object Number/Module Ordinal Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + builder.AppendLine(entry.OrdinalIndexImportModuleNameTableWORD, " Ordinal index import module name table"); + else + builder.AppendLine(entry.OrdinalIndexImportModuleNameTableByte, " Ordinal index import module name table"); + + // 8-bit Ordinal Flag & 32-bit Target Offset Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.EightBitOrdinalFlag)) + builder.AppendLine(entry.ImportedOrdinalNumberByte, " Imported ordinal number"); + else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) + builder.AppendLine(entry.ImportedOrdinalNumberDWORD, " Imported ordinal number"); + else + builder.AppendLine(entry.ImportedOrdinalNumberWORD, " Imported ordinal number"); + + // Additive Fixup Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) + { + // 32-bit Additive Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) + builder.AppendLine(entry.AdditiveFixupValueDWORD, " Additive fixup value"); + else + builder.AppendLine(entry.AdditiveFixupValueWORD, " Additive fixup value"); + } + } + + // MOD ORD# / PROCEDURE NAME OFFSET / ADDITIVE + else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByName)) + { + // 16-bit Object Number/Module Ordinal Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + builder.AppendLine(entry.OrdinalIndexImportModuleNameTableWORD, " Ordinal index import module name table"); + else + builder.AppendLine(entry.OrdinalIndexImportModuleNameTableByte, " Ordinal index import module name table"); + + // 32-bit Target Offset Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) + builder.AppendLine(entry.OffsetImportProcedureNameTableDWORD, " Offset import procedure name table"); + else + builder.AppendLine(entry.OffsetImportProcedureNameTableWORD, " Offset import procedure name table"); + + // Additive Fixup Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) + { + // 32-bit Additive Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) + builder.AppendLine(entry.AdditiveFixupValueDWORD, " Additive fixup value"); + else + builder.AppendLine(entry.AdditiveFixupValueWORD, " Additive fixup value"); + } + } + + // ORD # / ADDITIVE + else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReferenceViaEntryTable)) + { + // 16-bit Object Number/Module Ordinal Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + builder.AppendLine(entry.TargetObjectNumberWORD, " Target object number"); + else + builder.AppendLine(entry.TargetObjectNumberByte, " Target object number"); + + // Additive Fixup Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) + { + // 32-bit Additive Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) + builder.AppendLine(entry.AdditiveFixupValueDWORD, " Additive fixup value"); + else + builder.AppendLine(entry.AdditiveFixupValueWORD, " Additive fixup value"); + } + } + + // No other top-level flags recognized + else + { + builder.AppendLine(" Unknown entry format"); + } + + builder.AppendLine(); + builder.AppendLine(" Source Offset List:"); + builder.AppendLine(" -------------------------"); + if (entry.SourceOffsetList == null || entry.SourceOffsetList.Length == 0) + { + builder.AppendLine(" No source offset list entries"); + } + else + { + for (int j = 0; j < entry.SourceOffsetList.Length; j++) + { + builder.AppendLine(entry.SourceOffsetList[j], $" Source Offset List Entry {j}"); + } + } + builder.AppendLine(); + } + } + +#if NET48 + private static void Print(StringBuilder builder, ImportModuleNameTableEntry[] entries) +#else + private static void Print(StringBuilder builder, ImportModuleNameTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Import Module Name Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No import module name table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Import Module Name Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Length, " Length"); + builder.AppendLine(entry.Name, " Name"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ImportModuleProcedureNameTableEntry[] entries) +#else + private static void Print(StringBuilder builder, ImportModuleProcedureNameTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Import Module Procedure Name Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No import module procedure name table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Import Module Procedure Name Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Length, " Length"); + builder.AppendLine(entry.Name, " Name"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, PerPageChecksumTableEntry[] entries) +#else + private static void Print(StringBuilder builder, PerPageChecksumTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Per-Page Checksum Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No per-page checksum table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Per-Page Checksum Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Checksum, " Checksum"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, NonResidentNamesTableEntry[] entries) +#else + private static void Print(StringBuilder builder, NonResidentNamesTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Non-Resident Names Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No non-resident names table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Non-Resident Names Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Length, " Length"); + builder.AppendLine(entry.Name, " Name"); + builder.AppendLine(entry.OrdinalNumber, " Ordinal number"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DebugInformation di) +#else + private static void Print(StringBuilder builder, DebugInformation? di) +#endif + { + builder.AppendLine(" Debug Information:"); + builder.AppendLine(" -------------------------"); + if (di == null) + { + builder.AppendLine(" No debug information"); + builder.AppendLine(); + return; + } + + builder.AppendLine(di.Signature, " Signature"); + builder.AppendLine($" Format type: {di.FormatType} (0x{di.FormatType:X})"); + // Debugger data + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/MSDOS.cs b/MSDOS.cs new file mode 100644 index 0000000..32098bc --- /dev/null +++ b/MSDOS.cs @@ -0,0 +1,81 @@ +using System.Text; +using SabreTools.Models.MSDOS; + +namespace SabreTools.Printing +{ + public static class MSDOS + { + public static void Print(StringBuilder builder, Executable executable) + { + builder.AppendLine("MS-DOS Executable Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, executable.Header); + Print(builder, executable.RelocationTable); + } + +#if NET48 + private static void Print(StringBuilder builder, ExecutableHeader header) +#else + private static void Print(StringBuilder builder, ExecutableHeader? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Magic, " Magic number"); + builder.AppendLine(header.LastPageBytes, " Last page bytes"); + builder.AppendLine(header.Pages, " Pages"); + builder.AppendLine(header.RelocationItems, " Relocation items"); + builder.AppendLine(header.HeaderParagraphSize, " Header paragraph size"); + builder.AppendLine(header.MinimumExtraParagraphs, " Minimum extra paragraphs"); + builder.AppendLine(header.MaximumExtraParagraphs, " Maximum extra paragraphs"); + builder.AppendLine(header.InitialSSValue, " Initial SS value"); + builder.AppendLine(header.InitialSPValue, " Initial SP value"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine(header.InitialIPValue, " Initial IP value"); + builder.AppendLine(header.InitialCSValue, " Initial CS value"); + builder.AppendLine(header.RelocationTableAddr, " Relocation table address"); + builder.AppendLine(header.OverlayNumber, " Overlay number"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, RelocationEntry[] entries) +#else + private static void Print(StringBuilder builder, RelocationEntry?[]? entries) +#endif + { + builder.AppendLine(" Relocation Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No relocation table items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Relocation Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Offset, " Offset"); + builder.AppendLine(entry.Segment, " Segment"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/MicrosoftCabinet.cs b/MicrosoftCabinet.cs new file mode 100644 index 0000000..e401663 --- /dev/null +++ b/MicrosoftCabinet.cs @@ -0,0 +1,168 @@ +using System; +using System.Text; +using SabreTools.Models.MicrosoftCabinet; + +namespace SabreTools.Printing +{ + public static class MicrosoftCabinet + { + public static void Print(StringBuilder builder, Cabinet cabinet) + { + builder.AppendLine("Microsoft Cabinet Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, cabinet.Header); + Print(builder, cabinet.Folders); + Print(builder, cabinet.Files); + } + +#if NET48 + private static void Print(StringBuilder builder, CFHEADER header) +#else + private static void Print(StringBuilder builder, CFHEADER? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.Reserved1, " Reserved 1"); + builder.AppendLine(header.CabinetSize, " Cabinet size"); + builder.AppendLine(header.Reserved2, " Reserved 2"); + builder.AppendLine(header.FilesOffset, " Files offset"); + builder.AppendLine(header.Reserved3, " Reserved 3"); + builder.AppendLine(header.VersionMinor, " Minor version"); + builder.AppendLine(header.VersionMajor, " Major version"); + builder.AppendLine(header.FolderCount, " Folder count"); + builder.AppendLine(header.FileCount, " File count"); + builder.AppendLine($" Flags: {header.Flags} (0x{header.Flags:X})"); + builder.AppendLine(header.SetID, " Set ID"); + builder.AppendLine(header.CabinetIndex, " Cabinet index"); + + if (header.Flags.HasFlag(HeaderFlags.RESERVE_PRESENT)) + { + builder.AppendLine(header.HeaderReservedSize, " Header reserved size"); + builder.AppendLine(header.FolderReservedSize, " Folder reserved size"); + builder.AppendLine(header.DataReservedSize, " Data reserved size"); + builder.AppendLine(header.ReservedData, " Reserved data"); + } + + if (header.Flags.HasFlag(HeaderFlags.PREV_CABINET)) + { + builder.AppendLine(header.CabinetPrev, " Previous cabinet"); + builder.AppendLine(header.DiskPrev, " Previous disk"); + } + + if (header.Flags.HasFlag(HeaderFlags.NEXT_CABINET)) + { + builder.AppendLine(header.CabinetNext, " Next cabinet"); + builder.AppendLine(header.DiskNext, " Next disk"); + } + + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, CFFOLDER[] entries) +#else + private static void Print(StringBuilder builder, CFFOLDER?[]? entries) +#endif + { + builder.AppendLine(" Folders:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No folders"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Folder {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.CabStartOffset, " Cab start offset"); + builder.AppendLine(entry.DataCount, " Data count"); + builder.AppendLine($" Compression type: {entry.CompressionType} (0x{entry.CompressionType:X})"); + builder.AppendLine($" Masked compression type: {entry.CompressionType & CompressionType.MASK_TYPE}"); + builder.AppendLine(entry.ReservedData, " Reserved data"); + builder.AppendLine(); + + builder.AppendLine(" Data Blocks"); + builder.AppendLine(" -------------------------"); + if (entry.DataBlocks == null || entry.DataBlocks.Length == 0) + { + builder.AppendLine(" No data blocks"); + continue; + } + + for (int j = 0; j < entry.DataBlocks.Length; j++) + { + var dataBlock = entry.DataBlocks[j]; + builder.AppendLine($" Data Block {j}"); + if (dataBlock == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(dataBlock.Checksum, " Checksum"); + builder.AppendLine(dataBlock.CompressedSize, " Compressed size"); + builder.AppendLine(dataBlock.UncompressedSize, " Uncompressed size"); + builder.AppendLine(dataBlock.ReservedData, " Reserved data"); + //builder.AppendLine(dataBlock.CompressedData, " Compressed data"); + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, CFFILE[] entries) +#else + private static void Print(StringBuilder builder, CFFILE?[]? entries) +#endif + { + builder.AppendLine(" Files:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No files"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" File {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.FileSize, " File size"); + builder.AppendLine(entry.FolderStartOffset, " Folder start offset"); + builder.AppendLine($" Folder index: {entry.FolderIndex} (0x{entry.FolderIndex:X})"); + builder.AppendLine(entry.Date, " Date"); + builder.AppendLine(entry.Time, " Time"); + builder.AppendLine($" Attributes: {entry.Attributes} (0x{entry.Attributes:X})"); + builder.AppendLine(entry.Name, " Name"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/N3DS.cs b/N3DS.cs new file mode 100644 index 0000000..2b995f0 --- /dev/null +++ b/N3DS.cs @@ -0,0 +1,669 @@ +using System.Text; +using SabreTools.Models.N3DS; + +namespace SabreTools.Printing +{ + public static class N3DS + { + public static void Print(StringBuilder builder, Cart cart) + { + builder.AppendLine("3DS Cart Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, cart.Header); + Print(builder, cart.CardInfoHeader); + Print(builder, cart.DevelopmentCardInfoHeader); + Print(builder, cart.Partitions); + Print(builder, cart.ExtendedHeaders); + Print(builder, cart.ExeFSHeaders); + Print(builder, cart.RomFSHeaders); + } + +#if NET48 + private static void Print(StringBuilder builder, NCSDHeader header) +#else + private static void Print(StringBuilder builder, NCSDHeader? header) +#endif + { + builder.AppendLine(" NCSD Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No NCSD header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.RSA2048Signature, " RSA-2048 SHA-256 signature"); + builder.AppendLine(header.MagicNumber, " Magic number"); + builder.AppendLine(header.ImageSizeInMediaUnits, " Image size in media units"); + builder.AppendLine(header.MediaId, " Media ID"); + builder.AppendLine($" Partitions filesystem type: {header.PartitionsFSType} (0x{header.PartitionsFSType:X})"); + builder.AppendLine(header.PartitionsCryptType, " Partitions crypt type"); + builder.AppendLine(); + + builder.AppendLine(" Partition table:"); + builder.AppendLine(" -------------------------"); + if (header.PartitionsTable == null || header.PartitionsTable.Length == 0) + { + builder.AppendLine(" No partition table entries"); + } + else + { + for (int i = 0; i < header.PartitionsTable.Length; i++) + { + var partitionTableEntry = header.PartitionsTable[i]; + builder.AppendLine($" Partition table entry {i}"); + if (partitionTableEntry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(partitionTableEntry.Offset, " Offset"); + builder.AppendLine(partitionTableEntry.Length, " Length"); + } + } + builder.AppendLine(); + + // If we have a cart image + if (header.PartitionsFSType == FilesystemType.Normal || header.PartitionsFSType == FilesystemType.None) + { + builder.AppendLine(header.ExheaderHash, " Exheader SHA-256 hash"); + builder.AppendLine(header.AdditionalHeaderSize, " Additional header size"); + builder.AppendLine(header.SectorZeroOffset, " Sector zero offset"); + builder.AppendLine(header.PartitionFlags, " Partition flags"); + builder.AppendLine(); + + builder.AppendLine(" Partition ID table:"); + builder.AppendLine(" -------------------------"); + if (header.PartitionIdTable == null || header.PartitionIdTable.Length == 0) + { + builder.AppendLine(" No partition ID table entries"); + } + else + { + for (int i = 0; i < header.PartitionIdTable.Length; i++) + { + builder.AppendLine(header.PartitionIdTable[i], $" Partition {i} ID"); + } + } + builder.AppendLine(); + + builder.AppendLine(header.Reserved1, " Reserved 1"); + builder.AppendLine(header.Reserved2, " Reserved 2"); + builder.AppendLine(header.FirmUpdateByte1, " Firmware update byte 1"); + builder.AppendLine(header.FirmUpdateByte2, " Firmware update byte 2"); + } + + // If we have a firmware image + else if (header.PartitionsFSType == FilesystemType.FIRM) + { + builder.AppendLine(header.Unknown, " Unknown"); + builder.AppendLine(header.EncryptedMBR, " Encrypted MBR"); + } + + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, CardInfoHeader header) +#else + private static void Print(StringBuilder builder, CardInfoHeader? header) +#endif + { + builder.AppendLine(" Card Info Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No card info header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.WritableAddressMediaUnits, " Writable address in media units"); + builder.AppendLine(header.CardInfoBitmask, " Card info bitmask"); + builder.AppendLine(header.Reserved1, " Reserved 1"); + builder.AppendLine(header.FilledSize, " Filled size of cartridge"); + builder.AppendLine(header.Reserved2, " Reserved 2"); + builder.AppendLine(header.TitleVersion, " Title version"); + builder.AppendLine(header.CardRevision, " Card revision"); + builder.AppendLine(header.Reserved3, " Reserved 3"); + builder.AppendLine(header.CVerTitleID, " Title ID of CVer in included update partition"); + builder.AppendLine(header.CVerVersionNumber, " Version number of CVer in included update partition"); + builder.AppendLine(header.Reserved4, " Reserved 4"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DevelopmentCardInfoHeader header) +#else + private static void Print(StringBuilder builder, DevelopmentCardInfoHeader? header) +#endif + { + builder.AppendLine(" Development Card Info Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No development card info header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(); + builder.AppendLine(" Initial Data:"); + builder.AppendLine(" -------------------------"); + if (header.InitialData == null) + { + builder.AppendLine(" No initial data"); + } + else + { + builder.AppendLine(header.InitialData.CardSeedKeyY, " Card seed keyY"); + builder.AppendLine(header.InitialData.EncryptedCardSeed, " Encrypted card seed"); + builder.AppendLine(header.InitialData.CardSeedAESMAC, " Card seed AES-MAC"); + builder.AppendLine(header.InitialData.CardSeedNonce, " Card seed nonce"); + builder.AppendLine(header.InitialData.Reserved, " Reserved"); + builder.AppendLine(); + + builder.AppendLine(" Backup Header:"); + builder.AppendLine(" -------------------------"); + if (header.InitialData.BackupHeader == null) + { + builder.AppendLine(" No backup header"); + } + else + { + builder.AppendLine(header.InitialData.BackupHeader.MagicID, " Magic ID"); + builder.AppendLine(header.InitialData.BackupHeader.ContentSizeInMediaUnits, " Content size in media units"); + builder.AppendLine(header.InitialData.BackupHeader.PartitionId, " Partition ID"); + builder.AppendLine(header.InitialData.BackupHeader.MakerCode, " Maker code"); + builder.AppendLine(header.InitialData.BackupHeader.Version, " Version"); + builder.AppendLine(header.InitialData.BackupHeader.VerificationHash, " Verification hash"); + builder.AppendLine(header.InitialData.BackupHeader.ProgramId, " Program ID"); + builder.AppendLine(header.InitialData.BackupHeader.Reserved1, " Reserved 1"); + builder.AppendLine(header.InitialData.BackupHeader.LogoRegionHash, " Logo region SHA-256 hash"); + builder.AppendLine(header.InitialData.BackupHeader.ProductCode, " Product code"); + builder.AppendLine(header.InitialData.BackupHeader.ExtendedHeaderHash, " Extended header SHA-256 hash"); + builder.AppendLine(header.InitialData.BackupHeader.ExtendedHeaderSizeInBytes, " Extended header size in bytes"); + builder.AppendLine(header.InitialData.BackupHeader.Reserved2, " Reserved 2"); + builder.AppendLine($" Flags: {header.InitialData.BackupHeader.Flags} (0x{header.InitialData.BackupHeader.Flags:X})"); + builder.AppendLine(header.InitialData.BackupHeader.PlainRegionOffsetInMediaUnits, " Plain region offset, in media units"); + builder.AppendLine(header.InitialData.BackupHeader.PlainRegionSizeInMediaUnits, " Plain region size, in media units"); + builder.AppendLine(header.InitialData.BackupHeader.LogoRegionOffsetInMediaUnits, " Logo region offset, in media units"); + builder.AppendLine(header.InitialData.BackupHeader.LogoRegionSizeInMediaUnits, " Logo region size, in media units"); + builder.AppendLine(header.InitialData.BackupHeader.ExeFSOffsetInMediaUnits, " ExeFS offset, in media units"); + builder.AppendLine(header.InitialData.BackupHeader.ExeFSSizeInMediaUnits, " ExeFS size, in media units"); + builder.AppendLine(header.InitialData.BackupHeader.ExeFSHashRegionSizeInMediaUnits, " ExeFS hash region size, in media units"); + builder.AppendLine(header.InitialData.BackupHeader.Reserved3, " Reserved 3"); + builder.AppendLine(header.InitialData.BackupHeader.RomFSOffsetInMediaUnits, " RomFS offset, in media units"); + builder.AppendLine(header.InitialData.BackupHeader.RomFSSizeInMediaUnits, " RomFS size, in media units"); + builder.AppendLine(header.InitialData.BackupHeader.RomFSHashRegionSizeInMediaUnits, " RomFS hash region size, in media units"); + builder.AppendLine(header.InitialData.BackupHeader.Reserved4, " Reserved 4"); + builder.AppendLine(header.InitialData.BackupHeader.ExeFSSuperblockHash, " ExeFS superblock SHA-256 hash"); + builder.AppendLine(header.InitialData.BackupHeader.RomFSSuperblockHash, " RomFS superblock SHA-256 hash"); + } + } + builder.AppendLine(); + + builder.AppendLine(header.CardDeviceReserved1, " Card device reserved 1"); + builder.AppendLine(header.TitleKey, " Title key"); + builder.AppendLine(header.CardDeviceReserved2, " Card device reserved 2"); + builder.AppendLine(); + + builder.AppendLine(" Test Data:"); + builder.AppendLine(" -------------------------"); + if (header.TestData == null) + { + builder.AppendLine(" No test data"); + } + else + { + builder.AppendLine(header.TestData.Signature, " Signature"); + builder.AppendLine(header.TestData.AscendingByteSequence, " Ascending byte sequence"); + builder.AppendLine(header.TestData.DescendingByteSequence, " Descending byte sequence"); + builder.AppendLine(header.TestData.Filled00, " Filled with 00"); + builder.AppendLine(header.TestData.FilledFF, " Filled with FF"); + builder.AppendLine(header.TestData.Filled0F, " Filled with 0F"); + builder.AppendLine(header.TestData.FilledF0, " Filled with F0"); + builder.AppendLine(header.TestData.Filled55, " Filled with 55"); + builder.AppendLine(header.TestData.FilledAA, " Filled with AA"); + builder.AppendLine(header.TestData.FinalByte, " Final byte"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, NCCHHeader[] entries) +#else + private static void Print(StringBuilder builder, NCCHHeader?[]? entries) +#endif + { + builder.AppendLine(" NCCH Partition Header Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No NCCH partition headers"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" NCCH Partition Header {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + if (entry.MagicID == string.Empty) + { + builder.AppendLine(" Empty partition, no data can be parsed"); + } + else if (entry.MagicID != Constants.NCCHMagicNumber) + { + builder.AppendLine(" Unrecognized partition data, no data can be parsed"); + } + else + { + builder.AppendLine(entry.RSA2048Signature, " RSA-2048 SHA-256 signature"); + builder.AppendLine(entry.MagicID, " Magic ID"); + builder.AppendLine(entry.ContentSizeInMediaUnits, " Content size in media units"); + builder.AppendLine(entry.PartitionId, " Partition ID"); + builder.AppendLine(entry.MakerCode, " Maker code"); + builder.AppendLine(entry.Version, " Version"); + builder.AppendLine(entry.VerificationHash, " Verification hash"); + builder.AppendLine(entry.ProgramId, " Program ID"); + builder.AppendLine(entry.Reserved1, " Reserved 1"); + builder.AppendLine(entry.LogoRegionHash, " Logo region SHA-256 hash"); + builder.AppendLine(entry.ProductCode, " Product code"); + builder.AppendLine(entry.ExtendedHeaderHash, " Extended header SHA-256 hash"); + builder.AppendLine(entry.ExtendedHeaderSizeInBytes, " Extended header size in bytes"); + builder.AppendLine(entry.Reserved2, " Reserved 2"); + builder.AppendLine(" Flags:"); + if (entry.Flags == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.Flags.Reserved0, " Reserved 0"); + builder.AppendLine(entry.Flags.Reserved1, " Reserved 1"); + builder.AppendLine(entry.Flags.Reserved2, " Reserved 2"); + builder.AppendLine($" Crypto method: {entry.Flags.CryptoMethod} (0x{entry.Flags.CryptoMethod:X})"); + builder.AppendLine($" Content platform: {entry.Flags.ContentPlatform} (0x{entry.Flags.ContentPlatform:X})"); + builder.AppendLine($" Content type: {entry.Flags.MediaPlatformIndex} (0x{entry.Flags.MediaPlatformIndex:X})"); + builder.AppendLine(entry.Flags.ContentUnitSize, " Content unit size"); + builder.AppendLine($" Bitmasks: {entry.Flags.BitMasks} (0x{entry.Flags.BitMasks:X})"); + } + builder.AppendLine(entry.PlainRegionOffsetInMediaUnits, " Plain region offset, in media units"); + builder.AppendLine(entry.PlainRegionSizeInMediaUnits, " Plain region size, in media units"); + builder.AppendLine(entry.LogoRegionOffsetInMediaUnits, " Logo region offset, in media units"); + builder.AppendLine(entry.LogoRegionSizeInMediaUnits, " Logo region size, in media units"); + builder.AppendLine(entry.ExeFSOffsetInMediaUnits, " ExeFS offset, in media units"); + builder.AppendLine(entry.ExeFSSizeInMediaUnits, " ExeFS size, in media units"); + builder.AppendLine(entry.ExeFSHashRegionSizeInMediaUnits, " ExeFS hash region size, in media units"); + builder.AppendLine(entry.Reserved3, " Reserved 3"); + builder.AppendLine(entry.RomFSOffsetInMediaUnits, " RomFS offset, in media units"); + builder.AppendLine(entry.RomFSSizeInMediaUnits, " RomFS size, in media units"); + builder.AppendLine(entry.RomFSHashRegionSizeInMediaUnits, " RomFS hash region size, in media units"); + builder.AppendLine(entry.Reserved4, " Reserved 4"); + builder.AppendLine(entry.ExeFSSuperblockHash, " ExeFS superblock SHA-256 hash"); + builder.AppendLine(entry.RomFSSuperblockHash, " RomFS superblock SHA-256 hash"); + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, NCCHExtendedHeader[] entries) +#else + private static void Print(StringBuilder builder, NCCHExtendedHeader?[]? entries) +#endif + { + builder.AppendLine(" NCCH Extended Header Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No NCCH extended headers"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" NCCH Extended Header {i}"); + if (entry == null) + { + builder.AppendLine(" Unrecognized partition data, no data can be parsed"); + continue; + } + + builder.AppendLine(" System control info:"); + if (entry.SCI == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.SCI.ApplicationTitle, " Application title"); + builder.AppendLine(entry.SCI.Reserved1, " Reserved 1"); + builder.AppendLine(entry.SCI.Flag, " Flag"); + builder.AppendLine(entry.SCI.RemasterVersion, " Remaster version"); + + builder.AppendLine(" Text code set info:"); + if (entry.SCI.TextCodeSetInfo == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.SCI.TextCodeSetInfo.Address, " Address"); + builder.AppendLine(entry.SCI.TextCodeSetInfo.PhysicalRegionSizeInPages, " Physical region size (in page-multiples)"); + builder.AppendLine(entry.SCI.TextCodeSetInfo.SizeInBytes, " Size (in bytes)"); + } + + builder.AppendLine(entry.SCI.StackSize, " Stack size"); + + builder.AppendLine(" Read-only code set info:"); + if (entry.SCI.ReadOnlyCodeSetInfo == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.SCI.ReadOnlyCodeSetInfo.Address, " Address"); + builder.AppendLine(entry.SCI.ReadOnlyCodeSetInfo.PhysicalRegionSizeInPages, " Physical region size (in page-multiples)"); + builder.AppendLine(entry.SCI.ReadOnlyCodeSetInfo.SizeInBytes, " Size (in bytes)"); + } + + builder.AppendLine(entry.SCI.Reserved2, " Reserved 2"); + + builder.AppendLine(" Data code set info:"); + if (entry.SCI.DataCodeSetInfo == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.SCI.DataCodeSetInfo.Address, " Address"); + builder.AppendLine(entry.SCI.DataCodeSetInfo.PhysicalRegionSizeInPages, " Physical region size (in page-multiples)"); + builder.AppendLine(entry.SCI.DataCodeSetInfo.SizeInBytes, " Size (in bytes)"); + } + + builder.AppendLine(entry.SCI.BSSSize, " BSS size"); + builder.AppendLine(entry.SCI.DependencyModuleList, " Dependency module list"); + + builder.AppendLine(" System info:"); + if (entry.SCI.SystemInfo == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.SCI.SystemInfo.SaveDataSize, " SaveData size"); + builder.AppendLine(entry.SCI.SystemInfo.JumpID, " Jump ID"); + builder.AppendLine(entry.SCI.SystemInfo.Reserved, " Reserved"); + } + } + + builder.AppendLine(" Access control info:"); + if (entry.ACI == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(" ARM11 local system capabilities:"); + if (entry.ACI.ARM11LocalSystemCapabilities == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.ProgramID, " Program ID"); + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.CoreVersion, " Core version"); + builder.AppendLine($" Flag 1: {entry.ACI.ARM11LocalSystemCapabilities.Flag1} (0x{entry.ACI.ARM11LocalSystemCapabilities.Flag1:X})"); + builder.AppendLine($" Flag 2: {entry.ACI.ARM11LocalSystemCapabilities.Flag2} (0x{entry.ACI.ARM11LocalSystemCapabilities.Flag2:X})"); + builder.AppendLine($" Flag 0: {entry.ACI.ARM11LocalSystemCapabilities.Flag0} (0x{entry.ACI.ARM11LocalSystemCapabilities.Flag0:X})"); + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.Priority, " Priority"); + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.ResourceLimitDescriptors, " Resource limit descriptors"); + builder.AppendLine(" Storage info:"); + if (entry.ACI.ARM11LocalSystemCapabilities.StorageInfo == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.ExtdataID, " Extdata ID"); + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.SystemSavedataIDs, " System savedata IDs"); + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.StorageAccessibleUniqueIDs, " Storage accessible unique IDs"); + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.FileSystemAccessInfo, " File system access info"); + builder.AppendLine($" Other attributes: {entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.OtherAttributes} (0x{entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.OtherAttributes:X})"); + } + + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.ServiceAccessControl, " Service access control"); + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.ExtendedServiceAccessControl, " Extended service access control"); + builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.Reserved, " Reserved"); + builder.AppendLine($" Resource limit cateogry: {entry.ACI.ARM11LocalSystemCapabilities.ResourceLimitCategory} (0x{entry.ACI.ARM11LocalSystemCapabilities.ResourceLimitCategory:X})"); + } + + builder.AppendLine(" ARM11 kernel capabilities:"); + if (entry.ACI.ARM11KernelCapabilities == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.ACI.ARM11KernelCapabilities.Descriptors, " Descriptors"); + builder.AppendLine(entry.ACI.ARM11KernelCapabilities.Reserved, " Reserved"); + } + + builder.AppendLine(" ARM9 access control:"); + if (entry.ACI.ARM9AccessControl == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.ACI.ARM9AccessControl.Descriptors, " Descriptors"); + builder.AppendLine(entry.ACI.ARM9AccessControl.DescriptorVersion, " Descriptor version"); + } + + builder.AppendLine(entry.AccessDescSignature, " AccessDec signature (RSA-2048-SHA256)"); + builder.AppendLine(entry.NCCHHDRPublicKey, " NCCH HDR RSA-2048 public key"); + } + + builder.AppendLine(" Access control info (for limitations of first ACI):"); + if (entry.ACIForLimitations == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(" ARM11 local system capabilities:"); + if (entry.ACIForLimitations.ARM11LocalSystemCapabilities == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.ProgramID, " Program ID"); + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.CoreVersion, " Core version"); + builder.AppendLine($" Flag 1: {entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag1} (0x{entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag1:X})"); + builder.AppendLine($" Flag 2: {entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag2} (0x{entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag2:X})"); + builder.AppendLine($" Flag 0: {entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag0} (0x{entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag0:X})"); + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.Priority, " Priority"); + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.ResourceLimitDescriptors, " Resource limit descriptors"); + + builder.AppendLine(" Storage info:"); + if (entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.ExtdataID, " Extdata ID"); + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.SystemSavedataIDs, " System savedata IDs"); + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.StorageAccessibleUniqueIDs, " Storage accessible unique IDs"); + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.FileSystemAccessInfo, " File system access info"); + builder.AppendLine($" Other attributes: {entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.OtherAttributes} (0x{entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.OtherAttributes:X})"); + } + + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.ServiceAccessControl, " Service access control"); + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.ExtendedServiceAccessControl, " Extended service access control"); + builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.Reserved, " Reserved"); + builder.AppendLine($" Resource limit cateogry: {entry.ACIForLimitations.ARM11LocalSystemCapabilities.ResourceLimitCategory} (0x{entry.ACIForLimitations.ARM11LocalSystemCapabilities.ResourceLimitCategory:X})"); + } + + builder.AppendLine(" ARM11 kernel capabilities:"); + if (entry.ACIForLimitations.ARM11KernelCapabilities == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.ACIForLimitations.ARM11KernelCapabilities.Descriptors, " Descriptors"); + builder.AppendLine(entry.ACIForLimitations.ARM11KernelCapabilities.Reserved, " Reserved"); + } + + builder.AppendLine(" ARM9 access control:"); + if (entry.ACIForLimitations.ARM9AccessControl == null) + { + builder.AppendLine(" [NULL]"); + } + else + { + builder.AppendLine(entry.ACIForLimitations.ARM9AccessControl.Descriptors, " Descriptors"); + builder.AppendLine(entry.ACIForLimitations.ARM9AccessControl.DescriptorVersion, " Descriptor version"); + } + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ExeFSHeader[] entries) +#else + private static void Print(StringBuilder builder, ExeFSHeader?[]? entries) +#endif + { + builder.AppendLine(" ExeFS Header Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No ExeFS headers"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" ExeFS Header {i}"); + if (entry == null) + { + builder.AppendLine(" Unrecognized partition data, no data can be parsed"); + continue; + } + + builder.AppendLine(" File headers:"); + if (entry.FileHeaders == null || entry.FileHeaders.Length == 0) + { + builder.AppendLine(" No file headers"); + } + else + { + for (int j = 0; j < entry.FileHeaders.Length; j++) + { + var fileHeader = entry.FileHeaders[j]; + builder.AppendLine($" File Header {j}"); + if (fileHeader == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(fileHeader.FileName, " File name"); + builder.AppendLine(fileHeader.FileOffset, " File offset"); + builder.AppendLine(fileHeader.FileSize, " File size"); + } + } + + builder.AppendLine(entry.Reserved, " Reserved"); + + builder.AppendLine(" File hashes:"); + if (entry.FileHashes == null || entry.FileHashes.Length == 0) + { + builder.AppendLine(" No file hashes"); + } + else + { + for (int j = 0; j < entry.FileHashes.Length; j++) + { + var fileHash = entry.FileHashes[j]; + builder.AppendLine($" File Hash {j}"); + if (fileHash == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(fileHash, " SHA-256"); + } + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, RomFSHeader[] entries) +#else + private static void Print(StringBuilder builder, RomFSHeader?[]? entries) +#endif + { + builder.AppendLine(" RomFS Header Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No RomFS headers"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var romFSHeader = entries[i]; + builder.AppendLine($" RomFS Header {i}"); + if (romFSHeader == null) + { + builder.AppendLine(" Unrecognized RomFS data, no data can be parsed"); + continue; + } + + builder.AppendLine(romFSHeader.MagicString, " Magic string"); + builder.AppendLine(romFSHeader.MagicNumber, " Magic number"); + builder.AppendLine(romFSHeader.MasterHashSize, " Master hash size"); + builder.AppendLine(romFSHeader.Level1LogicalOffset, " Level 1 logical offset"); + builder.AppendLine(romFSHeader.Level1HashdataSize, " Level 1 hashdata size"); + builder.AppendLine(romFSHeader.Level1BlockSizeLog2, " Level 1 block size"); + builder.AppendLine(romFSHeader.Reserved1, " Reserved 1"); + builder.AppendLine(romFSHeader.Level2LogicalOffset, " Level 2 logical offset"); + builder.AppendLine(romFSHeader.Level2HashdataSize, " Level 2 hashdata size"); + builder.AppendLine(romFSHeader.Level2BlockSizeLog2, " Level 2 block size"); + builder.AppendLine(romFSHeader.Reserved2, " Reserved 2"); + builder.AppendLine(romFSHeader.Level3LogicalOffset, " Level 3 logical offset"); + builder.AppendLine(romFSHeader.Level3HashdataSize, " Level 3 hashdata size"); + builder.AppendLine(romFSHeader.Level3BlockSizeLog2, " Level 3 block size"); + builder.AppendLine(romFSHeader.Reserved3, " Reserved 3"); + builder.AppendLine(romFSHeader.Reserved4, " Reserved 4"); + builder.AppendLine(romFSHeader.OptionalInfoSize, " Optional info size"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/NCF.cs b/NCF.cs new file mode 100644 index 0000000..b6a812c --- /dev/null +++ b/NCF.cs @@ -0,0 +1,406 @@ +using System.Text; +using SabreTools.Models.NCF; + +namespace SabreTools.Printing +{ + public static class NCF + { + public static void Print(StringBuilder builder, File file) + { + builder.AppendLine("NCF Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + // Header + Print(builder, file.Header); + + // Directory and Directory Maps + Print(builder, file.DirectoryHeader); + Print(builder, file.DirectoryEntries); + // TODO: Should we print out the entire string table? + Print(builder, file.DirectoryInfo1Entries); + Print(builder, file.DirectoryInfo2Entries); + Print(builder, file.DirectoryCopyEntries); + Print(builder, file.DirectoryLocalEntries); + Print(builder, file.UnknownHeader); + Print(builder, file.UnknownEntries); + + // Checksums and Checksum Maps + Print(builder, file.ChecksumHeader); + Print(builder, file.ChecksumMapHeader); + Print(builder, file.ChecksumMapEntries); + Print(builder, file.ChecksumEntries); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.MajorVersion, " Major version"); + builder.AppendLine(header.MinorVersion, " Minor version"); + builder.AppendLine(header.CacheID, " Cache ID"); + builder.AppendLine(header.LastVersionPlayed, " Last version played"); + builder.AppendLine(header.Dummy1, " Dummy 1"); + builder.AppendLine(header.Dummy2, " Dummy 2"); + builder.AppendLine(header.FileSize, " File size"); + builder.AppendLine(header.BlockSize, " Block size"); + builder.AppendLine(header.BlockCount, " Block count"); + builder.AppendLine(header.Dummy3, " Dummy 3"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryHeader header) +#else + private static void Print(StringBuilder builder, DirectoryHeader? header) +#endif + { + builder.AppendLine(" Directory Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No directory header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.CacheID, " Cache ID"); + builder.AppendLine(header.LastVersionPlayed, " Last version played"); + builder.AppendLine(header.ItemCount, " Item count"); + builder.AppendLine(header.FileCount, " File count"); + builder.AppendLine(header.ChecksumDataLength, " Checksum data length"); + builder.AppendLine(header.DirectorySize, " Directory size"); + builder.AppendLine(header.NameSize, " Name size"); + builder.AppendLine(header.Info1Count, " Info 1 count"); + builder.AppendLine(header.CopyCount, " Copy count"); + builder.AppendLine(header.LocalCount, " Local count"); + builder.AppendLine(header.Dummy1, " Dummy 1"); + builder.AppendLine(header.Dummy2, " Dummy 2"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryEntry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryEntry?[]? entries) +#endif + { + builder.AppendLine(" Directory Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.NameOffset, " Name offset"); + builder.AppendLine(entry.Name, " Name"); + builder.AppendLine(entry.ItemSize, " Item size"); + builder.AppendLine(entry.ChecksumIndex, " Checksum index"); + builder.AppendLine($" Directory flags: {entry.DirectoryFlags} (0x{entry.DirectoryFlags:X})"); + builder.AppendLine(entry.ParentIndex, " Parent index"); + builder.AppendLine(entry.NextIndex, " Next index"); + builder.AppendLine(entry.FirstIndex, " First index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryInfo1Entry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryInfo1Entry?[]? entries) +#endif + { + builder.AppendLine(" Directory Info 1 Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory info 1 entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Info 1 Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Dummy0, " Dummy 0"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryInfo2Entry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryInfo2Entry?[]? entries) +#endif + { + builder.AppendLine(" Directory Info 2 Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory info 2 entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Info 2 Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Dummy0, " Dummy 0"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryCopyEntry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryCopyEntry?[]? entries) +#endif + { + builder.AppendLine(" Directory Copy Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory copy entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Copy Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.DirectoryIndex, " Directory index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryLocalEntry[] entries) +#else + private static void Print(StringBuilder builder, DirectoryLocalEntry?[]? entries) +#endif + { + builder.AppendLine(" Directory Local Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory local entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Local Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.DirectoryIndex, " Directory index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, UnknownHeader header) +#else + private static void Print(StringBuilder builder, UnknownHeader? header) +#endif + { + builder.AppendLine(" Unknown Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No unknown header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.Dummy1, " Dummy 1"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, UnknownEntry[] entries) +#else + private static void Print(StringBuilder builder, UnknownEntry?[]? entries) +#endif + { + builder.AppendLine(" Unknown Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No unknown entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Unknown Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Dummy0, " Dummy 0"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ChecksumHeader header) +#else + private static void Print(StringBuilder builder, ChecksumHeader? header) +#endif + { + builder.AppendLine(" Checksum Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No checksum header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.ChecksumSize, " Checksum size"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ChecksumMapHeader header) +#else + private static void Print(StringBuilder builder, ChecksumMapHeader? header) +#endif + { + builder.AppendLine(" Checksum Map Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No checksum map header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.Dummy1, " Dummy 1"); + builder.AppendLine(header.ItemCount, " Item count"); + builder.AppendLine(header.ChecksumCount, " Checksum count"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ChecksumMapEntry[] entries) +#else + private static void Print(StringBuilder builder, ChecksumMapEntry?[]? entries) +#endif + { + builder.AppendLine(" Checksum Map Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No checksum map entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Checksum Map Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.ChecksumCount, " Checksum count"); + builder.AppendLine(entry.FirstChecksumIndex, " First checksum index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ChecksumEntry[] entries) +#else + private static void Print(StringBuilder builder, ChecksumEntry?[]? entries) +#endif + { + builder.AppendLine(" Checksum Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No checksum entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Checksum Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Checksum, " Checksum"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/NewExecutable.cs b/NewExecutable.cs new file mode 100644 index 0000000..dce8e47 --- /dev/null +++ b/NewExecutable.cs @@ -0,0 +1,416 @@ +using System.Collections.Generic; +using System.Text; +using SabreTools.Models.NewExecutable; +using static SabreTools.Serialization.Extensions; + +namespace SabreTools.Printing +{ + public static class NewExecutable + { + public static void Print(StringBuilder builder, Executable executable) + { + builder.AppendLine("New Executable Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + // Stub + Print(builder, executable.Stub?.Header); + + // Header + Print(builder, executable.Header); + + // Tables + Print(builder, executable.SegmentTable); + Print(builder, executable.ResourceTable); + Print(builder, executable.ResidentNameTable); + Print(builder, executable.ModuleReferenceTable, executable.Stub?.Header, executable.Header); + Print(builder, executable.ImportedNameTable); + Print(builder, executable.EntryTable); + Print(builder, executable.NonResidentNameTable); + } + +#if NET48 + private static void Print(StringBuilder builder, SabreTools.Models.MSDOS.ExecutableHeader header) +#else + private static void Print(StringBuilder builder, SabreTools.Models.MSDOS.ExecutableHeader? header) +#endif + { + builder.AppendLine(" MS-DOS Stub Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No MS-DOS stub header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Magic, " Magic number"); + builder.AppendLine(header.LastPageBytes, " Last page bytes"); + builder.AppendLine(header.Pages, " Pages"); + builder.AppendLine(header.RelocationItems, " Relocation items"); + builder.AppendLine(header.HeaderParagraphSize, " Header paragraph size"); + builder.AppendLine(header.MinimumExtraParagraphs, " Minimum extra paragraphs"); + builder.AppendLine(header.MaximumExtraParagraphs, " Maximum extra paragraphs"); + builder.AppendLine(header.InitialSSValue, " Initial SS value"); + builder.AppendLine(header.InitialSPValue, " Initial SP value"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine(header.InitialIPValue, " Initial IP value"); + builder.AppendLine(header.InitialCSValue, " Initial CS value"); + builder.AppendLine(header.RelocationTableAddr, " Relocation table address"); + builder.AppendLine(header.OverlayNumber, " Overlay number"); + builder.AppendLine(); + + builder.AppendLine(" MS-DOS Stub Extended Header Information:"); + builder.AppendLine(" -------------------------"); + builder.AppendLine(header.Reserved1, " Reserved words"); + builder.AppendLine(header.OEMIdentifier, " OEM identifier"); + builder.AppendLine(header.OEMInformation, " OEM information"); + builder.AppendLine(header.Reserved2, " Reserved words"); + builder.AppendLine(header.NewExeHeaderAddr, " New EXE header address"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ExecutableHeader header) +#else + private static void Print(StringBuilder builder, ExecutableHeader? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Magic, " Magic number"); + builder.AppendLine(header.LinkerVersion, " Linker version"); + builder.AppendLine(header.LinkerRevision, " Linker revision"); + builder.AppendLine(header.EntryTableOffset, " Entry table offset"); + builder.AppendLine(header.EntryTableSize, " Entry table size"); + builder.AppendLine(header.CrcChecksum, " CRC checksum"); + builder.AppendLine($" Flag word: {header.FlagWord} (0x{header.FlagWord:X})"); + builder.AppendLine(header.AutomaticDataSegmentNumber, " Automatic data segment number"); + builder.AppendLine(header.InitialHeapAlloc, " Initial heap allocation"); + builder.AppendLine(header.InitialStackAlloc, " Initial stack allocation"); + builder.AppendLine(header.InitialCSIPSetting, " Initial CS:IP setting"); + builder.AppendLine(header.InitialSSSPSetting, " Initial SS:SP setting"); + builder.AppendLine(header.FileSegmentCount, " File segment count"); + builder.AppendLine(header.ModuleReferenceTableSize, " Module reference table size"); + builder.AppendLine(header.NonResidentNameTableSize, " Non-resident name table size"); + builder.AppendLine(header.SegmentTableOffset, " Segment table offset"); + builder.AppendLine(header.ResourceTableOffset, " Resource table offset"); + builder.AppendLine(header.ResidentNameTableOffset, " Resident name table offset"); + builder.AppendLine(header.ModuleReferenceTableOffset, " Module reference table offset"); + builder.AppendLine(header.ImportedNamesTableOffset, " Imported names table offset"); + builder.AppendLine(header.NonResidentNamesTableOffset, " Non-resident name table offset"); + builder.AppendLine(header.MovableEntriesCount, " Moveable entries count"); + builder.AppendLine(header.SegmentAlignmentShiftCount, " Segment alignment shift count"); + builder.AppendLine(header.ResourceEntriesCount, " Resource entries count"); + builder.AppendLine($" Target operating system: {header.TargetOperatingSystem} (0x{header.TargetOperatingSystem:X})"); + builder.AppendLine($" Additional flags: {header.AdditionalFlags} (0x{header.AdditionalFlags:X})"); + builder.AppendLine(header.ReturnThunkOffset, " Return thunk offset"); + builder.AppendLine(header.SegmentReferenceThunkOffset, " Segment reference thunk offset"); + builder.AppendLine(header.MinCodeSwapAreaSize, " Minimum code swap area size"); + builder.AppendLine(header.WindowsSDKRevision, " Windows SDK revision"); + builder.AppendLine(header.WindowsSDKVersion, " Windows SDK version"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, SegmentTableEntry[] entries) +#else + private static void Print(StringBuilder builder, SegmentTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Segment Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No segment table items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Segment Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Offset, " Offset"); + builder.AppendLine(entry.Length, " Length"); + builder.AppendLine($" Flag word: {entry.FlagWord} (0x{entry.FlagWord:X})"); + builder.AppendLine(entry.MinimumAllocationSize, " Minimum allocation size"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ResourceTable table) +#else + private static void Print(StringBuilder builder, ResourceTable? table) +#endif + { + builder.AppendLine(" Resource Table Information:"); + builder.AppendLine(" -------------------------"); + if (table == null) + { + builder.AppendLine(" No resource table"); + builder.AppendLine(); + return; + } + + builder.AppendLine(table.AlignmentShiftCount, " Alignment shift count"); + if (table.ResourceTypes == null || table.ResourceTypes.Length == 0) + { + builder.AppendLine(" No resource table items"); + } + else + { + for (int i = 0; i < table.ResourceTypes.Length; i++) + { + // TODO: If not integer type, print out name + var entry = table.ResourceTypes[i]; + builder.AppendLine($" Resource Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.TypeID, " Type ID"); + builder.AppendLine(entry.ResourceCount, " Resource count"); + builder.AppendLine(entry.Reserved, " Reserved"); + builder.AppendLine(" Resources = "); + if (entry.ResourceCount == 0 || entry.Resources == null || entry.Resources.Length == 0) + { + builder.AppendLine(" No resource items"); + } + else + { + for (int j = 0; j < entry.Resources.Length; j++) + { + // TODO: If not integer type, print out name + var resource = entry.Resources[j]; + builder.AppendLine($" Resource Entry {i}"); + if (resource == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(resource.Offset, " Offset"); + builder.AppendLine(resource.Length, " Length"); + builder.AppendLine($" Flag word: {resource.FlagWord} (0x{resource.FlagWord:X})"); + builder.AppendLine(resource.ResourceID, " Resource ID"); + builder.AppendLine(resource.Reserved, " Reserved"); + } + } + } + } + + if (table.TypeAndNameStrings == null || table.TypeAndNameStrings.Count == 0) + { + builder.AppendLine(" No resource table type/name strings"); + } + else + { + foreach (var typeAndNameString in table.TypeAndNameStrings) + { + builder.AppendLine($" Resource Type/Name Offset {typeAndNameString.Key}"); +#if NET6_0_OR_GREATER + if (typeAndNameString.Value == null) + { + builder.AppendLine(" [NULL]"); + continue; + } +#endif + builder.AppendLine(typeAndNameString.Value.Length, " Length"); + builder.AppendLine(typeAndNameString.Value.Text, " Text", Encoding.ASCII); + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ResidentNameTableEntry[] entries) +#else + private static void Print(StringBuilder builder, ResidentNameTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Resident-Name Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No resident-name table items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Resident-Name Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Length, " Length"); + builder.AppendLine(entry.NameString, " Name string", Encoding.ASCII); + builder.AppendLine(entry.OrdinalNumber, " Ordinal number"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ModuleReferenceTableEntry[] entries, SabreTools.Models.MSDOS.ExecutableHeader stub, ExecutableHeader header) +#else + private static void Print(StringBuilder builder, ModuleReferenceTableEntry?[]? entries, SabreTools.Models.MSDOS.ExecutableHeader? stub, ExecutableHeader? header) +#endif + { + builder.AppendLine(" Module-Reference Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No module-reference table items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + // TODO: Read the imported names table and print value here + var entry = entries[i]; + builder.AppendLine($" Module-Reference Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + +#if NET48 + builder.AppendLine($" Offset: {entry.Offset} (adjusted to be {entry.Offset + stub.NewExeHeaderAddr + header.ImportedNamesTableOffset})"); +#else + builder.AppendLine($" Offset: {entry.Offset} (adjusted to be {entry.Offset + (stub?.NewExeHeaderAddr ?? 0) + (header?.ImportedNamesTableOffset ?? 0)})"); +#endif + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Dictionary entries) +#else + private static void Print(StringBuilder builder, Dictionary? entries) +#endif + { + builder.AppendLine(" Imported-Name Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Count == 0) + { + builder.AppendLine(" No imported-name table items"); + builder.AppendLine(); + return; + } + + foreach (var entry in entries) + { + builder.AppendLine($" Imported-Name Table at Offset {entry.Key}"); +#if NET6_0_OR_GREATER + if (entry.Value == null) + { + builder.AppendLine(" [NULL]"); + continue; + } +#endif + builder.AppendLine(entry.Value.Length, " Length"); + builder.AppendLine(entry.Value.NameString, " Name string", Encoding.ASCII); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, EntryTableBundle[] entries) +#else + private static void Print(StringBuilder builder, EntryTableBundle?[]? entries) +#endif + { + builder.AppendLine(" Entry Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No entry table items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Entry Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.EntryCount, " Entry count"); + builder.AppendLine(entry.SegmentIndicator, " Segment indicator"); + switch (entry.GetEntryType()) + { + case SegmentEntryType.FixedSegment: + builder.AppendLine($" Flag word: {entry.FixedFlagWord} (0x{entry.FixedFlagWord:X})"); + builder.AppendLine(entry.FixedOffset, " Offset"); + break; + case SegmentEntryType.MoveableSegment: + builder.AppendLine($" Flag word: {entry.MoveableFlagWord} (0x{entry.MoveableFlagWord:X})"); + builder.AppendLine(entry.MoveableReserved, " Reserved"); + builder.AppendLine(entry.MoveableSegmentNumber, " Segment number"); + builder.AppendLine(entry.MoveableOffset, " Offset"); + break; + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, NonResidentNameTableEntry[] entries) +#else + private static void Print(StringBuilder builder, NonResidentNameTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Nonresident-Name Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No nonresident-name table items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Nonresident-Name Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Length, " Length"); + builder.AppendLine(entry.NameString, " Name string", Encoding.ASCII); + builder.AppendLine(entry.OrdinalNumber, " Ordinal number"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/Nitro.cs b/Nitro.cs new file mode 100644 index 0000000..39f1621 --- /dev/null +++ b/Nitro.cs @@ -0,0 +1,282 @@ +using System.Text; +using SabreTools.Models.Nitro; + +namespace SabreTools.Printing +{ + public static class Nitro + { + public static void Print(StringBuilder builder, Cart cart) + { + builder.AppendLine("NDS Cart Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, cart.CommonHeader); + Print(builder, cart.ExtendedDSiHeader); + Print(builder, cart.SecureArea); + Print(builder, cart.NameTable); + Print(builder, cart.FileAllocationTable); + } + +#if NET48 + private static void Print(StringBuilder builder, CommonHeader header) +#else + private static void Print(StringBuilder builder, CommonHeader? header) +#endif + { + builder.AppendLine(" Common Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No common header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.GameTitle, " Game title"); + builder.AppendLine(header.GameCode, " Game code"); + builder.AppendLine(header.MakerCode, " Maker code"); + builder.AppendLine($" Unit code: {header.UnitCode} (0x{header.UnitCode:X})"); + builder.AppendLine(header.EncryptionSeedSelect, " Encryption seed select"); + builder.AppendLine(header.DeviceCapacity, " Device capacity"); + builder.AppendLine(header.Reserved1, " Reserved 1"); + builder.AppendLine(header.GameRevision, " Game revision"); + builder.AppendLine(header.RomVersion, " Rom version"); + builder.AppendLine(header.ARM9RomOffset, " ARM9 rom offset"); + builder.AppendLine(header.ARM9EntryAddress, " ARM9 entry address"); + builder.AppendLine(header.ARM9LoadAddress, " ARM9 load address"); + builder.AppendLine(header.ARM9Size, " ARM9 size"); + builder.AppendLine(header.ARM7RomOffset, " ARM7 rom offset"); + builder.AppendLine(header.ARM7EntryAddress, " ARM7 entry address"); + builder.AppendLine(header.ARM7LoadAddress, " ARM7 load address"); + builder.AppendLine(header.ARM7Size, " ARM7 size"); + builder.AppendLine(header.FileNameTableOffset, " File name table offset"); + builder.AppendLine(header.FileNameTableLength, " File name table length"); + builder.AppendLine(header.FileAllocationTableOffset, " File allocation table offset"); + builder.AppendLine(header.FileAllocationTableLength, " File allocation table length"); + builder.AppendLine(header.ARM9OverlayOffset, " ARM9 overlay offset"); + builder.AppendLine(header.ARM9OverlayLength, " ARM9 overlay length"); + builder.AppendLine(header.ARM7OverlayOffset, " ARM7 overlay offset"); + builder.AppendLine(header.ARM7OverlayLength, " ARM7 overlay length"); + builder.AppendLine(header.NormalCardControlRegisterSettings, " Normal card control register settings"); + builder.AppendLine(header.SecureCardControlRegisterSettings, " Secure card control register settings"); + builder.AppendLine(header.IconBannerOffset, " Icon banner offset"); + builder.AppendLine(header.SecureAreaCRC, " Secure area CRC"); + builder.AppendLine(header.SecureTransferTimeout, " Secure transfer timeout"); + builder.AppendLine(header.ARM9Autoload, " ARM9 autoload"); + builder.AppendLine(header.ARM7Autoload, " ARM7 autoload"); + builder.AppendLine(header.SecureDisable, " Secure disable"); + builder.AppendLine(header.NTRRegionRomSize, " NTR region rom size"); + builder.AppendLine(header.HeaderSize, " Header size"); + builder.AppendLine(header.Reserved2, " Reserved 2"); + builder.AppendLine(header.NintendoLogo, " Nintendo logo"); + builder.AppendLine(header.NintendoLogoCRC, " Nintendo logo CRC"); + builder.AppendLine(header.HeaderCRC, " Header CRC"); + builder.AppendLine(header.DebuggerReserved, " Debugger reserved"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ExtendedDSiHeader header) +#else + private static void Print(StringBuilder builder, ExtendedDSiHeader? header) +#endif + { + builder.AppendLine(" Extended DSi Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No extended DSi header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.GlobalMBK15Settings, " Global MBK1..MBK5 settings"); + builder.AppendLine(header.LocalMBK68SettingsARM9, " Local MBK6..MBK8 settings for ARM9"); + builder.AppendLine(header.LocalMBK68SettingsARM7, " Local MBK6..MBK8 settings for ARM7"); + builder.AppendLine(header.GlobalMBK9Setting, " Global MBK9 setting"); + builder.AppendLine(header.RegionFlags, " Region flags"); + builder.AppendLine(header.AccessControl, " Access control"); + builder.AppendLine(header.ARM7SCFGEXTMask, " ARM7 SCFG EXT mask"); + builder.AppendLine(header.ReservedFlags, " Reserved/flags?"); + builder.AppendLine(header.ARM9iRomOffset, " ARM9i rom offset"); + builder.AppendLine(header.Reserved3, " Reserved 3"); + builder.AppendLine(header.ARM9iLoadAddress, " ARM9i load address"); + builder.AppendLine(header.ARM9iSize, " ARM9i size"); + builder.AppendLine(header.ARM7iRomOffset, " ARM7i rom offset"); + builder.AppendLine(header.Reserved4, " Reserved 4"); + builder.AppendLine(header.ARM7iLoadAddress, " ARM7i load address"); + builder.AppendLine(header.ARM7iSize, " ARM7i size"); + builder.AppendLine(header.DigestNTRRegionOffset, " Digest NTR region offset"); + builder.AppendLine(header.DigestNTRRegionLength, " Digest NTR region length"); + builder.AppendLine(header.DigestTWLRegionOffset, " Digest TWL region offset"); + builder.AppendLine(header.DigestTWLRegionLength, " Digest TWL region length"); + builder.AppendLine(header.DigestSectorHashtableRegionOffset, " Digest sector hashtable region offset"); + builder.AppendLine(header.DigestSectorHashtableRegionLength, " Digest sector hashtable region length"); + builder.AppendLine(header.DigestBlockHashtableRegionOffset, " Digest block hashtable region offset"); + builder.AppendLine(header.DigestBlockHashtableRegionLength, " Digest block hashtable region length"); + builder.AppendLine(header.DigestSectorSize, " Digest sector size"); + builder.AppendLine(header.DigestBlockSectorCount, " Digest block sector count"); + builder.AppendLine(header.IconBannerSize, " Icon banner size"); + builder.AppendLine(header.Unknown1, " Unknown 1"); + builder.AppendLine(header.ModcryptArea1Offset, " Modcrypt area 1 offset"); + builder.AppendLine(header.ModcryptArea1Size, " Modcrypt area 1 size"); + builder.AppendLine(header.ModcryptArea2Offset, " Modcrypt area 2 offset"); + builder.AppendLine(header.ModcryptArea2Size, " Modcrypt area 2 size"); + builder.AppendLine(header.TitleID, " Title ID"); + builder.AppendLine(header.DSiWarePublicSavSize, " DSiWare 'public.sav' size"); + builder.AppendLine(header.DSiWarePrivateSavSize, " DSiWare 'private.sav' size"); + builder.AppendLine(header.ReservedZero, " Reserved (zero)"); + builder.AppendLine(header.Unknown2, " Unknown 2"); + builder.AppendLine(header.ARM9WithSecureAreaSHA1HMACHash, " ARM9 (with encrypted secure area) SHA1 HMAC hash"); + builder.AppendLine(header.ARM7SHA1HMACHash, " ARM7 SHA1 HMAC hash"); + builder.AppendLine(header.DigestMasterSHA1HMACHash, " Digest master SHA1 HMAC hash"); + builder.AppendLine(header.BannerSHA1HMACHash, " Banner SHA1 HMAC hash"); + builder.AppendLine(header.ARM9iDecryptedSHA1HMACHash, " ARM9i (decrypted) SHA1 HMAC hash"); + builder.AppendLine(header.ARM7iDecryptedSHA1HMACHash, " ARM7i (decrypted) SHA1 HMAC hash"); + builder.AppendLine(header.Reserved5, " Reserved 5"); + builder.AppendLine(header.ARM9NoSecureAreaSHA1HMACHash, " ARM9 (without secure area) SHA1 HMAC hash"); + builder.AppendLine(header.Reserved6, " Reserved 6"); + builder.AppendLine(header.ReservedAndUnchecked, " Reserved and unchecked region"); + builder.AppendLine(header.RSASignature, " RSA signature"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, byte[] secureArea) +#else + private static void Print(StringBuilder builder, byte[]? secureArea) +#endif + { + builder.AppendLine(" Secure Area Information:"); + builder.AppendLine(" -------------------------"); + builder.AppendLine(secureArea, " Secure Area"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, NameTable table) +#else + private static void Print(StringBuilder builder, NameTable? table) +#endif + { + builder.AppendLine(" Name Table Information:"); + builder.AppendLine(" -------------------------"); + if (table == null) + { + builder.AppendLine(" No name table"); + builder.AppendLine(); + return; + } + builder.AppendLine(); + + Print(builder, table.FolderAllocationTable); + Print(builder, table.NameList); + } + +#if NET48 + private static void Print(StringBuilder builder, FolderAllocationTableEntry[] entries) +#else + private static void Print(StringBuilder builder, FolderAllocationTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Folder Allocation Table:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No folder allocation table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Folder Allocation Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.StartOffset, " Start offset"); + builder.AppendLine(entry.FirstFileIndex, " First file index"); + if (entry.Unknown == 0xF0) + { + builder.AppendLine(entry.ParentFolderIndex, " Parent folder index"); + builder.AppendLine(entry.Unknown, " Unknown"); + } + else + { + ushort totalEntries = (ushort)((entry.Unknown << 8) | entry.ParentFolderIndex); + builder.AppendLine(totalEntries, " Total entries"); + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, NameListEntry[] entries) +#else + private static void Print(StringBuilder builder, NameListEntry?[]? entries) +#endif + { + builder.AppendLine(" Name List:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No name list entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Name List Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Folder, " Folder"); + builder.AppendLine(entry.Name, " Name"); + if (entry.Folder) + builder.AppendLine(entry.Index, " Index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, FileAllocationTableEntry[] entries) +#else + private static void Print(StringBuilder builder, FileAllocationTableEntry?[]? entries) +#endif + { + builder.AppendLine(" File Allocation Table:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No file allocation table entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" File Allocation Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.StartOffset, " Start offset"); + builder.AppendLine(entry.EndOffset, " End offset"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/PAK.cs b/PAK.cs new file mode 100644 index 0000000..5764d53 --- /dev/null +++ b/PAK.cs @@ -0,0 +1,71 @@ +using System.Text; +using SabreTools.Models.PAK; + +namespace SabreTools.Printing +{ + public static class PAK + { + public static void Print(StringBuilder builder, File file) + { + builder.AppendLine("PAK Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, file.Header); + Print(builder, file.DirectoryItems); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.DirectoryOffset, " Directory offset"); + builder.AppendLine(header.DirectoryLength, " Directory length"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryItem[] entries) +#else + private static void Print(StringBuilder builder, DirectoryItem?[]? entries) +#endif + { + builder.AppendLine(" Directory Items Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Item {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.ItemName, " Item name"); + builder.AppendLine(entry.ItemOffset, " Item offset"); + builder.AppendLine(entry.ItemLength, " Item length"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/PFF.cs b/PFF.cs new file mode 100644 index 0000000..82c045a --- /dev/null +++ b/PFF.cs @@ -0,0 +1,99 @@ +using System.Text; +using SabreTools.Models.PFF; + +namespace SabreTools.Printing +{ + public static class PFF + { + public static void Print(StringBuilder builder, Archive archive) + { + builder.AppendLine("PFF Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, archive.Header); + Print(builder, archive.Segments); + Print(builder, archive.Footer); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.HeaderSize, " Header size"); + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.NumberOfFiles, " Number of files"); + builder.AppendLine(header.FileSegmentSize, " File segment size"); + builder.AppendLine(header.FileListOffset, " File list offset"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Segment[] segments) +#else + private static void Print(StringBuilder builder, Segment?[]? segments) +#endif + { + builder.AppendLine(" Segments Information:"); + builder.AppendLine(" -------------------------"); + if (segments == null || segments.Length == 0) + { + builder.AppendLine(" No segments"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < segments.Length; i++) + { + var segment = segments[i]; + builder.AppendLine($" Segment {i}"); + if (segment == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(segment.Deleted, " Deleted"); + builder.AppendLine(segment.FileLocation, " File location"); + builder.AppendLine(segment.FileSize, " File size"); + builder.AppendLine(segment.PackedDate, " Packed date"); + builder.AppendLine(segment.FileName, " File name"); + builder.AppendLine(segment.ModifiedDate, " Modified date"); + builder.AppendLine(segment.CompressionLevel, " Compression level"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Footer footer) +#else + private static void Print(StringBuilder builder, Footer? footer) +#endif + { + builder.AppendLine(" Footer Information:"); + builder.AppendLine(" -------------------------"); + if (footer == null) + { + builder.AppendLine(" No footer"); + builder.AppendLine(); + return; + } + + builder.AppendLine(footer.SystemIP, " System IP"); + builder.AppendLine(footer.Reserved, " Reserved"); + builder.AppendLine(footer.KingTag, " King tag"); + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/PlayJAudioFile.cs b/PlayJAudioFile.cs new file mode 100644 index 0000000..413a243 --- /dev/null +++ b/PlayJAudioFile.cs @@ -0,0 +1,200 @@ +using System.Text; +using SabreTools.Models.PlayJ; + +namespace SabreTools.Printing +{ + public static class PlayJAudioFile + { + public static void Print(StringBuilder builder, AudioFile audio) + { + builder.AppendLine("PlayJ Audio File Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, audio.Header); + Print(builder, audio.UnknownBlock1); + + if (audio.Header?.Version == 0x00000000) + { + Print(builder, audio.UnknownValue2); + Print(builder, audio.UnknownBlock3); + } + else if (audio.Header?.Version == 0x0000000A) + { + Print(builder, audio.DataFilesCount, audio.DataFiles); + } + } + +#if NET48 + private static void Print(StringBuilder builder, AudioHeader header) +#else + private static void Print(StringBuilder builder, AudioHeader? header) +#endif + { + builder.AppendLine(" Audio Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No audio header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.Version, " Version"); + if (header.Version == 0x00000000 && header is AudioHeaderV1 headerV1) + { + builder.AppendLine(headerV1.TrackID, " Track ID"); + builder.AppendLine(headerV1.UnknownOffset1, " Unknown offset 1"); + builder.AppendLine(headerV1.UnknownOffset2, " Unknown offset 2"); + builder.AppendLine(headerV1.UnknownOffset3, " Unknown offset 3"); + builder.AppendLine(headerV1.Unknown1, " Unknown 1"); + builder.AppendLine(headerV1.Unknown2, " Unknown 2"); + builder.AppendLine(headerV1.Year, " Year"); + builder.AppendLine(headerV1.TrackNumber, " Track number"); + builder.AppendLine($" Subgenre: {headerV1.Subgenre} (0x{headerV1.Subgenre:X})"); + builder.AppendLine(headerV1.Duration, " Duration in seconds"); + } + else if (header.Version == 0x0000000A && header is AudioHeaderV2 headerV2) + { + builder.AppendLine(headerV2.Unknown1, " Unknown 1"); + builder.AppendLine(headerV2.Unknown2, " Unknown 2"); + builder.AppendLine(headerV2.Unknown3, " Unknown 3"); + builder.AppendLine(headerV2.Unknown4, " Unknown 4"); + builder.AppendLine(headerV2.Unknown5, " Unknown 5"); + builder.AppendLine(headerV2.Unknown6, " Unknown 6"); + builder.AppendLine(headerV2.UnknownOffset1, " Unknown Offset 1"); + builder.AppendLine(headerV2.Unknown7, " Unknown 7"); + builder.AppendLine(headerV2.Unknown8, " Unknown 8"); + builder.AppendLine(headerV2.Unknown9, " Unknown 9"); + builder.AppendLine(headerV2.UnknownOffset2, " Unknown Offset 2"); + builder.AppendLine(headerV2.Unknown10, " Unknown 10"); + builder.AppendLine(headerV2.Unknown11, " Unknown 11"); + builder.AppendLine(headerV2.Unknown12, " Unknown 12"); + builder.AppendLine(headerV2.Unknown13, " Unknown 13"); + builder.AppendLine(headerV2.Unknown14, " Unknown 14"); + builder.AppendLine(headerV2.Unknown15, " Unknown 15"); + builder.AppendLine(headerV2.Unknown16, " Unknown 16"); + builder.AppendLine(headerV2.Unknown17, " Unknown 17"); + builder.AppendLine(headerV2.TrackID, " Track ID"); + builder.AppendLine(headerV2.Year, " Year"); + builder.AppendLine(headerV2.TrackNumber, " Track number"); + builder.AppendLine(headerV2.Unknown18, " Unknown 18"); + } + else + { + builder.AppendLine(" Unrecognized version, not parsed..."); + } + + builder.AppendLine(header.TrackLength, " Track length"); + builder.AppendLine(header.Track, " Track"); + builder.AppendLine(header.ArtistLength, " Artist length"); + builder.AppendLine(header.Artist, " Artist"); + builder.AppendLine(header.AlbumLength, " Album length"); + builder.AppendLine(header.Album, " Album"); + builder.AppendLine(header.WriterLength, " Writer length"); + builder.AppendLine(header.Writer, " Writer"); + builder.AppendLine(header.PublisherLength, " Publisher length"); + builder.AppendLine(header.Publisher, " Publisher"); + builder.AppendLine(header.LabelLength, " Label length"); + builder.AppendLine(header.Label, " Label"); + builder.AppendLine(header.CommentsLength, " Comments length"); + builder.AppendLine(header.Comments, " Comments"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, UnknownBlock1 block) +#else + private static void Print(StringBuilder builder, UnknownBlock1? block) +#endif + { + builder.AppendLine(" Unknown Block 1 Information:"); + builder.AppendLine(" -------------------------"); + if (block == null) + { + builder.AppendLine(" No unknown block 1r"); + builder.AppendLine(); + return; + } + + builder.AppendLine(block.Length, " Length"); + builder.AppendLine(block.Data, " Data"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, uint value) +#else + private static void Print(StringBuilder builder, uint? value) +#endif + { + builder.AppendLine(" Unknown Value 2 Information:"); + builder.AppendLine(" -------------------------"); +#if NET6_0_OR_GREATER + if (value == null) + { + builder.AppendLine(" No unknown block 1r"); + builder.AppendLine(); + return; + } +#endif + + builder.AppendLine(value, " Value"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, UnknownBlock3 block) +#else + private static void Print(StringBuilder builder, UnknownBlock3? block) +#endif + { + builder.AppendLine(" Unknown Block 3 Information:"); + builder.AppendLine(" -------------------------"); + if (block == null) + { + builder.AppendLine(" No unknown block 1r"); + builder.AppendLine(); + return; + } + + builder.AppendLine(block.Data, " Data"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, uint count, DataFile[] entries) +#else + private static void Print(StringBuilder builder, uint count, DataFile?[]? entries) +#endif + { + builder.AppendLine(" Data Files Information:"); + builder.AppendLine(" -------------------------"); + builder.AppendLine(count, " Data files count"); + if (count == 0 || entries == null || entries.Length == 0) + { + builder.AppendLine(" No data files"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Data File {i}:"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.FileNameLength, " File name length"); + builder.AppendLine(entry.FileName, " File name"); + builder.AppendLine(entry.DataLength, " Data length"); + builder.AppendLine(entry.Data, " Data"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/PortableExecutable.cs b/PortableExecutable.cs new file mode 100644 index 0000000..495b7e5 --- /dev/null +++ b/PortableExecutable.cs @@ -0,0 +1,2038 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; +using SabreTools.ASN1; +using SabreTools.IO; +using SabreTools.Models.PortableExecutable; +using static SabreTools.Serialization.Extensions; + +namespace SabreTools.Printing +{ + public static class PortableExecutable + { + public static void Print(StringBuilder builder, Executable executable) + { + builder.AppendLine("Portable Executable Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + // Stub + Print(builder, executable.Stub?.Header); + + // Header + Print(builder, executable.Signature, executable.COFFFileHeader); + Print(builder, executable.OptionalHeader, executable.SectionTable); + + // Tables + Print(builder, executable.SectionTable); + Print(builder, executable.COFFSymbolTable); + Print(builder, executable.COFFStringTable); + Print(builder, executable.AttributeCertificateTable); + Print(builder, executable.DelayLoadDirectoryTable); + + // Named Sections + Print(builder, executable.BaseRelocationTable, executable.SectionTable); + Print(builder, executable.DebugTable); + Print(builder, executable.ExportTable); + Print(builder, executable.ImportTable, executable.SectionTable); + Print(builder, executable.ResourceDirectoryTable); + } + +#if NET48 + private static void Print(StringBuilder builder, SabreTools.Models.MSDOS.ExecutableHeader header) +#else + private static void Print(StringBuilder builder, SabreTools.Models.MSDOS.ExecutableHeader? header) +#endif + { + builder.AppendLine(" MS-DOS Stub Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No MS-DOS stub header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Magic, " Magic number"); + builder.AppendLine(header.LastPageBytes, " Last page bytes"); + builder.AppendLine(header.Pages, " Pages"); + builder.AppendLine(header.RelocationItems, " Relocation items"); + builder.AppendLine(header.HeaderParagraphSize, " Header paragraph size"); + builder.AppendLine(header.MinimumExtraParagraphs, " Minimum extra paragraphs"); + builder.AppendLine(header.MaximumExtraParagraphs, " Maximum extra paragraphs"); + builder.AppendLine(header.InitialSSValue, " Initial SS value"); + builder.AppendLine(header.InitialSPValue, " Initial SP value"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine(header.InitialIPValue, " Initial IP value"); + builder.AppendLine(header.InitialCSValue, " Initial CS value"); + builder.AppendLine(header.RelocationTableAddr, " Relocation table address"); + builder.AppendLine(header.OverlayNumber, " Overlay number"); + builder.AppendLine(); + + builder.AppendLine(" MS-DOS Stub Extended Header Information:"); + builder.AppendLine(" -------------------------"); + builder.AppendLine(header.Reserved1, " Reserved words"); + builder.AppendLine(header.OEMIdentifier, " OEM identifier"); + builder.AppendLine(header.OEMInformation, " OEM information"); + builder.AppendLine(header.Reserved2, " Reserved words"); + builder.AppendLine(header.NewExeHeaderAddr, " New EXE header address"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, string signature, COFFFileHeader header) +#else + private static void Print(StringBuilder builder, string? signature, COFFFileHeader? header) +#endif + { + builder.AppendLine(" COFF File Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No COFF file header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(signature, " Signature"); + builder.AppendLine($" Machine: {header.Machine} (0x{header.Machine:X})"); + builder.AppendLine(header.NumberOfSections, " Number of sections"); + builder.AppendLine(header.TimeDateStamp, " Time/Date stamp"); + builder.AppendLine(header.PointerToSymbolTable, " Pointer to symbol table"); + builder.AppendLine(header.NumberOfSymbols, " Number of symbols"); + builder.AppendLine(header.SizeOfOptionalHeader, " Size of optional header"); + builder.AppendLine($" Characteristics: {header.Characteristics} (0x{header.Characteristics:X})"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, OptionalHeader header, SectionHeader[] table) +#else + private static void Print(StringBuilder builder, OptionalHeader? header, SectionHeader?[]? table) +#endif + { + builder.AppendLine(" Optional Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No optional header"); + builder.AppendLine(); + return; + } + + builder.AppendLine($" Magic: {header.Magic} (0x{header.Magic:X})"); + builder.AppendLine(header.MajorLinkerVersion, " Major linker version"); + builder.AppendLine(header.MinorLinkerVersion, " Minor linker version"); + builder.AppendLine(header.SizeOfCode, " Size of code section"); + builder.AppendLine(header.SizeOfInitializedData, " Size of initialized data"); + builder.AppendLine(header.SizeOfUninitializedData, " Size of uninitialized data"); + builder.AppendLine(header.AddressOfEntryPoint, " Address of entry point"); + builder.AppendLine(header.BaseOfCode, " Base of code"); + if (header.Magic == OptionalHeaderMagicNumber.PE32) + builder.AppendLine(header.BaseOfData, " Base of data"); + + if (header.Magic == OptionalHeaderMagicNumber.PE32) + builder.AppendLine(header.ImageBase_PE32, " Image base"); + else + builder.AppendLine(header.ImageBase_PE32Plus, " Image base"); + builder.AppendLine(header.SectionAlignment, " Section alignment"); + builder.AppendLine(header.FileAlignment, " File alignment"); + builder.AppendLine(header.MajorOperatingSystemVersion, " Major operating system version"); + builder.AppendLine(header.MinorOperatingSystemVersion, " Minor operating system version"); + builder.AppendLine(header.MajorImageVersion, " Major image version"); + builder.AppendLine(header.MinorImageVersion, " Minor image version"); + builder.AppendLine(header.MajorSubsystemVersion, " Major subsystem version"); + builder.AppendLine(header.MinorSubsystemVersion, " Minor subsystem version"); + builder.AppendLine(header.Win32VersionValue, " Win32 version value"); + builder.AppendLine(header.SizeOfImage, " Size of image"); + builder.AppendLine(header.SizeOfHeaders, " Size of headers"); + builder.AppendLine(header.CheckSum, " Checksum"); + builder.AppendLine($" Subsystem: {header.Subsystem} (0x{header.Subsystem:X})"); + builder.AppendLine($" DLL characteristics: {header.DllCharacteristics} (0x{header.DllCharacteristics:X})"); + if (header.Magic == OptionalHeaderMagicNumber.PE32) + { + builder.AppendLine(header.SizeOfStackReserve_PE32, " Size of stack reserve"); + builder.AppendLine(header.SizeOfStackCommit_PE32, " Size of stack commit"); + builder.AppendLine(header.SizeOfHeapReserve_PE32, " Size of heap reserve"); + builder.AppendLine(header.SizeOfHeapCommit_PE32, " Size of heap commit"); + } + else + { + builder.AppendLine(header.SizeOfStackReserve_PE32Plus, " Size of stack reserve"); + builder.AppendLine(header.SizeOfStackCommit_PE32Plus, " Size of stack commit"); + builder.AppendLine(header.SizeOfHeapReserve_PE32Plus, " Size of heap reserve"); + builder.AppendLine(header.SizeOfHeapCommit_PE32Plus, " Size of heap commit"); + } + builder.AppendLine(header.LoaderFlags, " Loader flags"); + builder.AppendLine(header.NumberOfRvaAndSizes, " Number of data-directory entries"); + + if (header.ExportTable != null) + { + builder.AppendLine(" Export Table (1)"); + builder.AppendLine(header.ExportTable.VirtualAddress, " Virtual address"); + builder.AppendLine(header.ExportTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.ExportTable.Size, " Size"); + } + if (header.ImportTable != null) + { + builder.AppendLine(" Import Table (2)"); + builder.AppendLine(header.ImportTable.VirtualAddress, " Virtual address"); + builder.AppendLine(header.ImportTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.ImportTable.Size, " Size"); + } + if (header.ResourceTable != null) + { + builder.AppendLine(" Resource Table (3)"); + builder.AppendLine(header.ResourceTable.VirtualAddress, " Virtual address"); + builder.AppendLine(header.ResourceTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.ResourceTable.Size, " Size"); + } + if (header.ExceptionTable != null) + { + builder.AppendLine(" Exception Table (4)"); + builder.AppendLine(header.ExceptionTable.VirtualAddress, " Virtual address"); + builder.AppendLine(header.ExceptionTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.ExceptionTable.Size, " Size"); + } + if (header.CertificateTable != null) + { + builder.AppendLine(" Certificate Table (5)"); + builder.AppendLine(header.CertificateTable.VirtualAddress, " Virtual address"); + builder.AppendLine(header.CertificateTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.CertificateTable.Size, " Size"); + } + if (header.BaseRelocationTable != null) + { + builder.AppendLine(" Base Relocation Table (6)"); + builder.AppendLine(header.BaseRelocationTable.VirtualAddress, " Virtual address"); + builder.AppendLine(header.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.BaseRelocationTable.Size, " Size"); + } + if (header.Debug != null) + { + builder.AppendLine(" Debug Table (7)"); + builder.AppendLine(header.Debug.VirtualAddress, " Virtual address"); + builder.AppendLine(header.Debug.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.Debug.Size, " Size"); + } + if (header.NumberOfRvaAndSizes >= 8) + { + builder.AppendLine(" Architecture Table (8)"); + builder.AppendLine(" Virtual address: 0 (0x00000000)"); + builder.AppendLine(" Physical address: 0 (0x00000000)"); + builder.AppendLine(" Size: 0 (0x00000000)"); + } + if (header.GlobalPtr != null) + { + builder.AppendLine(" Global Pointer Register (9)"); + builder.AppendLine(header.GlobalPtr.VirtualAddress, " Virtual address"); + builder.AppendLine(header.GlobalPtr.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.GlobalPtr.Size, " Size"); + } + if (header.ThreadLocalStorageTable != null) + { + builder.AppendLine(" Thread Local Storage (TLS) Table (10)"); + builder.AppendLine(header.ThreadLocalStorageTable.VirtualAddress, " Virtual address"); + builder.AppendLine(header.ThreadLocalStorageTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.ThreadLocalStorageTable.Size, " Size"); + } + if (header.LoadConfigTable != null) + { + builder.AppendLine(" Load Config Table (11)"); + builder.AppendLine(header.LoadConfigTable.VirtualAddress, " Virtual address"); + builder.AppendLine(header.LoadConfigTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.LoadConfigTable.Size, " Size"); + } + if (header.BoundImport != null) + { + builder.AppendLine(" Bound Import Table (12)"); + builder.AppendLine(header.BoundImport.VirtualAddress, " Virtual address"); + builder.AppendLine(header.BoundImport.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.BoundImport.Size, " Size"); + } + if (header.ImportAddressTable != null) + { + builder.AppendLine(" Import Address Table (13)"); + builder.AppendLine(header.ImportAddressTable.VirtualAddress, " Virtual address"); + builder.AppendLine(header.ImportAddressTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.ImportAddressTable.Size, " Size"); + } + if (header.DelayImportDescriptor != null) + { + builder.AppendLine(" Delay Import Descriptior (14)"); + builder.AppendLine(header.DelayImportDescriptor.VirtualAddress, " Virtual address"); + builder.AppendLine(header.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.DelayImportDescriptor.Size, " Size"); + } + if (header.CLRRuntimeHeader != null) + { + builder.AppendLine(" CLR Runtime Header (15)"); + builder.AppendLine(header.CLRRuntimeHeader.VirtualAddress, " Virtual address"); + builder.AppendLine(header.CLRRuntimeHeader.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(header.CLRRuntimeHeader.Size, " Size"); + } + if (header.NumberOfRvaAndSizes >= 16) + { + builder.AppendLine(" Reserved (16)"); + builder.AppendLine(" Virtual address: 0 (0x00000000)"); + builder.AppendLine(" Physical address: 0 (0x00000000)"); + builder.AppendLine(" Size: 0 (0x00000000)"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, SectionHeader[] table) +#else + private static void Print(StringBuilder builder, SectionHeader?[]? table) +#endif + { + builder.AppendLine(" Section Table Information:"); + builder.AppendLine(" -------------------------"); + if (table == null || table.Length == 0) + { + builder.AppendLine(" No section table items"); + builder.AppendLine(); + return; + } + +#if NET48 + for (int i = 0; i < table.Length; i++) +#else + for (int i = 0; i < table!.Length; i++) +#endif + { + var entry = table[i]; + builder.AppendLine($" Section Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Name, " Name"); + builder.AppendLine(entry.VirtualSize, " Virtual size"); + builder.AppendLine(entry.VirtualAddress, " Virtual address"); + builder.AppendLine(entry.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address"); + builder.AppendLine(entry.SizeOfRawData, " Size of raw data"); + builder.AppendLine(entry.PointerToRawData, " Pointer to raw data"); + builder.AppendLine(entry.PointerToRelocations, " Pointer to relocations"); + builder.AppendLine(entry.PointerToLinenumbers, " Pointer to linenumbers"); + builder.AppendLine(entry.NumberOfRelocations, " Number of relocations"); + builder.AppendLine(entry.NumberOfLinenumbers, " Number of linenumbers"); + builder.AppendLine($" Characteristics: {entry.Characteristics} (0x{entry.Characteristics:X})"); + // TODO: Add COFFRelocations + // TODO: Add COFFLineNumbers + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, COFFSymbolTableEntry[] symbolTable) +#else + private static void Print(StringBuilder builder, COFFSymbolTableEntry?[]? symbolTable) +#endif + { + builder.AppendLine(" COFF Symbol Table Information:"); + builder.AppendLine(" -------------------------"); + if (symbolTable == null || symbolTable.Length == 0) + { + builder.AppendLine(" No COFF symbol table items"); + builder.AppendLine(); + return; + } + + int auxSymbolsRemaining = 0; + int currentSymbolType = 0; + + for (int i = 0; i < symbolTable.Length; i++) + { + var entry = symbolTable[i]; + builder.AppendLine($" COFF Symbol Table Entry {i} (Subtype {currentSymbolType})"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + if (currentSymbolType == 0) + { + if (entry.ShortName != null) + { + builder.AppendLine(entry.ShortName, " Short name"); + } + else + { + builder.AppendLine(entry.Zeroes, " Zeroes"); + builder.AppendLine(entry.Offset, " Offset"); + } + builder.AppendLine(entry.Value, " Value"); + builder.AppendLine(entry.SectionNumber, " Section number"); + builder.AppendLine($" Symbol type: {entry.SymbolType} (0x{entry.SymbolType:X})"); + builder.AppendLine($" Storage class: {entry.StorageClass} (0x{entry.StorageClass:X})"); + builder.AppendLine(entry.NumberOfAuxSymbols, " Number of aux symbols"); + + auxSymbolsRemaining = entry.NumberOfAuxSymbols; + if (auxSymbolsRemaining == 0) + continue; + + if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL + && entry.SymbolType == SymbolType.IMAGE_SYM_TYPE_FUNC + && entry.SectionNumber > 0) + { + currentSymbolType = 1; + } + else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FUNCTION + && entry.ShortName != null + && ((entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x62 && entry.ShortName[2] == 0x66) // .bf + || (entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x65 && entry.ShortName[2] == 0x66))) // .ef + { + currentSymbolType = 2; + } + else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL + && entry.SectionNumber == (ushort)SectionNumber.IMAGE_SYM_UNDEFINED + && entry.Value == 0) + { + currentSymbolType = 3; + } + else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FILE) + { + // TODO: Symbol name should be ".file" + currentSymbolType = 4; + } + else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_STATIC) + { + // TODO: Should have the name of a section (like ".text") + currentSymbolType = 5; + } + else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_CLR_TOKEN) + { + currentSymbolType = 6; + } + } + else if (currentSymbolType == 1) + { + builder.AppendLine(entry.AuxFormat1TagIndex, " Tag index"); + builder.AppendLine(entry.AuxFormat1TotalSize, " Total size"); + builder.AppendLine(entry.AuxFormat1PointerToLinenumber, " Pointer to linenumber"); + builder.AppendLine(entry.AuxFormat1PointerToNextFunction, " Pointer to next function"); + builder.AppendLine(entry.AuxFormat1Unused, " Unused"); + auxSymbolsRemaining--; + } + else if (currentSymbolType == 2) + { + builder.AppendLine(entry.AuxFormat2Unused1, " Unused"); + builder.AppendLine(entry.AuxFormat2Linenumber, " Linenumber"); + builder.AppendLine(entry.AuxFormat2Unused2, " Unused"); + builder.AppendLine(entry.AuxFormat2PointerToNextFunction, " Pointer to next function"); + builder.AppendLine(entry.AuxFormat2Unused3, " Unused"); + auxSymbolsRemaining--; + } + else if (currentSymbolType == 3) + { + builder.AppendLine(entry.AuxFormat3TagIndex, " Tag index"); + builder.AppendLine(entry.AuxFormat3Characteristics, " Characteristics"); + builder.AppendLine(entry.AuxFormat3Unused, " Unused"); + auxSymbolsRemaining--; + } + else if (currentSymbolType == 4) + { + builder.AppendLine(entry.AuxFormat4FileName, " File name"); + auxSymbolsRemaining--; + } + else if (currentSymbolType == 5) + { + builder.AppendLine(entry.AuxFormat5Length, " Length"); + builder.AppendLine(entry.AuxFormat5NumberOfRelocations, " Number of relocations"); + builder.AppendLine(entry.AuxFormat5NumberOfLinenumbers, " Number of linenumbers"); + builder.AppendLine(entry.AuxFormat5CheckSum, " Checksum"); + builder.AppendLine(entry.AuxFormat5Number, " Number"); + builder.AppendLine(entry.AuxFormat5Selection, " Selection"); + builder.AppendLine(entry.AuxFormat5Unused, " Unused"); + auxSymbolsRemaining--; + } + else if (currentSymbolType == 6) + { + builder.AppendLine(entry.AuxFormat6AuxType, " Aux type"); + builder.AppendLine(entry.AuxFormat6Reserved1, " Reserved"); + builder.AppendLine(entry.AuxFormat6SymbolTableIndex, " Symbol table index"); + builder.AppendLine(entry.AuxFormat6Reserved2, " Reserved"); + auxSymbolsRemaining--; + } + + // If we hit the last aux symbol, go back to normal format + if (auxSymbolsRemaining == 0) + currentSymbolType = 0; + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, COFFStringTable stringTable) +#else + private static void Print(StringBuilder builder, COFFStringTable? stringTable) +#endif + { + builder.AppendLine(" COFF String Table Information:"); + builder.AppendLine(" -------------------------"); + if (stringTable?.Strings == null || stringTable.Strings.Length == 0) + { + builder.AppendLine(" No COFF string table items"); + builder.AppendLine(); + return; + } + + builder.AppendLine(stringTable.TotalSize, " Total size"); + for (int i = 0; i < stringTable.Strings.Length; i++) + { +#if NET48 + string entry = stringTable.Strings[i]; +#else + string? entry = stringTable.Strings[i]; +#endif + builder.AppendLine($" COFF String Table Entry {i})"); + builder.AppendLine(entry, " Value"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, AttributeCertificateTableEntry[] entries) +#else + private static void Print(StringBuilder builder, AttributeCertificateTableEntry?[]? entries) +#endif + { + builder.AppendLine(" Attribute Certificate Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No attribute certificate table items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Attribute Certificate Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Length, " Length"); + builder.AppendLine($" Revision: {entry.Revision} (0x{entry.Revision:X})"); + builder.AppendLine($" Certificate type: {entry.CertificateType} (0x{entry.CertificateType:X})"); + builder.AppendLine(); + if (entry.CertificateType == WindowsCertificateType.WIN_CERT_TYPE_PKCS_SIGNED_DATA) + { + builder.AppendLine(" Certificate Data [Formatted]"); + builder.AppendLine(" -------------------------"); + if (entry.Certificate == null) + { + builder.AppendLine(" INVALID DATA FOUND"); + } + else + { + var topLevelValues = AbstractSyntaxNotationOne.Parse(entry.Certificate, 0); + if (topLevelValues == null) + { + builder.AppendLine(" INVALID DATA FOUND"); + builder.AppendLine(entry.Certificate, " Raw data"); + } + else + { + foreach (TypeLengthValue tlv in topLevelValues) + { + string tlvString = tlv.Format(paddingLevel: 4); + builder.AppendLine(tlvString); + } + } + } + } + else + { + builder.AppendLine(" Certificate Data [Binary]"); + builder.AppendLine(" -------------------------"); + try + { + builder.AppendLine(entry.Certificate, " Raw data"); + } + catch + { + builder.AppendLine(" [DATA TOO LARGE TO FORMAT]"); + } + } + + builder.AppendLine(); + } + } + +#if NET48 + private static void Print(StringBuilder builder, DelayLoadDirectoryTable table) +#else + private static void Print(StringBuilder builder, DelayLoadDirectoryTable? table) +#endif + { + builder.AppendLine(" Delay-Load Directory Table Information:"); + builder.AppendLine(" -------------------------"); + if (table == null) + { + builder.AppendLine(" No delay-load directory table items"); + builder.AppendLine(); + return; + } + + builder.AppendLine(table.Attributes, " Attributes"); + builder.AppendLine(table.Name, " Name RVA"); + builder.AppendLine(table.ModuleHandle, " Module handle"); + builder.AppendLine(table.DelayImportAddressTable, " Delay import address table RVA"); + builder.AppendLine(table.DelayImportNameTable, " Delay import name table RVA"); + builder.AppendLine(table.BoundDelayImportTable, " Bound delay import table RVA"); + builder.AppendLine(table.UnloadDelayImportTable, " Unload delay import table RVA"); + builder.AppendLine(table.TimeStamp, " Timestamp"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, BaseRelocationBlock[] entries, SectionHeader[] table) +#else + private static void Print(StringBuilder builder, BaseRelocationBlock?[]? entries, SectionHeader?[]? table) +#endif + { + builder.AppendLine(" Base Relocation Table Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No base relocation table items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var baseRelocationTableEntry = entries[i]; + builder.AppendLine($" Base Relocation Table Entry {i}"); + if (baseRelocationTableEntry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(baseRelocationTableEntry.PageRVA, " Page RVA"); + builder.AppendLine(baseRelocationTableEntry.PageRVA.ConvertVirtualAddress(table ?? Array.Empty()), " Page physical address"); + builder.AppendLine(baseRelocationTableEntry.BlockSize, " Block size"); + + builder.AppendLine($" Base Relocation Table {i} Type and Offset Information:"); + builder.AppendLine(" -------------------------"); + if (baseRelocationTableEntry.TypeOffsetFieldEntries == null || baseRelocationTableEntry.TypeOffsetFieldEntries.Length == 0) + { + builder.AppendLine(" No base relocation table type and offset entries"); + continue; + } + + for (int j = 0; j < baseRelocationTableEntry.TypeOffsetFieldEntries.Length; j++) + { + var typeOffsetFieldEntry = baseRelocationTableEntry.TypeOffsetFieldEntries[j]; + builder.AppendLine($" Type and Offset Entry {j}"); +#if NET6_0_OR_GREATER + if (typeOffsetFieldEntry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } +#endif + builder.AppendLine($" Type: {typeOffsetFieldEntry.BaseRelocationType} (0x{typeOffsetFieldEntry.BaseRelocationType:X})"); + builder.AppendLine(typeOffsetFieldEntry.Offset, " Offset"); + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DebugTable table) +#else + private static void Print(StringBuilder builder, DebugTable? table) +#endif + { + builder.AppendLine(" Debug Table Information:"); + builder.AppendLine(" -------------------------"); + if (table?.DebugDirectoryTable == null || table.DebugDirectoryTable.Length == 0) + { + builder.AppendLine(" No debug table items"); + builder.AppendLine(); + return; + } + + // TODO: If more sections added, model this after the Export Table + for (int i = 0; i < table.DebugDirectoryTable.Length; i++) + { + var entry = table.DebugDirectoryTable[i]; + builder.AppendLine($" Debug Directory Table Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Characteristics, " Characteristics"); + builder.AppendLine(entry.TimeDateStamp, " Time/Date stamp"); + builder.AppendLine(entry.MajorVersion, " Major version"); + builder.AppendLine(entry.MinorVersion, " Minor version"); + builder.AppendLine($" Debug type: {entry.DebugType} (0x{entry.DebugType:X})"); + builder.AppendLine(entry.SizeOfData, " Size of data"); + builder.AppendLine(entry.AddressOfRawData, " Address of raw data"); + builder.AppendLine(entry.PointerToRawData, " Pointer to raw data"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ExportTable table) +#else + private static void Print(StringBuilder builder, ExportTable? table) +#endif + { + builder.AppendLine(" Export Table Information:"); + builder.AppendLine(" -------------------------"); + if (table == null) + { + builder.AppendLine(" No export table"); + builder.AppendLine(); + return; + } + + builder.AppendLine(" Export Directory Table Information:"); + builder.AppendLine(" -------------------------"); + if (table.ExportDirectoryTable == null) + { + builder.AppendLine(" No export directory table"); + } + else + { + builder.AppendLine(table.ExportDirectoryTable.ExportFlags, " Export flags"); + builder.AppendLine(table.ExportDirectoryTable.TimeDateStamp, " Time/Date stamp"); + builder.AppendLine(table.ExportDirectoryTable.MajorVersion, " Major version"); + builder.AppendLine(table.ExportDirectoryTable.MinorVersion, " Minor version"); + builder.AppendLine(table.ExportDirectoryTable.NameRVA, " Name RVA"); + builder.AppendLine(table.ExportDirectoryTable.Name, " Name"); + builder.AppendLine(table.ExportDirectoryTable.OrdinalBase, " Ordinal base"); + builder.AppendLine(table.ExportDirectoryTable.AddressTableEntries, " Address table entries"); + builder.AppendLine(table.ExportDirectoryTable.NumberOfNamePointers, " Number of name pointers"); + builder.AppendLine(table.ExportDirectoryTable.ExportAddressTableRVA, " Export address table RVA"); + builder.AppendLine(table.ExportDirectoryTable.NamePointerRVA, " Name pointer table RVA"); + builder.AppendLine(table.ExportDirectoryTable.OrdinalTableRVA, " Ordinal table RVA"); + } + builder.AppendLine(); + + builder.AppendLine(" Export Address Table Information:"); + builder.AppendLine(" -------------------------"); + if (table.ExportAddressTable == null || table.ExportAddressTable.Length == 0) + { + builder.AppendLine(" No export address table items"); + } + else + { + for (int i = 0; i < table.ExportAddressTable.Length; i++) + { + var exportAddressTableEntry = table.ExportAddressTable[i]; + builder.AppendLine($" Export Address Table Entry {i}"); + if (exportAddressTableEntry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(exportAddressTableEntry.ExportRVA, " Export RVA / Forwarder RVA"); + } + } + builder.AppendLine(); + + builder.AppendLine(" Name Pointer Table Information:"); + builder.AppendLine(" -------------------------"); + if (table.NamePointerTable?.Pointers == null || table.NamePointerTable.Pointers.Length == 0) + { + builder.AppendLine(" No name pointer table items"); + } + else + { + for (int i = 0; i < table.NamePointerTable.Pointers.Length; i++) + { + var namePointerTableEntry = table.NamePointerTable.Pointers[i]; + builder.AppendLine($" Name Pointer Table Entry {i}"); + builder.AppendLine(namePointerTableEntry, " Pointer"); + } + } + builder.AppendLine(); + + builder.AppendLine(" Ordinal Table Information:"); + builder.AppendLine(" -------------------------"); + if (table.OrdinalTable?.Indexes == null || table.OrdinalTable.Indexes.Length == 0) + { + builder.AppendLine(" No ordinal table items"); + } + else + { + for (int i = 0; i < table.OrdinalTable.Indexes.Length; i++) + { + var ordinalTableEntry = table.OrdinalTable.Indexes[i]; + builder.AppendLine($" Ordinal Table Entry {i}"); + builder.AppendLine(ordinalTableEntry, " Index"); + } + } + builder.AppendLine(); + + builder.AppendLine(" Export Name Table Information:"); + builder.AppendLine(" -------------------------"); + if (table.ExportNameTable?.Strings == null || table.ExportNameTable.Strings.Length == 0) + { + builder.AppendLine(" No export name table items"); + } + else + { + for (int i = 0; i < table.ExportNameTable.Strings.Length; i++) + { + var exportNameTableEntry = table.ExportNameTable.Strings[i]; + builder.AppendLine($" Export Name Table Entry {i}"); + builder.AppendLine(exportNameTableEntry, " String"); + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ImportTable table, SectionHeader[] sectionTable) +#else + private static void Print(StringBuilder builder, ImportTable? table, SectionHeader?[]? sectionTable) +#endif + { + builder.AppendLine(" Import Table Information:"); + builder.AppendLine(" -------------------------"); + if (table == null) + { + builder.AppendLine(" No import table"); + builder.AppendLine(); + return; + } + + builder.AppendLine(); + builder.AppendLine(" Import Directory Table Information:"); + builder.AppendLine(" -------------------------"); + if (table.ImportDirectoryTable == null || table.ImportDirectoryTable.Length == 0) + { + builder.AppendLine(" No import directory table items"); + } + else + { + for (int i = 0; i < table.ImportDirectoryTable.Length; i++) + { + var importDirectoryTableEntry = table.ImportDirectoryTable[i]; + builder.AppendLine($" Import Directory Table Entry {i}"); + if (importDirectoryTableEntry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(importDirectoryTableEntry.ImportLookupTableRVA, " Import lookup table RVA"); + builder.AppendLine(importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(sectionTable ?? Array.Empty()), " Import lookup table Physical Address"); + builder.AppendLine(importDirectoryTableEntry.TimeDateStamp, " Time/Date stamp"); + builder.AppendLine(importDirectoryTableEntry.ForwarderChain, " Forwarder chain"); + builder.AppendLine(importDirectoryTableEntry.NameRVA, " Name RVA"); + builder.AppendLine(importDirectoryTableEntry.Name, " Name"); + builder.AppendLine(importDirectoryTableEntry.ImportAddressTableRVA, " Import address table RVA"); + builder.AppendLine(importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(sectionTable ?? Array.Empty()), " Import address table Physical Address"); + } + } + builder.AppendLine(); + + builder.AppendLine(" Import Lookup Tables Information:"); + builder.AppendLine(" -------------------------"); + if (table.ImportLookupTables == null || table.ImportLookupTables.Count == 0) + { + builder.AppendLine(" No import lookup tables"); + } + else + { + foreach (var kvp in table.ImportLookupTables) + { + int index = kvp.Key; + var importLookupTable = kvp.Value; + + builder.AppendLine(); + builder.AppendLine($" Import Lookup Table {index} Information:"); + builder.AppendLine(" -------------------------"); + if (importLookupTable == null || importLookupTable.Length == 0) + { + builder.AppendLine(" No import lookup table items"); + continue; + } + + for (int i = 0; i < importLookupTable.Length; i++) + { + var importLookupTableEntry = importLookupTable[i]; + builder.AppendLine($" Import Lookup Table {index} Entry {i}"); + if (importLookupTableEntry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(importLookupTableEntry.OrdinalNameFlag, " Ordinal/Name flag"); + if (importLookupTableEntry.OrdinalNameFlag) + { + builder.AppendLine(importLookupTableEntry.OrdinalNumber, " Ordinal number"); + } + else + { + builder.AppendLine(importLookupTableEntry.HintNameTableRVA, " Hint/Name table RVA"); + builder.AppendLine(importLookupTableEntry.HintNameTableRVA.ConvertVirtualAddress(sectionTable ?? Array.Empty()), " Hint/Name table Physical Address"); + } + } + } + } + builder.AppendLine(); + + builder.AppendLine(" Import Address Tables Information:"); + builder.AppendLine(" -------------------------"); + if (table.ImportAddressTables == null || table.ImportAddressTables.Count == 0) + { + builder.AppendLine(" No import address tables"); + } + else + { + foreach (var kvp in table.ImportAddressTables) + { + int index = kvp.Key; + var importAddressTable = kvp.Value; + + builder.AppendLine(); + builder.AppendLine($" Import Address Table {index} Information:"); + builder.AppendLine(" -------------------------"); + if (importAddressTable == null || importAddressTable.Length == 0) + { + builder.AppendLine(" No import address table items"); + continue; + } + + for (int i = 0; i < importAddressTable.Length; i++) + { + var importAddressTableEntry = importAddressTable[i]; + builder.AppendLine($" Import Address Table {index} Entry {i}"); + if (importAddressTableEntry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(importAddressTableEntry.OrdinalNameFlag, " Ordinal/Name flag"); + if (importAddressTableEntry.OrdinalNameFlag) + { + builder.AppendLine(importAddressTableEntry.OrdinalNumber, " Ordinal number"); + } + else + { + builder.AppendLine(importAddressTableEntry.HintNameTableRVA, " Hint/Name table RVA"); + builder.AppendLine(importAddressTableEntry.HintNameTableRVA.ConvertVirtualAddress(sectionTable ?? Array.Empty()), " Hint/Name table Physical Address"); + } + } + } + } + builder.AppendLine(); + + builder.AppendLine(" Hint/Name Table Information:"); + builder.AppendLine(" -------------------------"); + if (table.HintNameTable == null || table.HintNameTable.Length == 0) + { + builder.AppendLine(" No hint/name table items"); + } + else + { + for (int i = 0; i < table.HintNameTable.Length; i++) + { + var hintNameTableEntry = table.HintNameTable[i]; + builder.AppendLine($" Hint/Name Table Entry {i}"); + if (hintNameTableEntry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(hintNameTableEntry.Hint, " Hint"); + builder.AppendLine(hintNameTableEntry.Name, " Name"); + } + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ResourceDirectoryTable table) +#else + private static void Print(StringBuilder builder, ResourceDirectoryTable? table) +#endif + { + builder.AppendLine(" Resource Directory Table Information:"); + builder.AppendLine(" -------------------------"); + if (table == null) + { + builder.AppendLine(" No resource directory table items"); + builder.AppendLine(); + return; + } + + Print(table, level: 0, types: new List(), builder); + builder.AppendLine(); + } + + private static void Print(ResourceDirectoryTable table, int level, List types, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + + builder.AppendLine(level, $"{padding}Table level"); + builder.AppendLine(table.Characteristics, $"{padding}Characteristics"); + builder.AppendLine(table.TimeDateStamp, $"{padding}Time/Date stamp"); + builder.AppendLine(table.MajorVersion, $"{padding}Major version"); + builder.AppendLine(table.MinorVersion, $"{padding}Minor version"); + builder.AppendLine(table.NumberOfNameEntries, $"{padding}Number of name entries"); + builder.AppendLine(table.NumberOfIDEntries, $"{padding}Number of ID entries"); + builder.AppendLine(); + + builder.AppendLine($"{padding}Entries"); + builder.AppendLine($"{padding}-------------------------"); + if (table.NumberOfNameEntries == 0 && table.NumberOfIDEntries == 0) + { + builder.AppendLine($"{padding}No entries"); + builder.AppendLine(); + } + else + { + if (table.Entries == null) + return; + + for (int i = 0; i < table.Entries.Length; i++) + { + var entry = table.Entries[i]; + if (entry == null) + continue; + + var newTypes = new List(types ?? new List()); + if (entry.Name?.UnicodeString != null) + newTypes.Add(Encoding.UTF8.GetString(entry.Name.UnicodeString)); + else + newTypes.Add(entry.IntegerID); + + PrintResourceDirectoryEntry(entry, level + 1, newTypes, builder); + } + } + } + + private static void PrintResourceDirectoryEntry(ResourceDirectoryEntry entry, int level, List types, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + + builder.AppendLine(level, $"{padding}Item level"); + if (entry.NameOffset != default) + { + builder.AppendLine(entry.NameOffset, $"{padding}Name offset"); + builder.AppendLine(entry.Name?.UnicodeString, $"{padding}Name ({entry.Name?.Length ?? 0})"); + } + else + { + builder.AppendLine(entry.IntegerID, $"{padding}Integer ID"); + } + + if (entry.DataEntry != null) + PrintResourceDataEntry(entry.DataEntry, level: level + 1, types, builder); + else if (entry.Subdirectory != null) + Print(entry.Subdirectory, level: level + 1, types, builder); + } + + private static void PrintResourceDataEntry(ResourceDataEntry entry, int level, List types, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + + // TODO: Use ordered list of base types to determine the shape of the data + builder.AppendLine($"{padding}Base types: {string.Join(", ", types)}"); + + builder.AppendLine(level, $"{padding}Entry level"); + builder.AppendLine(entry.DataRVA, $"{padding}Data RVA"); + builder.AppendLine(entry.Size, $"{padding}Size"); + builder.AppendLine(entry.Codepage, $"{padding}Codepage"); + builder.AppendLine(entry.Reserved, $"{padding}Reserved"); + + // TODO: Print out per-type data + if (types != null && types.Count > 0 && types[0] is uint resourceType) + { + switch ((ResourceType)resourceType) + { + case ResourceType.RT_CURSOR: + PrintResourceRT_CURSOR(entry, level, builder); + break; + case ResourceType.RT_BITMAP: + PrintResourceRT_BITMAP(entry, level, builder); + break; + case ResourceType.RT_ICON: + PrintResourceRT_ICON(entry, level, builder); + break; + case ResourceType.RT_MENU: + PrintResourceRT_MENU(entry, level, builder); + break; + case ResourceType.RT_DIALOG: + PrintResourceRT_DIALOG(entry, level, builder); + break; + case ResourceType.RT_STRING: + PrintResourceRT_STRING(entry, level, builder); + break; + case ResourceType.RT_FONTDIR: + PrintResourceRT_FONTDIR(entry, level, builder); + break; + case ResourceType.RT_FONT: + PrintResourceRT_FONT(entry, level, builder); + break; + case ResourceType.RT_ACCELERATOR: + PrintResourceRT_ACCELERATOR(entry, level, builder); + break; + case ResourceType.RT_RCDATA: + PrintResourceRT_RCDATA(entry, level, builder); + break; + case ResourceType.RT_MESSAGETABLE: + PrintResourceRT_MESSAGETABLE(entry, level, builder); + break; + case ResourceType.RT_GROUP_CURSOR: + PrintResourceRT_GROUP_CURSOR(entry, level, builder); + break; + case ResourceType.RT_GROUP_ICON: + PrintResourceRT_GROUP_ICON(entry, level, builder); + break; + case ResourceType.RT_VERSION: + PrintResourceRT_VERSION(entry, level, builder); + break; + case ResourceType.RT_DLGINCLUDE: + PrintResourceRT_DLGINCLUDE(entry, level, builder); + break; + case ResourceType.RT_PLUGPLAY: + PrintResourceRT_PLUGPLAY(entry, level, builder); + break; + case ResourceType.RT_VXD: + PrintResourceRT_VXD(entry, level, builder); + break; + case ResourceType.RT_ANICURSOR: + PrintResourceRT_ANICURSOR(entry, level, builder); + break; + case ResourceType.RT_ANIICON: + PrintResourceRT_ANIICON(entry, level, builder); + break; + case ResourceType.RT_HTML: + PrintResourceRT_HTML(entry, level, builder); + break; + case ResourceType.RT_MANIFEST: + PrintResourceRT_MANIFEST(entry, level, builder); + break; + default: + PrintResourceUNKNOWN(entry, level, types[0], builder); + break; + } + } + else if (types != null && types.Count > 0 && types[0] is string resourceString) + { + PrintResourceUNKNOWN(entry, level, types[0], builder); + } + + builder.AppendLine(); + } + + private static void PrintResourceRT_CURSOR(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Hardware-dependent cursor resource found, not parsed yet"); + } + + private static void PrintResourceRT_BITMAP(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Bitmap resource found, not parsed yet"); + } + + private static void PrintResourceRT_ICON(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Hardware-dependent icon resource found, not parsed yet"); + } + + private static void PrintResourceRT_MENU(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + +#if NET48 + MenuResource menu = null; +#else + MenuResource? menu = null; +#endif + try { menu = entry.AsMenu(); } catch { } + if (menu == null) + { + builder.AppendLine($"{padding}Menu resource found, but malformed"); + return; + } + + if (menu.MenuHeader != null) + { + builder.AppendLine(menu.MenuHeader.Version, $"{padding}Version"); + builder.AppendLine(menu.MenuHeader.HeaderSize, $"{padding}Header size"); + builder.AppendLine(); + builder.AppendLine($"{padding}Menu items"); + builder.AppendLine($"{padding}-------------------------"); + if (menu.MenuItems == null || menu.MenuItems.Length == 0) + { + builder.AppendLine($"{padding}No menu items"); + return; + } + + for (int i = 0; i < menu.MenuItems.Length; i++) + { + var menuItem = menu.MenuItems[i]; + builder.AppendLine($"{padding}Menu item {i}"); + if (menuItem == null) + { + builder.AppendLine($"{padding} [NULL]"); + continue; + } + + if (menuItem.NormalMenuText != null) + { + builder.AppendLine($"{padding} Resource info: {menuItem.NormalResInfo} (0x{menuItem.NormalResInfo:X})"); + builder.AppendLine(menuItem.NormalMenuText, $"{padding} Menu text"); + } + else + { + builder.AppendLine($"{padding} Item type: {menuItem.PopupItemType} (0x{menuItem.PopupItemType:X})"); + builder.AppendLine($"{padding} State: {menuItem.PopupState} (0x{menuItem.PopupState:X})"); + builder.AppendLine(menuItem.PopupID, $"{padding} ID"); + builder.AppendLine($"{padding} Resource info: {menuItem.PopupResInfo} (0x{menuItem.PopupResInfo:X})"); + builder.AppendLine(menuItem.PopupMenuText, $"{padding} Menu text"); + } + } + } + else if (menu.ExtendedMenuHeader != null) + { + builder.AppendLine(menu.ExtendedMenuHeader.Version, $"{padding}Version"); + builder.AppendLine(menu.ExtendedMenuHeader.Offset, $"{padding}Offset"); + builder.AppendLine(menu.ExtendedMenuHeader.HelpID, $"{padding}Help ID"); + builder.AppendLine(); + builder.AppendLine($"{padding}Menu items"); + builder.AppendLine($"{padding}-------------------------"); + if (menu.ExtendedMenuHeader.Offset == 0 + || menu.ExtendedMenuItems == null + || menu.ExtendedMenuItems.Length == 0) + { + builder.AppendLine($"{padding}No menu items"); + return; + } + + for (int i = 0; i < menu.ExtendedMenuItems.Length; i++) + { + var menuItem = menu.ExtendedMenuItems[i]; + + builder.AppendLine($"{padding}Menu item {i}"); + if (menuItem == null) + { + builder.AppendLine($"{padding} [NULL]"); + continue; + } + + builder.AppendLine($"{padding} Item type: {menuItem.ItemType} (0x{menuItem.ItemType:X})"); + builder.AppendLine($"{padding} State: {menuItem.State} (0x{menuItem.State:X})"); + builder.AppendLine(menuItem.ID, $"{padding} ID"); + builder.AppendLine($"{padding} Flags: {menuItem.Flags} (0x{menuItem.Flags:X})"); + builder.AppendLine(menuItem.MenuText, $"{padding} Menu text"); + } + } + else + { + builder.AppendLine($"{padding}Menu resource found, but malformed"); + } + } + + private static void PrintResourceRT_DIALOG(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + +#if NET48 + DialogBoxResource dialogBox = null; +#else + DialogBoxResource? dialogBox = null; +#endif + try { dialogBox = entry.AsDialogBox(); } catch { } + if (dialogBox == null) + { + builder.AppendLine($"{padding}Dialog box resource found, but malformed"); + return; + } + + if (dialogBox.DialogTemplate != null) + { + builder.AppendLine($"{padding}Style: {dialogBox.DialogTemplate.Style} (0x{dialogBox.DialogTemplate.Style:X})"); + builder.AppendLine($"{padding}Extended style: {dialogBox.DialogTemplate.ExtendedStyle} (0x{dialogBox.DialogTemplate.ExtendedStyle:X})"); + builder.AppendLine(dialogBox.DialogTemplate.ItemCount, $"{padding}Item count"); + builder.AppendLine(dialogBox.DialogTemplate.PositionX, $"{padding}X-coordinate of upper-left corner"); + builder.AppendLine(dialogBox.DialogTemplate.PositionY, $"{padding}Y-coordinate of upper-left corner"); + builder.AppendLine(dialogBox.DialogTemplate.WidthX, $"{padding}Width of the dialog box"); + builder.AppendLine(dialogBox.DialogTemplate.HeightY, $"{padding}Height of the dialog box"); + builder.AppendLine(dialogBox.DialogTemplate.MenuResource, $"{padding}Menu resource"); + builder.AppendLine(dialogBox.DialogTemplate.MenuResourceOrdinal, $"{padding}Menu resource ordinal"); + builder.AppendLine(dialogBox.DialogTemplate.ClassResource, $"{padding}Class resource"); + builder.AppendLine(dialogBox.DialogTemplate.ClassResourceOrdinal, $"{padding}Class resource ordinal"); + builder.AppendLine(dialogBox.DialogTemplate.TitleResource, $"{padding}Title resource"); + builder.AppendLine(dialogBox.DialogTemplate.PointSizeValue, $"{padding}Point size value"); + builder.AppendLine(dialogBox.DialogTemplate.Typeface, $"{padding}Typeface"); + builder.AppendLine(); + builder.AppendLine($"{padding}Dialog item templates"); + builder.AppendLine($"{padding}-------------------------"); + if (dialogBox.DialogTemplate.ItemCount == 0 + || dialogBox.DialogItemTemplates == null + || dialogBox.DialogItemTemplates.Length == 0) + { + builder.AppendLine($"{padding}No dialog item templates"); + return; + } + + for (int i = 0; i < dialogBox.DialogItemTemplates.Length; i++) + { + var dialogItemTemplate = dialogBox.DialogItemTemplates[i]; + builder.AppendLine($"{padding}Dialog item template {i}"); + if (dialogItemTemplate == null) + { + builder.AppendLine($"{padding} [NULL]"); + continue; + } + + builder.AppendLine($"{padding} Style: {dialogItemTemplate.Style} (0x{dialogItemTemplate.Style:X})"); + builder.AppendLine($"{padding} Extended style: {dialogItemTemplate.ExtendedStyle} (0x{dialogItemTemplate.ExtendedStyle:X})"); + builder.AppendLine(dialogItemTemplate.PositionX, $"{padding} X-coordinate of upper-left corner"); + builder.AppendLine(dialogItemTemplate.PositionY, $"{padding} Y-coordinate of upper-left corner"); + builder.AppendLine(dialogItemTemplate.WidthX, $"{padding} Width of the control"); + builder.AppendLine(dialogItemTemplate.HeightY, $"{padding} Height of the control"); + builder.AppendLine(dialogItemTemplate.ID, $"{padding} ID"); + builder.AppendLine(dialogItemTemplate.ClassResource, $"{padding} Class resource"); + builder.AppendLine($"{padding} Class resource ordinal: {dialogItemTemplate.ClassResourceOrdinal} (0x{dialogItemTemplate.ClassResourceOrdinal:X})"); + builder.AppendLine(dialogItemTemplate.TitleResource, $"{padding} Title resource"); + builder.AppendLine(dialogItemTemplate.TitleResourceOrdinal, $"{padding} Title resource ordinal"); + builder.AppendLine(dialogItemTemplate.CreationDataSize, $"{padding} Creation data size"); + if (dialogItemTemplate.CreationData != null && dialogItemTemplate.CreationData.Length != 0) + builder.AppendLine(dialogItemTemplate.CreationData, $"{padding} Creation data"); + else + builder.AppendLine($"{padding} Creation data: [EMPTY]"); + } + } + else if (dialogBox.ExtendedDialogTemplate != null) + { + builder.AppendLine(dialogBox.ExtendedDialogTemplate.Version, $"{padding}Version"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.Signature, $"{padding}Signature"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.HelpID, $"{padding}Help ID"); + builder.AppendLine($"{padding}Extended style: {dialogBox.ExtendedDialogTemplate.ExtendedStyle} (0x{dialogBox.ExtendedDialogTemplate.ExtendedStyle:X})"); + builder.AppendLine($"{padding}Style: {dialogBox.ExtendedDialogTemplate.Style} (0x{dialogBox.ExtendedDialogTemplate.Style:X})"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.DialogItems, $"{padding}Item count"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.PositionX, $"{padding}X-coordinate of upper-left corner"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.PositionY, $"{padding}Y-coordinate of upper-left corner"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.WidthX, $"{padding}Width of the dialog box"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.HeightY, $"{padding}Height of the dialog box"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.MenuResource, $"{padding}Menu resource"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.MenuResourceOrdinal, $"{padding}Menu resource ordinal"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.ClassResource, $"{padding}Class resource"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.ClassResourceOrdinal, $"{padding}Class resource ordinal"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.TitleResource, $"{padding}Title resource"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.PointSize, $"{padding}Point size"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.Weight, $"{padding}Weight"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.Italic, $"{padding}Italic"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.CharSet, $"{padding}Character set"); + builder.AppendLine(dialogBox.ExtendedDialogTemplate.Typeface, $"{padding}Typeface"); + builder.AppendLine(); + builder.AppendLine($"{padding}Dialog item templates"); + builder.AppendLine($"{padding}-------------------------"); + if (dialogBox.ExtendedDialogTemplate.DialogItems == 0 + || dialogBox.ExtendedDialogItemTemplates == null + || dialogBox.ExtendedDialogItemTemplates.Length == 0) + { + builder.AppendLine($"{padding}No dialog item templates"); + return; + } + + for (int i = 0; i < dialogBox.ExtendedDialogItemTemplates.Length; i++) + { + var dialogItemTemplate = dialogBox.ExtendedDialogItemTemplates[i]; + builder.AppendLine($"{padding}Dialog item template {i}"); + if (dialogItemTemplate == null) + { + builder.AppendLine($"{padding} [NULL]"); + continue; + } + + builder.AppendLine(dialogItemTemplate.HelpID, $"{padding} Help ID"); + builder.AppendLine($"{padding} Extended style: {dialogItemTemplate.ExtendedStyle} (0x{dialogItemTemplate.ExtendedStyle:X})"); + builder.AppendLine($"{padding} Style: {dialogItemTemplate.Style} (0x{dialogItemTemplate.Style:X})"); + builder.AppendLine(dialogItemTemplate.PositionX, $"{padding} X-coordinate of upper-left corner"); + builder.AppendLine(dialogItemTemplate.PositionY, $"{padding} Y-coordinate of upper-left corner"); + builder.AppendLine(dialogItemTemplate.WidthX, $"{padding} Width of the control"); + builder.AppendLine(dialogItemTemplate.HeightY, $"{padding} Height of the control"); + builder.AppendLine(dialogItemTemplate.ID, $"{padding} ID"); + builder.AppendLine(dialogItemTemplate.ClassResource, $"{padding} Class resource"); + builder.AppendLine($"{padding} Class resource ordinal: {dialogItemTemplate.ClassResourceOrdinal} (0x{dialogItemTemplate.ClassResourceOrdinal:X})"); + builder.AppendLine(dialogItemTemplate.TitleResource, $"{padding} Title resource"); + builder.AppendLine(dialogItemTemplate.TitleResourceOrdinal, $"{padding} Title resource ordinal"); + builder.AppendLine(dialogItemTemplate.CreationDataSize, $"{padding} Creation data size"); + if (dialogItemTemplate.CreationData != null && dialogItemTemplate.CreationData.Length != 0) + builder.AppendLine(dialogItemTemplate.CreationData, $"{padding} Creation data"); + else + builder.AppendLine($"{padding} Creation data: [EMPTY]"); + } + } + else + { + builder.AppendLine($"{padding}Dialog box resource found, but malformed"); + } + } + + private static void PrintResourceRT_STRING(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + +#if NET48 + Dictionary stringTable = null; +#else + Dictionary? stringTable = null; +#endif + try { stringTable = entry.AsStringTable(); } catch { } + if (stringTable == null) + { + builder.AppendLine($"{padding}String table resource found, but malformed"); + return; + } + + foreach (var kvp in stringTable) + { + int index = kvp.Key; +#if NET48 + string stringValue = kvp.Value; +#else + string? stringValue = kvp.Value; +#endif + builder.AppendLine(stringValue, $"{padding}String entry {index}"); + } + } + + private static void PrintResourceRT_FONTDIR(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Font directory resource found, not parsed yet"); + } + + private static void PrintResourceRT_FONT(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Font resource found, not parsed yet"); + } + + private static void PrintResourceRT_ACCELERATOR(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + +#if NET48 + AcceleratorTableEntry[] acceleratorTable = null; +#else + AcceleratorTableEntry[]? acceleratorTable = null; +#endif + try { acceleratorTable = entry.AsAcceleratorTableResource(); } catch { } + if (acceleratorTable == null) + { + builder.AppendLine($"{padding}Accelerator table resource found, but malformed"); + return; + } + + for (int i = 0; i < acceleratorTable.Length; i++) + { + var acceleratorTableEntry = acceleratorTable[i]; + builder.AppendLine($"{padding}Accelerator Table Entry {i}:"); + builder.AppendLine($"{padding} Flags: {acceleratorTableEntry.Flags} (0x{acceleratorTableEntry.Flags:X})"); + builder.AppendLine(acceleratorTableEntry.Ansi, $"{padding} Ansi"); + builder.AppendLine(acceleratorTableEntry.Id, $"{padding} Id"); + builder.AppendLine(acceleratorTableEntry.Padding, $"{padding} Padding"); + } + } + + private static void PrintResourceRT_RCDATA(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Application-defined resource found, not parsed yet"); + + // Then print the data, if needed + if (entry.Data == null) + { + builder.AppendLine($"{padding}Data: [NULL] (This may indicate a very large resource)"); + } + else + { + int offset = 0; +#if NET48 + byte[] magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16)); +#else + byte[]? magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16)); +#endif + + if (magic == null) + { + // No-op + } + else if (magic[0] == 0x4D && magic[1] == 0x5A) + { + builder.AppendLine($"{padding}Data: [Embedded Executable File]"); // TODO: Parse this out and print separately + } + else if (magic[0] == 0x4D && magic[1] == 0x53 && magic[2] == 0x46 && magic[3] == 0x54) + { + builder.AppendLine($"{padding}Data: [Embedded OLE Library File]"); // TODO: Parse this out and print separately + } + else + { + builder.AppendLine(magic, $"{padding}Data"); + + //if (entry.Data != null) + // builder.AppendLine(entry.Data, $"{padding}Value (Byte Data)"); + //if (entry.Data != null) + // builder.AppendLine(Encoding.ASCII.GetString(entry.Data), $"{padding}Value (ASCII)"); + //if (entry.Data != null) + // builder.AppendLine(Encoding.UTF8.GetString(entry.Data), $"{padding}Value (UTF-8)"); + //if (entry.Data != null) + // builder.AppendLine(Encoding.Unicode.GetString(entry.Data), $"{padding}Value (Unicode)"); + } + } + } + + private static void PrintResourceRT_MESSAGETABLE(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + +#if NET48 + MessageResourceData messageTable = null; +#else + MessageResourceData? messageTable = null; +#endif + try { messageTable = entry.AsMessageResourceData(); } catch { } + if (messageTable == null) + { + builder.AppendLine($"{padding}Message resource data found, but malformed"); + return; + } + + builder.AppendLine(messageTable.NumberOfBlocks, $"{padding}Number of blocks"); + builder.AppendLine(); + builder.AppendLine($"{padding}Message resource blocks"); + builder.AppendLine($"{padding}-------------------------"); + if (messageTable.NumberOfBlocks == 0 + || messageTable.Blocks == null + || messageTable.Blocks.Length == 0) + { + builder.AppendLine($"{padding}No message resource blocks"); + } + else + { + for (int i = 0; i < messageTable.Blocks.Length; i++) + { + var messageResourceBlock = messageTable.Blocks[i]; + builder.AppendLine($"{padding}Message resource block {i}"); + if (messageResourceBlock == null) + { + builder.AppendLine($"{padding} [NULL]"); + continue; + } + + builder.AppendLine(messageResourceBlock.LowId, $"{padding} Low ID"); + builder.AppendLine(messageResourceBlock.HighId, $"{padding} High ID"); + builder.AppendLine(messageResourceBlock.OffsetToEntries, $"{padding} Offset to entries"); + } + } + builder.AppendLine(); + + builder.AppendLine($"{padding}Message resource entries"); + builder.AppendLine($"{padding}-------------------------"); + if (messageTable.Entries == null || messageTable.Entries.Count == 0) + { + builder.AppendLine($"{padding}No message resource entries"); + } + else + { + foreach (var kvp in messageTable.Entries) + { + uint index = kvp.Key; + var messageResourceEntry = kvp.Value; + builder.AppendLine($"{padding}Message resource entry {index}"); + if (messageResourceEntry == null) + { + builder.AppendLine($"{padding} [NULL]"); + continue; + } + + builder.AppendLine(messageResourceEntry.Length, $"{padding} Length"); + builder.AppendLine(messageResourceEntry.Flags, $"{padding} Flags"); + builder.AppendLine(messageResourceEntry.Text, $"{padding} Text"); + } + } + } + + private static void PrintResourceRT_GROUP_CURSOR(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Hardware-independent cursor resource found, not parsed yet"); + } + + private static void PrintResourceRT_GROUP_ICON(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Hardware-independent icon resource found, not parsed yet"); + } + + private static void PrintResourceRT_VERSION(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + +#if NET48 + VersionInfo versionInfo = null; +#else + VersionInfo? versionInfo = null; +#endif + try { versionInfo = entry.AsVersionInfo(); } catch { } + if (versionInfo == null) + { + builder.AppendLine($"{padding}Version info resource found, but malformed"); + return; + } + + builder.AppendLine(versionInfo.Length, $"{padding}Length"); + builder.AppendLine(versionInfo.ValueLength, $"{padding}Value length"); + builder.AppendLine($"{padding}Resource type: {versionInfo.ResourceType} (0x{versionInfo.ResourceType:X})"); + builder.AppendLine(versionInfo.Key, $"{padding}Key"); + if (versionInfo.ValueLength != 0 && versionInfo.Value != null) + { + builder.AppendLine(versionInfo.Value.Signature, $"{padding}[Fixed File Info] Signature"); + builder.AppendLine(versionInfo.Value.StrucVersion, $"{padding}[Fixed File Info] Struct version"); + builder.AppendLine(versionInfo.Value.FileVersionMS, $"{padding}[Fixed File Info] File version (MS)"); + builder.AppendLine(versionInfo.Value.FileVersionLS, $"{padding}[Fixed File Info] File version (LS)"); + builder.AppendLine(versionInfo.Value.ProductVersionMS, $"{padding}[Fixed File Info] Product version (MS)"); + builder.AppendLine(versionInfo.Value.ProductVersionLS, $"{padding}[Fixed File Info] Product version (LS)"); + builder.AppendLine(versionInfo.Value.FileFlagsMask, $"{padding}[Fixed File Info] File flags mask"); + builder.AppendLine($"{padding}[Fixed File Info] File flags: {versionInfo.Value.FileFlags} (0x{versionInfo.Value.FileFlags:X})"); + builder.AppendLine($"{padding}[Fixed File Info] File OS: {versionInfo.Value.FileOS} (0x{versionInfo.Value.FileOS:X})"); + builder.AppendLine($"{padding}[Fixed File Info] Type: {versionInfo.Value.FileType} (0x{versionInfo.Value.FileType:X})"); + builder.AppendLine($"{padding}[Fixed File Info] Subtype: {versionInfo.Value.FileSubtype} (0x{versionInfo.Value.FileSubtype:X})"); + builder.AppendLine(versionInfo.Value.FileDateMS, $"{padding}[Fixed File Info] File date (MS)"); + builder.AppendLine(versionInfo.Value.FileDateLS, $"{padding}[Fixed File Info] File date (LS)"); + } + + if (versionInfo.StringFileInfo != null) + { + builder.AppendLine(versionInfo.StringFileInfo.Length, $"{padding}[String File Info] Length"); + builder.AppendLine(versionInfo.StringFileInfo.ValueLength, $"{padding}[String File Info] Value length"); + builder.AppendLine($"{padding}[String File Info] Resource type: {versionInfo.StringFileInfo.ResourceType} (0x{versionInfo.StringFileInfo.ResourceType:X})"); + builder.AppendLine(versionInfo.StringFileInfo.Key, $"{padding}[String File Info] Key"); + builder.AppendLine($"{padding}Children:"); + builder.AppendLine($"{padding}-------------------------"); + if (versionInfo.StringFileInfo.Children == null || versionInfo.StringFileInfo.Children.Length == 0) + { + builder.AppendLine($"{padding}No string file info children"); + } + else + { + for (int i = 0; i < versionInfo.StringFileInfo.Children.Length; i++) + { + var stringFileInfoChildEntry = versionInfo.StringFileInfo.Children[i]; + if (stringFileInfoChildEntry == null) + { + builder.AppendLine($"{padding} [String Table {i}] [NULL]"); + continue; + } + + builder.AppendLine(stringFileInfoChildEntry.Length, $"{padding} [String Table {i}] Length"); + builder.AppendLine(stringFileInfoChildEntry.ValueLength, $"{padding} [String Table {i}] Value length"); + builder.AppendLine($"{padding} [String Table {i}] ResourceType: {stringFileInfoChildEntry.ResourceType} (0x{stringFileInfoChildEntry.ResourceType:X})"); + builder.AppendLine(stringFileInfoChildEntry.Key, $"{padding} [String Table {i}] Key"); + builder.AppendLine($"{padding} [String Table {i}] Children:"); + builder.AppendLine($"{padding} -------------------------"); + if (stringFileInfoChildEntry.Children == null || stringFileInfoChildEntry.Children.Length == 0) + { + builder.AppendLine($"{padding} No string table {i} children"); + } + else + { + for (int j = 0; j < stringFileInfoChildEntry.Children.Length; j++) + { + var stringDataEntry = stringFileInfoChildEntry.Children[j]; + if (stringDataEntry == null) + { + builder.AppendLine($"{padding} [String Data {j}] [NULL]"); + continue; + } + + builder.AppendLine(stringDataEntry.Length, $"{padding} [String Data {j}] Length"); + builder.AppendLine(stringDataEntry.ValueLength, $"{padding} [String Data {j}] Value length"); + builder.AppendLine($"{padding} [String Data {j}] ResourceType: {stringDataEntry.ResourceType} (0x{stringDataEntry.ResourceType:X})"); + builder.AppendLine(stringDataEntry.Key, $"{padding} [String Data {j}] Key"); + builder.AppendLine(stringDataEntry.Value, $"{padding} [String Data {j}] Value"); + } + } + } + } + } + + if (versionInfo.VarFileInfo != null) + { + builder.AppendLine(versionInfo.VarFileInfo.Length, $"{padding}[Var File Info] Length"); + builder.AppendLine(versionInfo.VarFileInfo.ValueLength, $"{padding}[Var File Info] Value length"); + builder.AppendLine($"{padding}[Var File Info] Resource type: {versionInfo.VarFileInfo.ResourceType} (0x{versionInfo.VarFileInfo.ResourceType:X})"); + builder.AppendLine(versionInfo.VarFileInfo.Key, $"{padding}[Var File Info] Key"); + builder.AppendLine($"{padding}Children:"); + builder.AppendLine($"{padding}-------------------------"); + if (versionInfo.VarFileInfo.Children == null || versionInfo.VarFileInfo.Children.Length == 0) + { + builder.AppendLine($"{padding}No var file info children"); + } + else + { + for (int i = 0; i < versionInfo.VarFileInfo.Children.Length; i++) + { + var varFileInfoChildEntry = versionInfo.VarFileInfo.Children[i]; + if (varFileInfoChildEntry == null) + { + builder.AppendLine($"{padding} [String Table {i}] [NULL]"); + continue; + } + + builder.AppendLine(varFileInfoChildEntry.Length, $"{padding} [String Table {i}] Length"); + builder.AppendLine(varFileInfoChildEntry.ValueLength, $"{padding} [String Table {i}] Value length"); + builder.AppendLine($"{padding} [String Table {i}] ResourceType: {varFileInfoChildEntry.ResourceType} (0x{varFileInfoChildEntry.ResourceType:X})"); + builder.AppendLine(varFileInfoChildEntry.Key, $"{padding} [String Table {i}] Key"); + builder.AppendLine(varFileInfoChildEntry.Value, $"{padding} [String Table {i}] Value"); + } + } + } + } + + private static void PrintResourceRT_DLGINCLUDE(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}External header resource found, not parsed yet"); + } + + private static void PrintResourceRT_PLUGPLAY(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Plug and Play resource found, not parsed yet"); + } + + private static void PrintResourceRT_VXD(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}VXD found, not parsed yet"); + } + + private static void PrintResourceRT_ANICURSOR(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Animated cursor found, not parsed yet"); + } + + private static void PrintResourceRT_ANIICON(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}Animated icon found, not parsed yet"); + } + + private static void PrintResourceRT_HTML(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + builder.AppendLine($"{padding}HTML resource found, not parsed yet"); + + //if (entry.Data != null) + // builder.AppendLine(Encoding.ASCII.GetString(entry.Data), $"{padding}Value (ASCII)"); + //if (entry.Data != null) + // builder.AppendLine(Encoding.UTF8.GetString(entry.Data), $"{padding}Value (UTF-8)"); + //if (entry.Data != null) + // builder.AppendLine(Encoding.Unicode.GetString(entry.Data), $"{padding}Value (Unicode)"); + } + + private static void PrintResourceRT_MANIFEST(ResourceDataEntry entry, int level, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + +#if NET48 + AssemblyManifest assemblyManifest = null; +#else + AssemblyManifest? assemblyManifest = null; +#endif + try { assemblyManifest = entry.AsAssemblyManifest(); } catch { } + if (assemblyManifest == null) + { + builder.AppendLine($"{padding}Assembly manifest found, but malformed"); + return; + } + + builder.AppendLine(assemblyManifest.ManifestVersion, $"{padding}Manifest version"); + if (assemblyManifest.AssemblyIdentities != null && assemblyManifest.AssemblyIdentities.Length > 0) + { + for (int i = 0; i < assemblyManifest.AssemblyIdentities.Length; i++) + { + var assemblyIdentity = assemblyManifest.AssemblyIdentities[i]; + if (assemblyIdentity == null) + { + builder.AppendLine($"{padding} [Assembly Identity {i}] [NULL]"); + continue; + } + + builder.AppendLine(assemblyIdentity.Name, $"{padding}[Assembly Identity {i}] Name"); + builder.AppendLine(assemblyIdentity.Version, $"{padding}[Assembly Identity {i}] Version"); + builder.AppendLine(assemblyIdentity.Type, $"{padding}[Assembly Identity {i}] Type"); + builder.AppendLine(assemblyIdentity.ProcessorArchitecture, $"{padding}[Assembly Identity {i}] Processor architecture"); + builder.AppendLine(assemblyIdentity.PublicKeyToken, $"{padding}[Assembly Identity {i}] Public key token"); + builder.AppendLine(assemblyIdentity.Language, $"{padding}[Assembly Identity {i}] Language"); + } + } + + if (assemblyManifest.Description != null) + builder.AppendLine(assemblyManifest.Description.Value, $"{padding}[Assembly Description] Value"); + + if (assemblyManifest.COMInterfaceExternalProxyStub != null && assemblyManifest.COMInterfaceExternalProxyStub.Length > 0) + { + for (int i = 0; i < assemblyManifest.COMInterfaceExternalProxyStub.Length; i++) + { + var comInterfaceExternalProxyStub = assemblyManifest.COMInterfaceExternalProxyStub[i]; + if (comInterfaceExternalProxyStub == null) + { + builder.AppendLine($"{padding} [COM Interface External Proxy Stub {i}] [NULL]"); + continue; + } + + builder.AppendLine(comInterfaceExternalProxyStub.IID, $"{padding}[COM Interface External Proxy Stub {i}] IID"); + builder.AppendLine(comInterfaceExternalProxyStub.Name, $"{padding}[COM Interface External Proxy Stub {i}] Name"); + builder.AppendLine(comInterfaceExternalProxyStub.TLBID, $"{padding}[COM Interface External Proxy Stub {i}] TLBID"); + builder.AppendLine(comInterfaceExternalProxyStub.NumMethods, $"{padding}[COM Interface External Proxy Stub {i}] Number of methods"); + builder.AppendLine(comInterfaceExternalProxyStub.ProxyStubClsid32, $"{padding}[COM Interface External Proxy Stub {i}] Proxy stub (CLSID32)"); + builder.AppendLine(comInterfaceExternalProxyStub.BaseInterface, $"{padding}[COM Interface External Proxy Stub {i}] Base interface"); + } + } + + if (assemblyManifest.Dependency != null && assemblyManifest.Dependency.Length > 0) + { + for (int i = 0; i < assemblyManifest.Dependency.Length; i++) + { + var dependency = assemblyManifest.Dependency[i]; + if (dependency?.DependentAssembly != null) + { + if (dependency.DependentAssembly.AssemblyIdentity != null) + { + builder.AppendLine(dependency.DependentAssembly.AssemblyIdentity.Name, $"{padding}[Dependency {i} Assembly Identity] Name"); + builder.AppendLine(dependency.DependentAssembly.AssemblyIdentity.Version, $"{padding}[Dependency {i} Assembly Identity] Version"); + builder.AppendLine(dependency.DependentAssembly.AssemblyIdentity.Type, $"{padding}[Dependency {i} Assembly Identity] Type"); + builder.AppendLine(dependency.DependentAssembly.AssemblyIdentity.ProcessorArchitecture, $"{padding}[Dependency {i} Assembly Identity] Processor architecture"); + builder.AppendLine(dependency.DependentAssembly.AssemblyIdentity.PublicKeyToken, $"{padding}[Dependency {i} Assembly Identity] Public key token"); + builder.AppendLine(dependency.DependentAssembly.AssemblyIdentity.Language, $"{padding}[Dependency {i} Assembly Identity] Language"); + } + if (dependency.DependentAssembly.BindingRedirect != null && dependency.DependentAssembly.BindingRedirect.Length > 0) + { + for (int j = 0; j < dependency.DependentAssembly.BindingRedirect.Length; j++) + { + var bindingRedirect = dependency.DependentAssembly.BindingRedirect[j]; + if (bindingRedirect == null) + { + builder.AppendLine($"{padding}[Dependency {i} Binding Redirect {j}] [NULL]"); + continue; + } + + builder.AppendLine(bindingRedirect.OldVersion, $"{padding}[Dependency {i} Binding Redirect {j}] Old version"); + builder.AppendLine(bindingRedirect.NewVersion, $"{padding}[Dependency {i} Binding Redirect {j}] New version"); + } + } + } + + if (dependency != null) + builder.AppendLine(dependency.Optional, $"{padding}[Dependency {i}] Optional"); + } + } + + if (assemblyManifest.File != null && assemblyManifest.File.Length > 0) + { + for (int i = 0; i < assemblyManifest.File.Length; i++) + { + var file = assemblyManifest.File[i]; + if (file == null) + { + builder.AppendLine($"{padding}[File {i}] [NULL]"); + continue; + } + + builder.AppendLine(file.Name, $"{padding}[File {i}] Name"); + builder.AppendLine(file.Hash, $"{padding}[File {i}] Hash"); + builder.AppendLine(file.HashAlgorithm, $"{padding}[File {i}] Hash algorithm"); + builder.AppendLine(file.Size, $"{padding}[File {i}] Size"); + + if (file.COMClass != null && file.COMClass.Length > 0) + { + for (int j = 0; j < file.COMClass.Length; j++) + { + var comClass = file.COMClass[j]; + if (comClass == null) + { + builder.AppendLine($"{padding}[File {i} COM Class {j}] [NULL]"); + continue; + } + + builder.AppendLine(comClass.CLSID, $"{padding}[File {i} COM Class {j}] CLSID"); + builder.AppendLine(comClass.ThreadingModel, $"{padding}[File {i} COM Class {j}] Threading model"); + builder.AppendLine(comClass.ProgID, $"{padding}[File {i} COM Class {j}] Prog ID"); + builder.AppendLine(comClass.TLBID, $"{padding}[File {i} COM Class {j}] TLBID"); + builder.AppendLine(comClass.Description, $"{padding}[File {i} COM Class {j}] Description"); + + if (comClass.ProgIDs != null && comClass.ProgIDs.Length > 0) + { + for (int k = 0; k < comClass.ProgIDs.Length; k++) + { + var progId = comClass.ProgIDs[k]; + if (progId == null) + { + builder.AppendLine($"{padding}[File {i} COM Class {j} Prog ID {k}] [NULL]"); + continue; + } + + builder.AppendLine(progId.Value, $"{padding}[File {i} COM Class {j} Prog ID {k}] Value"); + } + } + } + } + + if (file.COMInterfaceProxyStub != null && file.COMInterfaceProxyStub.Length > 0) + { + for (int j = 0; j < file.COMInterfaceProxyStub.Length; j++) + { + var comInterfaceProxyStub = file.COMInterfaceProxyStub[j]; + if (comInterfaceProxyStub == null) + { + builder.AppendLine($"{padding}[File {i} COM Interface Proxy Stub {j}] [NULL]"); + continue; + } + + builder.AppendLine(comInterfaceProxyStub.IID, $"{padding}[File {i} COM Interface Proxy Stub {j}] IID"); + builder.AppendLine(comInterfaceProxyStub.Name, $"{padding}[File {i} COM Interface Proxy Stub {j}] Name"); + builder.AppendLine(comInterfaceProxyStub.TLBID, $"{padding}[File {i} COM Interface Proxy Stub {j}] TLBID"); + builder.AppendLine(comInterfaceProxyStub.NumMethods, $"{padding}[File {i} COM Interface Proxy Stub {j}] Number of methods"); + builder.AppendLine(comInterfaceProxyStub.ProxyStubClsid32, $"{padding}[File {i} COM Interface Proxy Stub {j}] Proxy stub (CLSID32)"); + builder.AppendLine(comInterfaceProxyStub.BaseInterface, $"{padding}[File {i} COM Interface Proxy Stub {j}] Base interface"); + } + } + + if (file.Typelib != null && file.Typelib.Length > 0) + { + for (int j = 0; j < file.Typelib.Length; j++) + { + var typeLib = file.Typelib[j]; + if (typeLib == null) + { + builder.AppendLine($"{padding}[File {i} Type Lib {j}] [NULL]"); + continue; + } + + builder.AppendLine(typeLib.TLBID, $"{padding}[File {i} Type Lib {j}] TLBID"); + builder.AppendLine(typeLib.Version, $"{padding}[File {i} Type Lib {j}] Version"); + builder.AppendLine(typeLib.HelpDir, $"{padding}[File {i} Type Lib {j}] Help directory"); + builder.AppendLine(typeLib.ResourceID, $"{padding}[File {i} Type Lib {j}] Resource ID"); + builder.AppendLine(typeLib.Flags, $"{padding}[File {i} Type Lib {j}] Flags"); + } + } + + if (file.WindowClass != null && file.WindowClass.Length > 0) + { + for (int j = 0; j < file.WindowClass.Length; j++) + { + var windowClass = file.WindowClass[j]; + if (windowClass == null) + { + builder.AppendLine($"{padding}[File {i} Window Class {j}] [NULL]"); + continue; + } + + builder.AppendLine(windowClass.Versioned, $"{padding}[File {i} Window Class {j}] Versioned"); + builder.AppendLine(windowClass.Value, $"{padding}[File {i} Window Class {j}] Value"); + } + } + } + } + + if (assemblyManifest.EverythingElse != null && assemblyManifest.EverythingElse.Length > 0) + { + for (int i = 0; i < assemblyManifest.EverythingElse.Length; i++) + { + var thing = assemblyManifest.EverythingElse[i]; + if (thing is XmlElement element) + { + builder.AppendLine(element.OuterXml, $"{padding}Unparsed XML Element {i}"); + } + else + { + builder.AppendLine($"{padding}Unparsed Item {i}: {thing ?? "[NULL]"}"); + } + } + } + } + + private static void PrintResourceUNKNOWN(ResourceDataEntry entry, int level, object resourceType, StringBuilder builder) + { + string padding = new string(' ', (level + 1) * 2); + + // Print the type first + if (resourceType is uint numericType) + builder.AppendLine($"{padding}Type {(ResourceType)numericType} found, not parsed yet"); + else if (resourceType is string stringType) + builder.AppendLine($"{padding}Type {stringType} found, not parsed yet"); + else + builder.AppendLine($"{padding}Unknown type {resourceType} found, not parsed yet"); + + // Then print the data, if needed + if (entry.Data == null) + { + builder.AppendLine($"{padding}Data: [NULL] (This may indicate a very large resource)"); + } + else + { + int offset = 0; +#if NET48 + byte[] magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16)); +#else + byte[]? magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16)); +#endif + + if (magic == null) + { + // No-op + } + else if (magic[0] == 0x4D && magic[1] == 0x5A) + { + builder.AppendLine($"{padding}Data: [Embedded Executable File]"); // TODO: Parse this out and print separately + } + else if (magic[0] == 0x4D && magic[1] == 0x53 && magic[2] == 0x46 && magic[3] == 0x54) + { + builder.AppendLine($"{padding}Data: [Embedded OLE Library File]"); // TODO: Parse this out and print separately + } + else + { + builder.AppendLine(magic, $"{padding}Data"); + + //if (entry.Data != null) + // builder.AppendLine(entry.Data, $"{padding}Value (Byte Data)"); + //if (entry.Data != null) + // builder.AppendLine(Encoding.ASCII.GetString(entry.Data), $"{padding}Value (ASCII)"); + //if (entry.Data != null) + // builder.AppendLine(Encoding.UTF8.GetString(entry.Data), $"{padding}Value (UTF-8)"); + //if (entry.Data != null) + // builder.AppendLine(Encoding.Unicode.GetString(entry.Data), $"{padding}Value (Unicode)"); + } + } + } + } +} \ No newline at end of file diff --git a/Quantum.cs b/Quantum.cs new file mode 100644 index 0000000..83f878e --- /dev/null +++ b/Quantum.cs @@ -0,0 +1,82 @@ +using System.Text; +using SabreTools.Models.Quantum; + +namespace SabreTools.Printing +{ + public static class Quantum + { + public static void Print(StringBuilder builder, Archive archive) + { + builder.AppendLine("Quantum Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, archive.Header); + Print(builder, archive.FileList); + builder.AppendLine(archive.CompressedDataOffset, " Compressed data offset"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.MajorVersion, " Major version"); + builder.AppendLine(header.MinorVersion, " Minor version"); + builder.AppendLine(header.FileCount, " File count"); + builder.AppendLine(header.TableSize, " Table size"); + builder.AppendLine(header.CompressionFlags, " Compression flags"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, FileDescriptor[] entries) +#else + private static void Print(StringBuilder builder, FileDescriptor?[]? entries) +#endif + { + builder.AppendLine(" File List Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No file list items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var fileDescriptor = entries[i]; + builder.AppendLine($" File Descriptor {i}"); + if (fileDescriptor == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(fileDescriptor.FileNameSize, " File name size"); + builder.AppendLine(fileDescriptor.FileName, " File name"); + builder.AppendLine(fileDescriptor.CommentFieldSize, " Comment field size"); + builder.AppendLine(fileDescriptor.CommentField, " Comment field"); + builder.AppendLine(fileDescriptor.ExpandedFileSize, " Expanded file size"); + builder.AppendLine(fileDescriptor.FileTime, " File time"); + builder.AppendLine(fileDescriptor.FileDate, " File date"); + if (fileDescriptor.Unknown != null) + builder.AppendLine(fileDescriptor.Unknown.Value, " Unknown (Checksum?)"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/SGA.cs b/SGA.cs new file mode 100644 index 0000000..55507ee --- /dev/null +++ b/SGA.cs @@ -0,0 +1,449 @@ +using System.Text; +using SabreTools.Models.SGA; + +namespace SabreTools.Printing +{ + public static class SGA + { + public static void Print(StringBuilder builder, File file) + { + builder.AppendLine("SGA Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + // Header + Print(builder, file.Header); + + // Directory + Print(builder, file.Directory); + // TODO: Should we print the string table? + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.MajorVersion, " Major version"); + builder.AppendLine(header.MinorVersion, " Minor version"); + switch (header) + { + case Header4 header4: + builder.AppendLine(header4.FileMD5, " File MD5"); + builder.AppendLine(header4.Name, " Name"); + builder.AppendLine(header4.HeaderMD5, " Header MD5"); + builder.AppendLine(header4.HeaderLength, " Header length"); + builder.AppendLine(header4.FileDataOffset, " File data offset"); + builder.AppendLine(header4.Dummy0, " Dummy 0"); + break; + + case Header6 header6: + builder.AppendLine(header6.Name, " Name"); + builder.AppendLine(header6.HeaderLength, " Header length"); + builder.AppendLine(header6.FileDataOffset, " File data offset"); + builder.AppendLine(header6.Dummy0, " Dummy 0"); + break; + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Directory directory) +#else + private static void Print(StringBuilder builder, Directory? directory) +#endif + { + builder.AppendLine(" Directory Information:"); + builder.AppendLine(" -------------------------"); + if (directory == null) + { + builder.AppendLine(" No directory"); + builder.AppendLine(); + return; + } + + switch (directory) + { + case Directory4 directory4: + Print(builder, directory4.DirectoryHeader); + Print(builder, directory4.Sections); + Print(builder, directory4.Folders); + Print(builder, directory4.Files); + break; + + case Directory5 directory5: + Print(builder, directory5.DirectoryHeader); + Print(builder, directory5.Sections); + Print(builder, directory5.Folders); + Print(builder, directory5.Files); + break; + + case Directory6 directory6: + Print(builder, directory6.DirectoryHeader); + Print(builder, directory6.Sections); + Print(builder, directory6.Folders); + Print(builder, directory6.Files); + break; + + case Directory7 directory7: + Print(builder, directory7.DirectoryHeader); + Print(builder, directory7.Sections); + Print(builder, directory7.Folders); + Print(builder, directory7.Files); + break; + + default: + builder.AppendLine($" Unrecognized directory type"); + break; + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryHeader4 header) +#else + private static void Print(StringBuilder builder, DirectoryHeader4? header) +#endif + { + builder.AppendLine(" Directory Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No directory header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.SectionOffset, " Section offset"); + builder.AppendLine(header.SectionCount, " Section count"); + builder.AppendLine(header.FolderOffset, " Folder offset"); + builder.AppendLine(header.FolderCount, " Folder count"); + builder.AppendLine(header.FileOffset, " File offset"); + builder.AppendLine(header.FileCount, " File count"); + builder.AppendLine(header.StringTableOffset, " String table offset"); + builder.AppendLine(header.StringTableCount, " String table count"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryHeader5 header) +#else + private static void Print(StringBuilder builder, DirectoryHeader5? header) +#endif + { + builder.AppendLine(" Directory Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No directory header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.SectionOffset, " Section offset"); + builder.AppendLine(header.SectionCount, " Section count"); + builder.AppendLine(header.FolderOffset, " Folder offset"); + builder.AppendLine(header.FolderCount, " Folder count"); + builder.AppendLine(header.FileOffset, " File offset"); + builder.AppendLine(header.FileCount, " File count"); + builder.AppendLine(header.StringTableOffset, " String table offset"); + builder.AppendLine(header.StringTableCount, " String table count"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryHeader7 header) +#else + private static void Print(StringBuilder builder, DirectoryHeader7? header) +#endif + { + builder.AppendLine(" Directory Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No directory header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.SectionOffset, " Section offset"); + builder.AppendLine(header.SectionCount, " Section count"); + builder.AppendLine(header.FolderOffset, " Folder offset"); + builder.AppendLine(header.FolderCount, " Folder count"); + builder.AppendLine(header.FileOffset, " File offset"); + builder.AppendLine(header.FileCount, " File count"); + builder.AppendLine(header.StringTableOffset, " String table offset"); + builder.AppendLine(header.StringTableCount, " String table count"); + builder.AppendLine(header.HashTableOffset, " Hash table offset"); + builder.AppendLine(header.BlockSize, " Block size"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Section4[] sections) +#else + private static void Print(StringBuilder builder, Section4?[]? sections) +#endif + { + builder.AppendLine(" Sections Information:"); + builder.AppendLine(" -------------------------"); + if (sections == null || sections.Length == 0) + { + builder.AppendLine(" No sections"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < sections.Length; i++) + { + builder.AppendLine($" Section {i}"); + var section = sections[i]; + if (section == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(section.Alias, " Alias"); + builder.AppendLine(section.Name, " Name"); + builder.AppendLine(section.FolderStartIndex, " Folder start index"); + builder.AppendLine(section.FolderEndIndex, " Folder end index"); + builder.AppendLine(section.FileStartIndex, " File start index"); + builder.AppendLine(section.FileEndIndex, " File end index"); + builder.AppendLine(section.FolderRootIndex, " Folder root index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Section5[] sections) +#else + private static void Print(StringBuilder builder, Section5?[]? sections) +#endif + { + builder.AppendLine(" Sections Information:"); + builder.AppendLine(" -------------------------"); + if (sections == null || sections.Length == 0) + { + builder.AppendLine(" No sections"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < sections.Length; i++) + { + builder.AppendLine($" Section {i}"); + var section = sections[i]; + if (section == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(section.Alias, " Alias"); + builder.AppendLine(section.Name, " Name"); + builder.AppendLine(section.FolderStartIndex, " Folder start index"); + builder.AppendLine(section.FolderEndIndex, " Folder end index"); + builder.AppendLine(section.FileStartIndex, " File start index"); + builder.AppendLine(section.FileEndIndex, " File end index"); + builder.AppendLine(section.FolderRootIndex, " Folder root index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Folder4[] folders) +#else + private static void Print(StringBuilder builder, Folder4?[]? folders) +#endif + { + builder.AppendLine(" Folders Information:"); + builder.AppendLine(" -------------------------"); + if (folders == null || folders.Length == 0) + { + builder.AppendLine(" No folders"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < folders.Length; i++) + { + builder.AppendLine($" Folder {i}"); + var folder = folders[i]; + if (folder == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(folder.NameOffset, " Name offset"); + builder.AppendLine(folder.Name, " Name"); + builder.AppendLine(folder.FolderStartIndex, " Folder start index"); + builder.AppendLine(folder.FolderEndIndex, " Folder end index"); + builder.AppendLine(folder.FileStartIndex, " File start index"); + builder.AppendLine(folder.FileEndIndex, " File end index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Folder5[] folders) +#else + private static void Print(StringBuilder builder, Folder5?[]? folders) +#endif + { + builder.AppendLine(" Folders Information:"); + builder.AppendLine(" -------------------------"); + if (folders == null || folders.Length == 0) + { + builder.AppendLine(" No folders"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < folders.Length; i++) + { + builder.AppendLine($" Folder {i}"); + var folder = folders[i] as Folder5; + if (folder == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(folder.NameOffset, " Name offset"); + builder.AppendLine(folder.Name, " Name"); + builder.AppendLine(folder.FolderStartIndex, " Folder start index"); + builder.AppendLine(folder.FolderEndIndex, " Folder end index"); + builder.AppendLine(folder.FileStartIndex, " File start index"); + builder.AppendLine(folder.FileEndIndex, " File end index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, File4[] files) +#else + private static void Print(StringBuilder builder, File4?[]? files) +#endif + { + builder.AppendLine(" Files Information:"); + builder.AppendLine(" -------------------------"); + if (files == null || files.Length == 0) + { + builder.AppendLine(" No files"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < files.Length; i++) + { + builder.AppendLine($" File {i}"); + var file = files[i]; + if (file == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(file.NameOffset, " Name offset"); + builder.AppendLine(file.Name, " Name"); + builder.AppendLine(file.Offset, " Offset"); + builder.AppendLine(file.SizeOnDisk, " Size on disk"); + builder.AppendLine(file.Size, " Size"); + builder.AppendLine(file.TimeModified, " Time modified"); + builder.AppendLine(file.Dummy0, " Dummy 0"); + builder.AppendLine(file.Type, " Type"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, File6[] files) +#else + private static void Print(StringBuilder builder, File6?[]? files) +#endif + { + builder.AppendLine(" Files Information:"); + builder.AppendLine(" -------------------------"); + if (files == null || files.Length == 0) + { + builder.AppendLine(" No files"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < files.Length; i++) + { + builder.AppendLine($" File {i}"); + var file = files[i]; + if (file == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(file.NameOffset, " Name offset"); + builder.AppendLine(file.Name, " Name"); + builder.AppendLine(file.Offset, " Offset"); + builder.AppendLine(file.SizeOnDisk, " Size on disk"); + builder.AppendLine(file.Size, " Size"); + builder.AppendLine(file.TimeModified, " Time modified"); + builder.AppendLine(file.Dummy0, " Dummy 0"); + builder.AppendLine(file.Type, " Type"); + builder.AppendLine(file.CRC32, " CRC32"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, File7[] files) +#else + private static void Print(StringBuilder builder, File7?[]? files) +#endif + { + builder.AppendLine(" Files Information:"); + builder.AppendLine(" -------------------------"); + if (files == null || files.Length == 0) + { + builder.AppendLine(" No files"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < files.Length; i++) + { + builder.AppendLine($" File {i}"); + var file = files[i]; + if (file == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(file.NameOffset, " Name offset"); + builder.AppendLine(file.Name, " Name"); + builder.AppendLine(file.Offset, " Offset"); + builder.AppendLine(file.SizeOnDisk, " Size on disk"); + builder.AppendLine(file.Size, " Size"); + builder.AppendLine(file.TimeModified, " Time modified"); + builder.AppendLine(file.Dummy0, " Dummy 0"); + builder.AppendLine(file.Type, " Type"); + builder.AppendLine(file.CRC32, " CRC32"); + builder.AppendLine(file.HashOffset, " Hash offset"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/SabreTools.Printing.csproj b/SabreTools.Printing.csproj index 1e79684..9a5b12b 100644 --- a/SabreTools.Printing.csproj +++ b/SabreTools.Printing.csproj @@ -1,35 +1,37 @@  - - - net48;net6.0;net7.0;net8.0 - win-x86;win-x64;linux-x64;osx-x64 - 1.1.0 - true + + + net48;net6.0;net7.0;net8.0 + win-x86;win-x64;linux-x64;osx-x64 + 1.1.0 + true + + + Matt Nadareski + Pretty-printing library for various models + Copyright (c) Matt Nadareski 2022-2023 + https://github.com/SabreTools/ + README.md + https://github.com/SabreTools/SabreTools.Printing + git + stringbuilder print pretty + MIT + + + + enable + + + + + + + + + + + + - - Matt Nadareski - Pretty-printing library for various models - Copyright (c) Matt Nadareski 2022-2023 - https://github.com/SabreTools/ - README.md - https://github.com/SabreTools/SabreTools.Printing - git - stringbuilder print pretty - MIT - - - - enable - - - - - - - - - - - - \ No newline at end of file + diff --git a/VBSP.cs b/VBSP.cs new file mode 100644 index 0000000..e887a52 --- /dev/null +++ b/VBSP.cs @@ -0,0 +1,85 @@ +using System.Text; +using SabreTools.Models.VBSP; +using static SabreTools.Models.VBSP.Constants; + +namespace SabreTools.Printing +{ + public static class VBSP + { + public static void Print(StringBuilder builder, File file) + { + builder.AppendLine("VBSP Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, file.Header); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.Version, " Version"); + builder.AppendLine(header.MapRevision, " Map revision"); + builder.AppendLine(); + + Print(builder, header.Lumps); + } + +#if NET48 + private static void Print(StringBuilder builder, Lump[] lumps) +#else + private static void Print(StringBuilder builder, Lump?[]? lumps) +#endif + { + builder.AppendLine(" Lumps Information:"); + builder.AppendLine(" -------------------------"); + if (lumps == null || lumps.Length == 0) + { + builder.AppendLine(" No lumps"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < lumps.Length; i++) + { + var lump = lumps[i]; + string specialLumpName = string.Empty; + switch (i) + { + case HL_VBSP_LUMP_ENTITIES: + specialLumpName = " (entities)"; + break; + case HL_VBSP_LUMP_PAKFILE: + specialLumpName = " (pakfile)"; + break; + } + + builder.AppendLine($" Lump {i}{specialLumpName}"); + if (lump == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(lump.Offset, " Offset"); + builder.AppendLine(lump.Length, " Length"); + builder.AppendLine(lump.Version, " Version"); + builder.AppendLine(lump.FourCC, " 4CC"); + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/VPK.cs b/VPK.cs new file mode 100644 index 0000000..6cd2b1b --- /dev/null +++ b/VPK.cs @@ -0,0 +1,155 @@ +using System.Text; +using SabreTools.Models.VPK; + +namespace SabreTools.Printing +{ + public static class VPK + { + public static void Print(StringBuilder builder, File file) + { + builder.AppendLine("VPK Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, file.Header); + Print(builder, file.ExtendedHeader); + Print(builder, file.ArchiveHashes); + Print(builder, file.DirectoryItems); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.Version, " Version"); + builder.AppendLine(header.DirectoryLength, " Directory length"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ExtendedHeader header) +#else + private static void Print(StringBuilder builder, ExtendedHeader? header) +#endif + { + builder.AppendLine(" Extended Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No extended header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Dummy0, " Dummy 0"); + builder.AppendLine(header.ArchiveHashLength, " Archive hash length"); + builder.AppendLine(header.ExtraLength, " Extra length"); + builder.AppendLine(header.Dummy1, " Dummy 1"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, ArchiveHash[] entries) +#else + private static void Print(StringBuilder builder, ArchiveHash?[]? entries) +#endif + { + builder.AppendLine(" Archive Hashes Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No archive hashes"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Archive Hash {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.ArchiveIndex, " Archive index"); + builder.AppendLine(entry.ArchiveOffset, " Archive offset"); + builder.AppendLine(entry.Length, " Length"); + builder.AppendLine(entry.Hash, " Hash"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryItem[] entries) +#else + private static void Print(StringBuilder builder, DirectoryItem?[]? entries) +#endif + { + builder.AppendLine(" Directory Items Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Item {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + builder.AppendLine(); + continue; + } + + builder.AppendLine(entry.Extension, " Extension"); + builder.AppendLine(entry.Path, " Path"); + builder.AppendLine(entry.Name, " Name"); + builder.AppendLine(); + + Print(builder, entry.DirectoryEntry); + // TODO: Print out preload data? + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryEntry entry) +#else + private static void Print(StringBuilder builder, DirectoryEntry? entry) +#endif + { + builder.AppendLine(" Directory Entry:"); + builder.AppendLine(" -------------------------"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + return; + } + + builder.AppendLine(entry.CRC, " CRC"); + builder.AppendLine(entry.PreloadBytes, " Preload bytes"); + builder.AppendLine(entry.ArchiveIndex, " Archive index"); + builder.AppendLine(entry.EntryOffset, " Entry offset"); + builder.AppendLine(entry.EntryLength, " Entry length"); + builder.AppendLine(entry.Dummy0, " Dummy 0"); + } + } +} \ No newline at end of file diff --git a/WAD.cs b/WAD.cs new file mode 100644 index 0000000..a2e3621 --- /dev/null +++ b/WAD.cs @@ -0,0 +1,114 @@ +using System.Text; +using SabreTools.Models.WAD; + +namespace SabreTools.Printing +{ + public static class WAD + { + public static void Print(StringBuilder builder, File file) + { + builder.AppendLine("WAD Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, file.Header); + Print(builder, file.Lumps); + Print(builder, file.LumpInfos); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.LumpCount, " Lump count"); + builder.AppendLine(header.LumpOffset, " Lump offset"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Lump[] entries) +#else + private static void Print(StringBuilder builder, Lump?[]? entries) +#endif + { + builder.AppendLine(" Lumps Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No lumps"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Lump {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.Offset, " Offset"); + builder.AppendLine(entry.DiskLength, " Disk length"); + builder.AppendLine(entry.Length, " Length"); + builder.AppendLine(entry.Type, " Type"); + builder.AppendLine(entry.Compression, " Compression"); + builder.AppendLine(entry.Padding0, " Padding 0"); + builder.AppendLine(entry.Padding1, " Padding 1"); + builder.AppendLine(entry.Name, " Name"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, LumpInfo[] entries) +#else + private static void Print(StringBuilder builder, LumpInfo?[]? entries) +#endif + { + builder.AppendLine(" Lump Infos Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No lump infos"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Lump Info {i}"); + if (entry == null) + { + builder.AppendLine(" Lump is compressed"); + continue; + } + + builder.AppendLine(entry.Name, " Name"); + builder.AppendLine(entry.Width, " Width"); + builder.AppendLine(entry.Height, " Height"); + builder.AppendLine(entry.PixelOffset, " Pixel offset"); + // TODO: Print unknown data? + // TODO: Print pixel data? + builder.AppendLine(entry.PaletteSize, " Palette size"); + // TODO: Print palette data? + } + builder.AppendLine(); + } + } +} \ No newline at end of file diff --git a/XZP.cs b/XZP.cs new file mode 100644 index 0000000..58ad678 --- /dev/null +++ b/XZP.cs @@ -0,0 +1,164 @@ +using System.Text; +using SabreTools.Models.XZP; + +namespace SabreTools.Printing +{ + public static class XZP + { + public static void Print(StringBuilder builder, File file) + { + builder.AppendLine("XZP Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, file.Header); + Print(builder, file.DirectoryEntries, "Directory"); + Print(builder, file.PreloadDirectoryEntries, "Preload Directory"); + Print(builder, file.PreloadDirectoryMappings); + Print(builder, file.DirectoryItems); + Print(builder, file.Footer); + } + +#if NET48 + private static void Print(StringBuilder builder, Header header) +#else + private static void Print(StringBuilder builder, Header? header) +#endif + { + builder.AppendLine(" Header Information:"); + builder.AppendLine(" -------------------------"); + if (header == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine(header.Version, " Version"); + builder.AppendLine(header.PreloadDirectoryEntryCount, " Preload directory entry count"); + builder.AppendLine(header.DirectoryEntryCount, " Directory entry count"); + builder.AppendLine(header.PreloadBytes, " Preload bytes"); + builder.AppendLine(header.HeaderLength, " Header length"); + builder.AppendLine(header.DirectoryItemCount, " Directory item count"); + builder.AppendLine(header.DirectoryItemOffset, " Directory item offset"); + builder.AppendLine(header.DirectoryItemLength, " Directory item length"); + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryEntry[] entries, string prefix) +#else + private static void Print(StringBuilder builder, DirectoryEntry?[]? entries, string prefix) +#endif + { + builder.AppendLine($" {prefix} Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Entry {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.FileNameCRC, " File name CRC"); + builder.AppendLine(entry.EntryLength, " Entry length"); + builder.AppendLine(entry.EntryOffset, " Entry offset"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryMapping[] entries) +#else + private static void Print(StringBuilder builder, DirectoryMapping?[]? entries) +#endif + { + builder.AppendLine(" Preload Directory Mappings Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No preload directory mappings"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Mapping {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.PreloadDirectoryEntryIndex, " Preload directory entry index"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, DirectoryItem[] entries) +#else + private static void Print(StringBuilder builder, DirectoryItem?[]? entries) +#endif + { + builder.AppendLine(" Directory Items Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Length == 0) + { + builder.AppendLine(" No directory items"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + builder.AppendLine($" Directory Item {i}"); + if (entry == null) + { + builder.AppendLine(" [NULL]"); + continue; + } + + builder.AppendLine(entry.FileNameCRC, " File name CRC"); + builder.AppendLine(entry.NameOffset, " Name offset"); + builder.AppendLine(entry.Name, " Name"); + builder.AppendLine(entry.TimeCreated, " Time created"); + } + builder.AppendLine(); + } + +#if NET48 + private static void Print(StringBuilder builder, Footer footer) +#else + private static void Print(StringBuilder builder, Footer? footer) +#endif + { + builder.AppendLine(" Footer Information:"); + builder.AppendLine(" -------------------------"); + if (footer == null) + { + builder.AppendLine(" No header"); + builder.AppendLine(); + return; + } + + builder.AppendLine(footer.FileLength, " File length"); + builder.AppendLine(footer.Signature, " Signature"); + builder.AppendLine(); + } + } +} \ No newline at end of file