Fix tar parsing and printing

This commit is contained in:
Matt Nadareski
2025-08-28 09:11:04 -04:00
parent ab44aa7e68
commit c9a462a1c6
5 changed files with 170 additions and 13 deletions

View File

@@ -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<Block> 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;
}
/// <summary>
/// Parse a Stream into a Block
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Block on success, null on error</returns>
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;
}
}
}

View File

@@ -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;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.GZip item)
{
var builder = new StringBuilder();
GZip.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
@@ -449,6 +463,16 @@ namespace SabreTools.Serialization
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.TapeArchive item)
{
var builder = new StringBuilder();
TapeArchive.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>

View File

@@ -0,0 +1,22 @@
using System.Text;
using SabreTools.Models.GZIP;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class GZip : IPrinter<Archive>
{
/// <inheritdoc/>
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
}
}
}

View File

@@ -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<Archive>
{
/// <inheritdoc/>
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<Entry>? 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();
}
}
}

View File

@@ -666,7 +666,7 @@ namespace SabreTools.Serialization.Wrappers
return WrapperType.TapeArchive;
if (extension.Equals("tar", StringComparison.OrdinalIgnoreCase))
return WrapperType.SevenZip;
return WrapperType.TapeArchive;
#endregion