diff --git a/SabreTools.Serialization/Deserializers/TapeArchive.cs b/SabreTools.Serialization/Deserializers/TapeArchive.cs index 46b95ccc..c9d7faae 100644 --- a/SabreTools.Serialization/Deserializers/TapeArchive.cs +++ b/SabreTools.Serialization/Deserializers/TapeArchive.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -65,19 +66,38 @@ namespace SabreTools.Serialization.Deserializers obj.Header = header; + // TODO: Replace this with AlignToBoundary when IO is updated + // Align to the block size + while (data.Position % 512 != 0 && data.Position < data.Length) + { + _ = data.ReadByteValue(); + } + #endregion #region Blocks - List blocks = []; + // Exit if the size is invalid + string sizeOctalString = Encoding.ASCII.GetString(header.Size!).TrimEnd('\0'); + if (sizeOctalString.Length == 0) + return obj; - // TODO: Implement - // Each block is 512 bytes of data. The size in the entry - // header is in octal string representation. To the best - // of my knowledge, the number of blocks is just the - // ceiling(size / 512). That is going to be how this is - // implemented. + // Get the block count from the size + int octalSize = Convert.ToInt32(sizeOctalString, 8); + int blockCount = (int)Math.Ceiling((decimal)octalSize / 512); + // Read all blocks sequentially + var blocks = new Block[blockCount]; + for (int i = 0; i < blocks.Length; i++) + { + var block = ParseBlock(data); + if (block == null) + break; + + blocks[i] = block; + } + + // TODO: Make this a direct assignment when Models is updated obj.Blocks = [.. blocks]; #endregion @@ -110,14 +130,14 @@ namespace SabreTools.Serialization.Deserializers if (data.Position >= data.Length) return obj; - // Peek at the next 6 bytes - byte[] temp = data.ReadBytes(6); + // Peek at the next 5 bytes + byte[] temp = data.ReadBytes(5); string tempString = Encoding.ASCII.GetString(temp); - data.Seek(-6, SeekOrigin.Current); - if (tempString != "ustar\0") + data.Seek(-5, SeekOrigin.Current); + if (tempString != "ustar") return obj; - byte[] magicBytes = data.ReadBytes(6); + byte[] magicBytes = data.ReadBytes(5); obj.Magic = Encoding.ASCII.GetString(magicBytes); byte[] versionBytes = data.ReadBytes(3); obj.Version = Encoding.ASCII.GetString(versionBytes); @@ -134,5 +154,24 @@ namespace SabreTools.Serialization.Deserializers return obj; } + + /// + /// Parse a Stream into a Block + /// + /// Stream to parse + /// Filled Block on success, null on error + public static Block? ParseBlock(Stream data) + { + // Handle incomplete blocks + if (data.Position + 512 >= data.Length) + return null; + + var obj = new Block(); + + // TODO: Assign this to obj.Data when Models is updated + _ = data.ReadBytes(512); + + return obj; + } } } diff --git a/SabreTools.Serialization/Printer.cs b/SabreTools.Serialization/Printer.cs index 3e9167f8..0de809b8 100644 --- a/SabreTools.Serialization/Printer.cs +++ b/SabreTools.Serialization/Printer.cs @@ -42,6 +42,7 @@ namespace SabreTools.Serialization Wrapper.CHD item => item.PrettyPrint(), Wrapper.CIA item => item.PrettyPrint(), Wrapper.GCF item => item.PrettyPrint(), + Wrapper.GZip item => item.PrettyPrint(), Wrapper.InstallShieldArchiveV3 item => item.PrettyPrint(), Wrapper.InstallShieldCabinet item => item.PrettyPrint(), Wrapper.IRD item => item.PrettyPrint(), @@ -66,6 +67,7 @@ namespace SabreTools.Serialization Wrapper.Quantum item => item.PrettyPrint(), Wrapper.SecuROMDFA item => item.PrettyPrint(), Wrapper.SGA item => item.PrettyPrint(), + Wrapper.TapeArchive item => item.PrettyPrint(), Wrapper.VBSP item => item.PrettyPrint(), Wrapper.VPK item => item.PrettyPrint(), Wrapper.WAD3 item => item.PrettyPrint(), @@ -92,6 +94,7 @@ namespace SabreTools.Serialization Wrapper.CHD item => item.ExportJSON(), Wrapper.CIA item => item.ExportJSON(), Wrapper.GCF item => item.ExportJSON(), + Wrapper.GZip item => item.ExportJSON(), Wrapper.InstallShieldArchiveV3 item => item.ExportJSON(), Wrapper.InstallShieldCabinet item => item.ExportJSON(), Wrapper.IRD item => item.ExportJSON(), @@ -116,6 +119,7 @@ namespace SabreTools.Serialization Wrapper.Quantum item => item.ExportJSON(), Wrapper.SecuROMDFA item => item.ExportJSON(), Wrapper.SGA item => item.ExportJSON(), + Wrapper.TapeArchive item => item.ExportJSON(), Wrapper.VBSP item => item.ExportJSON(), Wrapper.VPK item => item.ExportJSON(), Wrapper.WAD3 item => item.ExportJSON(), @@ -209,6 +213,16 @@ namespace SabreTools.Serialization return builder; } + /// + /// Export the item information as pretty-printed text + /// + private static StringBuilder PrettyPrint(this Wrapper.GZip item) + { + var builder = new StringBuilder(); + GZip.Print(builder, item.Model); + return builder; + } + /// /// Export the item information as pretty-printed text /// @@ -449,6 +463,16 @@ namespace SabreTools.Serialization return builder; } + /// + /// Export the item information as pretty-printed text + /// + private static StringBuilder PrettyPrint(this Wrapper.TapeArchive item) + { + var builder = new StringBuilder(); + TapeArchive.Print(builder, item.Model); + return builder; + } + /// /// Export the item information as pretty-printed text /// diff --git a/SabreTools.Serialization/Printers/GZip.cs b/SabreTools.Serialization/Printers/GZip.cs new file mode 100644 index 00000000..2f71cc10 --- /dev/null +++ b/SabreTools.Serialization/Printers/GZip.cs @@ -0,0 +1,22 @@ +using System.Text; +using SabreTools.Models.GZIP; +using SabreTools.Serialization.Interfaces; + +namespace SabreTools.Serialization.Printers +{ + public class GZip : IPrinter + { + /// + public void PrintInformation(StringBuilder builder, Archive model) + => Print(builder, model); + + public static void Print(StringBuilder builder, Archive file) + { + builder.AppendLine("gzip Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + // TODO: Implement + } + } +} diff --git a/SabreTools.Serialization/Printers/TapeArchive.cs b/SabreTools.Serialization/Printers/TapeArchive.cs new file mode 100644 index 00000000..497c0a3d --- /dev/null +++ b/SabreTools.Serialization/Printers/TapeArchive.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Text; +using SabreTools.Models.TAR; +using SabreTools.Serialization.Interfaces; + +namespace SabreTools.Serialization.Printers +{ + public class TapeArchive : IPrinter + { + /// + public void PrintInformation(StringBuilder builder, Archive model) + => Print(builder, model); + + public static void Print(StringBuilder builder, Archive file) + { + builder.AppendLine("Tape Archive Information:"); + builder.AppendLine("-------------------------"); + builder.AppendLine(); + + Print(builder, file.Entries); + } + + // TODO: Fix the entry type when Models is updated + //private static void Print(StringBuilder builder, Entry[]? entries) + private static void Print(StringBuilder builder, List? entries) + { + builder.AppendLine(" Entries Information:"); + builder.AppendLine(" -------------------------"); + if (entries == null || entries.Count == 0) + { + builder.AppendLine(" No entries"); + builder.AppendLine(); + return; + } + + for (int i = 0; i < entries.Count; i++) + { + var entry = entries[i]; + + builder.AppendLine($" Entry {i}"); + + var header = entry.Header; + if (header == null) + { + builder.AppendLine(" Header: [NULL]"); + } + else + { + builder.AppendLine(" Header:"); + builder.AppendLine(header.FileName?.TrimEnd('\0'), " File name"); + builder.AppendLine(header.Mode, " Mode"); + builder.AppendLine(header.UID, " UID"); + builder.AppendLine(header.GID, " GID"); + builder.AppendLine(header.Size, " Size"); + builder.AppendLine(header.ModifiedTime, " Modified time"); + builder.AppendLine(header.Checksum, " Checksum"); + builder.AppendLine($" Type flag: {header.TypeFlag} (0x{(byte)header.TypeFlag:X2})"); + builder.AppendLine(header.LinkName?.TrimEnd('\0'), " Link name"); + builder.AppendLine(header.Magic, " Magic"); + builder.AppendLine(header.Version?.TrimEnd('\0'), " Version"); + builder.AppendLine(header.UserName?.TrimEnd('\0'), " User name"); + builder.AppendLine(header.GroupName?.TrimEnd('\0'), " Group name"); + builder.AppendLine(header.DevMajor?.TrimEnd('\0'), " Device major"); + builder.AppendLine(header.DevMinor?.TrimEnd('\0'), " Device minor"); + builder.AppendLine(header.Prefix?.TrimEnd('\0'), " Prefix"); + } + } + + builder.AppendLine(); + } + } +} diff --git a/SabreTools.Serialization/Wrappers/WrapperFactory.cs b/SabreTools.Serialization/Wrappers/WrapperFactory.cs index a9136bf1..9bfc4cac 100644 --- a/SabreTools.Serialization/Wrappers/WrapperFactory.cs +++ b/SabreTools.Serialization/Wrappers/WrapperFactory.cs @@ -666,7 +666,7 @@ namespace SabreTools.Serialization.Wrappers return WrapperType.TapeArchive; if (extension.Equals("tar", StringComparison.OrdinalIgnoreCase)) - return WrapperType.SevenZip; + return WrapperType.TapeArchive; #endregion