using System.Collections.Generic; using System.IO; using SabreTools.Data.Models.GZIP; using SabreTools.IO.Extensions; using SabreTools.Numerics.Extensions; using SabreTools.Text.Extensions; using static SabreTools.Data.Models.GZIP.Constants; #pragma warning disable IDE0017 // Simplify object initialization namespace SabreTools.Serialization.Readers { public class GZip : BaseBinaryReader { /// public override Archive? Deserialize(Stream? data) { // If the data is invalid if (data is null || !data.CanRead) return null; try { // Cache the current offset long initialOffset = data.Position; var archive = new Archive(); #region Header var header = ParseHeader(data); if (header is null) return null; archive.Header = header; #endregion // Seek to the end to read the trailer data.SeekIfPossible(-8, SeekOrigin.End); #region Trailer var trailer = ParseTrailer(data); if (trailer is null) return null; archive.Trailer = trailer; #endregion return archive; } catch { // Ignore the actual error return null; } } /// /// Parse a Stream into a Header /// /// Stream to parse /// Filled Header on success, null on error public static Header? ParseHeader(Stream data) { var obj = new Header(); obj.ID1 = data.ReadByteValue(); obj.ID2 = data.ReadByteValue(); if (obj.ID1 != ID1 || obj.ID2 != ID2) return null; obj.CompressionMethod = (CompressionMethod)data.ReadByteValue(); obj.Flags = (Flags)data.ReadByteValue(); obj.LastModifiedTime = data.ReadUInt32LittleEndian(); obj.ExtraFlags = (ExtraFlags)data.ReadByteValue(); obj.OperatingSystem = (OperatingSystem)data.ReadByteValue(); #if NET20 || NET35 if ((obj.Flags & Flags.FEXTRA) != 0) #else if (obj.Flags.HasFlag(Flags.FEXTRA)) #endif { obj.ExtraLength = data.ReadUInt16LittleEndian(); // Cache the current position long currentPosition = data.Position; // Read the raw data first obj.ExtraFieldBytes = data.ReadBytes(obj.ExtraLength); data.SeekIfPossible(currentPosition, SeekOrigin.Begin); List extraFields = []; while (data.Position < currentPosition + obj.ExtraLength) { var extraField = ParseExtraFieldData(data); if (extraField is null) break; extraFields.Add(extraField); } obj.ExtraField = [.. extraFields]; } #if NET20 || NET35 if ((obj.Flags & Flags.FNAME) != 0) #else if (obj.Flags.HasFlag(Flags.FNAME)) #endif obj.OriginalFileName = data.ReadNullTerminatedAnsiString(); #if NET20 || NET35 if ((obj.Flags & Flags.FCOMMENT) != 0) #else if (obj.Flags.HasFlag(Flags.FCOMMENT)) #endif obj.FileComment = data.ReadNullTerminatedAnsiString(); #if NET20 || NET35 if ((obj.Flags & Flags.FHCRC) != 0) #else if (obj.Flags.HasFlag(Flags.FHCRC)) #endif obj.CRC16 = data.ReadUInt16LittleEndian(); return obj; } /// /// Parse a Stream into an ExtraFieldData /// /// Stream to parse /// Filled ExtraFieldData on success, null on error public static ExtraFieldData? ParseExtraFieldData(Stream data) { var obj = new ExtraFieldData(); obj.SubfieldID1 = data.ReadByteValue(); obj.SubfieldID2 = data.ReadByteValue(); obj.Length = data.ReadUInt16LittleEndian(); obj.Data = data.ReadBytes(obj.Length); return obj; } /// /// Parse a Stream into a Trailer /// /// Stream to parse /// Filled Trailer on success, null on error public static Trailer? ParseTrailer(Stream data) { var obj = new Trailer(); obj.CRC32 = data.ReadUInt32LittleEndian(); obj.InputSize = data.ReadUInt32LittleEndian(); return obj; } } }