diff --git a/SabreTools.Serialization/Deserializers/AACS.cs b/SabreTools.Serialization/Deserializers/AACS.cs index d1ac0394..497929c4 100644 --- a/SabreTools.Serialization/Deserializers/AACS.cs +++ b/SabreTools.Serialization/Deserializers/AACS.cs @@ -15,51 +15,55 @@ namespace SabreTools.Serialization.Deserializers if (data == null || !data.CanRead) return null; - // If the offset is out of bounds - if (data.Position < 0 || data.Position >= data.Length) - return null; - - // Create a new media key block to fill - var mediaKeyBlock = new MediaKeyBlock(); - - #region Records - - // Create the records list - var records = new List(); - - // Try to parse the records - while (data.Position < data.Length) + try { - // Try to parse the record - var record = ParseRecord(data); - if (record == null) - return null; + // Create a new media key block to fill + var mediaKeyBlock = new MediaKeyBlock(); - // Add the record - records.Add(record); + #region Records - // If we have an end of media key block record - if (record.RecordType == RecordType.EndOfMediaKeyBlock) - break; + // Create the records list + var records = new List(); - // Align to the 4-byte boundary if we're not at the end - if (data.Position < data.Length) + // Try to parse the records + while (data.Position < data.Length) { - while (data.Position < data.Length && (data.Position % 4) != 0) - _ = data.ReadByteValue(); - } - else - { - break; + // Try to parse the record + var record = ParseRecord(data); + if (record == null) + return null; + + // Add the record + records.Add(record); + + // If we have an end of media key block record + if (record.RecordType == RecordType.EndOfMediaKeyBlock) + break; + + // Align to the 4-byte boundary if we're not at the end + if (data.Position < data.Length) + { + while (data.Position < data.Length && (data.Position % 4) != 0) + _ = data.ReadByteValue(); + } + else + { + break; + } } + + // Set the records + mediaKeyBlock.Records = [.. records]; + + #endregion + + return mediaKeyBlock; + } + catch + { + // Ignore the actual error + return null; } - - // Set the records - mediaKeyBlock.Records = [.. records]; - - #endregion - - return mediaKeyBlock; } /// @@ -86,7 +90,7 @@ namespace SabreTools.Serialization.Deserializers RecordType.HostRevocationList => ParseHostRevocationListRecord(data, type, length), RecordType.VerifyMediaKey => ParseVerifyMediaKeyRecord(data, type, length), RecordType.Copyright => ParseCopyrightRecord(data, type, length), - + // Unknown record type _ => null, }; diff --git a/SabreTools.Serialization/Deserializers/AttractMode.cs b/SabreTools.Serialization/Deserializers/AttractMode.cs index 70882d11..fa0cc481 100644 --- a/SabreTools.Serialization/Deserializers/AttractMode.cs +++ b/SabreTools.Serialization/Deserializers/AttractMode.cs @@ -21,103 +21,100 @@ namespace SabreTools.Serialization.Deserializers /// public override MetadataFile? Deserialize(Stream? data) { - // If the stream is null - if (data == null) + // If tthe data is invalid + if (data == null || !data.CanRead) return default; try { - // If the stream length and offset are invalid - if (data.Length == 0 || data.Position < 0 || data.Position >= data.Length) - return default; + // Setup the reader and output + var reader = new SeparatedValueReader(data, Encoding.UTF8) + { + Separator = ';', + VerifyFieldCount = false, + }; + var dat = new MetadataFile(); + + // Read the header values first + if (!reader.ReadHeader() || reader.HeaderValues == null) + return null; + + dat.Header = [.. reader.HeaderValues]; + + // Loop through the rows and parse out values + var rows = new List(); + while (!reader.EndOfStream) + { + // If we have no next line + if (!reader.ReadNextLine() || reader.Line == null) + break; + + // Parse the line into a row + Row row; + if (reader.Line.Count < HeaderWithRomnameCount) + { + row = new Row + { + Name = reader.Line[0], + Title = reader.Line[1], + Emulator = reader.Line[2], + CloneOf = reader.Line[3], + Year = reader.Line[4], + Manufacturer = reader.Line[5], + Category = reader.Line[6], + Players = reader.Line[7], + Rotation = reader.Line[8], + Control = reader.Line[9], + Status = reader.Line[10], + DisplayCount = reader.Line[11], + DisplayType = reader.Line[12], + AltRomname = reader.Line[13], + AltTitle = reader.Line[14], + Extra = reader.Line[15], + Buttons = reader.Line[16], + }; + } + else + { + row = new Row + { + Name = reader.Line[0], + Title = reader.Line[1], + Emulator = reader.Line[2], + CloneOf = reader.Line[3], + Year = reader.Line[4], + Manufacturer = reader.Line[5], + Category = reader.Line[6], + Players = reader.Line[7], + Rotation = reader.Line[8], + Control = reader.Line[9], + Status = reader.Line[10], + DisplayCount = reader.Line[11], + DisplayType = reader.Line[12], + AltRomname = reader.Line[13], + AltTitle = reader.Line[14], + Extra = reader.Line[15], + Buttons = reader.Line[16], + }; + } + + rows.Add(row); + } + + // Assign the rows to the Dat and return + if (rows.Count > 0) + { + dat.Row = [.. rows]; + return dat; + } + + return null; } catch { - // Ignore errors in getting position for compressed streams - } - - // Setup the reader and output - var reader = new SeparatedValueReader(data, Encoding.UTF8) - { - Separator = ';', - VerifyFieldCount = false, - }; - var dat = new MetadataFile(); - - // Read the header values first - if (!reader.ReadHeader() || reader.HeaderValues == null) + // Ignore the actual error return null; - - dat.Header = [.. reader.HeaderValues]; - - // Loop through the rows and parse out values - var rows = new List(); - while (!reader.EndOfStream) - { - // If we have no next line - if (!reader.ReadNextLine() || reader.Line == null) - break; - - // Parse the line into a row - Row row; - if (reader.Line.Count < HeaderWithRomnameCount) - { - row = new Row - { - Name = reader.Line[0], - Title = reader.Line[1], - Emulator = reader.Line[2], - CloneOf = reader.Line[3], - Year = reader.Line[4], - Manufacturer = reader.Line[5], - Category = reader.Line[6], - Players = reader.Line[7], - Rotation = reader.Line[8], - Control = reader.Line[9], - Status = reader.Line[10], - DisplayCount = reader.Line[11], - DisplayType = reader.Line[12], - AltRomname = reader.Line[13], - AltTitle = reader.Line[14], - Extra = reader.Line[15], - Buttons = reader.Line[16], - }; - } - else - { - row = new Row - { - Name = reader.Line[0], - Title = reader.Line[1], - Emulator = reader.Line[2], - CloneOf = reader.Line[3], - Year = reader.Line[4], - Manufacturer = reader.Line[5], - Category = reader.Line[6], - Players = reader.Line[7], - Rotation = reader.Line[8], - Control = reader.Line[9], - Status = reader.Line[10], - DisplayCount = reader.Line[11], - DisplayType = reader.Line[12], - AltRomname = reader.Line[13], - AltTitle = reader.Line[14], - Extra = reader.Line[15], - Buttons = reader.Line[16], - }; - } - - rows.Add(row); } - - // Assign the rows to the Dat and return - if (rows.Count > 0) - { - dat.Row = [.. rows]; - return dat; - } - - return null; } #endregion diff --git a/SabreTools.Serialization/Deserializers/BDPlus.cs b/SabreTools.Serialization/Deserializers/BDPlus.cs index 7eaa9808..ab1bab52 100644 --- a/SabreTools.Serialization/Deserializers/BDPlus.cs +++ b/SabreTools.Serialization/Deserializers/BDPlus.cs @@ -15,44 +15,38 @@ namespace SabreTools.Serialization.Deserializers if (data == null || !data.CanRead) return null; - // If the offset is out of bounds - if (data.Position < 0 || data.Position >= data.Length) + try + { + // Try to parse the SVM + var svm = new SVM(); + + byte[] signature = data.ReadBytes(8); + svm.Signature = Encoding.ASCII.GetString(signature); + if (svm.Signature != SignatureString) + return null; + + svm.Unknown1 = data.ReadBytes(5); + svm.Year = data.ReadUInt16BigEndian(); + svm.Month = data.ReadByteValue(); + if (svm.Month < 1 || svm.Month > 12) + return null; + + svm.Day = data.ReadByteValue(); + if (svm.Day < 1 || svm.Day > 31) + return null; + + svm.Unknown2 = data.ReadUInt32(); + svm.Length = data.ReadUInt32(); + if (svm.Length > 0) + svm.Data = data.ReadBytes((int)svm.Length); + + return svm; + } + catch + { + // Ignore the actual error return null; - - // Try to parse the SVM - return ParseSVMData(data); - } - - /// - /// Parse a Stream into an SVM - /// - /// Stream to parse - /// Filled SVM on success, null on error - private static SVM? ParseSVMData(Stream data) - { - var svm = new SVM(); - - byte[] signature = data.ReadBytes(8); - svm.Signature = Encoding.ASCII.GetString(signature); - if (svm.Signature != SignatureString) - return null; - - svm.Unknown1 = data.ReadBytes(5); - svm.Year = data.ReadUInt16BigEndian(); - svm.Month = data.ReadByteValue(); - if (svm.Month < 1 || svm.Month > 12) - return null; - - svm.Day = data.ReadByteValue(); - if (svm.Day < 1 || svm.Day > 31) - return null; - - svm.Unknown2 = data.ReadUInt32(); - svm.Length = data.ReadUInt32(); - if (svm.Length > 0) - svm.Data = data.ReadBytes((int)svm.Length); - - return svm; + } } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/BFPK.cs b/SabreTools.Serialization/Deserializers/BFPK.cs index 812e28c5..35e0218c 100644 --- a/SabreTools.Serialization/Deserializers/BFPK.cs +++ b/SabreTools.Serialization/Deserializers/BFPK.cs @@ -15,42 +15,46 @@ namespace SabreTools.Serialization.Deserializers if (data == null || !data.CanRead) return null; - // If the offset is out of bounds - if (data.Position < 0 || data.Position >= data.Length) - return null; - - // Create a new archive to fill - var archive = new Archive(); - - #region Header - - // Try to parse the header - var header = data.ReadType
(); - if (header?.Magic != SignatureString) - return null; - - // Set the archive header - archive.Header = header; - - #endregion - - #region Files - - // If we have any files - var files = new FileEntry[header.Files]; - - // Read all entries in turn - for (int i = 0; i < header.Files; i++) + try { - files[i] = ParseFileEntry(data); + // Create a new archive to fill + var archive = new Archive(); + + #region Header + + // Try to parse the header + var header = data.ReadType
(); + if (header?.Magic != SignatureString) + return null; + + // Set the archive header + archive.Header = header; + + #endregion + + #region Files + + // If we have any files + var files = new FileEntry[header.Files]; + + // Read all entries in turn + for (int i = 0; i < header.Files; i++) + { + files[i] = ParseFileEntry(data); + } + + // Set the files + archive.Files = files; + + #endregion + + return archive; + } + catch + { + // Ignore the actual error + return null; } - - // Set the files - archive.Files = files; - - #endregion - - return archive; } /// diff --git a/SabreTools.Serialization/Deserializers/BSP.cs b/SabreTools.Serialization/Deserializers/BSP.cs index f488cc1a..acd644b2 100644 --- a/SabreTools.Serialization/Deserializers/BSP.cs +++ b/SabreTools.Serialization/Deserializers/BSP.cs @@ -17,98 +17,102 @@ namespace SabreTools.Serialization.Deserializers if (data == null || !data.CanRead) return null; - // If the offset is out of bounds - if (data.Position < 0 || data.Position >= data.Length) - return null; - - // Create a new Half-Life Level to fill - var file = new BspFile(); - - #region Header - - // Try to parse the header - var header = data.ReadType(); - if (header?.Lumps == null || header.Lumps.Length != BSP_HEADER_LUMPS) - return null; - if (header.Version < 29 || header.Version > 30) - return null; - - // Set the level header - file.Header = header; - - #endregion - - #region Lumps - - for (int l = 0; l < BSP_HEADER_LUMPS; l++) + try { - // Get the next lump entry - var lumpEntry = header.Lumps[l]; - if (lumpEntry == null) - continue; - if (lumpEntry.Offset == 0 || lumpEntry.Length == 0) - continue; + // Create a new Half-Life Level to fill + var file = new BspFile(); - // Seek to the lump offset - data.Seek(lumpEntry.Offset, SeekOrigin.Begin); + #region Header - // Read according to the lump type - switch ((LumpType)l) + // Try to parse the header + var header = data.ReadType(); + if (header?.Lumps == null || header.Lumps.Length != BSP_HEADER_LUMPS) + return null; + if (header.Version < 29 || header.Version > 30) + return null; + + // Set the level header + file.Header = header; + + #endregion + + #region Lumps + + for (int l = 0; l < BSP_HEADER_LUMPS; l++) { - case LumpType.LUMP_ENTITIES: - file.Entities = ParseEntitiesLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_PLANES: - file.PlanesLump = ParsePlanesLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_TEXTURES: - file.TextureLump = ParseTextureLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_VERTICES: - file.VerticesLump = ParseVerticesLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_VISIBILITY: - file.VisibilityLump = ParseVisibilityLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_NODES: - file.NodesLump = ParseNodesLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_TEXINFO: - file.TexinfoLump = ParseTexinfoLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_FACES: - file.FacesLump = ParseFacesLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_LIGHTING: - file.LightmapLump = ParseLightmapLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_CLIPNODES: - file.ClipnodesLump = ParseClipnodesLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_LEAVES: - file.LeavesLump = ParseLeavesLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_MARKSURFACES: - file.MarksurfacesLump = ParseMarksurfacesLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_EDGES: - file.EdgesLump = ParseEdgesLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_SURFEDGES: - file.SurfedgesLump = ParseSurfedgesLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - case LumpType.LUMP_MODELS: - file.ModelsLump = ParseModelsLump(data, lumpEntry.Offset, lumpEntry.Length); - break; - default: - // Unsupported LumpType value, ignore - break; + // Get the next lump entry + var lumpEntry = header.Lumps[l]; + if (lumpEntry == null) + continue; + if (lumpEntry.Offset == 0 || lumpEntry.Length == 0) + continue; + + // Seek to the lump offset + data.Seek(lumpEntry.Offset, SeekOrigin.Begin); + + // Read according to the lump type + switch ((LumpType)l) + { + case LumpType.LUMP_ENTITIES: + file.Entities = ParseEntitiesLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_PLANES: + file.PlanesLump = ParsePlanesLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_TEXTURES: + file.TextureLump = ParseTextureLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_VERTICES: + file.VerticesLump = ParseVerticesLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_VISIBILITY: + file.VisibilityLump = ParseVisibilityLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_NODES: + file.NodesLump = ParseNodesLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_TEXINFO: + file.TexinfoLump = ParseTexinfoLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_FACES: + file.FacesLump = ParseFacesLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_LIGHTING: + file.LightmapLump = ParseLightmapLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_CLIPNODES: + file.ClipnodesLump = ParseClipnodesLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_LEAVES: + file.LeavesLump = ParseLeavesLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_MARKSURFACES: + file.MarksurfacesLump = ParseMarksurfacesLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_EDGES: + file.EdgesLump = ParseEdgesLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_SURFEDGES: + file.SurfedgesLump = ParseSurfedgesLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + case LumpType.LUMP_MODELS: + file.ModelsLump = ParseModelsLump(data, lumpEntry.Offset, lumpEntry.Length); + break; + default: + // Unsupported LumpType value, ignore + break; + } } + + #endregion + + return file; + } + catch + { + // Ignore the actual error + return null; } - - #endregion - - return file; } /// diff --git a/SabreTools.Serialization/Deserializers/BaseBinaryDeserializer.cs b/SabreTools.Serialization/Deserializers/BaseBinaryDeserializer.cs index 09d7d0ce..7a640bc1 100644 --- a/SabreTools.Serialization/Deserializers/BaseBinaryDeserializer.cs +++ b/SabreTools.Serialization/Deserializers/BaseBinaryDeserializer.cs @@ -26,7 +26,7 @@ namespace SabreTools.Serialization.Deserializers public virtual TModel? Deserialize(byte[]? data, int offset) { // If the data is invalid - if (data == null) + if (data == null || data.Length == 0) return default; // If the offset is out of bounds diff --git a/SabreTools.Serialization/Deserializers/CFB.cs b/SabreTools.Serialization/Deserializers/CFB.cs index 72ac86fb..8267fb4c 100644 --- a/SabreTools.Serialization/Deserializers/CFB.cs +++ b/SabreTools.Serialization/Deserializers/CFB.cs @@ -17,210 +17,214 @@ namespace SabreTools.Serialization.Deserializers if (data == null || !data.CanRead) return null; - // If the offset is out of bounds - if (data.Position < 0 || data.Position >= data.Length) + try + { + // Create a new binary to fill + var binary = new Binary(); + + #region Header + + // Try to parse the file header + var fileHeader = ParseFileHeader(data); + if (fileHeader == null) + return null; + + // Set the file header + binary.Header = fileHeader; + + #endregion + + #region DIFAT Sector Numbers + + // Create a DIFAT sector table + var difatSectors = new List(); + + // Add the sectors from the header + if (fileHeader.DIFAT != null) + difatSectors.AddRange(fileHeader.DIFAT); + + // Loop through and add the DIFAT sectors + var currentSector = (SectorNumber?)fileHeader.FirstDIFATSectorLocation; + for (int i = 0; i < fileHeader.NumberOfDIFATSectors; i++) + { + // If we have a readable sector + if (currentSector <= SectorNumber.MAXREGSECT) + { + // Get the new next sector information + long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); + if (sectorOffset < 0 || sectorOffset >= data.Length) + return null; + + // Seek to the next sector + data.Seek(sectorOffset, SeekOrigin.Begin); + + // Try to parse the sectors + var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift); + if (sectorNumbers == null) + return null; + + // Add the sector shifts + difatSectors.AddRange(sectorNumbers); + } + + // Get the next sector from the DIFAT + currentSector = difatSectors[i]; + } + + // Assign the DIFAT sectors table + binary.DIFATSectorNumbers = [.. difatSectors]; + + #endregion + + #region FAT Sector Numbers + + // Create a FAT sector table + var fatSectors = new List(); + + // Loop through and add the FAT sectors + currentSector = binary.DIFATSectorNumbers[0]; + for (int i = 0; i < fileHeader.NumberOfFATSectors; i++) + { + // If we have a readable sector + if (currentSector <= SectorNumber.MAXREGSECT) + { + // Get the new next sector information + long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); + if (sectorOffset < 0 || sectorOffset >= data.Length) + return null; + + // Seek to the next sector + data.Seek(sectorOffset, SeekOrigin.Begin); + + // Try to parse the sectors + var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift); + if (sectorNumbers == null) + return null; + + // Add the sector shifts + fatSectors.AddRange(sectorNumbers); + } + + // Get the next sector from the DIFAT + currentSector = binary.DIFATSectorNumbers[i]; + } + + // Assign the FAT sectors table + binary.FATSectorNumbers = [.. fatSectors]; + + #endregion + + #region Mini FAT Sector Numbers + + // Create a mini FAT sector table + var miniFatSectors = new List(); + + // Loop through and add the mini FAT sectors + currentSector = (SectorNumber)fileHeader.FirstMiniFATSectorLocation; + for (int i = 0; i < fileHeader.NumberOfMiniFATSectors; i++) + { + // If we have a readable sector + if (currentSector <= SectorNumber.MAXREGSECT) + { + // Get the new next sector information + long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); + if (sectorOffset < 0 || sectorOffset >= data.Length) + return null; + + // Seek to the next sector + data.Seek(sectorOffset, SeekOrigin.Begin); + + // Try to parse the sectors + var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift); + if (sectorNumbers == null) + return null; + + // Add the sector shifts + miniFatSectors.AddRange(sectorNumbers); + } + + // Get the next sector from the DIFAT + currentSector = binary.DIFATSectorNumbers[i]; + } + + // Assign the mini FAT sectors table + binary.MiniFATSectorNumbers = [.. miniFatSectors]; + + #endregion + + #region Directory Entries + + // Get the offset of the first directory sector + long firstDirectoryOffset = (long)(fileHeader.FirstDirectorySectorLocation * Math.Pow(2, fileHeader.SectorShift)); + if (firstDirectoryOffset < 0 || firstDirectoryOffset >= data.Length) + return null; + + // Seek to the first directory sector + data.Seek(firstDirectoryOffset, SeekOrigin.Begin); + + // Create a directory sector table + var directorySectors = new List(); + + // Get the number of directory sectors + uint directorySectorCount = 0; + switch (fileHeader.MajorVersion) + { + case 3: + directorySectorCount = int.MaxValue; + break; + case 4: + directorySectorCount = fileHeader.NumberOfDirectorySectors; + break; + } + + // Loop through and add the directory sectors + currentSector = (SectorNumber)fileHeader.FirstDirectorySectorLocation; + for (int i = 0; i < directorySectorCount; i++) + { + // If we have an end of chain + if (currentSector == SectorNumber.ENDOFCHAIN) + break; + + // If we have a free sector for a version 3 filie + if (directorySectorCount == int.MaxValue && currentSector == SectorNumber.FREESECT) + break; + + // If we have a readable sector + if (currentSector <= SectorNumber.MAXREGSECT) + { + // Get the new next sector information + long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); + if (sectorOffset < 0 || sectorOffset >= data.Length) + return null; + + // Seek to the next sector + data.Seek(sectorOffset, SeekOrigin.Begin); + + // Try to parse the sectors + var directoryEntries = ParseDirectoryEntries(data, fileHeader.SectorShift, fileHeader.MajorVersion); + if (directoryEntries == null) + return null; + + // Add the sector shifts + directorySectors.AddRange(directoryEntries); + } + + // Get the next sector from the DIFAT + currentSector = binary.DIFATSectorNumbers[i]; + } + + // Assign the Directory sectors table + binary.DirectoryEntries = [.. directorySectors]; + + #endregion + + return binary; + } + catch + { + // Ignore the actual error return null; - - // Create a new binary to fill - var binary = new Binary(); - - #region Header - - // Try to parse the file header - var fileHeader = ParseFileHeader(data); - if (fileHeader == null) - return null; - - // Set the file header - binary.Header = fileHeader; - - #endregion - - #region DIFAT Sector Numbers - - // Create a DIFAT sector table - var difatSectors = new List(); - - // Add the sectors from the header - if (fileHeader.DIFAT != null) - difatSectors.AddRange(fileHeader.DIFAT); - - // Loop through and add the DIFAT sectors - var currentSector = (SectorNumber?)fileHeader.FirstDIFATSectorLocation; - for (int i = 0; i < fileHeader.NumberOfDIFATSectors; i++) - { - // If we have a readable sector - if (currentSector <= SectorNumber.MAXREGSECT) - { - // Get the new next sector information - long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); - if (sectorOffset < 0 || sectorOffset >= data.Length) - return null; - - // Seek to the next sector - data.Seek(sectorOffset, SeekOrigin.Begin); - - // Try to parse the sectors - var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift); - if (sectorNumbers == null) - return null; - - // Add the sector shifts - difatSectors.AddRange(sectorNumbers); - } - - // Get the next sector from the DIFAT - currentSector = difatSectors[i]; } - - // Assign the DIFAT sectors table - binary.DIFATSectorNumbers = [.. difatSectors]; - - #endregion - - #region FAT Sector Numbers - - // Create a FAT sector table - var fatSectors = new List(); - - // Loop through and add the FAT sectors - currentSector = binary.DIFATSectorNumbers[0]; - for (int i = 0; i < fileHeader.NumberOfFATSectors; i++) - { - // If we have a readable sector - if (currentSector <= SectorNumber.MAXREGSECT) - { - // Get the new next sector information - long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); - if (sectorOffset < 0 || sectorOffset >= data.Length) - return null; - - // Seek to the next sector - data.Seek(sectorOffset, SeekOrigin.Begin); - - // Try to parse the sectors - var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift); - if (sectorNumbers == null) - return null; - - // Add the sector shifts - fatSectors.AddRange(sectorNumbers); - } - - // Get the next sector from the DIFAT - currentSector = binary.DIFATSectorNumbers[i]; - } - - // Assign the FAT sectors table - binary.FATSectorNumbers = [.. fatSectors]; - - #endregion - - #region Mini FAT Sector Numbers - - // Create a mini FAT sector table - var miniFatSectors = new List(); - - // Loop through and add the mini FAT sectors - currentSector = (SectorNumber)fileHeader.FirstMiniFATSectorLocation; - for (int i = 0; i < fileHeader.NumberOfMiniFATSectors; i++) - { - // If we have a readable sector - if (currentSector <= SectorNumber.MAXREGSECT) - { - // Get the new next sector information - long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); - if (sectorOffset < 0 || sectorOffset >= data.Length) - return null; - - // Seek to the next sector - data.Seek(sectorOffset, SeekOrigin.Begin); - - // Try to parse the sectors - var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift); - if (sectorNumbers == null) - return null; - - // Add the sector shifts - miniFatSectors.AddRange(sectorNumbers); - } - - // Get the next sector from the DIFAT - currentSector = binary.DIFATSectorNumbers[i]; - } - - // Assign the mini FAT sectors table - binary.MiniFATSectorNumbers = [.. miniFatSectors]; - - #endregion - - #region Directory Entries - - // Get the offset of the first directory sector - long firstDirectoryOffset = (long)(fileHeader.FirstDirectorySectorLocation * Math.Pow(2, fileHeader.SectorShift)); - if (firstDirectoryOffset < 0 || firstDirectoryOffset >= data.Length) - return null; - - // Seek to the first directory sector - data.Seek(firstDirectoryOffset, SeekOrigin.Begin); - - // Create a directory sector table - var directorySectors = new List(); - - // Get the number of directory sectors - uint directorySectorCount = 0; - switch (fileHeader.MajorVersion) - { - case 3: - directorySectorCount = int.MaxValue; - break; - case 4: - directorySectorCount = fileHeader.NumberOfDirectorySectors; - break; - } - - // Loop through and add the directory sectors - currentSector = (SectorNumber)fileHeader.FirstDirectorySectorLocation; - for (int i = 0; i < directorySectorCount; i++) - { - // If we have an end of chain - if (currentSector == SectorNumber.ENDOFCHAIN) - break; - - // If we have a free sector for a version 3 filie - if (directorySectorCount == int.MaxValue && currentSector == SectorNumber.FREESECT) - break; - - // If we have a readable sector - if (currentSector <= SectorNumber.MAXREGSECT) - { - // Get the new next sector information - long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); - if (sectorOffset < 0 || sectorOffset >= data.Length) - return null; - - // Seek to the next sector - data.Seek(sectorOffset, SeekOrigin.Begin); - - // Try to parse the sectors - var directoryEntries = ParseDirectoryEntries(data, fileHeader.SectorShift, fileHeader.MajorVersion); - if (directoryEntries == null) - return null; - - // Add the sector shifts - directorySectors.AddRange(directoryEntries); - } - - // Get the next sector from the DIFAT - currentSector = binary.DIFATSectorNumbers[i]; - } - - // Assign the Directory sectors table - binary.DirectoryEntries = [.. directorySectors]; - - #endregion - - return binary; } /// diff --git a/SabreTools.Serialization/Deserializers/CHD.cs b/SabreTools.Serialization/Deserializers/CHD.cs index a7a28b9a..9dbfd7b7 100644 --- a/SabreTools.Serialization/Deserializers/CHD.cs +++ b/SabreTools.Serialization/Deserializers/CHD.cs @@ -16,23 +16,27 @@ namespace SabreTools.Serialization.Deserializers if (data == null || !data.CanRead) return null; - // If the offset is out of bounds - if (data.Position < 0 || data.Position >= data.Length) - return null; - - // Determine the header version - uint version = GetVersion(data); - - // Read and return the current CHD - return version switch + try { - 1 => ParseHeaderV1(data), - 2 => ParseHeaderV2(data), - 3 => ParseHeaderV3(data), - 4 => ParseHeaderV4(data), - 5 => ParseHeaderV5(data), - _ => null, - }; + // Determine the header version + uint version = GetVersion(data); + + // Read and return the current CHD + return version switch + { + 1 => ParseHeaderV1(data), + 2 => ParseHeaderV2(data), + 3 => ParseHeaderV3(data), + 4 => ParseHeaderV4(data), + 5 => ParseHeaderV5(data), + _ => null, + }; + } + catch + { + // Ignore the actual error + return null; + } } /// diff --git a/SabreTools.Serialization/Deserializers/CIA.cs b/SabreTools.Serialization/Deserializers/CIA.cs index a33032f0..ce69e5a7 100644 --- a/SabreTools.Serialization/Deserializers/CIA.cs +++ b/SabreTools.Serialization/Deserializers/CIA.cs @@ -14,136 +14,140 @@ namespace SabreTools.Serialization.Deserializers if (data == null || !data.CanRead) return null; - // If the offset is out of bounds - if (data.Position < 0 || data.Position >= data.Length) - return null; - - // Create a new CIA archive to fill - var cia = new Models.N3DS.CIA(); - - #region CIA Header - - // Try to parse the header - var header = data.ReadType(); - if (header == null) - return null; - if (header.CertificateChainSize > data.Length) - return null; - if (header.TicketSize > data.Length) - return null; - if (header.TMDFileSize > data.Length) - return null; - if (header.MetaSize > data.Length) - return null; - if ((long)header.ContentSize > data.Length) - return null; - - // Set the CIA archive header - cia.Header = header; - - #endregion - - // Align to 64-byte boundary, if needed - while (data.Position < data.Length - 1 && data.Position % 64 != 0) + try { - _ = data.ReadByteValue(); - } + // Create a new CIA archive to fill + var cia = new Models.N3DS.CIA(); - #region Certificate Chain + #region CIA Header - // Create the certificate chain - cia.CertificateChain = new Certificate[3]; - - // Try to parse the certificates - for (int i = 0; i < 3; i++) - { - var certificate = ParseCertificate(data); - if (certificate == null) + // Try to parse the header + var header = data.ReadType(); + if (header == null) + return null; + if (header.CertificateChainSize > data.Length) + return null; + if (header.TicketSize > data.Length) + return null; + if (header.TMDFileSize > data.Length) + return null; + if (header.MetaSize > data.Length) + return null; + if ((long)header.ContentSize > data.Length) return null; - cia.CertificateChain[i] = certificate; - } + // Set the CIA archive header + cia.Header = header; - #endregion + #endregion - // Align to 64-byte boundary, if needed - while (data.Position < data.Length - 1 && data.Position % 64 != 0) - { - _ = data.ReadByteValue(); - } + // Align to 64-byte boundary, if needed + while (data.Position < data.Length - 1 && data.Position % 64 != 0) + { + _ = data.ReadByteValue(); + } - #region Ticket + #region Certificate Chain - // Try to parse the ticket - var ticket = ParseTicket(data); - if (ticket == null) - return null; + // Create the certificate chain + cia.CertificateChain = new Certificate[3]; - // Set the ticket - cia.Ticket = ticket; + // Try to parse the certificates + for (int i = 0; i < 3; i++) + { + var certificate = ParseCertificate(data); + if (certificate == null) + return null; - #endregion + cia.CertificateChain[i] = certificate; + } - // Align to 64-byte boundary, if needed - while (data.Position < data.Length - 1 && data.Position % 64 != 0) - { - _ = data.ReadByteValue(); - } + #endregion - #region Title Metadata + // Align to 64-byte boundary, if needed + while (data.Position < data.Length - 1 && data.Position % 64 != 0) + { + _ = data.ReadByteValue(); + } - // Try to parse the title metadata - var titleMetadata = ParseTitleMetadata(data); - if (titleMetadata == null) - return null; + #region Ticket - // Set the title metadata - cia.TMDFileData = titleMetadata; - - #endregion - - // Align to 64-byte boundary, if needed - while (data.Position < data.Length - 1 && data.Position % 64 != 0) - { - _ = data.ReadByteValue(); - } - - #region Content File Data - - // Create the partition table - cia.Partitions = new NCCHHeader[8]; - - // Iterate and build the partitions - for (int i = 0; i < 8; i++) - { - cia.Partitions[i] = N3DS.ParseNCCHHeader(data); - } - - #endregion - - // Align to 64-byte boundary, if needed - while (data.Position < data.Length - 1 && data.Position % 64 != 0) - { - _ = data.ReadByteValue(); - } - - #region Meta Data - - // If we have a meta data - if (header.MetaSize > 0) - { - // Try to parse the meta - var meta = data.ReadType(); - if (meta == null) + // Try to parse the ticket + var ticket = ParseTicket(data); + if (ticket == null) return null; - // Set the meta - cia.MetaData = meta; + // Set the ticket + cia.Ticket = ticket; + + #endregion + + // Align to 64-byte boundary, if needed + while (data.Position < data.Length - 1 && data.Position % 64 != 0) + { + _ = data.ReadByteValue(); + } + + #region Title Metadata + + // Try to parse the title metadata + var titleMetadata = ParseTitleMetadata(data); + if (titleMetadata == null) + return null; + + // Set the title metadata + cia.TMDFileData = titleMetadata; + + #endregion + + // Align to 64-byte boundary, if needed + while (data.Position < data.Length - 1 && data.Position % 64 != 0) + { + _ = data.ReadByteValue(); + } + + #region Content File Data + + // Create the partition table + cia.Partitions = new NCCHHeader[8]; + + // Iterate and build the partitions + for (int i = 0; i < 8; i++) + { + cia.Partitions[i] = N3DS.ParseNCCHHeader(data); + } + + #endregion + + // Align to 64-byte boundary, if needed + while (data.Position < data.Length - 1 && data.Position % 64 != 0) + { + _ = data.ReadByteValue(); + } + + #region Meta Data + + // If we have a meta data + if (header.MetaSize > 0) + { + // Try to parse the meta + var meta = data.ReadType(); + if (meta == null) + return null; + + // Set the meta + cia.MetaData = meta; + } + + #endregion + + return cia; + } + catch + { + // Ignore the actual error + return null; } - - #endregion - - return cia; } /// diff --git a/SabreTools.Serialization/Deserializers/ClrMamePro.cs b/SabreTools.Serialization/Deserializers/ClrMamePro.cs index 70d9d0ee..6a008932 100644 --- a/SabreTools.Serialization/Deserializers/ClrMamePro.cs +++ b/SabreTools.Serialization/Deserializers/ClrMamePro.cs @@ -78,316 +78,313 @@ namespace SabreTools.Serialization.Deserializers /// public MetadataFile? Deserialize(Stream? data, bool quotes) { - // If the stream is null - if (data == null) - return default; + // If tthe data is invalid + if (data == null || !data.CanRead) + return null; try { - // If the stream length and offset are invalid - if (data.Length == 0 || data.Position < 0 || data.Position >= data.Length) - return default; + // Setup the reader and output + var reader = new ClrMameProReader(data, Encoding.UTF8) { Quotes = quotes }; + var dat = new MetadataFile(); + + // Loop through and parse out the values + string? lastTopLevel = reader.TopLevel; + + GameBase? game = null; + var games = new List(); + var releases = new List(); + var biosSets = new List(); + var roms = new List(); + var disks = new List(); + var medias = new List(); + var samples = new List(); + var archives = new List(); + var chips = new List(); + var videos = new List