From 6b7b05eb316e15206708f7851de10a67bd8b879e Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 29 Sep 2025 22:04:10 -0400 Subject: [PATCH] Port some extensions for GZip from ST --- SabreTools.Serialization/Wrappers/GZip.cs | 100 ++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/SabreTools.Serialization/Wrappers/GZip.cs b/SabreTools.Serialization/Wrappers/GZip.cs index cacf716c..c21d10cc 100644 --- a/SabreTools.Serialization/Wrappers/GZip.cs +++ b/SabreTools.Serialization/Wrappers/GZip.cs @@ -1,5 +1,6 @@ using System.IO; using SabreTools.Data.Models.GZIP; +using SabreTools.IO.Extensions; namespace SabreTools.Serialization.Wrappers { @@ -14,6 +15,60 @@ namespace SabreTools.Serialization.Wrappers #region Extension Properties + /// + /// Content CRC-32 as stored in the extra field + /// + /// Only guaranteed for Torrent GZip format + public byte[]? ContentCrc32 + { + get + { + // Only valid for Torrent GZip + if (Header == null || !IsTorrentGZip) + return null; + + // CRC-32 is the second packed field + int extraIndex = 0x10; + return Header.ExtraFieldBytes.ReadBytes(ref extraIndex, 0x04); + } + } + + /// + /// Content MD5 as stored in the extra field + /// + /// Only guaranteed for Torrent GZip format + public byte[]? ContentMd5 + { + get + { + // Only valid for Torrent GZip + if (Header == null || !IsTorrentGZip) + return null; + + // MD5 is the first packed field + int extraIndex = 0x00; + return Header.ExtraFieldBytes.ReadBytes(ref extraIndex, 0x10); + } + } + + /// + /// Content size as stored in the extra field + /// + /// Only guaranteed for Torrent GZip format + public ulong ContentSize + { + get + { + // Only valid for Torrent GZip + if (Header == null || !IsTorrentGZip) + return 0; + + // MD5 is the first packed field + int extraIndex = 0x00; + return Header.ExtraFieldBytes.ReadUInt64LittleEndian(ref extraIndex); + } + } + /// /// Offset to the compressed data /// @@ -54,6 +109,51 @@ namespace SabreTools.Serialization.Wrappers /// public Header? Header => Model.Header; + /// + /// Indicates if the archive is in the standard + /// "Torrent GZip" format. This format is used by + /// some programs to store extended hashes in the + /// header while maintaining the format otherwise. + /// + public bool IsTorrentGZip + { + get + { + // If the header is invalid + if (Header == null) + return false; + + // Torrent GZip uses normal deflate, not GZIP deflate + if (Header.CompressionMethod != CompressionMethod.Deflate) + return false; + + // Only the extra field should be present + if (Header.Flags != Flags.FEXTRA) + return false; + + // The modification should be 0x00000000, but some implementations + // do not set this correctly, so it is skipped. + + // No extra flags are set + if (Header.ExtraFlags != 0x00) + return false; + + // The OS should be FAT, regardless of the original platform, but + // some implementations do not set this correctly, so it is skipped. + + // The extra field is non-standard, using the following format: + // - 0x00-0x0F - MD5 hash of the internal file + // - 0x10-0x13 - CRC-32 checksum of the internal file + // - 0x14-0x1B - Little-endian file size of the internal file + if (Header.ExtraLength != 0x1C) + return false; + if (Header.ExtraFieldBytes == null || Header.ExtraFieldBytes.Length != 0x1C) + return false; + + return true; + } + } + /// public Trailer? Trailer => Model.Trailer;