diff --git a/SabreTools.Serialization/Printer.cs b/SabreTools.Serialization/Printer.cs index c6fea58d..48c3a5f6 100644 --- a/SabreTools.Serialization/Printer.cs +++ b/SabreTools.Serialization/Printer.cs @@ -54,6 +54,7 @@ namespace SabreTools.Serialization Wrapper.PAK item => item.PrettyPrint(), Wrapper.PFF item => item.PrettyPrint(), Wrapper.PIC item => item.PrettyPrint(), + Wrapper.PKZIP item => item.PrettyPrint(), Wrapper.PlayJAudioFile item => item.PrettyPrint(), Wrapper.PlayJPlaylist item => item.PrettyPrint(), Wrapper.PortableExecutable item => item.PrettyPrint(), @@ -97,6 +98,7 @@ namespace SabreTools.Serialization Wrapper.PAK item => item.ExportJSON(), Wrapper.PFF item => item.ExportJSON(), Wrapper.PIC item => item.ExportJSON(), + Wrapper.PKZIP item => item.ExportJSON(), Wrapper.PlayJAudioFile item => item.ExportJSON(), Wrapper.PlayJPlaylist item => item.ExportJSON(), Wrapper.PortableExecutable item => item.ExportJSON(), @@ -314,6 +316,16 @@ namespace SabreTools.Serialization PIC.Print(builder, item.Model); return builder; } + + /// + /// Export the item information as pretty-printed text + /// + private static StringBuilder PrettyPrint(this Wrapper.PKZIP item) + { + var builder = new StringBuilder(); + PKZIP.Print(builder, item.Model); + return builder; + } /// /// Export the item information as pretty-printed text diff --git a/SabreTools.Serialization/Printers/PKZIP.cs b/SabreTools.Serialization/Printers/PKZIP.cs new file mode 100644 index 00000000..c001c36c --- /dev/null +++ b/SabreTools.Serialization/Printers/PKZIP.cs @@ -0,0 +1,279 @@ +using System.Text; +using SabreTools.Models.PKZIP; +using SabreTools.Serialization.Interfaces; + +namespace SabreTools.Serialization.Printers +{ + public class PKZIP : IPrinter + { + /// + public void PrintInformation(StringBuilder builder, Archive model) + => Print(builder, model); + + public static void Print(StringBuilder builder, Archive archive) + { + builder.AppendLine("PKZIP Archive (or Derived Format) Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, archive.EndOfCentralDirectoryRecord); + Print(builder, archive.ZIP64EndOfCentralDirectoryLocator); + Print(builder, archive.ZIP64EndOfCentralDirectoryRecord); + Print(builder, archive.CentralDirectoryHeaders); + Print(builder, archive.ArchiveExtraDataRecord); + Print(builder, + archive.LocalFileHeaders, + archive.EncryptionHeaders, + archive.FileData, + archive.DataDescriptors, + archive.ZIP64DataDescriptors); + } + + private static void Print(StringBuilder builder, EndOfCentralDirectoryRecord? record) + { + builder.AppendLine(" End of Central Directory Record Information:"); + builder.AppendLine(" -------------------------"); + if (record == null) + { + builder.AppendLine(" No end of central directory record"); + builder.AppendLine(); + return; + } + + builder.AppendLine(record.Signature, " Signature"); + builder.AppendLine(record.DiskNumber, " Disk number"); + builder.AppendLine(record.StartDiskNumber, " Start disk number"); + builder.AppendLine(record.TotalEntriesOnDisk, " Total entries on disk"); + builder.AppendLine(record.TotalEntries, " Total entries"); + builder.AppendLine(record.CentralDirectorySize, " Central directory size"); + builder.AppendLine(record.CentralDirectoryOffset, " Central directory offset"); + builder.AppendLine(record.FileCommentLength, " File comment length"); + builder.AppendLine(record.FileComment, " File comment"); + builder.AppendLine(); + } + + private static void Print(StringBuilder builder, EndOfCentralDirectoryLocator64? locator) + { + builder.AppendLine(" ZIP64 End of Central Directory Locator Information:"); + builder.AppendLine(" -------------------------"); + if (locator == null) + { + builder.AppendLine(" No ZIP64 end of central directory locator"); + builder.AppendLine(); + return; + } + + builder.AppendLine(locator.Signature, " Signature"); + builder.AppendLine(locator.StartDiskNumber, " Start disk number"); + builder.AppendLine(locator.CentralDirectoryOffset, " Central directory offset"); + builder.AppendLine(locator.TotalDisks, " Total disks"); + builder.AppendLine(); + } + + private static void Print(StringBuilder builder, EndOfCentralDirectoryRecord64? record) + { + builder.AppendLine(" ZIP64 End of Central Directory Record Information:"); + builder.AppendLine(" -------------------------"); + if (record == null) + { + builder.AppendLine(" No ZIP64 end of central directory record"); + builder.AppendLine(); + return; + } + + builder.AppendLine(record.Signature, " Signature"); + builder.AppendLine(record.DirectoryRecordSize, " Directory record size"); + builder.AppendLine($" Host system: {record.HostSystem} (0x{record.HostSystem:X})"); + builder.AppendLine(record.VersionMadeBy, " Version made by"); + builder.AppendLine(record.VersionNeededToExtract, " Version needed to extract"); + builder.AppendLine(record.DiskNumber, " Disk number"); + builder.AppendLine(record.StartDiskNumber, " Start disk number"); + builder.AppendLine(record.TotalEntriesOnDisk, " Total entries on disk"); + builder.AppendLine(record.TotalEntries, " Total entries"); + builder.AppendLine(record.CentralDirectorySize, " Central directory size"); + builder.AppendLine(record.CentralDirectoryOffset, " Central directory offset"); + //builder.AppendLine(record.ExtensibleDataSector, " Extensible data sector"); + builder.AppendLine(); + } + + private static void Print(StringBuilder builder, CentralDirectoryFileHeader?[]? headers) + { + builder.AppendLine(" Central Directory File Headers Information:"); + builder.AppendLine(" -------------------------"); + if (headers == null || headers.Length == 0) + { + builder.AppendLine(" No central directory file headers"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < headers.Length; i++) + { + var record = headers[i]; + Print(builder, record, i); + } + builder.AppendLine(); + } + + private static void Print(StringBuilder builder, CentralDirectoryFileHeader? header, int index) + { + builder.AppendLine($" Central Directory File Header Entry {index}"); + if (header == null) + { + builder.AppendLine(" [NULL]"); + return; + } + + builder.AppendLine(header.Signature, " Signature"); + builder.AppendLine($" Host system: {header.HostSystem} (0x{header.HostSystem:X})"); + builder.AppendLine(header.VersionMadeBy, " Version made by"); + builder.AppendLine(header.VersionNeededToExtract, " Version needed to extract"); + builder.AppendLine($" Flags: {header.Flags} (0x{header.Flags:X})"); + builder.AppendLine($" Compression method: {header.CompressionMethod} (0x{header.CompressionMethod:X})"); + builder.AppendLine(header.LastModifedFileTime, " Last modified file time"); // TODO: Parse from MS-DOS + builder.AppendLine(header.LastModifiedFileDate, " Last modified file date"); // TODO: Parse from MS-DOS + builder.AppendLine(header.CRC32, " CRC-32"); + builder.AppendLine(header.CompressedSize, " Compressed size"); + builder.AppendLine(header.UncompressedSize, " Uncompressed size"); + builder.AppendLine(header.FileNameLength, " File name length"); + builder.AppendLine(header.ExtraFieldLength, " Extra field length"); + builder.AppendLine(header.FileCommentLength, " File comment length"); + builder.AppendLine(header.DiskNumberStart, " Disk number start"); + builder.AppendLine($" Internal file attributes: {header.InternalFileAttributes} (0x{header.InternalFileAttributes:X})"); + builder.AppendLine(header.ExternalFileAttributes, " External file attributes"); + builder.AppendLine(header.RelativeOffsetOfLocalHeader, " Relative offset of local header"); + builder.AppendLine(header.FileName, " File name"); + builder.AppendLine(header.ExtraField, " Extra field"); + builder.AppendLine(header.FileComment, " File comment"); + } + + private static void Print(StringBuilder builder, ArchiveExtraDataRecord? record) + { + builder.AppendLine(" Archive Extra Data Record Information:"); + builder.AppendLine(" -------------------------"); + if (record == null) + { + builder.AppendLine(" No archive extra data record"); + builder.AppendLine(); + return; + } + + builder.AppendLine(record.Signature, " Signature"); + builder.AppendLine(record.ExtraFieldLength, " Extra field length"); + builder.AppendLine(record.ExtraFieldData, " Extra field data"); + builder.AppendLine(); + } + + private static void Print(StringBuilder builder, + LocalFileHeader[]? localFileHeaders, + byte[]?[]? encryptionHeaders, + byte[][]? fileData, + DataDescriptor?[]? dataDescriptors, + DataDescriptor64?[]? zip64DataDescriptors) + { + builder.AppendLine(" Local File Information:"); + builder.AppendLine(" -------------------------"); + if (localFileHeaders == null || localFileHeaders.Length == 0) + { + builder.AppendLine(" No local files"); + builder.AppendLine(); + return; + } + + if (encryptionHeaders == null || localFileHeaders.Length > encryptionHeaders.Length + || fileData == null || localFileHeaders.Length > fileData.Length + || dataDescriptors == null || localFileHeaders.Length > dataDescriptors.Length + || zip64DataDescriptors == null || localFileHeaders.Length > zip64DataDescriptors.Length) + { + builder.AppendLine(" Mismatch in local file array values"); + builder.AppendLine(); + } + + for (int i = 0; i < localFileHeaders.Length; i++) + { + var localFileHeader = localFileHeaders[i]; + var encryptionHeader = encryptionHeaders != null && i < encryptionHeaders.Length - 1 ? encryptionHeaders[i] : null; + var fileDatum = fileData != null && i < fileData.Length - 1 ? fileData[i] : null; + var dataDescriptor = dataDescriptors != null && i < dataDescriptors.Length - 1 ? dataDescriptors[i] : null; + var zip64DataDescriptor = zip64DataDescriptors != null && i < zip64DataDescriptors.Length - 1 ? zip64DataDescriptors[i] : null; + + Print(builder, localFileHeader, encryptionHeader, fileDatum, dataDescriptor, zip64DataDescriptor, i); + } + builder.AppendLine(); + } + + private static void Print(StringBuilder builder, + LocalFileHeader localFileHeader, + byte[]? encryptionHeader, + byte[]? fileData, + DataDescriptor? dataDescriptor, + DataDescriptor64? zip64DataDescriptor, + int index) + { + builder.AppendLine($" Local File Entry {index}"); + if (localFileHeader == null) + { + builder.AppendLine(" [NULL]"); + return; + } + + builder.AppendLine(localFileHeader.Signature, " [Local File Header] Signature"); + builder.AppendLine(localFileHeader.Version, " [Local File Header] Version"); + builder.AppendLine($" [Local File Header] Flags: {localFileHeader.Flags} (0x{localFileHeader.Flags:X})"); + builder.AppendLine($" [Local File Header] Compression method: {localFileHeader.CompressionMethod} (0x{localFileHeader.CompressionMethod:X})"); + builder.AppendLine(localFileHeader.LastModifedFileTime, " [Local File Header] Last modified file time"); // TODO: Parse from MS-DOS + builder.AppendLine(localFileHeader.LastModifiedFileDate, " [Local File Header] Last modified file date"); // TODO: Parse from MS-DOS + builder.AppendLine(localFileHeader.CRC32, " [Local File Header] CRC-32"); + builder.AppendLine(localFileHeader.CompressedSize, " [Local File Header] Compressed size"); + builder.AppendLine(localFileHeader.UncompressedSize, " [Local File Header] Uncompressed size"); + builder.AppendLine(localFileHeader.FileNameLength, " [Local File Header] File name length"); + builder.AppendLine(localFileHeader.ExtraFieldLength, " [Local File Header] Extra field length"); + builder.AppendLine(localFileHeader.FileName, " [Local File Header] File name"); + builder.AppendLine(localFileHeader.ExtraField, " [Local File Header] Extra field"); + + if (encryptionHeader == null) + { + builder.AppendLine(" [Encryption Header]: [NULL]"); + } + else + { + builder.AppendLine(encryptionHeader.Length, " [Encryption Header] Length"); + builder.AppendLine(encryptionHeader, " [Encryption Header] Data"); + } + + if (fileData == null) + { + builder.AppendLine(" [File Data]: [NULL]"); + } + else + { + builder.AppendLine(fileData.Length, " [File Data] Length"); + //builder.AppendLine(fileData, " [File Data] Data"); + } + + if (dataDescriptor == null) + { + builder.AppendLine(" [Data Descriptor]: [NULL]"); + } + else + { + builder.AppendLine(dataDescriptor.Signature, " [Data Descriptor] Signature"); + builder.AppendLine(dataDescriptor.CRC32, " [Data Descriptor] CRC-32"); + builder.AppendLine(dataDescriptor.CompressedSize, " [Data Descriptor] Compressed size"); + builder.AppendLine(dataDescriptor.UncompressedSize, " [Data Descriptor] Uncompressed size"); + } + + if (zip64DataDescriptor == null) + { + builder.AppendLine(" [ZIP64 Data Descriptor]: [NULL]"); + } + else + { + builder.AppendLine(zip64DataDescriptor.Signature, " [ZIP64 Data Descriptor] Signature"); + builder.AppendLine(zip64DataDescriptor.CRC32, " [ZIP64 Data Descriptor] CRC-32"); + builder.AppendLine(zip64DataDescriptor.CompressedSize, " [ZIP64 Data Descriptor] Compressed size"); + builder.AppendLine(zip64DataDescriptor.UncompressedSize, " [ZIP64 Data Descriptor] Uncompressed size"); + } + } + } +} \ No newline at end of file