using System.IO; using SabreTools.Data.Models.WIA; using SabreTools.IO.Extensions; using SabreTools.Numerics.Extensions; #pragma warning disable IDE0017 // Simplify object initialization namespace SabreTools.Serialization.Readers { public class WIA : BaseBinaryReader { /// public override DiscImage? Deserialize(Stream? data) { // If the data is invalid if (data is null || !data.CanRead) return null; // Need at least Header1 if (data.Length - data.Position < Constants.Header1Size) return null; try { long initialOffset = data.Position; var archive = new DiscImage(); // Parse Header1 archive.Header1 = ParseWiaHeader1(data); if (archive.Header1.Magic != Constants.WiaMagic && archive.Header1.Magic != Constants.RvzMagic) return null; // Parse Header2 archive.Header2 = ParseWiaHeader2(data); // Parse partition entries (Wii discs only) if (archive.Header2.NumberOfPartitionEntries > 0 && archive.Header2.PartitionEntriesOffset > 0) { data.Seek(initialOffset + (long)archive.Header2.PartitionEntriesOffset, SeekOrigin.Begin); archive.PartitionEntries = new PartitionEntry[archive.Header2.NumberOfPartitionEntries]; for (int i = 0; i < archive.PartitionEntries.Length; i++) { archive.PartitionEntries[i] = ParsePartitionEntry(data); ; } } // Parse raw data entries if (archive.Header2.NumberOfRawDataEntries > 0 && archive.Header2.RawDataEntriesOffset > 0) { data.Seek(initialOffset + (long)archive.Header2.RawDataEntriesOffset, SeekOrigin.Begin); archive.RawDataEntries = new RawDataEntry[archive.Header2.NumberOfRawDataEntries]; for (int i = 0; i < archive.Header2.NumberOfRawDataEntries; i++) { archive.RawDataEntries[i] = ParseRawDataEntry(data); } } // Parse group entries if (archive.Header2.NumberOfGroupEntries > 0 && archive.Header2.GroupEntriesOffset > 0) { data.Seek(initialOffset + (long)archive.Header2.GroupEntriesOffset, SeekOrigin.Begin); if (archive.Header1.Magic == Constants.RvzMagic) { archive.RvzGroupEntries = new RvzGroupEntry[archive.Header2.NumberOfGroupEntries]; for (int i = 0; i < archive.Header2.NumberOfGroupEntries; i++) { archive.RvzGroupEntries[i] = ParseRvzGroupEntry(data); } } else { archive.GroupEntries = new WiaGroupEntry[archive.Header2.NumberOfGroupEntries]; for (int i = 0; i < archive.Header2.NumberOfGroupEntries; i++) { archive.GroupEntries[i] = ParseWiaGroupEntry(data); ; } } } return archive; } catch { return null; } } /// /// Parse a Stream into a PartitionDataEntry /// /// Stream to parse /// Filled PartitionDataEntry on success, null on error public static PartitionDataEntry ParsePartitionDataEntry(Stream data) { var obj = new PartitionDataEntry(); obj.FirstSector = data.ReadUInt32BigEndian(); obj.NumberOfSectors = data.ReadUInt32BigEndian(); obj.GroupIndex = data.ReadUInt32BigEndian(); obj.NumberOfGroups = data.ReadUInt32BigEndian(); return obj; } /// /// Parse a Stream into a PartitionEntry /// /// Stream to parse /// Filled PartitionEntry on success, null on error public static PartitionEntry ParsePartitionEntry(Stream data) { var obj = new PartitionEntry(); obj.PartitionKey = data.ReadBytes(16); obj.DataEntry0 = ParsePartitionDataEntry(data); obj.DataEntry1 = ParsePartitionDataEntry(data); return obj; } /// /// Parse a Stream into a RawDataEntry /// /// Stream to parse /// Filled RawDataEntry on success, null on error public static RawDataEntry ParseRawDataEntry(Stream data) { var obj = new RawDataEntry(); obj.DataOffset = data.ReadUInt64BigEndian(); obj.DataSize = data.ReadUInt64BigEndian(); obj.GroupIndex = data.ReadUInt32BigEndian(); obj.NumberOfGroups = data.ReadUInt32BigEndian(); return obj; } /// /// Parse a byte array into a RawDataEntry /// /// Byte array to parse /// Filled RawDataEntry on success, null on error public static RawDataEntry ParseRawDataEntry(byte[] data, ref int offset) { var obj = new RawDataEntry(); obj.DataOffset = data.ReadUInt64BigEndian(ref offset); obj.DataSize = data.ReadUInt64BigEndian(ref offset); obj.GroupIndex = data.ReadUInt32BigEndian(ref offset); obj.NumberOfGroups = data.ReadUInt32BigEndian(ref offset); return obj; } /// /// Parse a Stream into a RvzGroupEntry /// /// Stream to parse /// Filled RvzGroupEntry on success, null on error public static RvzGroupEntry ParseRvzGroupEntry(Stream data) { var obj = new RvzGroupEntry(); obj.DataOffset = data.ReadUInt32BigEndian(); obj.DataSize = data.ReadUInt32BigEndian(); obj.RvzPackedSize = data.ReadUInt32BigEndian(); return obj; } /// /// Parse a byte array into a RvzGroupEntry /// /// Byte array to parse /// Filled RvzGroupEntry on success, null on error public static RvzGroupEntry ParseRvzGroupEntry(byte[] data, ref int offset) { var obj = new RvzGroupEntry(); obj.DataOffset = data.ReadUInt32BigEndian(ref offset); obj.DataSize = data.ReadUInt32BigEndian(ref offset); obj.RvzPackedSize = data.ReadUInt32BigEndian(ref offset); return obj; } /// /// Parse a Stream into a WiaGroupEntry /// /// Stream to parse /// Filled WiaGroupEntry on success, null on error public static WiaGroupEntry ParseWiaGroupEntry(Stream data) { var obj = new WiaGroupEntry(); obj.DataOffset = data.ReadUInt32BigEndian(); obj.DataSize = data.ReadUInt32BigEndian(); return obj; } /// /// Parse a byte array into a WiaGroupEntry /// /// Byte array to parse /// Filled WiaGroupEntry on success, null on error public static WiaGroupEntry ParseWiaGroupEntry(byte[] data, ref int offset) { var obj = new WiaGroupEntry(); obj.DataOffset = data.ReadUInt32BigEndian(ref offset); obj.DataSize = data.ReadUInt32BigEndian(ref offset); return obj; } /// /// Parse a Stream into a WiaHeader1 /// /// Stream to parse /// Filled WiaHeader1 on success, null on error public static WiaHeader1 ParseWiaHeader1(Stream data) { var obj = new WiaHeader1(); obj.Magic = data.ReadUInt32LittleEndian(); obj.Version = data.ReadUInt32BigEndian(); obj.VersionCompatible = data.ReadUInt32BigEndian(); obj.Header2Size = data.ReadUInt32BigEndian(); obj.Header2Hash = data.ReadBytes(20); obj.IsoFileSize = data.ReadUInt64BigEndian(); obj.WiaFileSize = data.ReadUInt64BigEndian(); obj.Header1Hash = data.ReadBytes(20); return obj; } /// /// Parse a Stream into a WiaHeader2 /// /// Stream to parse /// Filled WiaHeader2 on success, null on error public static WiaHeader2 ParseWiaHeader2(Stream data) { var obj = new WiaHeader2(); obj.DiscType = (WiaDiscType)data.ReadUInt32BigEndian(); obj.CompressionType = (WiaRvzCompressionType)data.ReadUInt32BigEndian(); obj.CompressionLevel = data.ReadInt32BigEndian(); obj.ChunkSize = data.ReadUInt32BigEndian(); obj.DiscHeader = data.ReadBytes(0x80); obj.NumberOfPartitionEntries = data.ReadUInt32BigEndian(); obj.PartitionEntrySize = data.ReadUInt32BigEndian(); obj.PartitionEntriesOffset = data.ReadUInt64BigEndian(); obj.PartitionEntriesHash = data.ReadBytes(20); obj.NumberOfRawDataEntries = data.ReadUInt32BigEndian(); obj.RawDataEntriesOffset = data.ReadUInt64BigEndian(); obj.RawDataEntriesSize = data.ReadUInt32BigEndian(); obj.NumberOfGroupEntries = data.ReadUInt32BigEndian(); obj.GroupEntriesOffset = data.ReadUInt64BigEndian(); obj.GroupEntriesSize = data.ReadUInt32BigEndian(); obj.CompressorDataSize = data.ReadByteValue(); obj.CompressorData = data.ReadBytes(7); return obj; } } }