Deserializers should have their own guards

This commit is contained in:
Matt Nadareski
2024-11-28 22:57:12 -05:00
parent 25c0d036f2
commit 991d9ead27
46 changed files with 5379 additions and 5325 deletions

View File

@@ -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<Record>();
// 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<Record>();
// 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;
}
/// <summary>
@@ -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,
};

View File

@@ -21,103 +21,100 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc/>
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<Row>();
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<Row>();
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

View File

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

View File

@@ -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<Header>();
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<Header>();
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;
}
/// <summary>

View File

@@ -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<BspHeader>();
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<BspHeader>();
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;
}
/// <summary>

View File

@@ -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

View File

@@ -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<SectorNumber>();
// 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<SectorNumber>();
// 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<SectorNumber>();
// 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<DirectoryEntry>();
// 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<SectorNumber>();
// 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<SectorNumber>();
// 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<SectorNumber>();
// 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<DirectoryEntry>();
// 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;
}
/// <summary>

View File

@@ -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;
}
}
/// <summary>

View File

@@ -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<CIAHeader>();
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<CIAHeader>();
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<MetaData>();
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<MetaData>();
if (meta == null)
return null;
// Set the meta
cia.MetaData = meta;
}
#endregion
return cia;
}
catch
{
// Ignore the actual error
return null;
}
#endregion
return cia;
}
/// <summary>

View File

@@ -78,316 +78,313 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
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<GameBase>();
var releases = new List<Release>();
var biosSets = new List<BiosSet>();
var roms = new List<Rom>();
var disks = new List<Disk>();
var medias = new List<Media>();
var samples = new List<Sample>();
var archives = new List<Archive>();
var chips = new List<Chip>();
var videos = new List<Video>();
var dipSwitches = new List<DipSwitch>();
while (!reader.EndOfStream)
{
// If we have no next line
if (!reader.ReadNextLine())
break;
// Ignore certain row types
switch (reader.RowType)
{
case CmpRowType.None:
case CmpRowType.Comment:
continue;
case CmpRowType.EndTopLevel:
switch (lastTopLevel)
{
case "game":
case "machine":
case "resource":
case "set":
if (game != null)
{
game.Release = [.. releases];
game.BiosSet = [.. biosSets];
game.Rom = [.. roms];
game.Disk = [.. disks];
game.Media = [.. medias];
game.Sample = [.. samples];
game.Archive = [.. archives];
game.Chip = [.. chips];
game.Video = [.. videos];
game.DipSwitch = [.. dipSwitches];
games.Add(game);
game = null;
}
releases.Clear();
biosSets.Clear();
roms.Clear();
disks.Clear();
medias.Clear();
samples.Clear();
archives.Clear();
chips.Clear();
videos.Clear();
dipSwitches.Clear();
break;
}
continue;
}
// If we're at the root
if (reader.RowType == CmpRowType.TopLevel)
{
lastTopLevel = reader.TopLevel;
switch (reader.TopLevel)
{
case "clrmamepro":
dat.ClrMamePro = new Models.ClrMamePro.ClrMamePro();
break;
case "game":
game = new Game();
break;
case "machine":
game = new Machine();
break;
case "resource":
game = new Resource();
break;
case "set":
game = new Set();
break;
}
}
// If we're in the doscenter block
else if (reader.TopLevel == "clrmamepro"
&& reader.RowType == CmpRowType.Standalone)
{
// Create the block if we haven't already
dat.ClrMamePro ??= new Models.ClrMamePro.ClrMamePro();
switch (reader.Standalone?.Key?.ToLowerInvariant())
{
case "name":
dat.ClrMamePro.Name = reader.Standalone?.Value;
break;
case "description":
dat.ClrMamePro.Description = reader.Standalone?.Value;
break;
case "rootdir":
dat.ClrMamePro.RootDir = reader.Standalone?.Value;
break;
case "category":
dat.ClrMamePro.Category = reader.Standalone?.Value;
break;
case "version":
dat.ClrMamePro.Version = reader.Standalone?.Value;
break;
case "date":
dat.ClrMamePro.Date = reader.Standalone?.Value;
break;
case "author":
dat.ClrMamePro.Author = reader.Standalone?.Value;
break;
case "homepage":
dat.ClrMamePro.Homepage = reader.Standalone?.Value;
break;
case "url":
dat.ClrMamePro.Url = reader.Standalone?.Value;
break;
case "comment":
dat.ClrMamePro.Comment = reader.Standalone?.Value;
break;
case "header":
dat.ClrMamePro.Header = reader.Standalone?.Value;
break;
case "type":
dat.ClrMamePro.Type = reader.Standalone?.Value;
break;
case "forcemerging":
dat.ClrMamePro.ForceMerging = reader.Standalone?.Value;
break;
case "forcezipping":
dat.ClrMamePro.ForceZipping = reader.Standalone?.Value;
break;
case "forcepacking":
dat.ClrMamePro.ForcePacking = reader.Standalone?.Value;
break;
}
}
// If we're in a game, machine, resource, or set block
else if ((reader.TopLevel == "game"
|| reader.TopLevel == "machine"
|| reader.TopLevel == "resource"
|| reader.TopLevel == "set")
&& reader.RowType == CmpRowType.Standalone)
{
// Create the block if we haven't already
game ??= reader.TopLevel switch
{
"game" => new Game(),
"machine" => new Machine(),
"resource" => new Resource(),
"set" => new Set(),
_ => throw new FormatException($"Unknown top-level block: {reader.TopLevel}"),
};
switch (reader.Standalone?.Key?.ToLowerInvariant())
{
case "name":
game.Name = reader.Standalone?.Value;
break;
case "description":
game.Description = reader.Standalone?.Value;
break;
case "year":
game.Year = reader.Standalone?.Value;
break;
case "manufacturer":
game.Manufacturer = reader.Standalone?.Value;
break;
case "category":
game.Category = reader.Standalone?.Value;
break;
case "cloneof":
game.CloneOf = reader.Standalone?.Value;
break;
case "romof":
game.RomOf = reader.Standalone?.Value;
break;
case "sampleof":
game.SampleOf = reader.Standalone?.Value;
break;
case "sample":
var sample = new Sample
{
Name = reader.Standalone?.Value ?? string.Empty,
};
samples.Add(sample);
break;
}
}
// If we're in an item block
else if ((reader.TopLevel == "game"
|| reader.TopLevel == "machine"
|| reader.TopLevel == "resource"
|| reader.TopLevel == "set")
&& game != null
&& reader.RowType == CmpRowType.Internal)
{
// Create the block
switch (reader.InternalName)
{
case "release":
var release = CreateRelease(reader);
if (release != null)
releases.Add(release);
break;
case "biosset":
var biosSet = CreateBiosSet(reader);
if (biosSet != null)
biosSets.Add(biosSet);
break;
case "rom":
var rom = CreateRom(reader);
if (rom != null)
roms.Add(rom);
break;
case "disk":
var disk = CreateDisk(reader);
if (disk != null)
disks.Add(disk);
break;
case "media":
var media = CreateMedia(reader);
if (media != null)
medias.Add(media);
break;
case "sample":
var sample = CreateSample(reader);
if (sample != null)
samples.Add(sample);
break;
case "archive":
var archive = CreateArchive(reader);
if (archive != null)
archives.Add(archive);
break;
case "chip":
var chip = CreateChip(reader);
if (chip != null)
chips.Add(chip);
break;
case "video":
var video = CreateVideo(reader);
if (video != null)
videos.Add(video);
break;
case "sound":
var sound = CreateSound(reader);
if (sound != null)
game.Sound = sound;
break;
case "input":
var input = CreateInput(reader);
if (input != null)
game.Input = input;
break;
case "dipswitch":
var dipSwitch = CreateDipSwitch(reader);
if (dipSwitch != null)
dipSwitches.Add(dipSwitch);
break;
case "driver":
var driver = CreateDriver(reader);
if (driver != null)
game.Driver = driver;
break;
default:
continue;
}
}
}
// Add extra pieces and return
if (games.Count > 0)
{
dat.Game = [.. games];
return dat;
}
return null;
}
catch
{
// Ignore errors in getting position for compressed streams
// Ignore the actual error
return null;
}
// 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<GameBase>();
var releases = new List<Release>();
var biosSets = new List<BiosSet>();
var roms = new List<Rom>();
var disks = new List<Disk>();
var medias = new List<Media>();
var samples = new List<Sample>();
var archives = new List<Archive>();
var chips = new List<Chip>();
var videos = new List<Video>();
var dipSwitches = new List<DipSwitch>();
while (!reader.EndOfStream)
{
// If we have no next line
if (!reader.ReadNextLine())
break;
// Ignore certain row types
switch (reader.RowType)
{
case CmpRowType.None:
case CmpRowType.Comment:
continue;
case CmpRowType.EndTopLevel:
switch (lastTopLevel)
{
case "game":
case "machine":
case "resource":
case "set":
if (game != null)
{
game.Release = [.. releases];
game.BiosSet = [.. biosSets];
game.Rom = [.. roms];
game.Disk = [.. disks];
game.Media = [.. medias];
game.Sample = [.. samples];
game.Archive = [.. archives];
game.Chip = [.. chips];
game.Video = [.. videos];
game.DipSwitch = [.. dipSwitches];
games.Add(game);
game = null;
}
releases.Clear();
biosSets.Clear();
roms.Clear();
disks.Clear();
medias.Clear();
samples.Clear();
archives.Clear();
chips.Clear();
videos.Clear();
dipSwitches.Clear();
break;
}
continue;
}
// If we're at the root
if (reader.RowType == CmpRowType.TopLevel)
{
lastTopLevel = reader.TopLevel;
switch (reader.TopLevel)
{
case "clrmamepro":
dat.ClrMamePro = new Models.ClrMamePro.ClrMamePro();
break;
case "game":
game = new Game();
break;
case "machine":
game = new Machine();
break;
case "resource":
game = new Resource();
break;
case "set":
game = new Set();
break;
}
}
// If we're in the doscenter block
else if (reader.TopLevel == "clrmamepro"
&& reader.RowType == CmpRowType.Standalone)
{
// Create the block if we haven't already
dat.ClrMamePro ??= new Models.ClrMamePro.ClrMamePro();
switch (reader.Standalone?.Key?.ToLowerInvariant())
{
case "name":
dat.ClrMamePro.Name = reader.Standalone?.Value;
break;
case "description":
dat.ClrMamePro.Description = reader.Standalone?.Value;
break;
case "rootdir":
dat.ClrMamePro.RootDir = reader.Standalone?.Value;
break;
case "category":
dat.ClrMamePro.Category = reader.Standalone?.Value;
break;
case "version":
dat.ClrMamePro.Version = reader.Standalone?.Value;
break;
case "date":
dat.ClrMamePro.Date = reader.Standalone?.Value;
break;
case "author":
dat.ClrMamePro.Author = reader.Standalone?.Value;
break;
case "homepage":
dat.ClrMamePro.Homepage = reader.Standalone?.Value;
break;
case "url":
dat.ClrMamePro.Url = reader.Standalone?.Value;
break;
case "comment":
dat.ClrMamePro.Comment = reader.Standalone?.Value;
break;
case "header":
dat.ClrMamePro.Header = reader.Standalone?.Value;
break;
case "type":
dat.ClrMamePro.Type = reader.Standalone?.Value;
break;
case "forcemerging":
dat.ClrMamePro.ForceMerging = reader.Standalone?.Value;
break;
case "forcezipping":
dat.ClrMamePro.ForceZipping = reader.Standalone?.Value;
break;
case "forcepacking":
dat.ClrMamePro.ForcePacking = reader.Standalone?.Value;
break;
}
}
// If we're in a game, machine, resource, or set block
else if ((reader.TopLevel == "game"
|| reader.TopLevel == "machine"
|| reader.TopLevel == "resource"
|| reader.TopLevel == "set")
&& reader.RowType == CmpRowType.Standalone)
{
// Create the block if we haven't already
game ??= reader.TopLevel switch
{
"game" => new Game(),
"machine" => new Machine(),
"resource" => new Resource(),
"set" => new Set(),
_ => throw new FormatException($"Unknown top-level block: {reader.TopLevel}"),
};
switch (reader.Standalone?.Key?.ToLowerInvariant())
{
case "name":
game.Name = reader.Standalone?.Value;
break;
case "description":
game.Description = reader.Standalone?.Value;
break;
case "year":
game.Year = reader.Standalone?.Value;
break;
case "manufacturer":
game.Manufacturer = reader.Standalone?.Value;
break;
case "category":
game.Category = reader.Standalone?.Value;
break;
case "cloneof":
game.CloneOf = reader.Standalone?.Value;
break;
case "romof":
game.RomOf = reader.Standalone?.Value;
break;
case "sampleof":
game.SampleOf = reader.Standalone?.Value;
break;
case "sample":
var sample = new Sample
{
Name = reader.Standalone?.Value ?? string.Empty,
};
samples.Add(sample);
break;
}
}
// If we're in an item block
else if ((reader.TopLevel == "game"
|| reader.TopLevel == "machine"
|| reader.TopLevel == "resource"
|| reader.TopLevel == "set")
&& game != null
&& reader.RowType == CmpRowType.Internal)
{
// Create the block
switch (reader.InternalName)
{
case "release":
var release = CreateRelease(reader);
if (release != null)
releases.Add(release);
break;
case "biosset":
var biosSet = CreateBiosSet(reader);
if (biosSet != null)
biosSets.Add(biosSet);
break;
case "rom":
var rom = CreateRom(reader);
if (rom != null)
roms.Add(rom);
break;
case "disk":
var disk = CreateDisk(reader);
if (disk != null)
disks.Add(disk);
break;
case "media":
var media = CreateMedia(reader);
if (media != null)
medias.Add(media);
break;
case "sample":
var sample = CreateSample(reader);
if (sample != null)
samples.Add(sample);
break;
case "archive":
var archive = CreateArchive(reader);
if (archive != null)
archives.Add(archive);
break;
case "chip":
var chip = CreateChip(reader);
if (chip != null)
chips.Add(chip);
break;
case "video":
var video = CreateVideo(reader);
if (video != null)
videos.Add(video);
break;
case "sound":
var sound = CreateSound(reader);
if (sound != null)
game.Sound = sound;
break;
case "input":
var input = CreateInput(reader);
if (input != null)
game.Input = input;
break;
case "dipswitch":
var dipSwitch = CreateDipSwitch(reader);
if (dipSwitch != null)
dipSwitches.Add(dipSwitch);
break;
case "driver":
var driver = CreateDriver(reader);
if (driver != null)
game.Driver = driver;
break;
default:
continue;
}
}
}
// Add extra pieces and return
if (games.Count > 0)
{
dat.Game = [.. games];
return dat;
}
return null;
}
/// <summary>

View File

@@ -16,105 +16,109 @@ 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;
// Setup the reader and output
var reader = new StreamReader(data);
var cueSheet = new Models.CueSheets.CueSheet();
var cueFiles = new List<CueFile>();
// Read the next line from the input
string? lastLine = null;
while (true)
try
{
string? line = lastLine ?? reader.ReadLine();
lastLine = null;
// Setup the reader and output
var reader = new StreamReader(data);
var cueSheet = new Models.CueSheets.CueSheet();
var cueFiles = new List<CueFile>();
// If we have a null line, break from the loop
if (line == null)
break;
// If we have an empty line, we skip
if (string.IsNullOrEmpty(line))
continue;
// http://stackoverflow.com/questions/554013/regular-expression-to-split-on-spaces-unless-in-quotes
var matchCol = Regex.Matches(line, @"[^\s""]+|""[^""]*""");
var splitLine = new List<string>();
foreach (Match? match in matchCol)
// Read the next line from the input
string? lastLine = null;
while (true)
{
if (match != null)
splitLine.Add(match.Groups[0].Value);
string? line = lastLine ?? reader.ReadLine();
lastLine = null;
// If we have a null line, break from the loop
if (line == null)
break;
// If we have an empty line, we skip
if (string.IsNullOrEmpty(line))
continue;
// http://stackoverflow.com/questions/554013/regular-expression-to-split-on-spaces-unless-in-quotes
var matchCol = Regex.Matches(line, @"[^\s""]+|""[^""]*""");
var splitLine = new List<string>();
foreach (Match? match in matchCol)
{
if (match != null)
splitLine.Add(match.Groups[0].Value);
}
switch (splitLine[0])
{
// Read comments
case "REM":
// We ignore all comments for now
break;
// Read MCN
case "CATALOG":
if (splitLine.Count < 2)
throw new FormatException($"CATALOG line malformed: {line}");
cueSheet.Catalog = splitLine[1].Trim('"');
break;
// Read external CD-Text file path
case "CDTEXTFILE":
if (splitLine.Count < 2)
throw new FormatException($"CDTEXTFILE line malformed: {line}");
cueSheet.CdTextFile = splitLine[1].Trim('"');
break;
// Read CD-Text enhanced performer
case "PERFORMER":
if (splitLine.Count < 2)
throw new FormatException($"PERFORMER line malformed: {line}");
cueSheet.Performer = splitLine[1].Trim('"');
break;
// Read CD-Text enhanced songwriter
case "SONGWRITER":
if (splitLine.Count < 2)
throw new FormatException($"SONGWRITER line malformed: {line}");
cueSheet.Songwriter = splitLine[1].Trim('"');
break;
// Read CD-Text enhanced title
case "TITLE":
if (splitLine.Count < 2)
throw new FormatException($"TITLE line malformed: {line}");
cueSheet.Title = splitLine[1].Trim('"');
break;
// Read file information
case "FILE":
if (splitLine.Count < 3)
throw new FormatException($"FILE line malformed: {line}");
var file = CreateCueFile(splitLine[1], splitLine[2], reader, out lastLine);
if (file == default)
throw new FormatException($"FILE line malformed: {line}");
cueFiles.Add(file);
break;
}
}
switch (splitLine[0])
{
// Read comments
case "REM":
// We ignore all comments for now
break;
if (cueFiles.Count == 0)
return null;
// Read MCN
case "CATALOG":
if (splitLine.Count < 2)
throw new FormatException($"CATALOG line malformed: {line}");
cueSheet.Catalog = splitLine[1].Trim('"');
break;
// Read external CD-Text file path
case "CDTEXTFILE":
if (splitLine.Count < 2)
throw new FormatException($"CDTEXTFILE line malformed: {line}");
cueSheet.CdTextFile = splitLine[1].Trim('"');
break;
// Read CD-Text enhanced performer
case "PERFORMER":
if (splitLine.Count < 2)
throw new FormatException($"PERFORMER line malformed: {line}");
cueSheet.Performer = splitLine[1].Trim('"');
break;
// Read CD-Text enhanced songwriter
case "SONGWRITER":
if (splitLine.Count < 2)
throw new FormatException($"SONGWRITER line malformed: {line}");
cueSheet.Songwriter = splitLine[1].Trim('"');
break;
// Read CD-Text enhanced title
case "TITLE":
if (splitLine.Count < 2)
throw new FormatException($"TITLE line malformed: {line}");
cueSheet.Title = splitLine[1].Trim('"');
break;
// Read file information
case "FILE":
if (splitLine.Count < 3)
throw new FormatException($"FILE line malformed: {line}");
var file = CreateCueFile(splitLine[1], splitLine[2], reader, out lastLine);
if (file == default)
throw new FormatException($"FILE line malformed: {line}");
cueFiles.Add(file);
break;
}
cueSheet.Files = [.. cueFiles];
return cueSheet;
}
if (cueFiles.Count == 0)
catch
{
// Ignore the actual error
return null;
cueSheet.Files = [.. cueFiles];
return cueSheet;
}
}
/// <summary>

View File

@@ -11,144 +11,141 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc/>
public override MetadataFile? Deserialize(Stream? data)
{
// 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) { DosCenter = true };
var dat = new MetadataFile();
// Loop through and parse out the values
string? lastTopLevel = reader.TopLevel;
Game? game = null;
var games = new List<Game>();
var files = new List<Models.DosCenter.File>();
while (!reader.EndOfStream)
{
// If we have no next line
if (!reader.ReadNextLine())
break;
// Ignore certain row types
switch (reader.RowType)
{
case CmpRowType.None:
case CmpRowType.Comment:
continue;
case CmpRowType.EndTopLevel:
switch (lastTopLevel)
{
case "game":
if (game != null)
{
game.File = [.. files];
games.Add(game);
}
game = null;
files.Clear();
break;
}
continue;
}
// If we're at the root
if (reader.RowType == CmpRowType.TopLevel)
{
lastTopLevel = reader.TopLevel;
switch (reader.TopLevel)
{
case "doscenter":
dat.DosCenter = new Models.DosCenter.DosCenter();
break;
case "game":
game = new Game();
break;
}
}
// If we're in the doscenter block
else if (reader.TopLevel == "doscenter" && reader.RowType == CmpRowType.Standalone)
{
// Create the block if we haven't already
dat.DosCenter ??= new Models.DosCenter.DosCenter();
switch (reader.Standalone?.Key?.ToLowerInvariant())
{
case "name:":
dat.DosCenter.Name = reader.Standalone?.Value;
break;
case "description:":
dat.DosCenter.Description = reader.Standalone?.Value;
break;
case "version:":
dat.DosCenter.Version = reader.Standalone?.Value;
break;
case "date:":
dat.DosCenter.Date = reader.Standalone?.Value;
break;
case "author:":
dat.DosCenter.Author = reader.Standalone?.Value;
break;
case "homepage:":
dat.DosCenter.Homepage = reader.Standalone?.Value;
break;
case "comment:":
dat.DosCenter.Comment = reader.Standalone?.Value;
break;
}
}
// If we're in a game block
else if (reader.TopLevel == "game" && reader.RowType == CmpRowType.Standalone)
{
// Create the block if we haven't already
game ??= new Game();
switch (reader.Standalone?.Key?.ToLowerInvariant())
{
case "name":
game.Name = reader.Standalone?.Value;
break;
}
}
// If we're in a file block
else if (reader.TopLevel == "game" && reader.RowType == CmpRowType.Internal)
{
// If we have an unknown type, log it
if (reader.InternalName != "file")
continue;
// Create the file and add to the list
var file = CreateFile(reader);
if (file != null)
files.Add(file);
}
}
// Add extra pieces and return
if (games.Count > 0)
{
dat.Game = [.. games];
return dat;
}
return null;
}
catch
{
// Ignore errors in getting position for compressed streams
// Ignore the actual error
return null;
}
// Setup the reader and output
var reader = new ClrMameProReader(data, Encoding.UTF8) { DosCenter = true };
var dat = new MetadataFile();
// Loop through and parse out the values
string? lastTopLevel = reader.TopLevel;
Game? game = null;
var games = new List<Game>();
var files = new List<Models.DosCenter.File>();
while (!reader.EndOfStream)
{
// If we have no next line
if (!reader.ReadNextLine())
break;
// Ignore certain row types
switch (reader.RowType)
{
case CmpRowType.None:
case CmpRowType.Comment:
continue;
case CmpRowType.EndTopLevel:
switch (lastTopLevel)
{
case "game":
if (game != null)
{
game.File = [.. files];
games.Add(game);
}
game = null;
files.Clear();
break;
}
continue;
}
// If we're at the root
if (reader.RowType == CmpRowType.TopLevel)
{
lastTopLevel = reader.TopLevel;
switch (reader.TopLevel)
{
case "doscenter":
dat.DosCenter = new Models.DosCenter.DosCenter();
break;
case "game":
game = new Game();
break;
}
}
// If we're in the doscenter block
else if (reader.TopLevel == "doscenter" && reader.RowType == CmpRowType.Standalone)
{
// Create the block if we haven't already
dat.DosCenter ??= new Models.DosCenter.DosCenter();
switch (reader.Standalone?.Key?.ToLowerInvariant())
{
case "name:":
dat.DosCenter.Name = reader.Standalone?.Value;
break;
case "description:":
dat.DosCenter.Description = reader.Standalone?.Value;
break;
case "version:":
dat.DosCenter.Version = reader.Standalone?.Value;
break;
case "date:":
dat.DosCenter.Date = reader.Standalone?.Value;
break;
case "author:":
dat.DosCenter.Author = reader.Standalone?.Value;
break;
case "homepage:":
dat.DosCenter.Homepage = reader.Standalone?.Value;
break;
case "comment:":
dat.DosCenter.Comment = reader.Standalone?.Value;
break;
}
}
// If we're in a game block
else if (reader.TopLevel == "game" && reader.RowType == CmpRowType.Standalone)
{
// Create the block if we haven't already
game ??= new Game();
switch (reader.Standalone?.Key?.ToLowerInvariant())
{
case "name":
game.Name = reader.Standalone?.Value;
break;
}
}
// If we're in a file block
else if (reader.TopLevel == "game" && reader.RowType == CmpRowType.Internal)
{
// If we have an unknown type, log it
if (reader.InternalName != "file")
continue;
// Create the file and add to the list
var file = CreateFile(reader);
if (file != null)
files.Add(file);
}
}
// Add extra pieces and return
if (games.Count > 0)
{
dat.Game = [.. games];
return dat;
}
return null;
}
/// <summary>

View File

@@ -15,56 +15,60 @@ 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;
// Setup the reader and output
var reader = new SeparatedValueReader(data, Encoding.UTF8)
try
{
Header = false,
Separator = '\t',
VerifyFieldCount = false,
};
var dat = new MetadataFile();
// Loop through the rows and parse out values
var rows = new List<Row>();
while (!reader.EndOfStream)
{
// If we have no next line
if (!reader.ReadNextLine() || reader.Line == null)
break;
// If the next line has an invalid count
if (reader.Line.Count < 5)
break;
// Parse the line into a row
var row = new Row
// Setup the reader and output
var reader = new SeparatedValueReader(data, Encoding.UTF8)
{
SHA256 = reader.Line[0],
Name = reader.Line[1],
SHA1 = reader.Line[2],
MD5 = reader.Line[3],
CRC32 = reader.Line[4],
Header = false,
Separator = '\t',
VerifyFieldCount = false,
};
var dat = new MetadataFile();
// If we have the size field
if (reader.Line.Count > 5)
row.Size = reader.Line[5];
// Loop through the rows and parse out values
var rows = new List<Row>();
while (!reader.EndOfStream)
{
// If we have no next line
if (!reader.ReadNextLine() || reader.Line == null)
break;
rows.Add(row);
// If the next line has an invalid count
if (reader.Line.Count < 5)
break;
// Parse the line into a row
var row = new Row
{
SHA256 = reader.Line[0],
Name = reader.Line[1],
SHA1 = reader.Line[2],
MD5 = reader.Line[3],
CRC32 = reader.Line[4],
};
// If we have the size field
if (reader.Line.Count > 5)
row.Size = reader.Line[5];
rows.Add(row);
}
// Assign the rows to the Dat and return
if (rows.Count > 0)
{
dat.Row = [.. rows];
return dat;
}
return null;
}
// Assign the rows to the Dat and return
if (rows.Count > 0)
catch
{
dat.Row = [.. rows];
return dat;
// Ignore the actual error
return null;
}
return null;
}
}
}

View File

@@ -14,369 +14,373 @@ 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 Game Cache to fill
var file = new Models.GCF.File();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Dummy0 != 0x00000001)
return null;
if (header?.MajorVersion != 0x00000001)
return null;
if (header.MinorVersion != 3 && header.MinorVersion != 5 && header.MinorVersion != 6)
return null;
// Set the game cache header
file.Header = header;
#endregion
#region Block Entry Header
// Try to parse the block entry header
var blockEntryHeader = data.ReadType<BlockEntryHeader>();
if (blockEntryHeader == null)
return null;
// Set the game cache block entry header
file.BlockEntryHeader = blockEntryHeader;
#endregion
#region Block Entries
// Create the block entry array
file.BlockEntries = new BlockEntry[blockEntryHeader.BlockCount];
// Try to parse the block entries
for (int i = 0; i < blockEntryHeader.BlockCount; i++)
try
{
var blockEntry = data.ReadType<BlockEntry>();
if (blockEntry == null)
// Create a new Half-Life Game Cache to fill
var file = new Models.GCF.File();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Dummy0 != 0x00000001)
return null;
if (header?.MajorVersion != 0x00000001)
return null;
if (header.MinorVersion != 3 && header.MinorVersion != 5 && header.MinorVersion != 6)
return null;
file.BlockEntries[i] = blockEntry;
}
// Set the game cache header
file.Header = header;
#endregion
#endregion
#region Fragmentation Map Header
#region Block Entry Header
// Try to parse the fragmentation map header
var fragmentationMapHeader = data.ReadType<FragmentationMapHeader>();
if (fragmentationMapHeader == null)
return null;
// Set the game cache fragmentation map header
file.FragmentationMapHeader = fragmentationMapHeader;
#endregion
#region Fragmentation Maps
// Create the fragmentation map array
file.FragmentationMaps = new FragmentationMap[fragmentationMapHeader.BlockCount];
// Try to parse the fragmentation maps
for (int i = 0; i < fragmentationMapHeader.BlockCount; i++)
{
var fragmentationMap = data.ReadType<FragmentationMap>();
if (fragmentationMap == null)
// Try to parse the block entry header
var blockEntryHeader = data.ReadType<BlockEntryHeader>();
if (blockEntryHeader == null)
return null;
file.FragmentationMaps[i] = fragmentationMap;
}
// Set the game cache block entry header
file.BlockEntryHeader = blockEntryHeader;
#endregion
#endregion
#region Block Entry Map Header
#region Block Entries
if (header.MinorVersion < 6)
{
// Try to parse the block entry map header
var blockEntryMapHeader = data.ReadType<BlockEntryMapHeader>();
if (blockEntryMapHeader == null)
return null;
// Create the block entry array
file.BlockEntries = new BlockEntry[blockEntryHeader.BlockCount];
// Set the game cache block entry map header
file.BlockEntryMapHeader = blockEntryMapHeader;
}
#endregion
#region Block Entry Maps
if (header.MinorVersion < 6)
{
// Create the block entry map array
file.BlockEntryMaps = new BlockEntryMap[file.BlockEntryMapHeader!.BlockCount];
// Try to parse the block entry maps
for (int i = 0; i < file.BlockEntryMapHeader.BlockCount; i++)
// Try to parse the block entries
for (int i = 0; i < blockEntryHeader.BlockCount; i++)
{
var blockEntryMap = data.ReadType<BlockEntryMap>();
if (blockEntryMap == null)
var blockEntry = data.ReadType<BlockEntry>();
if (blockEntry == null)
return null;
file.BlockEntryMaps[i] = blockEntryMap;
file.BlockEntries[i] = blockEntry;
}
}
#endregion
#endregion
// Cache the current offset
long initialOffset = data.Position;
#region Fragmentation Map Header
#region Directory Header
// Try to parse the directory header
var directoryHeader = data.ReadType<DirectoryHeader>();
if (directoryHeader == null)
return null;
// Set the game cache directory header
file.DirectoryHeader = directoryHeader;
#endregion
#region Directory Entries
// Create the directory entry array
file.DirectoryEntries = new DirectoryEntry[directoryHeader.ItemCount];
// Try to parse the directory entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryEntry = data.ReadType<DirectoryEntry>();
if (directoryEntry == null)
// Try to parse the fragmentation map header
var fragmentationMapHeader = data.ReadType<FragmentationMapHeader>();
if (fragmentationMapHeader == null)
return null;
file.DirectoryEntries[i] = directoryEntry;
}
#endregion
// Set the game cache fragmentation map header
file.FragmentationMapHeader = fragmentationMapHeader;
#region Directory Names
#endregion
if (directoryHeader.NameSize > 0)
{
// Get the current offset for adjustment
long directoryNamesStart = data.Position;
#region Fragmentation Maps
// Get the ending offset
long directoryNamesEnd = data.Position + directoryHeader.NameSize;
// Create the fragmentation map array
file.FragmentationMaps = new FragmentationMap[fragmentationMapHeader.BlockCount];
// Create the string dictionary
file.DirectoryNames = [];
// Loop and read the null-terminated strings
while (data.Position < directoryNamesEnd)
// Try to parse the fragmentation maps
for (int i = 0; i < fragmentationMapHeader.BlockCount; i++)
{
long nameOffset = data.Position - directoryNamesStart;
string? directoryName = data.ReadNullTerminatedAnsiString();
if (data.Position > directoryNamesEnd)
{
data.Seek(-directoryName?.Length ?? 0, SeekOrigin.Current);
byte[] endingData = data.ReadBytes((int)(directoryNamesEnd - data.Position));
directoryName = Encoding.ASCII.GetString(endingData);
}
var fragmentationMap = data.ReadType<FragmentationMap>();
if (fragmentationMap == null)
return null;
file.DirectoryNames[nameOffset] = directoryName;
file.FragmentationMaps[i] = fragmentationMap;
}
}
#endregion
#endregion
#region Directory Info 1 Entries
#region Block Entry Map Header
// Create the directory info 1 entry array
file.DirectoryInfo1Entries = new DirectoryInfo1Entry[directoryHeader.Info1Count];
if (header.MinorVersion < 6)
{
// Try to parse the block entry map header
var blockEntryMapHeader = data.ReadType<BlockEntryMapHeader>();
if (blockEntryMapHeader == null)
return null;
// Try to parse the directory info 1 entries
for (int i = 0; i < directoryHeader.Info1Count; i++)
{
var directoryInfo1Entry = data.ReadType<DirectoryInfo1Entry>();
if (directoryInfo1Entry == null)
// Set the game cache block entry map header
file.BlockEntryMapHeader = blockEntryMapHeader;
}
#endregion
#region Block Entry Maps
if (header.MinorVersion < 6)
{
// Create the block entry map array
file.BlockEntryMaps = new BlockEntryMap[file.BlockEntryMapHeader!.BlockCount];
// Try to parse the block entry maps
for (int i = 0; i < file.BlockEntryMapHeader.BlockCount; i++)
{
var blockEntryMap = data.ReadType<BlockEntryMap>();
if (blockEntryMap == null)
return null;
file.BlockEntryMaps[i] = blockEntryMap;
}
}
#endregion
// Cache the current offset
long initialOffset = data.Position;
#region Directory Header
// Try to parse the directory header
var directoryHeader = data.ReadType<DirectoryHeader>();
if (directoryHeader == null)
return null;
file.DirectoryInfo1Entries[i] = directoryInfo1Entry;
}
// Set the game cache directory header
file.DirectoryHeader = directoryHeader;
#endregion
#endregion
#region Directory Info 2 Entries
#region Directory Entries
// Create the directory info 2 entry array
file.DirectoryInfo2Entries = new DirectoryInfo2Entry[directoryHeader.ItemCount];
// Create the directory entry array
file.DirectoryEntries = new DirectoryEntry[directoryHeader.ItemCount];
// Try to parse the directory info 2 entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryInfo2Entry = data.ReadType<DirectoryInfo2Entry>();
if (directoryInfo2Entry == null)
// Try to parse the directory entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryEntry = data.ReadType<DirectoryEntry>();
if (directoryEntry == null)
return null;
file.DirectoryEntries[i] = directoryEntry;
}
#endregion
#region Directory Names
if (directoryHeader.NameSize > 0)
{
// Get the current offset for adjustment
long directoryNamesStart = data.Position;
// Get the ending offset
long directoryNamesEnd = data.Position + directoryHeader.NameSize;
// Create the string dictionary
file.DirectoryNames = [];
// Loop and read the null-terminated strings
while (data.Position < directoryNamesEnd)
{
long nameOffset = data.Position - directoryNamesStart;
string? directoryName = data.ReadNullTerminatedAnsiString();
if (data.Position > directoryNamesEnd)
{
data.Seek(-directoryName?.Length ?? 0, SeekOrigin.Current);
byte[] endingData = data.ReadBytes((int)(directoryNamesEnd - data.Position));
directoryName = Encoding.ASCII.GetString(endingData);
}
file.DirectoryNames[nameOffset] = directoryName;
}
}
#endregion
#region Directory Info 1 Entries
// Create the directory info 1 entry array
file.DirectoryInfo1Entries = new DirectoryInfo1Entry[directoryHeader.Info1Count];
// Try to parse the directory info 1 entries
for (int i = 0; i < directoryHeader.Info1Count; i++)
{
var directoryInfo1Entry = data.ReadType<DirectoryInfo1Entry>();
if (directoryInfo1Entry == null)
return null;
file.DirectoryInfo1Entries[i] = directoryInfo1Entry;
}
#endregion
#region Directory Info 2 Entries
// Create the directory info 2 entry array
file.DirectoryInfo2Entries = new DirectoryInfo2Entry[directoryHeader.ItemCount];
// Try to parse the directory info 2 entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryInfo2Entry = data.ReadType<DirectoryInfo2Entry>();
if (directoryInfo2Entry == null)
return null;
file.DirectoryInfo2Entries[i] = directoryInfo2Entry;
}
#endregion
#region Directory Copy Entries
// Create the directory copy entry array
file.DirectoryCopyEntries = new DirectoryCopyEntry[directoryHeader.CopyCount];
// Try to parse the directory copy entries
for (int i = 0; i < directoryHeader.CopyCount; i++)
{
var directoryCopyEntry = data.ReadType<DirectoryCopyEntry>();
if (directoryCopyEntry == null)
return null;
file.DirectoryCopyEntries[i] = directoryCopyEntry;
}
#endregion
#region Directory Local Entries
// Create the directory local entry array
file.DirectoryLocalEntries = new DirectoryLocalEntry[directoryHeader.LocalCount];
// Try to parse the directory local entries
for (int i = 0; i < directoryHeader.LocalCount; i++)
{
var directoryLocalEntry = data.ReadType<DirectoryLocalEntry>();
if (directoryLocalEntry == null)
return null;
file.DirectoryLocalEntries[i] = directoryLocalEntry;
}
#endregion
// Seek to end of directory section, just in case
data.Seek(initialOffset + directoryHeader.DirectorySize, SeekOrigin.Begin);
#region Directory Map Header
if (header.MinorVersion >= 5)
{
// Try to parse the directory map header
var directoryMapHeader = data.ReadType<DirectoryMapHeader>();
if (directoryMapHeader?.Dummy0 != 0x00000001)
return null;
if (directoryMapHeader?.Dummy1 != 0x00000000)
return null;
// Set the game cache directory map header
file.DirectoryMapHeader = directoryMapHeader;
}
#endregion
#region Directory Map Entries
// Create the directory map entry array
file.DirectoryMapEntries = new DirectoryMapEntry[directoryHeader.ItemCount];
// Try to parse the directory map entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryMapEntry = data.ReadType<DirectoryMapEntry>();
if (directoryMapEntry == null)
return null;
file.DirectoryMapEntries[i] = directoryMapEntry;
}
#endregion
#region Checksum Header
// Try to parse the checksum header
var checksumHeader = data.ReadType<ChecksumHeader>();
if (checksumHeader?.Dummy0 != 0x00000001)
return null;
file.DirectoryInfo2Entries[i] = directoryInfo2Entry;
}
// Set the game cache checksum header
file.ChecksumHeader = checksumHeader;
#endregion
#endregion
#region Directory Copy Entries
// Cache the current offset
initialOffset = data.Position;
// Create the directory copy entry array
file.DirectoryCopyEntries = new DirectoryCopyEntry[directoryHeader.CopyCount];
#region Checksum Map Header
// Try to parse the directory copy entries
for (int i = 0; i < directoryHeader.CopyCount; i++)
{
var directoryCopyEntry = data.ReadType<DirectoryCopyEntry>();
if (directoryCopyEntry == null)
// Try to parse the checksum map header
var checksumMapHeader = data.ReadType<ChecksumMapHeader>();
if (checksumMapHeader?.Dummy0 != 0x14893721)
return null;
if (checksumMapHeader?.Dummy1 != 0x00000001)
return null;
file.DirectoryCopyEntries[i] = directoryCopyEntry;
}
// Set the game cache checksum map header
file.ChecksumMapHeader = checksumMapHeader;
#endregion
#endregion
#region Directory Local Entries
#region Checksum Map Entries
// Create the directory local entry array
file.DirectoryLocalEntries = new DirectoryLocalEntry[directoryHeader.LocalCount];
// Create the checksum map entry array
file.ChecksumMapEntries = new ChecksumMapEntry[checksumMapHeader.ItemCount];
// Try to parse the directory local entries
for (int i = 0; i < directoryHeader.LocalCount; i++)
{
var directoryLocalEntry = data.ReadType<DirectoryLocalEntry>();
if (directoryLocalEntry == null)
// Try to parse the checksum map entries
for (int i = 0; i < checksumMapHeader.ItemCount; i++)
{
var checksumMapEntry = data.ReadType<ChecksumMapEntry>();
if (checksumMapEntry == null)
return null;
file.ChecksumMapEntries[i] = checksumMapEntry;
}
#endregion
#region Checksum Entries
// Create the checksum entry array
file.ChecksumEntries = new ChecksumEntry[checksumMapHeader.ChecksumCount];
// Try to parse the checksum entries
for (int i = 0; i < checksumMapHeader.ChecksumCount; i++)
{
var checksumEntry = data.ReadType<ChecksumEntry>();
if (checksumEntry == null)
return null;
file.ChecksumEntries[i] = checksumEntry;
}
#endregion
// Seek to end of checksum section, just in case
data.Seek(initialOffset + checksumHeader.ChecksumSize, SeekOrigin.Begin);
#region Data Block Header
// Try to parse the data block header
var dataBlockHeader = ParseDataBlockHeader(data, header.MinorVersion);
if (dataBlockHeader == null)
return null;
file.DirectoryLocalEntries[i] = directoryLocalEntry;
// Set the game cache data block header
file.DataBlockHeader = dataBlockHeader;
#endregion
return file;
}
#endregion
// Seek to end of directory section, just in case
data.Seek(initialOffset + directoryHeader.DirectorySize, SeekOrigin.Begin);
#region Directory Map Header
if (header.MinorVersion >= 5)
catch
{
// Try to parse the directory map header
var directoryMapHeader = data.ReadType<DirectoryMapHeader>();
if (directoryMapHeader?.Dummy0 != 0x00000001)
// Ignore the actual error
return null;
if (directoryMapHeader?.Dummy1 != 0x00000000)
return null;
// Set the game cache directory map header
file.DirectoryMapHeader = directoryMapHeader;
}
#endregion
#region Directory Map Entries
// Create the directory map entry array
file.DirectoryMapEntries = new DirectoryMapEntry[directoryHeader.ItemCount];
// Try to parse the directory map entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryMapEntry = data.ReadType<DirectoryMapEntry>();
if (directoryMapEntry == null)
return null;
file.DirectoryMapEntries[i] = directoryMapEntry;
}
#endregion
#region Checksum Header
// Try to parse the checksum header
var checksumHeader = data.ReadType<ChecksumHeader>();
if (checksumHeader?.Dummy0 != 0x00000001)
return null;
// Set the game cache checksum header
file.ChecksumHeader = checksumHeader;
#endregion
// Cache the current offset
initialOffset = data.Position;
#region Checksum Map Header
// Try to parse the checksum map header
var checksumMapHeader = data.ReadType<ChecksumMapHeader>();
if (checksumMapHeader?.Dummy0 != 0x14893721)
return null;
if (checksumMapHeader?.Dummy1 != 0x00000001)
return null;
// Set the game cache checksum map header
file.ChecksumMapHeader = checksumMapHeader;
#endregion
#region Checksum Map Entries
// Create the checksum map entry array
file.ChecksumMapEntries = new ChecksumMapEntry[checksumMapHeader.ItemCount];
// Try to parse the checksum map entries
for (int i = 0; i < checksumMapHeader.ItemCount; i++)
{
var checksumMapEntry = data.ReadType<ChecksumMapEntry>();
if (checksumMapEntry == null)
return null;
file.ChecksumMapEntries[i] = checksumMapEntry;
}
#endregion
#region Checksum Entries
// Create the checksum entry array
file.ChecksumEntries = new ChecksumEntry[checksumMapHeader.ChecksumCount];
// Try to parse the checksum entries
for (int i = 0; i < checksumMapHeader.ChecksumCount; i++)
{
var checksumEntry = data.ReadType<ChecksumEntry>();
if (checksumEntry == null)
return null;
file.ChecksumEntries[i] = checksumEntry;
}
#endregion
// Seek to end of checksum section, just in case
data.Seek(initialOffset + checksumHeader.ChecksumSize, SeekOrigin.Begin);
#region Data Block Header
// Try to parse the data block header
var dataBlockHeader = ParseDataBlockHeader(data, header.MinorVersion);
if (dataBlockHeader == null)
return null;
// Set the game cache data block header
file.DataBlockHeader = dataBlockHeader;
#endregion
return file;
}
/// <summary>

View File

@@ -96,160 +96,140 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSFV(Stream? data)
{
// 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 StreamReader(data);
var sfvList = new List<SFV>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
var sfv = new SFV
{
File = string.Join(" ", lineParts, 0, lineParts.Length - 1),
Hash = lineParts[lineParts.Length - 1],
};
sfvList.Add(sfv);
}
// Assign the hashes to the hashfile and return
if (sfvList.Count > 0)
return new Models.Hashfile.Hashfile { SFV = [.. sfvList] };
return null;
}
catch
{
// Ignore errors in getting position for compressed streams
// Ignore the actual error
return null;
}
// Setup the reader and output
var reader = new StreamReader(data);
var sfvList = new List<SFV>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
var sfv = new SFV
{
File = string.Join(" ", lineParts, 0, lineParts.Length - 1),
Hash = lineParts[lineParts.Length - 1],
};
sfvList.Add(sfv);
}
// Assign the hashes to the hashfile and return
if (sfvList.Count > 0)
return new Models.Hashfile.Hashfile { SFV = [.. sfvList] };
return null;
}
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeMD2(Stream? data)
{
// 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 StreamReader(data);
var md2List = new List<MD2>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var md2 = new MD2
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
md2List.Add(md2);
}
// Assign the hashes to the hashfile and return
if (md2List.Count > 0)
return new Models.Hashfile.Hashfile { MD2 = [.. md2List] };
return null;
}
catch
{
// Ignore errors in getting position for compressed streams
// Ignore the actual error
return null;
}
// Setup the reader and output
var reader = new StreamReader(data);
var md2List = new List<MD2>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var md2 = new MD2
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
md2List.Add(md2);
}
// Assign the hashes to the hashfile and return
if (md2List.Count > 0)
return new Models.Hashfile.Hashfile { MD2 = [.. md2List] };
return null;
}
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeMD4(Stream? data)
{
// 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 StreamReader(data);
var md4List = new List<MD4>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var md4 = new MD4
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
md4List.Add(md4);
}
// Assign the hashes to the hashfile and return
if (md4List.Count > 0)
return new Models.Hashfile.Hashfile { MD4 = [.. md4List] };
return null;
}
catch
{
// Ignore errors in getting position for compressed streams
// Ignore the actual error
return null;
}
// Setup the reader and output
var reader = new StreamReader(data);
var md4List = new List<MD4>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var md4 = new MD4
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
md4List.Add(md4);
}
// Assign the hashes to the hashfile and return
if (md4List.Count > 0)
return new Models.Hashfile.Hashfile { MD4 = [.. md4List] };
return null;
}
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeMD5(Stream? data)
{
// If the stream is null
if (data == null)
return default;
try
{
// If the stream length and offset are invalid
if (data.Length == 0 || data.Position < 0 || data.Position >= data.Length)
return default;
}
catch
{
// Ignore errors in getting position for compressed streams
}
// If tthe data is invalid
if (data == null || !data.CanRead)
return null;
// Setup the reader and output
var reader = new StreamReader(data);
@@ -283,236 +263,221 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSHA1(Stream? data)
{
// 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 StreamReader(data);
var sha1List = new List<SHA1>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var sha1 = new SHA1
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
sha1List.Add(sha1);
}
// Assign the hashes to the hashfile and return
if (sha1List.Count > 0)
return new Models.Hashfile.Hashfile { SHA1 = [.. sha1List] };
return null;
}
catch
{
// Ignore errors in getting position for compressed streams
// Ignore the actual error
return null;
}
// Setup the reader and output
var reader = new StreamReader(data);
var sha1List = new List<SHA1>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var sha1 = new SHA1
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
sha1List.Add(sha1);
}
// Assign the hashes to the hashfile and return
if (sha1List.Count > 0)
return new Models.Hashfile.Hashfile { SHA1 = [.. sha1List] };
return null;
}
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSHA256(Stream? data)
{
// 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 StreamReader(data);
var sha256List = new List<SHA256>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var sha256 = new SHA256
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
sha256List.Add(sha256);
}
// Assign the hashes to the hashfile and return
if (sha256List.Count > 0)
return new Models.Hashfile.Hashfile { SHA256 = [.. sha256List] };
return null;
}
catch
{
// Ignore errors in getting position for compressed streams
// Ignore the actual error
return null;
}
// Setup the reader and output
var reader = new StreamReader(data);
var sha256List = new List<SHA256>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var sha256 = new SHA256
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
sha256List.Add(sha256);
}
// Assign the hashes to the hashfile and return
if (sha256List.Count > 0)
return new Models.Hashfile.Hashfile { SHA256 = [.. sha256List] };
return null;
}
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSHA384(Stream? data)
{
// 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 StreamReader(data);
var sha384List = new List<SHA384>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var sha384 = new SHA384
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
sha384List.Add(sha384);
}
// Assign the hashes to the hashfile and return
if (sha384List.Count > 0)
return new Models.Hashfile.Hashfile { SHA384 = [.. sha384List] };
return null;
}
catch
{
// Ignore errors in getting position for compressed streams
// Ignore the actual error
return null;
}
// Setup the reader and output
var reader = new StreamReader(data);
var sha384List = new List<SHA384>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var sha384 = new SHA384
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
sha384List.Add(sha384);
}
// Assign the hashes to the hashfile and return
if (sha384List.Count > 0)
return new Models.Hashfile.Hashfile { SHA384 = [.. sha384List] };
return null;
}
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSHA512(Stream? data)
{
// 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 StreamReader(data);
var sha512List = new List<SHA512>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var sha512 = new SHA512
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
sha512List.Add(sha512);
}
// Assign the hashes to the hashfile and return
if (sha512List.Count > 0)
return new Models.Hashfile.Hashfile { SHA512 = [.. sha512List] };
return null;
}
catch
{
// Ignore errors in getting position for compressed streams
// Ignore the actual error
return null;
}
// Setup the reader and output
var reader = new StreamReader(data);
var sha512List = new List<SHA512>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var sha512 = new SHA512
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
sha512List.Add(sha512);
}
// Assign the hashes to the hashfile and return
if (sha512List.Count > 0)
return new Models.Hashfile.Hashfile { SHA512 = [.. sha512List] };
return null;
}
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSpamSum(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 StreamReader(data);
var spamsumList = new List<SpamSum>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var spamSum = new SpamSum
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
spamsumList.Add(spamSum);
}
// Assign the hashes to the hashfile and return
if (spamsumList.Count > 0)
return new Models.Hashfile.Hashfile { SpamSum = [.. spamsumList] };
return null;
}
catch
{
// Ignore errors in getting position for compressed streams
// Ignore the actual error
return null;
}
// Setup the reader and output
var reader = new StreamReader(data);
var spamsumList = new List<SpamSum>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var spamSum = new SpamSum
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
spamsumList.Add(spamSum);
}
// Assign the hashes to the hashfile and return
if (spamsumList.Count > 0)
return new Models.Hashfile.Hashfile { SpamSum = [.. spamsumList] };
return null;
}
#endregion

View File

@@ -13,80 +13,84 @@ 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 IRD to fill
var ird = new Models.IRD.File();
ird.Magic = data.ReadBytes(4);
string magic = Encoding.ASCII.GetString(ird.Magic);
if (magic != "3IRD")
return null;
ird.Version = data.ReadByteValue();
if (ird.Version < 6)
return null;
byte[] titleId = data.ReadBytes(9);
ird.TitleID = Encoding.ASCII.GetString(titleId);
ird.TitleLength = data.ReadByteValue();
byte[] title = data.ReadBytes(ird.TitleLength);
ird.Title = Encoding.ASCII.GetString(title);
byte[] systemVersion = data.ReadBytes(4);
ird.SystemVersion = Encoding.ASCII.GetString(systemVersion);
byte[] gameVersion = data.ReadBytes(5);
ird.GameVersion = Encoding.ASCII.GetString(gameVersion);
byte[] appVersion = data.ReadBytes(5);
ird.AppVersion = Encoding.ASCII.GetString(appVersion);
if (ird.Version == 7)
ird.UID = data.ReadUInt32();
ird.HeaderLength = data.ReadByteValue();
ird.Header = data.ReadBytes((int)ird.HeaderLength);
ird.FooterLength = data.ReadByteValue();
ird.Footer = data.ReadBytes((int)ird.FooterLength);
ird.RegionCount = data.ReadByteValue();
ird.RegionHashes = new byte[ird.RegionCount][];
for (int i = 0; i < ird.RegionCount; i++)
try
{
ird.RegionHashes[i] = data.ReadBytes(16) ?? [];
}
// Create a new IRD to fill
var ird = new Models.IRD.File();
ird.FileCount = data.ReadByteValue();
ird.FileKeys = new ulong[ird.FileCount];
ird.FileHashes = new byte[ird.FileCount][];
for (int i = 0; i < ird.FileCount; i++)
ird.Magic = data.ReadBytes(4);
string magic = Encoding.ASCII.GetString(ird.Magic);
if (magic != "3IRD")
return null;
ird.Version = data.ReadByteValue();
if (ird.Version < 6)
return null;
byte[] titleId = data.ReadBytes(9);
ird.TitleID = Encoding.ASCII.GetString(titleId);
ird.TitleLength = data.ReadByteValue();
byte[] title = data.ReadBytes(ird.TitleLength);
ird.Title = Encoding.ASCII.GetString(title);
byte[] systemVersion = data.ReadBytes(4);
ird.SystemVersion = Encoding.ASCII.GetString(systemVersion);
byte[] gameVersion = data.ReadBytes(5);
ird.GameVersion = Encoding.ASCII.GetString(gameVersion);
byte[] appVersion = data.ReadBytes(5);
ird.AppVersion = Encoding.ASCII.GetString(appVersion);
if (ird.Version == 7)
ird.UID = data.ReadUInt32();
ird.HeaderLength = data.ReadByteValue();
ird.Header = data.ReadBytes((int)ird.HeaderLength);
ird.FooterLength = data.ReadByteValue();
ird.Footer = data.ReadBytes((int)ird.FooterLength);
ird.RegionCount = data.ReadByteValue();
ird.RegionHashes = new byte[ird.RegionCount][];
for (int i = 0; i < ird.RegionCount; i++)
{
ird.RegionHashes[i] = data.ReadBytes(16) ?? [];
}
ird.FileCount = data.ReadByteValue();
ird.FileKeys = new ulong[ird.FileCount];
ird.FileHashes = new byte[ird.FileCount][];
for (int i = 0; i < ird.FileCount; i++)
{
ird.FileKeys[i] = data.ReadUInt64();
ird.FileHashes[i] = data.ReadBytes(16) ?? [];
}
ird.ExtraConfig = data.ReadUInt16();
ird.Attachments = data.ReadUInt16();
if (ird.Version >= 9)
ird.PIC = data.ReadBytes(115);
ird.Data1Key = data.ReadBytes(16);
ird.Data2Key = data.ReadBytes(16);
if (ird.Version < 9)
ird.PIC = data.ReadBytes(115);
if (ird.Version > 7)
ird.UID = data.ReadUInt32();
ird.CRC = data.ReadUInt32();
return ird;
}
catch
{
ird.FileKeys[i] = data.ReadUInt64();
ird.FileHashes[i] = data.ReadBytes(16) ?? [];
// Ignore the actual error
return null;
}
ird.ExtraConfig = data.ReadUInt16();
ird.Attachments = data.ReadUInt16();
if (ird.Version >= 9)
ird.PIC = data.ReadBytes(115);
ird.Data1Key = data.ReadBytes(16);
ird.Data2Key = data.ReadBytes(16);
if (ird.Version < 9)
ird.PIC = data.ReadBytes(115);
if (ird.Version > 7)
ird.UID = data.ReadUInt32();
ird.CRC = data.ReadUInt32();
return ird;
}
}
}

View File

@@ -14,78 +14,82 @@ 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<Header>();
if (header?.Signature1 != Constants.HeaderSignature)
return null;
if (header.TocAddress >= data.Length)
return null;
// Set the archive header
archive.Header = header;
#endregion
#region Directories
// Get the directories offset
uint directoriesOffset = header.TocAddress;
if (directoriesOffset < 0 || directoriesOffset >= data.Length)
return null;
// Seek to the directories
data.Seek(directoriesOffset, SeekOrigin.Begin);
// Try to parse the directories
var directories = new List<Models.InstallShieldArchiveV3.Directory>();
for (int i = 0; i < header.DirCount; i++)
try
{
var directory = ParseDirectory(data);
if (directory?.Name == null)
// Create a new archive to fill
var archive = new Archive();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Signature1 != Constants.HeaderSignature)
return null;
if (header.TocAddress >= data.Length)
return null;
directories.Add(directory);
data.Seek(directory.ChunkSize - directory.Name.Length - 6, SeekOrigin.Current);
}
// Set the archive header
archive.Header = header;
// Set the directories
archive.Directories = [.. directories];
#endregion
#endregion
#region Directories
#region Files
// Get the directories offset
uint directoriesOffset = header.TocAddress;
if (directoriesOffset < 0 || directoriesOffset >= data.Length)
return null;
// Try to parse the files
var files = new List<Models.InstallShieldArchiveV3.File>();
for (int i = 0; i < archive.Directories.Length; i++)
{
var directory = archive.Directories[i];
for (int j = 0; j < directory.FileCount; j++)
// Seek to the directories
data.Seek(directoriesOffset, SeekOrigin.Begin);
// Try to parse the directories
var directories = new List<Models.InstallShieldArchiveV3.Directory>();
for (int i = 0; i < header.DirCount; i++)
{
var file = data.ReadType<Models.InstallShieldArchiveV3.File>();
if (file?.Name == null)
var directory = ParseDirectory(data);
if (directory?.Name == null)
return null;
files.Add(file);
data.Seek(file.ChunkSize - file.Name.Length - 30, SeekOrigin.Current);
directories.Add(directory);
data.Seek(directory.ChunkSize - directory.Name.Length - 6, SeekOrigin.Current);
}
// Set the directories
archive.Directories = [.. directories];
#endregion
#region Files
// Try to parse the files
var files = new List<Models.InstallShieldArchiveV3.File>();
for (int i = 0; i < archive.Directories.Length; i++)
{
var directory = archive.Directories[i];
for (int j = 0; j < directory.FileCount; j++)
{
var file = data.ReadType<Models.InstallShieldArchiveV3.File>();
if (file?.Name == null)
return null;
files.Add(file);
data.Seek(file.ChunkSize - file.Name.Length - 30, SeekOrigin.Current);
}
}
// 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;
}
/// <summary>

View File

@@ -17,312 +17,316 @@ 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 cabinet to fill
var cabinet = new Cabinet();
#region Common Header
// Try to parse the cabinet header
var commonHeader = data.ReadType<CommonHeader>();
if (commonHeader?.Signature != SignatureString)
return null;
// Set the cabinet header
cabinet.CommonHeader = commonHeader;
#endregion
#region Volume Header
// Try to parse the volume header
var volumeHeader = ParseVolumeHeader(data, GetMajorVersion(commonHeader));
if (volumeHeader == null)
return null;
// Set the volume header
cabinet.VolumeHeader = volumeHeader;
#endregion
#region Descriptor
// Get the descriptor offset
uint descriptorOffset = commonHeader.DescriptorOffset;
if (descriptorOffset < 0 || descriptorOffset >= data.Length)
return null;
// Seek to the descriptor
data.Seek(descriptorOffset, SeekOrigin.Begin);
// Try to parse the descriptor
var descriptor = data.ReadType<Descriptor>();
if (descriptor == null)
return null;
// Set the descriptor
cabinet.Descriptor = descriptor;
#endregion
#region File Descriptor Offsets
// Get the file table offset
uint fileTableOffset = commonHeader.DescriptorOffset + descriptor.FileTableOffset;
if (fileTableOffset < 0 || fileTableOffset >= data.Length)
return null;
// Seek to the file table
data.Seek(fileTableOffset, SeekOrigin.Begin);
// Get the number of file table items
uint fileTableItems;
if (GetMajorVersion(commonHeader) <= 5)
fileTableItems = descriptor.DirectoryCount + descriptor.FileCount;
else
fileTableItems = descriptor.DirectoryCount;
// Create and fill the file table
cabinet.FileDescriptorOffsets = new uint[fileTableItems];
for (int i = 0; i < cabinet.FileDescriptorOffsets.Length; i++)
try
{
cabinet.FileDescriptorOffsets[i] = data.ReadUInt32();
}
// Create a new cabinet to fill
var cabinet = new Cabinet();
#endregion
#region Common Header
#region Directory Descriptors
// Try to parse the cabinet header
var commonHeader = data.ReadType<CommonHeader>();
if (commonHeader?.Signature != SignatureString)
return null;
// Create and fill the directory descriptors
cabinet.DirectoryNames = new string[descriptor.DirectoryCount];
for (int i = 0; i < descriptor.DirectoryCount; i++)
{
// Get the directory descriptor offset
uint offset = descriptorOffset
+ descriptor.FileTableOffset
+ cabinet.FileDescriptorOffsets[i];
// Set the cabinet header
cabinet.CommonHeader = commonHeader;
// If we have an invalid offset
if (offset < 0 || offset >= data.Length)
continue;
#endregion
// Seek to the file descriptor offset
data.Seek(offset, SeekOrigin.Begin);
#region Volume Header
// Create and add the file descriptor
string? directoryName = ParseDirectoryName(data, GetMajorVersion(commonHeader));
if (directoryName != null)
cabinet.DirectoryNames[i] = directoryName;
}
// Try to parse the volume header
var volumeHeader = ParseVolumeHeader(data, GetMajorVersion(commonHeader));
if (volumeHeader == null)
return null;
#endregion
// Set the volume header
cabinet.VolumeHeader = volumeHeader;
#region File Descriptors
#endregion
// Create and fill the file descriptors
cabinet.FileDescriptors = new FileDescriptor[descriptor.FileCount];
for (int i = 0; i < descriptor.FileCount; i++)
{
// Get the file descriptor offset
uint offset;
#region Descriptor
// Get the descriptor offset
uint descriptorOffset = commonHeader.DescriptorOffset;
if (descriptorOffset < 0 || descriptorOffset >= data.Length)
return null;
// Seek to the descriptor
data.Seek(descriptorOffset, SeekOrigin.Begin);
// Try to parse the descriptor
var descriptor = data.ReadType<Descriptor>();
if (descriptor == null)
return null;
// Set the descriptor
cabinet.Descriptor = descriptor;
#endregion
#region File Descriptor Offsets
// Get the file table offset
uint fileTableOffset = commonHeader.DescriptorOffset + descriptor.FileTableOffset;
if (fileTableOffset < 0 || fileTableOffset >= data.Length)
return null;
// Seek to the file table
data.Seek(fileTableOffset, SeekOrigin.Begin);
// Get the number of file table items
uint fileTableItems;
if (GetMajorVersion(commonHeader) <= 5)
{
offset = descriptorOffset
+ descriptor.FileTableOffset
+ cabinet.FileDescriptorOffsets[descriptor.DirectoryCount + i];
}
fileTableItems = descriptor.DirectoryCount + descriptor.FileCount;
else
fileTableItems = descriptor.DirectoryCount;
// Create and fill the file table
cabinet.FileDescriptorOffsets = new uint[fileTableItems];
for (int i = 0; i < cabinet.FileDescriptorOffsets.Length; i++)
{
offset = descriptorOffset
cabinet.FileDescriptorOffsets[i] = data.ReadUInt32();
}
#endregion
#region Directory Descriptors
// Create and fill the directory descriptors
cabinet.DirectoryNames = new string[descriptor.DirectoryCount];
for (int i = 0; i < descriptor.DirectoryCount; i++)
{
// Get the directory descriptor offset
uint offset = descriptorOffset
+ descriptor.FileTableOffset
+ descriptor.FileTableOffset2
+ (uint)(i * 0x57);
+ cabinet.FileDescriptorOffsets[i];
// If we have an invalid offset
if (offset < 0 || offset >= data.Length)
continue;
// Seek to the file descriptor offset
data.Seek(offset, SeekOrigin.Begin);
// Create and add the file descriptor
string? directoryName = ParseDirectoryName(data, GetMajorVersion(commonHeader));
if (directoryName != null)
cabinet.DirectoryNames[i] = directoryName;
}
// If we have an invalid offset
if (offset < 0 || offset >= data.Length)
continue;
#endregion
// Seek to the file descriptor offset
data.Seek(offset, SeekOrigin.Begin);
#region File Descriptors
// Create and add the file descriptor
FileDescriptor fileDescriptor = ParseFileDescriptor(data, GetMajorVersion(commonHeader), descriptorOffset + descriptor.FileTableOffset);
cabinet.FileDescriptors[i] = fileDescriptor;
}
#endregion
#region File Group Offsets
// Create and fill the file group offsets
cabinet.FileGroupOffsets = new Dictionary<long, OffsetList?>();
for (int i = 0; i < (descriptor.FileGroupOffsets?.Length ?? 0); i++)
{
// Get the file group offset
uint offset = descriptor.FileGroupOffsets![i];
if (offset == 0)
continue;
// Adjust the file group offset
offset += commonHeader.DescriptorOffset;
if (offset < 0 || offset >= data.Length)
continue;
// Seek to the file group offset
data.Seek(offset, SeekOrigin.Begin);
// Create and add the offset
OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
cabinet.FileGroupOffsets[descriptor.FileGroupOffsets[i]] = offsetList;
// If we have a nonzero next offset
uint nextOffset = offsetList.NextOffset;
while (nextOffset != 0)
// Create and fill the file descriptors
cabinet.FileDescriptors = new FileDescriptor[descriptor.FileCount];
for (int i = 0; i < descriptor.FileCount; i++)
{
// Get the next offset to read
uint internalOffset = nextOffset + commonHeader.DescriptorOffset;
// Get the file descriptor offset
uint offset;
if (GetMajorVersion(commonHeader) <= 5)
{
offset = descriptorOffset
+ descriptor.FileTableOffset
+ cabinet.FileDescriptorOffsets[descriptor.DirectoryCount + i];
}
else
{
offset = descriptorOffset
+ descriptor.FileTableOffset
+ descriptor.FileTableOffset2
+ (uint)(i * 0x57);
}
// If we have an invalid offset
if (offset < 0 || offset >= data.Length)
continue;
// Seek to the file descriptor offset
data.Seek(offset, SeekOrigin.Begin);
// Create and add the file descriptor
FileDescriptor fileDescriptor = ParseFileDescriptor(data, GetMajorVersion(commonHeader), descriptorOffset + descriptor.FileTableOffset);
cabinet.FileDescriptors[i] = fileDescriptor;
}
#endregion
#region File Group Offsets
// Create and fill the file group offsets
cabinet.FileGroupOffsets = new Dictionary<long, OffsetList?>();
for (int i = 0; i < (descriptor.FileGroupOffsets?.Length ?? 0); i++)
{
// Get the file group offset
uint offset = descriptor.FileGroupOffsets![i];
if (offset == 0)
continue;
// Adjust the file group offset
offset += commonHeader.DescriptorOffset;
if (offset < 0 || offset >= data.Length)
continue;
// Seek to the file group offset
data.Seek(internalOffset, SeekOrigin.Begin);
data.Seek(offset, SeekOrigin.Begin);
// Create and add the offset
offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
cabinet.FileGroupOffsets[nextOffset] = offsetList;
OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
cabinet.FileGroupOffsets[descriptor.FileGroupOffsets[i]] = offsetList;
// Set the next offset
nextOffset = offsetList.NextOffset;
}
}
// If we have a nonzero next offset
uint nextOffset = offsetList.NextOffset;
while (nextOffset != 0)
{
// Get the next offset to read
uint internalOffset = nextOffset + commonHeader.DescriptorOffset;
#endregion
// Seek to the file group offset
data.Seek(internalOffset, SeekOrigin.Begin);
#region File Groups
// Create and add the offset
offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
cabinet.FileGroupOffsets[nextOffset] = offsetList;
// Create the file groups array
cabinet.FileGroups = new FileGroup[cabinet.FileGroupOffsets.Count];
// Create and fill the file groups
int fileGroupId = 0;
foreach (var kvp in cabinet.FileGroupOffsets)
{
// Get the offset
OffsetList? list = kvp.Value;
if (list == null)
{
fileGroupId++;
continue;
// Set the next offset
nextOffset = offsetList.NextOffset;
}
}
// If we have an invalid offset
if (list.DescriptorOffset <= 0)
#endregion
#region File Groups
// Create the file groups array
cabinet.FileGroups = new FileGroup[cabinet.FileGroupOffsets.Count];
// Create and fill the file groups
int fileGroupId = 0;
foreach (var kvp in cabinet.FileGroupOffsets)
{
fileGroupId++;
continue;
// Get the offset
OffsetList? list = kvp.Value;
if (list == null)
{
fileGroupId++;
continue;
}
// If we have an invalid offset
if (list.DescriptorOffset <= 0)
{
fileGroupId++;
continue;
}
/// Seek to the file group
data.Seek(list.DescriptorOffset + descriptorOffset, SeekOrigin.Begin);
// Try to parse the file group
var fileGroup = ParseFileGroup(data, GetMajorVersion(commonHeader), descriptorOffset);
if (fileGroup == null)
return null;
// Add the file group
cabinet.FileGroups[fileGroupId++] = fileGroup;
}
/// Seek to the file group
data.Seek(list.DescriptorOffset + descriptorOffset, SeekOrigin.Begin);
#endregion
// Try to parse the file group
var fileGroup = ParseFileGroup(data, GetMajorVersion(commonHeader), descriptorOffset);
if (fileGroup == null)
return null;
#region Component Offsets
// Add the file group
cabinet.FileGroups[fileGroupId++] = fileGroup;
}
#endregion
#region Component Offsets
// Create and fill the component offsets
cabinet.ComponentOffsets = new Dictionary<long, OffsetList?>();
for (int i = 0; i < (descriptor.ComponentOffsets?.Length ?? 0); i++)
{
// Get the component offset
uint offset = descriptor.ComponentOffsets![i];
if (offset == 0)
continue;
// Adjust the component offset
offset += commonHeader.DescriptorOffset;
if (offset < 0 || offset >= data.Length)
continue;
// Seek to the component offset
data.Seek(offset, SeekOrigin.Begin);
// Create and add the offset
OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
cabinet.ComponentOffsets[descriptor.ComponentOffsets[i]] = offsetList;
// If we have a nonzero next offset
uint nextOffset = offsetList.NextOffset;
while (nextOffset != 0)
// Create and fill the component offsets
cabinet.ComponentOffsets = new Dictionary<long, OffsetList?>();
for (int i = 0; i < (descriptor.ComponentOffsets?.Length ?? 0); i++)
{
// Get the next offset to read
uint internalOffset = nextOffset + commonHeader.DescriptorOffset;
// Get the component offset
uint offset = descriptor.ComponentOffsets![i];
if (offset == 0)
continue;
// Seek to the file group offset
data.Seek(internalOffset, SeekOrigin.Begin);
// Adjust the component offset
offset += commonHeader.DescriptorOffset;
if (offset < 0 || offset >= data.Length)
continue;
// Seek to the component offset
data.Seek(offset, SeekOrigin.Begin);
// Create and add the offset
offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
cabinet.ComponentOffsets[nextOffset] = offsetList;
OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
cabinet.ComponentOffsets[descriptor.ComponentOffsets[i]] = offsetList;
// Set the next offset
nextOffset = offsetList.NextOffset;
// If we have a nonzero next offset
uint nextOffset = offsetList.NextOffset;
while (nextOffset != 0)
{
// Get the next offset to read
uint internalOffset = nextOffset + commonHeader.DescriptorOffset;
// Seek to the file group offset
data.Seek(internalOffset, SeekOrigin.Begin);
// Create and add the offset
offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
cabinet.ComponentOffsets[nextOffset] = offsetList;
// Set the next offset
nextOffset = offsetList.NextOffset;
}
}
#endregion
#region Components
// Create the components array
cabinet.Components = new Component[cabinet.ComponentOffsets.Count];
// Create and fill the components
int componentId = 0;
foreach (KeyValuePair<long, OffsetList?> kvp in cabinet.ComponentOffsets)
{
// Get the offset
OffsetList? list = kvp.Value;
if (list == null)
{
componentId++;
continue;
}
// If we have an invalid offset
if (list.DescriptorOffset <= 0)
{
componentId++;
continue;
}
// Seek to the component
data.Seek(list.DescriptorOffset + descriptorOffset, SeekOrigin.Begin);
// Try to parse the component
var component = ParseComponent(data, GetMajorVersion(commonHeader), descriptorOffset);
if (component == null)
return null;
// Add the component
cabinet.Components[componentId++] = component;
}
#endregion
// TODO: Parse setup types
return cabinet;
}
#endregion
#region Components
// Create the components array
cabinet.Components = new Component[cabinet.ComponentOffsets.Count];
// Create and fill the components
int componentId = 0;
foreach (KeyValuePair<long, OffsetList?> kvp in cabinet.ComponentOffsets)
catch
{
// Get the offset
OffsetList? list = kvp.Value;
if (list == null)
{
componentId++;
continue;
}
// If we have an invalid offset
if (list.DescriptorOffset <= 0)
{
componentId++;
continue;
}
// Seek to the component
data.Seek(list.DescriptorOffset + descriptorOffset, SeekOrigin.Begin);
// Try to parse the component
var component = ParseComponent(data, GetMajorVersion(commonHeader), descriptorOffset);
if (component == null)
return null;
// Add the component
cabinet.Components[componentId++] = component;
// Ignore the actual error
return null;
}
#endregion
// TODO: Parse setup types
return cabinet;
}
/// <summary>

View File

@@ -27,7 +27,7 @@ namespace SabreTools.Serialization.Deserializers
public T? Deserialize(byte[]? data, int offset, Encoding encoding)
{
// If the data is invalid
if (data == null)
if (data == null || data.Length == 0)
return default;
// If the offset is out of bounds
@@ -83,28 +83,17 @@ namespace SabreTools.Serialization.Deserializers
try
{
// If the stream length and offset are invalid
if (data.Length == 0 || data.Position < 0 || data.Position >= data.Length)
return default;
}
catch
{
// Ignore errors in getting position for compressed streams
}
// Setup the serializer and the reader
var serializer = JsonSerializer.Create();
var streamReader = new StreamReader(data, encoding);
var jsonReader = new JsonTextReader(streamReader);
// Setup the serializer and the reader
var serializer = JsonSerializer.Create();
var streamReader = new StreamReader(data, encoding);
var jsonReader = new JsonTextReader(streamReader);
// Perform the deserialization and return
try
{
// Perform the deserialization and return
return serializer.Deserialize<T>(jsonReader);
}
catch
{
// Absorb all exceptions
// Ignore the actual error
return default;
}
}

View File

@@ -16,406 +16,410 @@ 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;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new executable to fill
var executable = new Executable();
#region MS-DOS Stub
// Parse the MS-DOS stub
var stub = new MSDOS().Deserialize(data);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
#endregion
#region Information Block
// Try to parse the executable header
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
var informationBlock = data.ReadType<InformationBlock>();
if (informationBlock?.Signature != LESignatureString && informationBlock?.Signature != LXSignatureString)
return null;
// Set the executable header
executable.InformationBlock = informationBlock;
#endregion
#region Object Table
// Get the object table offset
long offset = informationBlock.ObjectTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
try
{
// Seek to the object table
data.Seek(offset, SeekOrigin.Begin);
// Cache the current offset
int initialOffset = (int)data.Position;
// Create the object table
executable.ObjectTable = new ObjectTableEntry[informationBlock.ObjectTableCount];
// Create a new executable to fill
var executable = new Executable();
// Try to parse the object table
for (int i = 0; i < executable.ObjectTable.Length; i++)
{
var entry = data.ReadType<ObjectTableEntry>();
if (entry == null)
return null;
#region MS-DOS Stub
executable.ObjectTable[i] = entry;
}
}
#endregion
#region Object Page Map
// Get the object page map offset
offset = informationBlock.ObjectPageMapOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the object page map
data.Seek(offset, SeekOrigin.Begin);
// Create the object page map
executable.ObjectPageMap = new ObjectPageMapEntry[informationBlock.ObjectTableCount];
// Try to parse the object page map
for (int i = 0; i < executable.ObjectPageMap.Length; i++)
{
var entry = data.ReadType<ObjectPageMapEntry>();
if (entry == null)
return null;
executable.ObjectPageMap[i] = entry;
}
}
#endregion
#region Object Iterate Data Map
offset = informationBlock.ObjectIterateDataMapOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the object page map
data.Seek(offset, SeekOrigin.Begin);
// TODO: Implement when model found
// No model has been found in the documentation about what
// each of the entries looks like for this map.
}
#endregion
#region Resource Table
// Get the resource table offset
offset = informationBlock.ResourceTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the resource table
data.Seek(offset, SeekOrigin.Begin);
// Create the resource table
executable.ResourceTable = new ResourceTableEntry[informationBlock.ResourceTableCount];
// Try to parse the resource table
for (int i = 0; i < executable.ResourceTable.Length; i++)
{
var entry = data.ReadType<ResourceTableEntry>();
if (entry == null)
return null;
executable.ResourceTable[i] = entry;
}
}
#endregion
#region Resident Names Table
// Get the resident names table offset
offset = informationBlock.ResidentNamesTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the resident names table
data.Seek(offset, SeekOrigin.Begin);
// Create the resident names table
var residentNamesTable = new List<ResidentNamesTableEntry>();
// Try to parse the resident names table
while (true)
{
var entry = ParseResidentNamesTableEntry(data);
residentNamesTable.Add(entry);
// If we have a 0-length entry
if (entry.Length == 0)
break;
}
// Assign the resident names table
executable.ResidentNamesTable = [.. residentNamesTable];
}
#endregion
#region Entry Table
// Get the entry table offset
offset = informationBlock.EntryTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the entry table
data.Seek(offset, SeekOrigin.Begin);
// Create the entry table
var entryTable = new List<EntryTableBundle>();
// Try to parse the entry table
while (true)
{
var bundle = ParseEntryTableBundle(data);
if (bundle != null)
entryTable.Add(bundle);
// If we have a 0-length entry
if (bundle == null || bundle.Entries == 0)
break;
}
// Assign the entry table
executable.EntryTable = [.. entryTable];
}
#endregion
#region Module Format Directives Table
// Get the module format directives table offset
offset = informationBlock.ModuleDirectivesTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the module format directives table
data.Seek(offset, SeekOrigin.Begin);
// Create the module format directives table
executable.ModuleFormatDirectivesTable = new ModuleFormatDirectivesTableEntry[informationBlock.ModuleDirectivesCount];
// Try to parse the module format directives table
for (int i = 0; i < executable.ModuleFormatDirectivesTable.Length; i++)
{
var entry = data.ReadType<ModuleFormatDirectivesTableEntry>();
if (entry == null)
return null;
executable.ModuleFormatDirectivesTable[i] = entry;
}
}
#endregion
#region Verify Record Directive Table
// TODO: Figure out where the offset to this table is stored
// The documentation suggests it's either part of or immediately following
// the Module Format Directives Table
#endregion
#region Fix-up Page Table
// Get the fix-up page table offset
offset = informationBlock.FixupPageTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the fix-up page table
data.Seek(offset, SeekOrigin.Begin);
// Create the fix-up page table
executable.FixupPageTable = new FixupPageTableEntry[executable.ObjectPageMap?.Length ?? 0 + 1];
// Try to parse the fix-up page table
for (int i = 0; i < executable.FixupPageTable.Length; i++)
{
var entry = data.ReadType<FixupPageTableEntry>();
if (entry == null)
return null;
executable.FixupPageTable[i] = entry;
}
}
#endregion
#region Fix-up Record Table
// Get the fix-up record table offset
offset = informationBlock.FixupRecordTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the fix-up record table
data.Seek(offset, SeekOrigin.Begin);
// Create the fix-up record table
executable.FixupRecordTable = new FixupRecordTableEntry[executable.ObjectPageMap?.Length ?? 0 + 1];
// Try to parse the fix-up record table
for (int i = 0; i < executable.FixupRecordTable.Length; i++)
{
var entry = ParseFixupRecordTableEntry(data);
if (entry == null)
return null;
executable.FixupRecordTable[i] = entry;
}
}
#endregion
#region Imported Module Name Table
// Get the imported module name table offset
offset = informationBlock.ImportedModulesNameTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the imported module name table
data.Seek(offset, SeekOrigin.Begin);
// Create the imported module name table
executable.ImportModuleNameTable = new ImportModuleNameTableEntry[informationBlock.ImportedModulesCount];
// Try to parse the imported module name table
for (int i = 0; i < executable.ImportModuleNameTable.Length; i++)
{
var entry = ParseImportModuleNameTableEntry(data);
if (entry == null)
return null;
executable.ImportModuleNameTable[i] = entry;
}
}
#endregion
#region Imported Module Procedure Name Table
// Get the imported module procedure name table offset
offset = informationBlock.ImportProcedureNameTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the imported module procedure name table
data.Seek(offset, SeekOrigin.Begin);
// Get the size of the imported module procedure name table
long tableSize = informationBlock.FixupPageTableOffset
+ informationBlock.FixupSectionSize
- informationBlock.ImportProcedureNameTableOffset;
// Create the imported module procedure name table
var importModuleProcedureNameTable = new List<ImportModuleProcedureNameTableEntry>();
// Try to parse the imported module procedure name table
while (data.Position < offset + tableSize)
{
var entry = ParseImportModuleProcedureNameTableEntry(data);
if (entry == null)
return null;
importModuleProcedureNameTable.Add(entry);
}
// Assign the resident names table
executable.ImportModuleProcedureNameTable = [.. importModuleProcedureNameTable];
}
#endregion
#region Per-Page Checksum Table
// Get the per-page checksum table offset
offset = informationBlock.PerPageChecksumTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the per-page checksum name table
data.Seek(offset, SeekOrigin.Begin);
// Create the per-page checksum name table
executable.PerPageChecksumTable = new PerPageChecksumTableEntry[informationBlock.ModuleNumberPages];
// Try to parse the per-page checksum name table
for (int i = 0; i < executable.PerPageChecksumTable.Length; i++)
{
var entry = data.ReadType<PerPageChecksumTableEntry>();
if (entry == null)
return null;
executable.PerPageChecksumTable[i] = entry;
}
}
#endregion
#region Non-Resident Names Table
// Get the non-resident names table offset
offset = informationBlock.NonResidentNamesTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the non-resident names table
data.Seek(offset, SeekOrigin.Begin);
// Create the non-resident names table
var nonResidentNamesTable = new List<NonResidentNamesTableEntry>();
// Try to parse the non-resident names table
while (true)
{
var entry = ParseNonResidentNameTableEntry(data);
nonResidentNamesTable.Add(entry);
// If we have a 0-length entry
if (entry.Length == 0)
break;
}
// Assign the non-resident names table
executable.NonResidentNamesTable = [.. nonResidentNamesTable];
}
#endregion
#region Debug Information
// Get the debug information offset
offset = informationBlock.DebugInformationOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the debug information
data.Seek(offset, SeekOrigin.Begin);
// Try to parse the debug information
var debugInformation = ParseDebugInformation(data, informationBlock.DebugInformationLength);
if (debugInformation == null)
// Parse the MS-DOS stub
var stub = new MSDOS().Deserialize(data);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the debug information
executable.DebugInformation = debugInformation;
// Set the MS-DOS stub
executable.Stub = stub;
#endregion
#region Information Block
// Try to parse the executable header
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
var informationBlock = data.ReadType<InformationBlock>();
if (informationBlock?.Signature != LESignatureString && informationBlock?.Signature != LXSignatureString)
return null;
// Set the executable header
executable.InformationBlock = informationBlock;
#endregion
#region Object Table
// Get the object table offset
long offset = informationBlock.ObjectTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the object table
data.Seek(offset, SeekOrigin.Begin);
// Create the object table
executable.ObjectTable = new ObjectTableEntry[informationBlock.ObjectTableCount];
// Try to parse the object table
for (int i = 0; i < executable.ObjectTable.Length; i++)
{
var entry = data.ReadType<ObjectTableEntry>();
if (entry == null)
return null;
executable.ObjectTable[i] = entry;
}
}
#endregion
#region Object Page Map
// Get the object page map offset
offset = informationBlock.ObjectPageMapOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the object page map
data.Seek(offset, SeekOrigin.Begin);
// Create the object page map
executable.ObjectPageMap = new ObjectPageMapEntry[informationBlock.ObjectTableCount];
// Try to parse the object page map
for (int i = 0; i < executable.ObjectPageMap.Length; i++)
{
var entry = data.ReadType<ObjectPageMapEntry>();
if (entry == null)
return null;
executable.ObjectPageMap[i] = entry;
}
}
#endregion
#region Object Iterate Data Map
offset = informationBlock.ObjectIterateDataMapOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the object page map
data.Seek(offset, SeekOrigin.Begin);
// TODO: Implement when model found
// No model has been found in the documentation about what
// each of the entries looks like for this map.
}
#endregion
#region Resource Table
// Get the resource table offset
offset = informationBlock.ResourceTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the resource table
data.Seek(offset, SeekOrigin.Begin);
// Create the resource table
executable.ResourceTable = new ResourceTableEntry[informationBlock.ResourceTableCount];
// Try to parse the resource table
for (int i = 0; i < executable.ResourceTable.Length; i++)
{
var entry = data.ReadType<ResourceTableEntry>();
if (entry == null)
return null;
executable.ResourceTable[i] = entry;
}
}
#endregion
#region Resident Names Table
// Get the resident names table offset
offset = informationBlock.ResidentNamesTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the resident names table
data.Seek(offset, SeekOrigin.Begin);
// Create the resident names table
var residentNamesTable = new List<ResidentNamesTableEntry>();
// Try to parse the resident names table
while (true)
{
var entry = ParseResidentNamesTableEntry(data);
residentNamesTable.Add(entry);
// If we have a 0-length entry
if (entry.Length == 0)
break;
}
// Assign the resident names table
executable.ResidentNamesTable = [.. residentNamesTable];
}
#endregion
#region Entry Table
// Get the entry table offset
offset = informationBlock.EntryTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the entry table
data.Seek(offset, SeekOrigin.Begin);
// Create the entry table
var entryTable = new List<EntryTableBundle>();
// Try to parse the entry table
while (true)
{
var bundle = ParseEntryTableBundle(data);
if (bundle != null)
entryTable.Add(bundle);
// If we have a 0-length entry
if (bundle == null || bundle.Entries == 0)
break;
}
// Assign the entry table
executable.EntryTable = [.. entryTable];
}
#endregion
#region Module Format Directives Table
// Get the module format directives table offset
offset = informationBlock.ModuleDirectivesTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the module format directives table
data.Seek(offset, SeekOrigin.Begin);
// Create the module format directives table
executable.ModuleFormatDirectivesTable = new ModuleFormatDirectivesTableEntry[informationBlock.ModuleDirectivesCount];
// Try to parse the module format directives table
for (int i = 0; i < executable.ModuleFormatDirectivesTable.Length; i++)
{
var entry = data.ReadType<ModuleFormatDirectivesTableEntry>();
if (entry == null)
return null;
executable.ModuleFormatDirectivesTable[i] = entry;
}
}
#endregion
#region Verify Record Directive Table
// TODO: Figure out where the offset to this table is stored
// The documentation suggests it's either part of or immediately following
// the Module Format Directives Table
#endregion
#region Fix-up Page Table
// Get the fix-up page table offset
offset = informationBlock.FixupPageTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the fix-up page table
data.Seek(offset, SeekOrigin.Begin);
// Create the fix-up page table
executable.FixupPageTable = new FixupPageTableEntry[executable.ObjectPageMap?.Length ?? 0 + 1];
// Try to parse the fix-up page table
for (int i = 0; i < executable.FixupPageTable.Length; i++)
{
var entry = data.ReadType<FixupPageTableEntry>();
if (entry == null)
return null;
executable.FixupPageTable[i] = entry;
}
}
#endregion
#region Fix-up Record Table
// Get the fix-up record table offset
offset = informationBlock.FixupRecordTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the fix-up record table
data.Seek(offset, SeekOrigin.Begin);
// Create the fix-up record table
executable.FixupRecordTable = new FixupRecordTableEntry[executable.ObjectPageMap?.Length ?? 0 + 1];
// Try to parse the fix-up record table
for (int i = 0; i < executable.FixupRecordTable.Length; i++)
{
var entry = ParseFixupRecordTableEntry(data);
if (entry == null)
return null;
executable.FixupRecordTable[i] = entry;
}
}
#endregion
#region Imported Module Name Table
// Get the imported module name table offset
offset = informationBlock.ImportedModulesNameTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the imported module name table
data.Seek(offset, SeekOrigin.Begin);
// Create the imported module name table
executable.ImportModuleNameTable = new ImportModuleNameTableEntry[informationBlock.ImportedModulesCount];
// Try to parse the imported module name table
for (int i = 0; i < executable.ImportModuleNameTable.Length; i++)
{
var entry = ParseImportModuleNameTableEntry(data);
if (entry == null)
return null;
executable.ImportModuleNameTable[i] = entry;
}
}
#endregion
#region Imported Module Procedure Name Table
// Get the imported module procedure name table offset
offset = informationBlock.ImportProcedureNameTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the imported module procedure name table
data.Seek(offset, SeekOrigin.Begin);
// Get the size of the imported module procedure name table
long tableSize = informationBlock.FixupPageTableOffset
+ informationBlock.FixupSectionSize
- informationBlock.ImportProcedureNameTableOffset;
// Create the imported module procedure name table
var importModuleProcedureNameTable = new List<ImportModuleProcedureNameTableEntry>();
// Try to parse the imported module procedure name table
while (data.Position < offset + tableSize)
{
var entry = ParseImportModuleProcedureNameTableEntry(data);
if (entry == null)
return null;
importModuleProcedureNameTable.Add(entry);
}
// Assign the resident names table
executable.ImportModuleProcedureNameTable = [.. importModuleProcedureNameTable];
}
#endregion
#region Per-Page Checksum Table
// Get the per-page checksum table offset
offset = informationBlock.PerPageChecksumTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the per-page checksum name table
data.Seek(offset, SeekOrigin.Begin);
// Create the per-page checksum name table
executable.PerPageChecksumTable = new PerPageChecksumTableEntry[informationBlock.ModuleNumberPages];
// Try to parse the per-page checksum name table
for (int i = 0; i < executable.PerPageChecksumTable.Length; i++)
{
var entry = data.ReadType<PerPageChecksumTableEntry>();
if (entry == null)
return null;
executable.PerPageChecksumTable[i] = entry;
}
}
#endregion
#region Non-Resident Names Table
// Get the non-resident names table offset
offset = informationBlock.NonResidentNamesTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the non-resident names table
data.Seek(offset, SeekOrigin.Begin);
// Create the non-resident names table
var nonResidentNamesTable = new List<NonResidentNamesTableEntry>();
// Try to parse the non-resident names table
while (true)
{
var entry = ParseNonResidentNameTableEntry(data);
nonResidentNamesTable.Add(entry);
// If we have a 0-length entry
if (entry.Length == 0)
break;
}
// Assign the non-resident names table
executable.NonResidentNamesTable = [.. nonResidentNamesTable];
}
#endregion
#region Debug Information
// Get the debug information offset
offset = informationBlock.DebugInformationOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the debug information
data.Seek(offset, SeekOrigin.Begin);
// Try to parse the debug information
var debugInformation = ParseDebugInformation(data, informationBlock.DebugInformationLength);
if (debugInformation == null)
return null;
// Set the debug information
executable.DebugInformation = debugInformation;
}
#endregion
return executable;
}
catch
{
// Ignore the actual error
return null;
}
#endregion
return executable;
}
/// <summary>

View File

@@ -11,79 +11,70 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc/>
public override MetadataFile? Deserialize(Stream? data)
{
// If the stream is null
if (data == null)
// If the 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;
}
catch
{
// Ignore errors in getting position for compressed streams
}
// Setup the reader and output
var reader = new StreamReader(data, Encoding.UTF8);
var dat = new MetadataFile();
// Setup the reader and output
var reader = new StreamReader(data, Encoding.UTF8);
var dat = new MetadataFile();
Set? set = null;
var sets = new List<Set>();
var rows = new List<Row>();
Set? set = null;
var sets = new List<Set>();
var rows = new List<Row>();
while (!reader.EndOfStream)
{
// Read the line and don't split yet
string? line = reader.ReadLine();
if (string.IsNullOrEmpty(line))
while (!reader.EndOfStream)
{
// If we have a set to process
if (set != null)
// Read the line and don't split yet
string? line = reader.ReadLine();
if (string.IsNullOrEmpty(line))
{
set.Row = [.. rows];
sets.Add(set);
set = null;
rows.Clear();
// If we have a set to process
if (set != null)
{
set.Row = [.. rows];
sets.Add(set);
set = null;
rows.Clear();
}
continue;
}
continue;
}
// Set lines are unique
if (line.StartsWith("ROMs required for driver"))
{
string driver = line.Substring("ROMs required for driver".Length).Trim('"', ' ', '.');
set = new Set { Driver = driver };
continue;
}
else if (line.StartsWith("No ROMs required for driver"))
{
string driver = line.Substring("No ROMs required for driver".Length).Trim('"', ' ', '.');
set = new Set { Driver = driver };
continue;
}
else if (line.StartsWith("ROMs required for device"))
{
string device = line.Substring("ROMs required for device".Length).Trim('"', ' ', '.');
set = new Set { Device = device };
continue;
}
else if (line.StartsWith("No ROMs required for device"))
{
string device = line.Substring("No ROMs required for device".Length).Trim('"', ' ', '.');
set = new Set { Device = device };
continue;
}
else if (line.Equals("Name Size Checksum", StringComparison.OrdinalIgnoreCase))
{
// No-op
continue;
}
// Set lines are unique
if (line.StartsWith("ROMs required for driver"))
{
string driver = line.Substring("ROMs required for driver".Length).Trim('"', ' ', '.');
set = new Set { Driver = driver };
continue;
}
else if (line.StartsWith("No ROMs required for driver"))
{
string driver = line.Substring("No ROMs required for driver".Length).Trim('"', ' ', '.');
set = new Set { Driver = driver };
continue;
}
else if (line.StartsWith("ROMs required for device"))
{
string device = line.Substring("ROMs required for device".Length).Trim('"', ' ', '.');
set = new Set { Device = device };
continue;
}
else if (line.StartsWith("No ROMs required for device"))
{
string device = line.Substring("No ROMs required for device".Length).Trim('"', ' ', '.');
set = new Set { Device = device };
continue;
}
else if (line.Equals("Name Size Checksum", StringComparison.OrdinalIgnoreCase))
{
// No-op
continue;
}
// Split the line for the name iteratively
// Split the line for the name iteratively
#if NETFRAMEWORK || NETCOREAPP3_1
string[] lineParts = line.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
if (lineParts.Length == 1)
@@ -93,112 +84,118 @@ namespace SabreTools.Serialization.Deserializers
if (lineParts.Length == 1)
lineParts = line.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
#else
string[] lineParts = line.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (lineParts.Length == 1)
lineParts = line.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (lineParts.Length == 1)
lineParts = line.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (lineParts.Length == 1)
lineParts = line.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
string[] lineParts = line.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (lineParts.Length == 1)
lineParts = line.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (lineParts.Length == 1)
lineParts = line.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (lineParts.Length == 1)
lineParts = line.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
#endif
// Read the name and set the rest of the line for processing
string name = lineParts[0];
string trimmedLine = line.Substring(name.Length);
if (trimmedLine == null)
continue;
// Read the name and set the rest of the line for processing
string name = lineParts[0];
string trimmedLine = line.Substring(name.Length);
if (trimmedLine == null)
continue;
#if NETFRAMEWORK || NETCOREAPP3_1
lineParts = trimmedLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
#else
lineParts = trimmedLine.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
lineParts = trimmedLine.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
#endif
// The number of items in the row explains what type of row it is
var row = new Row();
switch (lineParts.Length)
{
// Normal CHD (Name, MD5/SHA1)
case 1:
row.Name = name;
if (line.Contains("MD5("))
row.MD5 = lineParts[0].Substring("MD5".Length).Trim('(', ')');
else
row.SHA1 = lineParts[0].Substring("SHA1".Length).Trim('(', ')');
break;
// The number of items in the row explains what type of row it is
var row = new Row();
switch (lineParts.Length)
{
// Normal CHD (Name, MD5/SHA1)
case 1:
row.Name = name;
if (line.Contains("MD5("))
row.MD5 = lineParts[0].Substring("MD5".Length).Trim('(', ')');
else
row.SHA1 = lineParts[0].Substring("SHA1".Length).Trim('(', ')');
break;
// Normal ROM (Name, Size, CRC, MD5/SHA1)
case 3 when line.Contains("CRC"):
row.Name = name;
row.Size = lineParts[0];
row.CRC = lineParts[1].Substring("CRC".Length).Trim('(', ')');
if (line.Contains("MD5("))
row.MD5 = lineParts[2].Substring("MD5".Length).Trim('(', ')');
else
row.SHA1 = lineParts[2].Substring("SHA1".Length).Trim('(', ')');
break;
// Normal ROM (Name, Size, CRC, MD5/SHA1)
case 3 when line.Contains("CRC"):
row.Name = name;
row.Size = lineParts[0];
row.CRC = lineParts[1].Substring("CRC".Length).Trim('(', ')');
if (line.Contains("MD5("))
row.MD5 = lineParts[2].Substring("MD5".Length).Trim('(', ')');
else
row.SHA1 = lineParts[2].Substring("SHA1".Length).Trim('(', ')');
break;
// Bad CHD (Name, BAD, SHA1, BAD_DUMP)
case 3 when line.Contains("BAD_DUMP"):
row.Name = name;
row.Bad = true;
if (line.Contains("MD5("))
row.MD5 = lineParts[1].Substring("MD5".Length).Trim('(', ')');
else
row.SHA1 = lineParts[1].Substring("SHA1".Length).Trim('(', ')');
break;
// Bad CHD (Name, BAD, SHA1, BAD_DUMP)
case 3 when line.Contains("BAD_DUMP"):
row.Name = name;
row.Bad = true;
if (line.Contains("MD5("))
row.MD5 = lineParts[1].Substring("MD5".Length).Trim('(', ')');
else
row.SHA1 = lineParts[1].Substring("SHA1".Length).Trim('(', ')');
break;
// Nodump CHD (Name, NO GOOD DUMP KNOWN)
case 4 when line.Contains("NO GOOD DUMP KNOWN"):
row.Name = name;
row.NoGoodDumpKnown = true;
break;
// Nodump CHD (Name, NO GOOD DUMP KNOWN)
case 4 when line.Contains("NO GOOD DUMP KNOWN"):
row.Name = name;
row.NoGoodDumpKnown = true;
break;
// Bad ROM (Name, Size, BAD, CRC, MD5/SHA1, BAD_DUMP)
case 5 when line.Contains("BAD_DUMP"):
row.Name = name;
row.Size = lineParts[0];
row.Bad = true;
row.CRC = lineParts[2].Substring("CRC".Length).Trim('(', ')');
if (line.Contains("MD5("))
row.MD5 = lineParts[3].Substring("MD5".Length).Trim('(', ')');
else
row.SHA1 = lineParts[3].Substring("SHA1".Length).Trim('(', ')');
break;
// Bad ROM (Name, Size, BAD, CRC, MD5/SHA1, BAD_DUMP)
case 5 when line.Contains("BAD_DUMP"):
row.Name = name;
row.Size = lineParts[0];
row.Bad = true;
row.CRC = lineParts[2].Substring("CRC".Length).Trim('(', ')');
if (line.Contains("MD5("))
row.MD5 = lineParts[3].Substring("MD5".Length).Trim('(', ')');
else
row.SHA1 = lineParts[3].Substring("SHA1".Length).Trim('(', ')');
break;
// Nodump ROM (Name, Size, NO GOOD DUMP KNOWN)
case 5 when line.Contains("NO GOOD DUMP KNOWN"):
row.Name = name;
row.Size = lineParts[0];
row.NoGoodDumpKnown = true;
break;
// Nodump ROM (Name, Size, NO GOOD DUMP KNOWN)
case 5 when line.Contains("NO GOOD DUMP KNOWN"):
row.Name = name;
row.Size = lineParts[0];
row.NoGoodDumpKnown = true;
break;
default:
row = null;
break;
default:
row = null;
break;
}
if (row != null)
rows.Add(row);
}
if (row != null)
rows.Add(row);
}
// If we have a set to process
if (set != null)
{
set.Row = [.. rows];
sets.Add(set);
set = null;
rows.Clear();
}
// If we have a set to process
if (set != null)
// Add extra pieces and return
if (sets.Count > 0)
{
dat.Set = [.. sets];
return dat;
}
return null;
}
catch
{
set.Row = [.. rows];
sets.Add(set);
set = null;
rows.Clear();
// Ignore the actual error
return null;
}
// Add extra pieces and return
if (sets.Count > 0)
{
dat.Set = [.. sets];
return dat;
}
return null;
}
}
}

View File

@@ -15,48 +15,52 @@ 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;
try
{
// Cache the current offset
int initialOffset = (int)data.Position;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new executable to fill
var executable = new Executable();
// Create a new executable to fill
var executable = new Executable();
#region Executable Header
#region Executable Header
// Try to parse the executable header
var executableHeader = ParseExecutableHeader(data);
if (executableHeader == null)
return null;
// Try to parse the executable header
var executableHeader = ParseExecutableHeader(data);
if (executableHeader == null)
return null;
// Set the executable header
executable.Header = executableHeader;
// Set the executable header
executable.Header = executableHeader;
#endregion
#endregion
#region Relocation Table
#region Relocation Table
// If the offset for the relocation table doesn't exist
int tableAddress = initialOffset + executableHeader.RelocationTableAddr;
if (tableAddress >= data.Length)
return executable;
// If the offset for the relocation table doesn't exist
int tableAddress = initialOffset + executableHeader.RelocationTableAddr;
if (tableAddress >= data.Length)
// Try to parse the relocation table
data.Seek(tableAddress, SeekOrigin.Begin);
var relocationTable = ParseRelocationTable(data, executableHeader.RelocationItems);
if (relocationTable == null)
return null;
// Set the relocation table
executable.RelocationTable = relocationTable;
#endregion
// Return the executable
return executable;
// Try to parse the relocation table
data.Seek(tableAddress, SeekOrigin.Begin);
var relocationTable = ParseRelocationTable(data, executableHeader.RelocationItems);
if (relocationTable == null)
}
catch
{
// Ignore the actual error
return null;
// Set the relocation table
executable.RelocationTable = relocationTable;
#endregion
// Return the executable
return executable;
}
}
/// <summary>

View File

@@ -16,73 +16,77 @@ 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;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new cabinet to fill
var cabinet = new Cabinet();
#region Cabinet Header
// Try to parse the cabinet header
var cabinetHeader = ParseCabinetHeader(data);
if (cabinetHeader == null)
return null;
// Set the cabinet header
cabinet.Header = cabinetHeader;
#endregion
#region Folders
// Set the folder array
cabinet.Folders = new CFFOLDER[cabinetHeader.FolderCount];
// Try to parse each folder, if we have any
for (int i = 0; i < cabinetHeader.FolderCount; i++)
try
{
var folder = ParseFolder(data, cabinetHeader);
if (folder == null)
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new cabinet to fill
var cabinet = new Cabinet();
#region Cabinet Header
// Try to parse the cabinet header
var cabinetHeader = ParseCabinetHeader(data);
if (cabinetHeader == null)
return null;
// Set the folder
cabinet.Folders[i] = folder;
}
// Set the cabinet header
cabinet.Header = cabinetHeader;
#endregion
#endregion
#region Files
#region Folders
// Get the files offset
int filesOffset = (int)cabinetHeader.FilesOffset + initialOffset;
if (filesOffset > data.Length)
return null;
// Set the folder array
cabinet.Folders = new CFFOLDER[cabinetHeader.FolderCount];
// Seek to the offset
data.Seek(filesOffset, SeekOrigin.Begin);
// Try to parse each folder, if we have any
for (int i = 0; i < cabinetHeader.FolderCount; i++)
{
var folder = ParseFolder(data, cabinetHeader);
if (folder == null)
return null;
// Set the file array
cabinet.Files = new CFFILE[cabinetHeader.FileCount];
// Set the folder
cabinet.Folders[i] = folder;
}
// Try to parse each file, if we have any
for (int i = 0; i < cabinetHeader.FileCount; i++)
{
var file = ParseFile(data);
if (file == null)
#endregion
#region Files
// Get the files offset
int filesOffset = (int)cabinetHeader.FilesOffset + initialOffset;
if (filesOffset > data.Length)
return null;
// Set the file
cabinet.Files[i] = file;
// Seek to the offset
data.Seek(filesOffset, SeekOrigin.Begin);
// Set the file array
cabinet.Files = new CFFILE[cabinetHeader.FileCount];
// Try to parse each file, if we have any
for (int i = 0; i < cabinetHeader.FileCount; i++)
{
var file = ParseFile(data);
if (file == null)
return null;
// Set the file
cabinet.Files[i] = file;
}
#endregion
return cabinet;
}
catch
{
// Ignore the actual error
return null;
}
#endregion
return cabinet;
}
/// <summary>

View File

@@ -17,319 +17,323 @@ 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 User Data
// Check for User Data
uint possibleSignature = data.ReadUInt32();
data.Seek(-4, SeekOrigin.Current);
if (possibleSignature == UserDataSignatureUInt32)
try
{
// Save the current position for offset correction
long basePtr = data.Position;
// Create a new archive to fill
var archive = new Archive();
// Deserialize the user data, returning null if invalid
var userData = data.ReadType<UserData>();
if (userData?.Signature != UserDataSignatureString)
return null;
#region User Data
// Set the user data
archive.UserData = userData;
// Set the starting position according to the header offset
data.Seek(basePtr + (int)archive.UserData.HeaderOffset, SeekOrigin.Begin);
}
#endregion
#region Archive Header
// Check for the Header
possibleSignature = data.ReadUInt32();
data.Seek(-4, SeekOrigin.Current);
if (possibleSignature == ArchiveHeaderSignatureUInt32)
{
// Try to parse the archive header
var archiveHeader = ParseArchiveHeader(data);
if (archiveHeader == null)
return null;
// Set the archive header
archive.ArchiveHeader = archiveHeader;
}
else
{
return null;
}
#endregion
#region Hash Table
// TODO: The hash table has to be be decrypted before reading
// Version 1
if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format1)
{
// If we have a hash table
long hashTableOffset = archive.ArchiveHeader.HashTablePosition;
if (hashTableOffset != 0)
// Check for User Data
uint possibleSignature = data.ReadUInt32();
data.Seek(-4, SeekOrigin.Current);
if (possibleSignature == UserDataSignatureUInt32)
{
// Seek to the offset
data.Seek(hashTableOffset, SeekOrigin.Begin);
// Save the current position for offset correction
long basePtr = data.Position;
// Find the ending offset based on size
long hashTableEnd = hashTableOffset + archive.ArchiveHeader.HashTableSize;
// Read in the hash table
var hashTable = new List<HashEntry>();
while (data.Position < hashTableEnd)
{
var hashEntry = data.ReadType<HashEntry>();
if (hashEntry == null)
return null;
hashTable.Add(hashEntry);
}
archive.HashTable = [.. hashTable];
}
}
// Version 2 and 3
else if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format2
|| archive.ArchiveHeader.FormatVersion == FormatVersion.Format3)
{
// If we have a hash table
long hashTableOffset = ((uint)archive.ArchiveHeader.HashTablePositionHi << 23) | archive.ArchiveHeader.HashTablePosition;
if (hashTableOffset != 0)
{
// Seek to the offset
data.Seek(hashTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long hashTableEnd = hashTableOffset + archive.ArchiveHeader.HashTableSize;
// Read in the hash table
var hashTable = new List<HashEntry>();
while (data.Position < hashTableEnd)
{
var hashEntry = data.ReadType<HashEntry>();
if (hashEntry == null)
return null;
hashTable.Add(hashEntry);
}
archive.HashTable = [.. hashTable];
}
}
// Version 4
else if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format4)
{
// If we have a hash table
long hashTableOffset = ((uint)archive.ArchiveHeader.HashTablePositionHi << 23) | archive.ArchiveHeader.HashTablePosition;
if (hashTableOffset != 0)
{
// Seek to the offset
data.Seek(hashTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long hashTableEnd = hashTableOffset + (long)archive.ArchiveHeader.HashTableSizeLong;
// Read in the hash table
var hashTable = new List<HashEntry>();
while (data.Position < hashTableEnd)
{
var hashEntry = data.ReadType<HashEntry>();
if (hashEntry == null)
return null;
hashTable.Add(hashEntry);
}
archive.HashTable = [.. hashTable];
}
}
#endregion
#region Block Table
// Version 1
if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format1)
{
// If we have a block table
long blockTableOffset = archive.ArchiveHeader.BlockTablePosition;
if (blockTableOffset != 0)
{
// Seek to the offset
data.Seek(blockTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long blockTableEnd = blockTableOffset + archive.ArchiveHeader.BlockTableSize;
// Read in the block table
var blockTable = new List<BlockEntry>();
while (data.Position < blockTableEnd)
{
var blockEntry = data.ReadType<BlockEntry>();
if (blockEntry == null)
return null;
blockTable.Add(blockEntry);
}
archive.BlockTable = [.. blockTable];
}
}
// Version 2 and 3
else if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format2
|| archive.ArchiveHeader.FormatVersion == FormatVersion.Format3)
{
// If we have a block table
long blockTableOffset = ((uint)archive.ArchiveHeader.BlockTablePositionHi << 23) | archive.ArchiveHeader.BlockTablePosition;
if (blockTableOffset != 0)
{
// Seek to the offset
data.Seek(blockTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long blockTableEnd = blockTableOffset + archive.ArchiveHeader.BlockTableSize;
// Read in the block table
var blockTable = new List<BlockEntry>();
while (data.Position < blockTableEnd)
{
var blockEntry = data.ReadType<BlockEntry>();
if (blockEntry == null)
return null;
blockTable.Add(blockEntry);
}
archive.BlockTable = [.. blockTable];
}
}
// Version 4
else if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format4)
{
// If we have a block table
long blockTableOffset = ((uint)archive.ArchiveHeader.BlockTablePositionHi << 23) | archive.ArchiveHeader.BlockTablePosition;
if (blockTableOffset != 0)
{
// Seek to the offset
data.Seek(blockTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long blockTableEnd = blockTableOffset + (long)archive.ArchiveHeader.BlockTableSizeLong;
// Read in the block table
var blockTable = new List<BlockEntry>();
while (data.Position < blockTableEnd)
{
var blockEntry = data.ReadType<BlockEntry>();
if (blockEntry == null)
return null;
blockTable.Add(blockEntry);
}
archive.BlockTable = [.. blockTable];
}
}
#endregion
#region Hi-Block Table
// Version 2, 3, and 4
if (archive.ArchiveHeader.FormatVersion >= FormatVersion.Format2)
{
// If we have a hi-block table
long hiBlockTableOffset = (long)archive.ArchiveHeader.HiBlockTablePosition;
if (hiBlockTableOffset != 0)
{
// Seek to the offset
data.Seek(hiBlockTableOffset, SeekOrigin.Begin);
// Read in the hi-block table
var hiBlockTable = new List<short>();
for (int i = 0; i < (archive.BlockTable?.Length ?? 0); i++)
{
short hiBlockEntry = data.ReadInt16();
hiBlockTable.Add(hiBlockEntry);
}
archive.HiBlockTable = [.. hiBlockTable];
}
}
#endregion
#region BET Table
// Version 3 and 4
if (archive.ArchiveHeader.FormatVersion >= FormatVersion.Format3)
{
// If we have a BET table
long betTableOffset = (long)archive.ArchiveHeader.BetTablePosition;
if (betTableOffset != 0)
{
// Seek to the offset
data.Seek(betTableOffset, SeekOrigin.Begin);
// Read in the BET table
var betTable = ParseBetTable(data);
if (betTable != null)
// Deserialize the user data, returning null if invalid
var userData = data.ReadType<UserData>();
if (userData?.Signature != UserDataSignatureString)
return null;
archive.BetTable = betTable;
// Set the user data
archive.UserData = userData;
// Set the starting position according to the header offset
data.Seek(basePtr + (int)archive.UserData.HeaderOffset, SeekOrigin.Begin);
}
}
#endregion
#endregion
#region HET Table
#region Archive Header
// Version 3 and 4
if (archive.ArchiveHeader.FormatVersion >= FormatVersion.Format3)
{
// If we have a HET table
long hetTableOffset = (long)archive.ArchiveHeader.HetTablePosition;
if (hetTableOffset != 0)
// Check for the Header
possibleSignature = data.ReadUInt32();
data.Seek(-4, SeekOrigin.Current);
if (possibleSignature == ArchiveHeaderSignatureUInt32)
{
// Seek to the offset
data.Seek(hetTableOffset, SeekOrigin.Begin);
// Read in the HET table
var hetTable = ParseHetTable(data);
if (hetTable != null)
// Try to parse the archive header
var archiveHeader = ParseArchiveHeader(data);
if (archiveHeader == null)
return null;
archive.HetTable = hetTable;
// Set the archive header
archive.ArchiveHeader = archiveHeader;
}
else
{
return null;
}
#endregion
#region Hash Table
// TODO: The hash table has to be be decrypted before reading
// Version 1
if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format1)
{
// If we have a hash table
long hashTableOffset = archive.ArchiveHeader.HashTablePosition;
if (hashTableOffset != 0)
{
// Seek to the offset
data.Seek(hashTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long hashTableEnd = hashTableOffset + archive.ArchiveHeader.HashTableSize;
// Read in the hash table
var hashTable = new List<HashEntry>();
while (data.Position < hashTableEnd)
{
var hashEntry = data.ReadType<HashEntry>();
if (hashEntry == null)
return null;
hashTable.Add(hashEntry);
}
archive.HashTable = [.. hashTable];
}
}
// Version 2 and 3
else if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format2
|| archive.ArchiveHeader.FormatVersion == FormatVersion.Format3)
{
// If we have a hash table
long hashTableOffset = ((uint)archive.ArchiveHeader.HashTablePositionHi << 23) | archive.ArchiveHeader.HashTablePosition;
if (hashTableOffset != 0)
{
// Seek to the offset
data.Seek(hashTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long hashTableEnd = hashTableOffset + archive.ArchiveHeader.HashTableSize;
// Read in the hash table
var hashTable = new List<HashEntry>();
while (data.Position < hashTableEnd)
{
var hashEntry = data.ReadType<HashEntry>();
if (hashEntry == null)
return null;
hashTable.Add(hashEntry);
}
archive.HashTable = [.. hashTable];
}
}
// Version 4
else if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format4)
{
// If we have a hash table
long hashTableOffset = ((uint)archive.ArchiveHeader.HashTablePositionHi << 23) | archive.ArchiveHeader.HashTablePosition;
if (hashTableOffset != 0)
{
// Seek to the offset
data.Seek(hashTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long hashTableEnd = hashTableOffset + (long)archive.ArchiveHeader.HashTableSizeLong;
// Read in the hash table
var hashTable = new List<HashEntry>();
while (data.Position < hashTableEnd)
{
var hashEntry = data.ReadType<HashEntry>();
if (hashEntry == null)
return null;
hashTable.Add(hashEntry);
}
archive.HashTable = [.. hashTable];
}
}
#endregion
#region Block Table
// Version 1
if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format1)
{
// If we have a block table
long blockTableOffset = archive.ArchiveHeader.BlockTablePosition;
if (blockTableOffset != 0)
{
// Seek to the offset
data.Seek(blockTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long blockTableEnd = blockTableOffset + archive.ArchiveHeader.BlockTableSize;
// Read in the block table
var blockTable = new List<BlockEntry>();
while (data.Position < blockTableEnd)
{
var blockEntry = data.ReadType<BlockEntry>();
if (blockEntry == null)
return null;
blockTable.Add(blockEntry);
}
archive.BlockTable = [.. blockTable];
}
}
// Version 2 and 3
else if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format2
|| archive.ArchiveHeader.FormatVersion == FormatVersion.Format3)
{
// If we have a block table
long blockTableOffset = ((uint)archive.ArchiveHeader.BlockTablePositionHi << 23) | archive.ArchiveHeader.BlockTablePosition;
if (blockTableOffset != 0)
{
// Seek to the offset
data.Seek(blockTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long blockTableEnd = blockTableOffset + archive.ArchiveHeader.BlockTableSize;
// Read in the block table
var blockTable = new List<BlockEntry>();
while (data.Position < blockTableEnd)
{
var blockEntry = data.ReadType<BlockEntry>();
if (blockEntry == null)
return null;
blockTable.Add(blockEntry);
}
archive.BlockTable = [.. blockTable];
}
}
// Version 4
else if (archive.ArchiveHeader.FormatVersion == FormatVersion.Format4)
{
// If we have a block table
long blockTableOffset = ((uint)archive.ArchiveHeader.BlockTablePositionHi << 23) | archive.ArchiveHeader.BlockTablePosition;
if (blockTableOffset != 0)
{
// Seek to the offset
data.Seek(blockTableOffset, SeekOrigin.Begin);
// Find the ending offset based on size
long blockTableEnd = blockTableOffset + (long)archive.ArchiveHeader.BlockTableSizeLong;
// Read in the block table
var blockTable = new List<BlockEntry>();
while (data.Position < blockTableEnd)
{
var blockEntry = data.ReadType<BlockEntry>();
if (blockEntry == null)
return null;
blockTable.Add(blockEntry);
}
archive.BlockTable = [.. blockTable];
}
}
#endregion
#region Hi-Block Table
// Version 2, 3, and 4
if (archive.ArchiveHeader.FormatVersion >= FormatVersion.Format2)
{
// If we have a hi-block table
long hiBlockTableOffset = (long)archive.ArchiveHeader.HiBlockTablePosition;
if (hiBlockTableOffset != 0)
{
// Seek to the offset
data.Seek(hiBlockTableOffset, SeekOrigin.Begin);
// Read in the hi-block table
var hiBlockTable = new List<short>();
for (int i = 0; i < (archive.BlockTable?.Length ?? 0); i++)
{
short hiBlockEntry = data.ReadInt16();
hiBlockTable.Add(hiBlockEntry);
}
archive.HiBlockTable = [.. hiBlockTable];
}
}
#endregion
#region BET Table
// Version 3 and 4
if (archive.ArchiveHeader.FormatVersion >= FormatVersion.Format3)
{
// If we have a BET table
long betTableOffset = (long)archive.ArchiveHeader.BetTablePosition;
if (betTableOffset != 0)
{
// Seek to the offset
data.Seek(betTableOffset, SeekOrigin.Begin);
// Read in the BET table
var betTable = ParseBetTable(data);
if (betTable != null)
return null;
archive.BetTable = betTable;
}
}
#endregion
#region HET Table
// Version 3 and 4
if (archive.ArchiveHeader.FormatVersion >= FormatVersion.Format3)
{
// If we have a HET table
long hetTableOffset = (long)archive.ArchiveHeader.HetTablePosition;
if (hetTableOffset != 0)
{
// Seek to the offset
data.Seek(hetTableOffset, SeekOrigin.Begin);
// Read in the HET table
var hetTable = ParseHetTable(data);
if (hetTable != null)
return null;
archive.HetTable = hetTable;
}
}
#endregion
return archive;
}
catch
{
// Ignore the actual error
return null;
}
#endregion
return archive;
}
/// <summary>

View File

@@ -16,122 +16,126 @@ 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 cart image to fill
var cart = new Cart();
#region NCSD Header
// Try to parse the header
var header = ParseNCSDHeader(data);
if (header == null)
return null;
// Set the cart image header
cart.Header = header;
#endregion
#region Card Info Header
// Try to parse the card info header
var cardInfoHeader = ParseCardInfoHeader(data);
if (cardInfoHeader == null)
return null;
// Set the card info header
cart.CardInfoHeader = cardInfoHeader;
#endregion
#region Development Card Info Header
// Try to parse the development card info header
var developmentCardInfoHeader = data.ReadType<DevelopmentCardInfoHeader>();
if (developmentCardInfoHeader == null)
return null;
// Set the development card info header
cart.DevelopmentCardInfoHeader = developmentCardInfoHeader;
#endregion
// Cache the media unit size for further use
long mediaUnitSize = 0;
if (header.PartitionFlags != null)
mediaUnitSize = (uint)(0x200 * Math.Pow(2, header.PartitionFlags[(int)NCSDFlags.MediaUnitSize]));
#region Partitions
// Create the tables
cart.Partitions = new NCCHHeader[8];
cart.ExtendedHeaders = new NCCHExtendedHeader?[8];
cart.ExeFSHeaders = new ExeFSHeader?[8];
cart.RomFSHeaders = new RomFSHeader?[8];
// Iterate and build the partitions
for (int i = 0; i < 8; i++)
try
{
// Find the offset to the partition
long partitionOffset = cart.Header.PartitionsTable?[i]?.Offset ?? 0;
partitionOffset *= mediaUnitSize;
if (partitionOffset == 0)
continue;
// Create a new cart image to fill
var cart = new Cart();
// Seek to the start of the partition
data.Seek(partitionOffset, SeekOrigin.Begin);
#region NCSD Header
// Handle the normal header
var partition = ParseNCCHHeader(data);
if (partition == null || partition.MagicID != NCCHMagicNumber)
continue;
// Try to parse the header
var header = ParseNCSDHeader(data);
if (header == null)
return null;
// Set the normal header
cart.Partitions[i] = partition;
// Set the cart image header
cart.Header = header;
// Handle the extended header, if it exists
if (partition.ExtendedHeaderSizeInBytes > 0)
#endregion
#region Card Info Header
// Try to parse the card info header
var cardInfoHeader = ParseCardInfoHeader(data);
if (cardInfoHeader == null)
return null;
// Set the card info header
cart.CardInfoHeader = cardInfoHeader;
#endregion
#region Development Card Info Header
// Try to parse the development card info header
var developmentCardInfoHeader = data.ReadType<DevelopmentCardInfoHeader>();
if (developmentCardInfoHeader == null)
return null;
// Set the development card info header
cart.DevelopmentCardInfoHeader = developmentCardInfoHeader;
#endregion
// Cache the media unit size for further use
long mediaUnitSize = 0;
if (header.PartitionFlags != null)
mediaUnitSize = (uint)(0x200 * Math.Pow(2, header.PartitionFlags[(int)NCSDFlags.MediaUnitSize]));
#region Partitions
// Create the tables
cart.Partitions = new NCCHHeader[8];
cart.ExtendedHeaders = new NCCHExtendedHeader?[8];
cart.ExeFSHeaders = new ExeFSHeader?[8];
cart.RomFSHeaders = new RomFSHeader?[8];
// Iterate and build the partitions
for (int i = 0; i < 8; i++)
{
var extendedHeader = data.ReadType<NCCHExtendedHeader>();
if (extendedHeader != null)
cart.ExtendedHeaders[i] = extendedHeader;
}
// Handle the ExeFS, if it exists
if (partition.ExeFSSizeInMediaUnits > 0)
{
long offset = partition.ExeFSOffsetInMediaUnits * mediaUnitSize;
data.Seek(partitionOffset + offset, SeekOrigin.Begin);
var exeFsHeader = ParseExeFSHeader(data);
if (exeFsHeader == null)
return null;
cart.ExeFSHeaders[i] = exeFsHeader;
}
// Handle the RomFS, if it exists
if (partition.RomFSSizeInMediaUnits > 0)
{
long offset = partition.RomFSOffsetInMediaUnits * mediaUnitSize;
data.Seek(partitionOffset + offset, SeekOrigin.Begin);
var romFsHeader = data.ReadType<RomFSHeader>();
if (romFsHeader?.MagicString != RomFSMagicNumber)
continue;
if (romFsHeader?.MagicNumber != RomFSSecondMagicNumber)
// Find the offset to the partition
long partitionOffset = cart.Header.PartitionsTable?[i]?.Offset ?? 0;
partitionOffset *= mediaUnitSize;
if (partitionOffset == 0)
continue;
cart.RomFSHeaders[i] = romFsHeader;
// Seek to the start of the partition
data.Seek(partitionOffset, SeekOrigin.Begin);
// Handle the normal header
var partition = ParseNCCHHeader(data);
if (partition == null || partition.MagicID != NCCHMagicNumber)
continue;
// Set the normal header
cart.Partitions[i] = partition;
// Handle the extended header, if it exists
if (partition.ExtendedHeaderSizeInBytes > 0)
{
var extendedHeader = data.ReadType<NCCHExtendedHeader>();
if (extendedHeader != null)
cart.ExtendedHeaders[i] = extendedHeader;
}
// Handle the ExeFS, if it exists
if (partition.ExeFSSizeInMediaUnits > 0)
{
long offset = partition.ExeFSOffsetInMediaUnits * mediaUnitSize;
data.Seek(partitionOffset + offset, SeekOrigin.Begin);
var exeFsHeader = ParseExeFSHeader(data);
if (exeFsHeader == null)
return null;
cart.ExeFSHeaders[i] = exeFsHeader;
}
// Handle the RomFS, if it exists
if (partition.RomFSSizeInMediaUnits > 0)
{
long offset = partition.RomFSOffsetInMediaUnits * mediaUnitSize;
data.Seek(partitionOffset + offset, SeekOrigin.Begin);
var romFsHeader = data.ReadType<RomFSHeader>();
if (romFsHeader?.MagicString != RomFSMagicNumber)
continue;
if (romFsHeader?.MagicNumber != RomFSSecondMagicNumber)
continue;
cart.RomFSHeaders[i] = romFsHeader;
}
}
#endregion
return cart;
}
catch
{
// Ignore the actual error
return null;
}
#endregion
return cart;
}
/// <summary>

View File

@@ -15,261 +15,265 @@ 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 No Cache to fill
var file = new Models.NCF.File();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Dummy0 != 0x00000001)
return null;
if (header?.MajorVersion != 0x00000002)
return null;
if (header?.MinorVersion != 1)
return null;
// Set the no cache header
file.Header = header;
#endregion
// Cache the current offset
long initialOffset = data.Position;
#region Directory Header
// Try to parse the directory header
var directoryHeader = data.ReadType<DirectoryHeader>();
if (directoryHeader?.Dummy0 != 0x00000004)
return null;
// Set the game cache directory header
file.DirectoryHeader = directoryHeader;
#endregion
#region Directory Entries
// Create the directory entry array
file.DirectoryEntries = new DirectoryEntry[directoryHeader.ItemCount];
// Try to parse the directory entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
try
{
var directoryEntry = data.ReadType<DirectoryEntry>();
if (directoryEntry == null)
// Create a new Half-Life No Cache to fill
var file = new Models.NCF.File();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Dummy0 != 0x00000001)
return null;
if (header?.MajorVersion != 0x00000002)
return null;
if (header?.MinorVersion != 1)
return null;
file.DirectoryEntries[i] = directoryEntry;
}
// Set the no cache header
file.Header = header;
#endregion
#endregion
#region Directory Names
// Cache the current offset
long initialOffset = data.Position;
if (directoryHeader.NameSize > 0)
{
// Get the current offset for adjustment
long directoryNamesStart = data.Position;
#region Directory Header
// Get the ending offset
long directoryNamesEnd = data.Position + directoryHeader.NameSize;
// Try to parse the directory header
var directoryHeader = data.ReadType<DirectoryHeader>();
if (directoryHeader?.Dummy0 != 0x00000004)
return null;
// Create the string dictionary
file.DirectoryNames = new Dictionary<long, string?>();
// Set the game cache directory header
file.DirectoryHeader = directoryHeader;
// Loop and read the null-terminated strings
while (data.Position < directoryNamesEnd)
#endregion
#region Directory Entries
// Create the directory entry array
file.DirectoryEntries = new DirectoryEntry[directoryHeader.ItemCount];
// Try to parse the directory entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
long nameOffset = data.Position - directoryNamesStart;
string? directoryName = data.ReadNullTerminatedAnsiString();
if (data.Position > directoryNamesEnd)
{
data.Seek(-directoryName?.Length ?? 0, SeekOrigin.Current);
byte[] endingData = data.ReadBytes((int)(directoryNamesEnd - data.Position));
directoryName = Encoding.ASCII.GetString(endingData);
}
var directoryEntry = data.ReadType<DirectoryEntry>();
if (directoryEntry == null)
return null;
file.DirectoryNames[nameOffset] = directoryName;
file.DirectoryEntries[i] = directoryEntry;
}
}
#endregion
#endregion
#region Directory Info 1 Entries
#region Directory Names
// Create the directory info 1 entry array
file.DirectoryInfo1Entries = new DirectoryInfo1Entry[directoryHeader.Info1Count];
if (directoryHeader.NameSize > 0)
{
// Get the current offset for adjustment
long directoryNamesStart = data.Position;
// Try to parse the directory info 1 entries
for (int i = 0; i < directoryHeader.Info1Count; i++)
{
var directoryInfo1Entry = data.ReadType<DirectoryInfo1Entry>();
if (directoryInfo1Entry == null)
// Get the ending offset
long directoryNamesEnd = data.Position + directoryHeader.NameSize;
// Create the string dictionary
file.DirectoryNames = new Dictionary<long, string?>();
// Loop and read the null-terminated strings
while (data.Position < directoryNamesEnd)
{
long nameOffset = data.Position - directoryNamesStart;
string? directoryName = data.ReadNullTerminatedAnsiString();
if (data.Position > directoryNamesEnd)
{
data.Seek(-directoryName?.Length ?? 0, SeekOrigin.Current);
byte[] endingData = data.ReadBytes((int)(directoryNamesEnd - data.Position));
directoryName = Encoding.ASCII.GetString(endingData);
}
file.DirectoryNames[nameOffset] = directoryName;
}
}
#endregion
#region Directory Info 1 Entries
// Create the directory info 1 entry array
file.DirectoryInfo1Entries = new DirectoryInfo1Entry[directoryHeader.Info1Count];
// Try to parse the directory info 1 entries
for (int i = 0; i < directoryHeader.Info1Count; i++)
{
var directoryInfo1Entry = data.ReadType<DirectoryInfo1Entry>();
if (directoryInfo1Entry == null)
return null;
file.DirectoryInfo1Entries[i] = directoryInfo1Entry;
}
#endregion
#region Directory Info 2 Entries
// Create the directory info 2 entry array
file.DirectoryInfo2Entries = new DirectoryInfo2Entry[directoryHeader.ItemCount];
// Try to parse the directory info 2 entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryInfo2Entry = data.ReadType<DirectoryInfo2Entry>();
if (directoryInfo2Entry == null)
return null;
file.DirectoryInfo2Entries[i] = directoryInfo2Entry;
}
#endregion
#region Directory Copy Entries
// Create the directory copy entry array
file.DirectoryCopyEntries = new DirectoryCopyEntry[directoryHeader.CopyCount];
// Try to parse the directory copy entries
for (int i = 0; i < directoryHeader.CopyCount; i++)
{
var directoryCopyEntry = data.ReadType<DirectoryCopyEntry>();
if (directoryCopyEntry == null)
return null;
file.DirectoryCopyEntries[i] = directoryCopyEntry;
}
#endregion
#region Directory Local Entries
// Create the directory local entry array
file.DirectoryLocalEntries = new DirectoryLocalEntry[directoryHeader.LocalCount];
// Try to parse the directory local entries
for (int i = 0; i < directoryHeader.LocalCount; i++)
{
var directoryLocalEntry = data.ReadType<DirectoryLocalEntry>();
if (directoryLocalEntry == null)
return null;
file.DirectoryLocalEntries[i] = directoryLocalEntry;
}
#endregion
// Seek to end of directory section, just in case
data.Seek(initialOffset + directoryHeader.DirectorySize, SeekOrigin.Begin);
#region Unknown Header
// Try to parse the unknown header
var unknownHeader = data.ReadType<UnknownHeader>();
if (unknownHeader?.Dummy0 != 0x00000001)
return null;
if (unknownHeader?.Dummy1 != 0x00000000)
return null;
file.DirectoryInfo1Entries[i] = directoryInfo1Entry;
}
// Set the game cache unknown header
file.UnknownHeader = unknownHeader;
#endregion
#endregion
#region Directory Info 2 Entries
#region Unknown Entries
// Create the directory info 2 entry array
file.DirectoryInfo2Entries = new DirectoryInfo2Entry[directoryHeader.ItemCount];
// Create the unknown entry array
file.UnknownEntries = new UnknownEntry[directoryHeader.ItemCount];
// Try to parse the directory info 2 entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryInfo2Entry = data.ReadType<DirectoryInfo2Entry>();
if (directoryInfo2Entry == null)
// Try to parse the unknown entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var unknownEntry = data.ReadType<UnknownEntry>();
if (unknownEntry == null)
return null;
file.UnknownEntries[i] = unknownEntry;
}
#endregion
#region Checksum Header
// Try to parse the checksum header
var checksumHeader = data.ReadType<ChecksumHeader>();
if (checksumHeader?.Dummy0 != 0x00000001)
return null;
file.DirectoryInfo2Entries[i] = directoryInfo2Entry;
}
// Set the game cache checksum header
file.ChecksumHeader = checksumHeader;
#endregion
#endregion
#region Directory Copy Entries
// Cache the current offset
initialOffset = data.Position;
// Create the directory copy entry array
file.DirectoryCopyEntries = new DirectoryCopyEntry[directoryHeader.CopyCount];
#region Checksum Map Header
// Try to parse the directory copy entries
for (int i = 0; i < directoryHeader.CopyCount; i++)
{
var directoryCopyEntry = data.ReadType<DirectoryCopyEntry>();
if (directoryCopyEntry == null)
// Try to parse the checksum map header
var checksumMapHeader = data.ReadType<ChecksumMapHeader>();
if (checksumMapHeader?.Dummy0 != 0x14893721)
return null;
if (checksumMapHeader?.Dummy1 != 0x00000001)
return null;
file.DirectoryCopyEntries[i] = directoryCopyEntry;
// Set the game cache checksum map header
file.ChecksumMapHeader = checksumMapHeader;
#endregion
#region Checksum Map Entries
// Create the checksum map entry array
file.ChecksumMapEntries = new ChecksumMapEntry[checksumMapHeader.ItemCount];
// Try to parse the checksum map entries
for (int i = 0; i < checksumMapHeader.ItemCount; i++)
{
var checksumMapEntry = data.ReadType<ChecksumMapEntry>();
if (checksumMapEntry == null)
return null;
file.ChecksumMapEntries[i] = checksumMapEntry;
}
#endregion
#region Checksum Entries
// Create the checksum entry array
file.ChecksumEntries = new ChecksumEntry[checksumMapHeader.ChecksumCount];
// Try to parse the checksum entries
for (int i = 0; i < checksumMapHeader.ChecksumCount; i++)
{
var checksumEntry = data.ReadType<ChecksumEntry>();
if (checksumEntry == null)
return null;
file.ChecksumEntries[i] = checksumEntry;
}
#endregion
// Seek to end of checksum section, just in case
data.Seek(initialOffset + checksumHeader.ChecksumSize, SeekOrigin.Begin);
return file;
}
#endregion
#region Directory Local Entries
// Create the directory local entry array
file.DirectoryLocalEntries = new DirectoryLocalEntry[directoryHeader.LocalCount];
// Try to parse the directory local entries
for (int i = 0; i < directoryHeader.LocalCount; i++)
catch
{
var directoryLocalEntry = data.ReadType<DirectoryLocalEntry>();
if (directoryLocalEntry == null)
return null;
file.DirectoryLocalEntries[i] = directoryLocalEntry;
}
#endregion
// Seek to end of directory section, just in case
data.Seek(initialOffset + directoryHeader.DirectorySize, SeekOrigin.Begin);
#region Unknown Header
// Try to parse the unknown header
var unknownHeader = data.ReadType<UnknownHeader>();
if (unknownHeader?.Dummy0 != 0x00000001)
// Ignore the actual error
return null;
if (unknownHeader?.Dummy1 != 0x00000000)
return null;
// Set the game cache unknown header
file.UnknownHeader = unknownHeader;
#endregion
#region Unknown Entries
// Create the unknown entry array
file.UnknownEntries = new UnknownEntry[directoryHeader.ItemCount];
// Try to parse the unknown entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var unknownEntry = data.ReadType<UnknownEntry>();
if (unknownEntry == null)
return null;
file.UnknownEntries[i] = unknownEntry;
}
#endregion
#region Checksum Header
// Try to parse the checksum header
var checksumHeader = data.ReadType<ChecksumHeader>();
if (checksumHeader?.Dummy0 != 0x00000001)
return null;
// Set the game cache checksum header
file.ChecksumHeader = checksumHeader;
#endregion
// Cache the current offset
initialOffset = data.Position;
#region Checksum Map Header
// Try to parse the checksum map header
var checksumMapHeader = data.ReadType<ChecksumMapHeader>();
if (checksumMapHeader?.Dummy0 != 0x14893721)
return null;
if (checksumMapHeader?.Dummy1 != 0x00000001)
return null;
// Set the game cache checksum map header
file.ChecksumMapHeader = checksumMapHeader;
#endregion
#region Checksum Map Entries
// Create the checksum map entry array
file.ChecksumMapEntries = new ChecksumMapEntry[checksumMapHeader.ItemCount];
// Try to parse the checksum map entries
for (int i = 0; i < checksumMapHeader.ItemCount; i++)
{
var checksumMapEntry = data.ReadType<ChecksumMapEntry>();
if (checksumMapEntry == null)
return null;
file.ChecksumMapEntries[i] = checksumMapEntry;
}
#endregion
#region Checksum Entries
// Create the checksum entry array
file.ChecksumEntries = new ChecksumEntry[checksumMapHeader.ChecksumCount];
// Try to parse the checksum entries
for (int i = 0; i < checksumMapHeader.ChecksumCount; i++)
{
var checksumEntry = data.ReadType<ChecksumEntry>();
if (checksumEntry == null)
return null;
file.ChecksumEntries[i] = checksumEntry;
}
#endregion
// Seek to end of checksum section, just in case
data.Seek(initialOffset + checksumHeader.ChecksumSize, SeekOrigin.Begin);
return file;
}
}
}

View File

@@ -15,194 +15,198 @@ 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;
try
{
// Cache the current offset
int initialOffset = (int)data.Position;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new executable to fill
var executable = new Executable();
// Create a new executable to fill
var executable = new Executable();
#region MS-DOS Stub
#region MS-DOS Stub
// Parse the MS-DOS stub
var stub = new MSDOS().Deserialize(data);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Parse the MS-DOS stub
var stub = new MSDOS().Deserialize(data);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
// Set the MS-DOS stub
executable.Stub = stub;
#endregion
#endregion
#region Executable Header
#region Executable Header
// Try to parse the executable header
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
var header = data.ReadType<ExecutableHeader>();
if (header?.Magic != SignatureString)
return null;
// Try to parse the executable header
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
var header = data.ReadType<ExecutableHeader>();
if (header?.Magic != SignatureString)
return null;
// Set the executable header
executable.Header = header;
// Set the executable header
executable.Header = header;
#endregion
#endregion
#region Segment Table
#region Segment Table
// If the offset for the segment table doesn't exist
int tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.SegmentTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the segment table
data.Seek(tableAddress, SeekOrigin.Begin);
var segmentTable = ParseSegmentTable(data, header.FileSegmentCount);
if (segmentTable == null)
return null;
// Set the segment table
executable.SegmentTable = segmentTable;
#endregion
#region Resource Table
// If the offset for the segment table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.ResourceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the resource table
data.Seek(tableAddress, SeekOrigin.Begin);
var resourceTable = ParseResourceTable(data, header.ResourceEntriesCount);
if (resourceTable == null)
return null;
// Set the resource table
executable.ResourceTable = resourceTable;
#endregion
#region Resident-Name Table
// If the offset for the resident-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.ResidentNameTableOffset;
int endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.ModuleReferenceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the resident-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var residentNameTable = ParseResidentNameTable(data, endOffset);
if (residentNameTable == null)
return null;
// Set the resident-name table
executable.ResidentNameTable = residentNameTable;
#endregion
#region Module-Reference Table
// If the offset for the module-reference table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.ModuleReferenceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the module-reference table
data.Seek(tableAddress, SeekOrigin.Begin);
var moduleReferenceTable = ParseModuleReferenceTable(data, header.ModuleReferenceTableSize);
if (moduleReferenceTable == null)
return null;
// Set the module-reference table
executable.ModuleReferenceTable = moduleReferenceTable;
#endregion
#region Imported-Name Table
// If the offset for the imported-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.ImportedNamesTableOffset;
endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.EntryTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the imported-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var importedNameTable = ParseImportedNameTable(data, endOffset);
if (importedNameTable == null)
return null;
// Set the imported-name table
executable.ImportedNameTable = importedNameTable;
#endregion
#region Entry Table
// If the offset for the imported-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.EntryTableOffset;
endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.EntryTableOffset
+ header.EntryTableSize;
if (tableAddress >= data.Length)
return executable;
// Try to parse the imported-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var entryTable = ParseEntryTable(data, endOffset);
if (entryTable == null)
return null;
// Set the entry table
executable.EntryTable = entryTable;
#endregion
#region Nonresident-Name Table
// If the offset for the nonresident-name table doesn't exist
tableAddress = initialOffset
+ (int)header.NonResidentNamesTableOffset;
endOffset = initialOffset
+ (int)header.NonResidentNamesTableOffset
+ header.NonResidentNameTableSize;
if (tableAddress >= data.Length)
return executable;
// Try to parse the nonresident-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var nonResidentNameTable = ParseNonResidentNameTable(data, endOffset);
if (nonResidentNameTable == null)
return null;
// Set the nonresident-name table
executable.NonResidentNameTable = nonResidentNameTable;
#endregion
// If the offset for the segment table doesn't exist
int tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.SegmentTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the segment table
data.Seek(tableAddress, SeekOrigin.Begin);
var segmentTable = ParseSegmentTable(data, header.FileSegmentCount);
if (segmentTable == null)
}
catch
{
// Ignore the actual error
return null;
// Set the segment table
executable.SegmentTable = segmentTable;
#endregion
#region Resource Table
// If the offset for the segment table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.ResourceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the resource table
data.Seek(tableAddress, SeekOrigin.Begin);
var resourceTable = ParseResourceTable(data, header.ResourceEntriesCount);
if (resourceTable == null)
return null;
// Set the resource table
executable.ResourceTable = resourceTable;
#endregion
#region Resident-Name Table
// If the offset for the resident-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.ResidentNameTableOffset;
int endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.ModuleReferenceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the resident-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var residentNameTable = ParseResidentNameTable(data, endOffset);
if (residentNameTable == null)
return null;
// Set the resident-name table
executable.ResidentNameTable = residentNameTable;
#endregion
#region Module-Reference Table
// If the offset for the module-reference table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.ModuleReferenceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the module-reference table
data.Seek(tableAddress, SeekOrigin.Begin);
var moduleReferenceTable = ParseModuleReferenceTable(data, header.ModuleReferenceTableSize);
if (moduleReferenceTable == null)
return null;
// Set the module-reference table
executable.ModuleReferenceTable = moduleReferenceTable;
#endregion
#region Imported-Name Table
// If the offset for the imported-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.ImportedNamesTableOffset;
endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.EntryTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the imported-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var importedNameTable = ParseImportedNameTable(data, endOffset);
if (importedNameTable == null)
return null;
// Set the imported-name table
executable.ImportedNameTable = importedNameTable;
#endregion
#region Entry Table
// If the offset for the imported-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.EntryTableOffset;
endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ header.EntryTableOffset
+ header.EntryTableSize;
if (tableAddress >= data.Length)
return executable;
// Try to parse the imported-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var entryTable = ParseEntryTable(data, endOffset);
if (entryTable == null)
return null;
// Set the entry table
executable.EntryTable = entryTable;
#endregion
#region Nonresident-Name Table
// If the offset for the nonresident-name table doesn't exist
tableAddress = initialOffset
+ (int)header.NonResidentNamesTableOffset;
endOffset = initialOffset
+ (int)header.NonResidentNamesTableOffset
+ header.NonResidentNameTableSize;
if (tableAddress >= data.Length)
return executable;
// Try to parse the nonresident-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var nonResidentNameTable = ParseNonResidentNameTable(data, endOffset);
if (nonResidentNameTable == null)
return null;
// Set the nonresident-name table
executable.NonResidentNameTable = nonResidentNameTable;
#endregion
return executable;
}
}
/// <summary>

View File

@@ -15,106 +15,110 @@ 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 cart image to fill
var cart = new Cart();
#region Header
// Try to parse the header
var header = data.ReadType<CommonHeader>();
if (header == null)
return null;
// Set the cart image header
cart.CommonHeader = header;
#endregion
#region Extended DSi Header
// If we have a DSi-compatible cartridge
if (header.UnitCode == Unitcode.NDSPlusDSi || header.UnitCode == Unitcode.DSi)
try
{
var extendedDSiHeader = data.ReadType<ExtendedDSiHeader>();
if (extendedDSiHeader == null)
// Create a new cart image to fill
var cart = new Cart();
#region Header
// Try to parse the header
var header = data.ReadType<CommonHeader>();
if (header == null)
return null;
cart.ExtendedDSiHeader = extendedDSiHeader;
}
// Set the cart image header
cart.CommonHeader = header;
#endregion
#endregion
#region Secure Area
#region Extended DSi Header
// Try to get the secure area offset
long secureAreaOffset = 0x4000;
if (secureAreaOffset > data.Length)
return null;
// If we have a DSi-compatible cartridge
if (header.UnitCode == Unitcode.NDSPlusDSi || header.UnitCode == Unitcode.DSi)
{
var extendedDSiHeader = data.ReadType<ExtendedDSiHeader>();
if (extendedDSiHeader == null)
return null;
// Seek to the secure area
data.Seek(secureAreaOffset, SeekOrigin.Begin);
cart.ExtendedDSiHeader = extendedDSiHeader;
}
// Read the secure area without processing
cart.SecureArea = data.ReadBytes(0x800);
#endregion
#endregion
#region Secure Area
#region Name Table
// Try to get the name table offset
long nameTableOffset = header.FileNameTableOffset;
if (nameTableOffset < 0 || nameTableOffset > data.Length)
return null;
// Seek to the name table
data.Seek(nameTableOffset, SeekOrigin.Begin);
// Try to parse the name table
var nameTable = ParseNameTable(data);
if (nameTable == null)
return null;
// Set the name table
cart.NameTable = nameTable;
#endregion
#region File Allocation Table
// Try to get the file allocation table offset
long fileAllocationTableOffset = header.FileAllocationTableOffset;
if (fileAllocationTableOffset < 0 || fileAllocationTableOffset > data.Length)
return null;
// Seek to the file allocation table
data.Seek(fileAllocationTableOffset, SeekOrigin.Begin);
// Create the file allocation table
var fileAllocationTable = new List<FileAllocationTableEntry>();
// Try to parse the file allocation table
while (data.Position - fileAllocationTableOffset < header.FileAllocationTableLength)
{
var entry = data.ReadType<FileAllocationTableEntry>();
if (entry == null)
// Try to get the secure area offset
long secureAreaOffset = 0x4000;
if (secureAreaOffset > data.Length)
return null;
fileAllocationTable.Add(entry);
// Seek to the secure area
data.Seek(secureAreaOffset, SeekOrigin.Begin);
// Read the secure area without processing
cart.SecureArea = data.ReadBytes(0x800);
#endregion
#region Name Table
// Try to get the name table offset
long nameTableOffset = header.FileNameTableOffset;
if (nameTableOffset < 0 || nameTableOffset > data.Length)
return null;
// Seek to the name table
data.Seek(nameTableOffset, SeekOrigin.Begin);
// Try to parse the name table
var nameTable = ParseNameTable(data);
if (nameTable == null)
return null;
// Set the name table
cart.NameTable = nameTable;
#endregion
#region File Allocation Table
// Try to get the file allocation table offset
long fileAllocationTableOffset = header.FileAllocationTableOffset;
if (fileAllocationTableOffset < 0 || fileAllocationTableOffset > data.Length)
return null;
// Seek to the file allocation table
data.Seek(fileAllocationTableOffset, SeekOrigin.Begin);
// Create the file allocation table
var fileAllocationTable = new List<FileAllocationTableEntry>();
// Try to parse the file allocation table
while (data.Position - fileAllocationTableOffset < header.FileAllocationTableLength)
{
var entry = data.ReadType<FileAllocationTableEntry>();
if (entry == null)
return null;
fileAllocationTable.Add(entry);
}
// Set the file allocation table
cart.FileAllocationTable = [.. fileAllocationTable];
#endregion
// TODO: Read and optionally parse out the other areas
// Look for offsets and lengths in the header pieces
return cart;
}
catch
{
// Ignore the actual error
return null;
}
// Set the file allocation table
cart.FileAllocationTable = [.. fileAllocationTable];
#endregion
// TODO: Read and optionally parse out the other areas
// Look for offsets and lengths in the header pieces
return cart;
}
/// <summary>
@@ -134,7 +138,7 @@ namespace SabreTools.Serialization.Deserializers
var entry = data.ReadType<FolderAllocationTableEntry>();
if (entry == null)
return null;
folderAllocationTable.Add(entry);
// If we have the root entry

View File

@@ -14,51 +14,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 Half-Life Package to fill
var file = new Models.PAK.File();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Signature != SignatureString)
return null;
// Set the package header
file.Header = header;
#endregion
#region Directory Items
// Get the directory items offset
uint directoryItemsOffset = header.DirectoryOffset;
if (directoryItemsOffset < 0 || directoryItemsOffset >= data.Length)
return null;
// Seek to the directory items
data.Seek(directoryItemsOffset, SeekOrigin.Begin);
// Create the directory item array
file.DirectoryItems = new DirectoryItem[header.DirectoryLength / 64];
// Try to parse the directory items
for (int i = 0; i < file.DirectoryItems.Length; i++)
try
{
var directoryItem = data.ReadType<DirectoryItem>();
if (directoryItem == null)
// Create a new Half-Life Package to fill
var file = new Models.PAK.File();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Signature != SignatureString)
return null;
file.DirectoryItems[i] = directoryItem;
// Set the package header
file.Header = header;
#endregion
#region Directory Items
// Get the directory items offset
uint directoryItemsOffset = header.DirectoryOffset;
if (directoryItemsOffset < 0 || directoryItemsOffset >= data.Length)
return null;
// Seek to the directory items
data.Seek(directoryItemsOffset, SeekOrigin.Begin);
// Create the directory item array
file.DirectoryItems = new DirectoryItem[header.DirectoryLength / 64];
// Try to parse the directory items
for (int i = 0; i < file.DirectoryItems.Length; i++)
{
var directoryItem = data.ReadType<DirectoryItem>();
if (directoryItem == null)
return null;
file.DirectoryItems[i] = directoryItem;
}
#endregion
return file;
}
catch
{
// Ignore the actual error
return null;
}
#endregion
return file;
}
}
}

View File

@@ -15,71 +15,75 @@ 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 = ParseHeader(data);
if (header == null)
return null;
// Set the archive header
archive.Header = header;
#endregion
#region Segments
// Get the segments
long offset = header.FileListOffset;
if (offset < 0 || offset >= data.Length)
return null;
// Seek to the segments
data.Seek(offset, SeekOrigin.Begin);
// Create the segments array
archive.Segments = new Segment[header.NumberOfFiles];
// Read all segments in turn
for (int i = 0; i < header.NumberOfFiles; i++)
try
{
var file = ParseSegment(data, header.FileSegmentSize);
if (file == null)
continue;
// Create a new archive to fill
var archive = new Archive();
archive.Segments[i] = file;
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the archive header
archive.Header = header;
#endregion
#region Segments
// Get the segments
long offset = header.FileListOffset;
if (offset < 0 || offset >= data.Length)
return null;
// Seek to the segments
data.Seek(offset, SeekOrigin.Begin);
// Create the segments array
archive.Segments = new Segment[header.NumberOfFiles];
// Read all segments in turn
for (int i = 0; i < header.NumberOfFiles; i++)
{
var file = ParseSegment(data, header.FileSegmentSize);
if (file == null)
continue;
archive.Segments[i] = file;
}
#endregion
#region Footer
// Get the footer offset
offset = header.FileListOffset + (header.FileSegmentSize * header.NumberOfFiles);
if (offset < 0 || offset >= data.Length)
return null;
// Seek to the footer
data.Seek(offset, SeekOrigin.Begin);
// Try to parse the footer
var footer = data.ReadType<Footer>();
if (footer == null)
return null;
// Set the archive footer
archive.Footer = footer;
#endregion
return archive;
}
#endregion
#region Footer
// Get the footer offset
offset = header.FileListOffset + (header.FileSegmentSize * header.NumberOfFiles);
if (offset < 0 || offset >= data.Length)
catch
{
// Ignore the actual error
return null;
// Seek to the footer
data.Seek(offset, SeekOrigin.Begin);
// Try to parse the footer
var footer = data.ReadType<Footer>();
if (footer == null)
return null;
// Set the archive footer
archive.Footer = footer;
#endregion
return archive;
}
}
/// <summary>

View File

@@ -18,36 +18,40 @@ 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;
var di = new DiscInformation();
// Read the initial disc information
di.DataStructureLength = data.ReadUInt16BigEndian();
if (di.DataStructureLength > data.Length)
return null;
di.Reserved0 = data.ReadByteValue();
di.Reserved1 = data.ReadByteValue();
// Create a list for the units
var diUnits = new List<DiscInformationUnit>();
// Loop and read all available units
for (int i = 0; i < 32; i++)
try
{
var unit = ParseDiscInformationUnit(data);
if (unit == null)
continue;
var di = new DiscInformation();
diUnits.Add(unit);
// Read the initial disc information
di.DataStructureLength = data.ReadUInt16BigEndian();
if (di.DataStructureLength > data.Length)
return null;
di.Reserved0 = data.ReadByteValue();
di.Reserved1 = data.ReadByteValue();
// Create a list for the units
var diUnits = new List<DiscInformationUnit>();
// Loop and read all available units
for (int i = 0; i < 32; i++)
{
var unit = ParseDiscInformationUnit(data);
if (unit == null)
continue;
diUnits.Add(unit);
}
// Assign the units and return
di.Units = [.. diUnits];
return di;
}
catch
{
// Ignore the actual error
return null;
}
// Assign the units and return
di.Units = [.. diUnits];
return di;
}
/// <summary>

View File

@@ -20,283 +20,287 @@ 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;
var archive = new Archive();
#region End of Central Directory Record
// Find the end of central directory record
long eocdrOffset = SearchForEndOfCentralDirectoryRecord(data);
if (eocdrOffset < 0 || eocdrOffset >= data.Length)
return null;
// Seek to the end of central directory record
data.Seek(eocdrOffset, SeekOrigin.Begin);
// Read the end of central directory record
var eocdr = ParseEndOfCentralDirectoryRecord(data);
if (eocdr == null)
return null;
// Assign the end of central directory record
archive.EndOfCentralDirectoryRecord = eocdr;
#endregion
#region ZIP64 End of Central Directory Locator and Record
// Set a flag for ZIP64 not found by default
bool zip64 = false;
// Process ZIP64 if any fields are set to max value
if (eocdr.DiskNumber == 0xFFFF
|| eocdr.StartDiskNumber == 0xFFFF
|| eocdr.TotalEntriesOnDisk == 0xFFFF
|| eocdr.TotalEntries == 0xFFFF
|| eocdr.CentralDirectorySize == 0xFFFFFFFF
|| eocdr.CentralDirectoryOffset == 0xFFFFFFFF)
try
{
// Set the ZIP64 flag
zip64 = true;
var archive = new Archive();
// Find the ZIP64 end of central directory locator
long eocdlOffset = SearchForZIP64EndOfCentralDirectoryLocator(data);
if (eocdlOffset < 0 || eocdlOffset >= data.Length)
#region End of Central Directory Record
// Find the end of central directory record
long eocdrOffset = SearchForEndOfCentralDirectoryRecord(data);
if (eocdrOffset < 0 || eocdrOffset >= data.Length)
return null;
// Seek to the ZIP64 end of central directory locator
data.Seek(eocdlOffset, SeekOrigin.Begin);
// Seek to the end of central directory record
data.Seek(eocdrOffset, SeekOrigin.Begin);
// Read the ZIP64 end of central directory locator
var eocdl64 = data.ReadType<EndOfCentralDirectoryLocator64>();
if (eocdl64 == null)
// Read the end of central directory record
var eocdr = ParseEndOfCentralDirectoryRecord(data);
if (eocdr == null)
return null;
// Assign the ZIP64 end of central directory record
archive.ZIP64EndOfCentralDirectoryLocator = eocdl64;
// Assign the end of central directory record
archive.EndOfCentralDirectoryRecord = eocdr;
// Try to get the ZIP64 end of central directory record offset
if ((long)eocdl64.CentralDirectoryOffset < 0 || (long)eocdl64.CentralDirectoryOffset >= data.Length)
return null;
#endregion
// Seek to the ZIP64 end of central directory record
data.Seek((long)eocdl64.CentralDirectoryOffset, SeekOrigin.Begin);
#region ZIP64 End of Central Directory Locator and Record
// Read the ZIP64 end of central directory record
var eocdr64 = ParseEndOfCentralDirectoryRecord64(data);
if (eocdr64 == null)
return null;
// Set a flag for ZIP64 not found by default
bool zip64 = false;
// Assign the ZIP64 end of central directory record
archive.ZIP64EndOfCentralDirectoryRecord = eocdr64;
}
#endregion
#region Central Directory Records
// Try to get the central directory record offset
long cdrOffset, cdrSize;
if (zip64 && archive.ZIP64EndOfCentralDirectoryRecord != null)
{
cdrOffset = (long)archive.ZIP64EndOfCentralDirectoryRecord.CentralDirectoryOffset;
cdrSize = (long)archive.ZIP64EndOfCentralDirectoryRecord.CentralDirectorySize;
}
else if (archive.EndOfCentralDirectoryRecord != null)
{
cdrOffset = archive.EndOfCentralDirectoryRecord.CentralDirectoryOffset;
cdrSize = archive.EndOfCentralDirectoryRecord.CentralDirectorySize;
}
else
{
return null;
}
// Try to get the central directory record offset
if (cdrOffset < 0 || cdrOffset >= data.Length)
return null;
// Seek to the first central directory record
data.Seek(cdrOffset, SeekOrigin.Begin);
// Cache the current offset
long currentOffset = data.Position;
// Read the central directory records
var cdrs = new List<CentralDirectoryFileHeader>();
while (data.Position < currentOffset + cdrSize)
{
// Read the central directory record
var cdr = ParseCentralDirectoryFileHeader(data);
if (cdr == null)
return null;
// Add the central directory record
cdrs.Add(cdr);
}
// Assign the central directory records
archive.CentralDirectoryHeaders = [.. cdrs];
#endregion
// TODO: Handle digital signature -- immediately following central directory records
#region Archive Extra Data Record
// Find the archive extra data record
long aedrOffset = SearchForArchiveExtraDataRecord(data, cdrOffset);
if (aedrOffset >= 0 && aedrOffset < data.Length)
{
// Seek to the archive extra data record
data.Seek(aedrOffset, SeekOrigin.Begin);
// Read the archive extra data record
var aedr = ParseArchiveExtraDataRecord(data);
if (aedr == null)
return null;
// Assign the archive extra data record
archive.ArchiveExtraDataRecord = aedr;
}
#endregion
#region Local File
// Setup all of the collections
var localFileHeaders = new List<LocalFileHeader>();
var encryptionHeaders = new List<byte[]?>();
var fileData = new List<byte[]>(); // TODO: Should this data be read here?
var dataDescriptors = new List<DataDescriptor?>();
var zip64DataDescriptors = new List<DataDescriptor64?>();
// Read the local file headers
for (int i = 0; i < archive.CentralDirectoryHeaders.Length; i++)
{
var header = archive.CentralDirectoryHeaders[i];
// Get the local file header offset
long headerOffset = header.RelativeOffsetOfLocalHeader;
if (headerOffset == 0xFFFFFFFF && header.ExtraField != null)
// Process ZIP64 if any fields are set to max value
if (eocdr.DiskNumber == 0xFFFF
|| eocdr.StartDiskNumber == 0xFFFF
|| eocdr.TotalEntriesOnDisk == 0xFFFF
|| eocdr.TotalEntries == 0xFFFF
|| eocdr.CentralDirectorySize == 0xFFFFFFFF
|| eocdr.CentralDirectoryOffset == 0xFFFFFFFF)
{
// TODO: Parse into a proper structure instead of this
byte[] extraData = header.ExtraField;
if (BitConverter.ToUInt16(extraData, 0) == 0x0001)
headerOffset = BitConverter.ToInt64(extraData, 4);
// Set the ZIP64 flag
zip64 = true;
// Find the ZIP64 end of central directory locator
long eocdlOffset = SearchForZIP64EndOfCentralDirectoryLocator(data);
if (eocdlOffset < 0 || eocdlOffset >= data.Length)
return null;
// Seek to the ZIP64 end of central directory locator
data.Seek(eocdlOffset, SeekOrigin.Begin);
// Read the ZIP64 end of central directory locator
var eocdl64 = data.ReadType<EndOfCentralDirectoryLocator64>();
if (eocdl64 == null)
return null;
// Assign the ZIP64 end of central directory record
archive.ZIP64EndOfCentralDirectoryLocator = eocdl64;
// Try to get the ZIP64 end of central directory record offset
if ((long)eocdl64.CentralDirectoryOffset < 0 || (long)eocdl64.CentralDirectoryOffset >= data.Length)
return null;
// Seek to the ZIP64 end of central directory record
data.Seek((long)eocdl64.CentralDirectoryOffset, SeekOrigin.Begin);
// Read the ZIP64 end of central directory record
var eocdr64 = ParseEndOfCentralDirectoryRecord64(data);
if (eocdr64 == null)
return null;
// Assign the ZIP64 end of central directory record
archive.ZIP64EndOfCentralDirectoryRecord = eocdr64;
}
if (headerOffset < 0 || headerOffset >= data.Length)
return null;
#endregion
// Seek to the local file header
data.Seek(headerOffset, SeekOrigin.Begin);
#region Central Directory Records
// Try to parse the local header
var localFileHeader = ParseLocalFileHeader(data);
if (localFileHeader == null)
// Try to get the central directory record offset
long cdrOffset, cdrSize;
if (zip64 && archive.ZIP64EndOfCentralDirectoryRecord != null)
{
// Add a placeholder null item
localFileHeaders.Add(new LocalFileHeader());
encryptionHeaders.Add(null);
fileData.Add([]);
dataDescriptors.Add(null);
zip64DataDescriptors.Add(null);
continue;
cdrOffset = (long)archive.ZIP64EndOfCentralDirectoryRecord.CentralDirectoryOffset;
cdrSize = (long)archive.ZIP64EndOfCentralDirectoryRecord.CentralDirectorySize;
}
else if (archive.EndOfCentralDirectoryRecord != null)
{
cdrOffset = archive.EndOfCentralDirectoryRecord.CentralDirectoryOffset;
cdrSize = archive.EndOfCentralDirectoryRecord.CentralDirectorySize;
}
else
{
return null;
}
// Add the local file header
localFileHeaders.Add(localFileHeader);
// Try to get the central directory record offset
if (cdrOffset < 0 || cdrOffset >= data.Length)
return null;
// Only read the encryption header if necessary
// Seek to the first central directory record
data.Seek(cdrOffset, SeekOrigin.Begin);
// Cache the current offset
long currentOffset = data.Position;
// Read the central directory records
var cdrs = new List<CentralDirectoryFileHeader>();
while (data.Position < currentOffset + cdrSize)
{
// Read the central directory record
var cdr = ParseCentralDirectoryFileHeader(data);
if (cdr == null)
return null;
// Add the central directory record
cdrs.Add(cdr);
}
// Assign the central directory records
archive.CentralDirectoryHeaders = [.. cdrs];
#endregion
// TODO: Handle digital signature -- immediately following central directory records
#region Archive Extra Data Record
// Find the archive extra data record
long aedrOffset = SearchForArchiveExtraDataRecord(data, cdrOffset);
if (aedrOffset >= 0 && aedrOffset < data.Length)
{
// Seek to the archive extra data record
data.Seek(aedrOffset, SeekOrigin.Begin);
// Read the archive extra data record
var aedr = ParseArchiveExtraDataRecord(data);
if (aedr == null)
return null;
// Assign the archive extra data record
archive.ArchiveExtraDataRecord = aedr;
}
#endregion
#region Local File
// Setup all of the collections
var localFileHeaders = new List<LocalFileHeader>();
var encryptionHeaders = new List<byte[]?>();
var fileData = new List<byte[]>(); // TODO: Should this data be read here?
var dataDescriptors = new List<DataDescriptor?>();
var zip64DataDescriptors = new List<DataDescriptor64?>();
// Read the local file headers
for (int i = 0; i < archive.CentralDirectoryHeaders.Length; i++)
{
var header = archive.CentralDirectoryHeaders[i];
// Get the local file header offset
long headerOffset = header.RelativeOffsetOfLocalHeader;
if (headerOffset == 0xFFFFFFFF && header.ExtraField != null)
{
// TODO: Parse into a proper structure instead of this
byte[] extraData = header.ExtraField;
if (BitConverter.ToUInt16(extraData, 0) == 0x0001)
headerOffset = BitConverter.ToInt64(extraData, 4);
}
if (headerOffset < 0 || headerOffset >= data.Length)
return null;
// Seek to the local file header
data.Seek(headerOffset, SeekOrigin.Begin);
// Try to parse the local header
var localFileHeader = ParseLocalFileHeader(data);
if (localFileHeader == null)
{
// Add a placeholder null item
localFileHeaders.Add(new LocalFileHeader());
encryptionHeaders.Add(null);
fileData.Add([]);
dataDescriptors.Add(null);
zip64DataDescriptors.Add(null);
continue;
}
// Add the local file header
localFileHeaders.Add(localFileHeader);
// Only read the encryption header if necessary
#if NET20 || NET35
if ((header.Flags & GeneralPurposeBitFlags.FileEncrypted) != 0)
#else
if (header.Flags.HasFlag(GeneralPurposeBitFlags.FileEncrypted))
if (header.Flags.HasFlag(GeneralPurposeBitFlags.FileEncrypted))
#endif
{
// Try to read the encryption header data -- TODO: Verify amount to read
byte[] encryptionHeader = data.ReadBytes(12);
if (encryptionHeader.Length != 12)
return null;
// Add the encryption header
encryptionHeaders.Add(encryptionHeader);
}
else
{
// Add the null encryption header
encryptionHeaders.Add(null);
}
// Try to read the file data
byte[] fileDatum = data.ReadBytes((int)header.CompressedSize);
if (fileDatum.Length < header.CompressedSize)
return null;
// Add the file data
fileData.Add(fileDatum);
// Only read the data descriptor if necessary
#if NET20 || NET35
if ((header.Flags & GeneralPurposeBitFlags.NoCRC) != 0)
#else
if (header.Flags.HasFlag(GeneralPurposeBitFlags.NoCRC))
#endif
{
// Select the data descriptor that is being used
if (zip64)
{
// Try to parse the data descriptor
var dataDescriptor64 = ParseDataDescriptor64(data);
if (dataDescriptor64 == null)
// Try to read the encryption header data -- TODO: Verify amount to read
byte[] encryptionHeader = data.ReadBytes(12);
if (encryptionHeader.Length != 12)
return null;
// Add the data descriptor
dataDescriptors.Add(null);
zip64DataDescriptors.Add(dataDescriptor64);
// Add the encryption header
encryptionHeaders.Add(encryptionHeader);
}
else
{
// Try to parse the data descriptor
var dataDescriptor = ParseDataDescriptor(data);
if (dataDescriptor == null)
return null;
// Add the null encryption header
encryptionHeaders.Add(null);
}
// Add the data descriptor
dataDescriptors.Add(dataDescriptor);
// Try to read the file data
byte[] fileDatum = data.ReadBytes((int)header.CompressedSize);
if (fileDatum.Length < header.CompressedSize)
return null;
// Add the file data
fileData.Add(fileDatum);
// Only read the data descriptor if necessary
#if NET20 || NET35
if ((header.Flags & GeneralPurposeBitFlags.NoCRC) != 0)
#else
if (header.Flags.HasFlag(GeneralPurposeBitFlags.NoCRC))
#endif
{
// Select the data descriptor that is being used
if (zip64)
{
// Try to parse the data descriptor
var dataDescriptor64 = ParseDataDescriptor64(data);
if (dataDescriptor64 == null)
return null;
// Add the data descriptor
dataDescriptors.Add(null);
zip64DataDescriptors.Add(dataDescriptor64);
}
else
{
// Try to parse the data descriptor
var dataDescriptor = ParseDataDescriptor(data);
if (dataDescriptor == null)
return null;
// Add the data descriptor
dataDescriptors.Add(dataDescriptor);
zip64DataDescriptors.Add(null);
}
}
else
{
// Add the null data descriptor
dataDescriptors.Add(null);
zip64DataDescriptors.Add(null);
}
}
else
{
// Add the null data descriptor
dataDescriptors.Add(null);
zip64DataDescriptors.Add(null);
}
// Assign the local file headers
archive.LocalFileHeaders = [.. localFileHeaders];
// Assign the encryption headers
archive.EncryptionHeaders = [.. encryptionHeaders];
// Assign the file data
archive.FileData = [.. fileData];
// Assign the data descriptors
archive.DataDescriptors = [.. dataDescriptors];
archive.ZIP64DataDescriptors = [.. zip64DataDescriptors];
#endregion
// TODO: Handle archive decryption header
return archive;
}
catch
{
// Ignore the actual error
return null;
}
// Assign the local file headers
archive.LocalFileHeaders = [.. localFileHeaders];
// Assign the encryption headers
archive.EncryptionHeaders = [.. encryptionHeaders];
// Assign the file data
archive.FileData = [.. fileData];
// Assign the data descriptors
archive.DataDescriptors = [.. dataDescriptors];
archive.ZIP64DataDescriptors = [.. zip64DataDescriptors];
#endregion
// TODO: Handle archive decryption header
return archive;
}
/// <summary>

View File

@@ -14,7 +14,7 @@ namespace SabreTools.Serialization.Deserializers
var deserializer = new PlayJAudio();
return deserializer.Deserialize(data, adjust);
}
/// <inheritdoc/>
public override AudioFile? Deserialize(Stream? data)
=> Deserialize(data, 0);
@@ -27,147 +27,147 @@ 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;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new audio file to fill
var audioFile = new AudioFile();
#region Audio Header
// Try to parse the audio header
var audioHeader = ParseAudioHeader(data);
if (audioHeader == null)
return null;
// Set the audio header
audioFile.Header = audioHeader;
#endregion
#region Unknown Block 1
uint unknownOffset1 = (audioHeader.Version == 0x00000000)
? (audioHeader as AudioHeaderV1)?.UnknownOffset1 ?? 0
: ((audioHeader as AudioHeaderV2)?.UnknownOffset1 ?? 0) + 0x54;
// If we have an unknown block 1 offset
if (unknownOffset1 > 0)
try
{
// Get the unknown block 1 offset
long offset = unknownOffset1 + adjust;
if (offset < 0 || offset >= data.Length)
// Create a new audio file to fill
var audioFile = new AudioFile();
#region Audio Header
// Try to parse the audio header
var audioHeader = ParseAudioHeader(data);
if (audioHeader == null)
return null;
// Seek to the unknown block 1
data.Seek(offset, SeekOrigin.Begin);
}
// Set the audio header
audioFile.Header = audioHeader;
// Try to parse the unknown block 1
var unknownBlock1 = ParseUnknownBlock1(data);
if (unknownBlock1 == null)
return null;
#endregion
// Set the unknown block 1
audioFile.UnknownBlock1 = unknownBlock1;
#region Unknown Block 1
#endregion
uint unknownOffset1 = (audioHeader.Version == 0x00000000)
? (audioHeader as AudioHeaderV1)?.UnknownOffset1 ?? 0
: ((audioHeader as AudioHeaderV2)?.UnknownOffset1 ?? 0) + 0x54;
#region V1 Only
// If we have a V1 file
if (audioHeader.Version == 0x00000000)
{
#region Unknown Value 2
// Get the V1 unknown offset 2
uint? unknownOffset2 = (audioHeader as AudioHeaderV1)?.UnknownOffset2;
// If we have an unknown value 2 offset
if (unknownOffset2 != null && unknownOffset2 > 0)
// If we have an unknown block 1 offset
if (unknownOffset1 > 0)
{
// Get the unknown value 2 offset
long offset = unknownOffset2.Value + adjust;
// Get the unknown block 1 offset
long offset = unknownOffset1 + adjust;
if (offset < 0 || offset >= data.Length)
return null;
// Seek to the unknown value 2
// Seek to the unknown block 1
data.Seek(offset, SeekOrigin.Begin);
}
// Set the unknown value 2
audioFile.UnknownValue2 = data.ReadUInt32();
#endregion
#region Unknown Block 3
// Get the V1 unknown offset 3
uint? unknownOffset3 = (audioHeader as AudioHeaderV1)?.UnknownOffset3;
// If we have an unknown block 3 offset
if (unknownOffset3 != null && unknownOffset3 > 0)
{
// Get the unknown block 3 offset
long offset = unknownOffset3.Value + adjust;
if (offset < 0 || offset >= data.Length)
return null;
// Seek to the unknown block 3
data.Seek(offset, SeekOrigin.Begin);
}
// Try to parse the unknown block 3
var unknownBlock3 = ParseUnknownBlock3(data);
if (unknownBlock3 == null)
// Try to parse the unknown block 1
var unknownBlock1 = ParseUnknownBlock1(data);
if (unknownBlock1 == null)
return null;
// Set the unknown block 3
audioFile.UnknownBlock3 = unknownBlock3;
#endregion
}
#endregion
#region V2 Only
// If we have a V2 file
if (audioHeader.Version == 0x0000000A)
{
#region Data Files Count
// Set the data files count
audioFile.DataFilesCount = data.ReadUInt32();
// Set the unknown block 1
audioFile.UnknownBlock1 = unknownBlock1;
#endregion
#region Data Files
#region V1 Only
// Create the data files array
audioFile.DataFiles = new DataFile[audioFile.DataFilesCount];
// Try to parse the data files
for (int i = 0; i < audioFile.DataFiles.Length; i++)
// If we have a V1 file
if (audioHeader.Version == 0x00000000)
{
var dataFile = ParseDataFile(data);
if (dataFile == null)
#region Unknown Value 2
// Get the V1 unknown offset 2
uint? unknownOffset2 = (audioHeader as AudioHeaderV1)?.UnknownOffset2;
// If we have an unknown value 2 offset
if (unknownOffset2 != null && unknownOffset2 > 0)
{
// Get the unknown value 2 offset
long offset = unknownOffset2.Value + adjust;
if (offset < 0 || offset >= data.Length)
return null;
// Seek to the unknown value 2
data.Seek(offset, SeekOrigin.Begin);
}
// Set the unknown value 2
audioFile.UnknownValue2 = data.ReadUInt32();
#endregion
#region Unknown Block 3
// Get the V1 unknown offset 3
uint? unknownOffset3 = (audioHeader as AudioHeaderV1)?.UnknownOffset3;
// If we have an unknown block 3 offset
if (unknownOffset3 != null && unknownOffset3 > 0)
{
// Get the unknown block 3 offset
long offset = unknownOffset3.Value + adjust;
if (offset < 0 || offset >= data.Length)
return null;
// Seek to the unknown block 3
data.Seek(offset, SeekOrigin.Begin);
}
// Try to parse the unknown block 3
var unknownBlock3 = ParseUnknownBlock3(data);
if (unknownBlock3 == null)
return null;
audioFile.DataFiles[i] = dataFile;
// Set the unknown block 3
audioFile.UnknownBlock3 = unknownBlock3;
#endregion
}
#endregion
#region V2 Only
// If we have a V2 file
if (audioHeader.Version == 0x0000000A)
{
#region Data Files Count
// Set the data files count
audioFile.DataFilesCount = data.ReadUInt32();
#endregion
#region Data Files
// Create the data files array
audioFile.DataFiles = new DataFile[audioFile.DataFilesCount];
// Try to parse the data files
for (int i = 0; i < audioFile.DataFiles.Length; i++)
{
var dataFile = ParseDataFile(data);
if (dataFile == null)
return null;
audioFile.DataFiles[i] = dataFile;
}
#endregion
}
#endregion
return audioFile;
}
catch
{
// Ignore the actual error
return null;
}
#endregion
return audioFile;
}
/// <summary>

View File

@@ -13,44 +13,48 @@ 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 playlist to fill
var playlist = new Playlist();
#region Playlist Header
// Try to parse the playlist header
var playlistHeader = ParsePlaylistHeader(data);
if (playlistHeader == null)
return null;
// Set the playlist header
playlist.Header = playlistHeader;
#endregion
#region Audio Files
// Create the audio files array
playlist.AudioFiles = new AudioFile[playlistHeader.TrackCount];
// Try to parse the audio files
for (int i = 0; i < playlist.AudioFiles.Length; i++)
try
{
long currentOffset = data.Position;
var entryHeader = PlayJAudio.DeserializeStream(data, currentOffset);
if (entryHeader == null)
continue;
// Create a new playlist to fill
var playlist = new Playlist();
playlist.AudioFiles[i] = entryHeader;
#region Playlist Header
// Try to parse the playlist header
var playlistHeader = ParsePlaylistHeader(data);
if (playlistHeader == null)
return null;
// Set the playlist header
playlist.Header = playlistHeader;
#endregion
#region Audio Files
// Create the audio files array
playlist.AudioFiles = new AudioFile[playlistHeader.TrackCount];
// Try to parse the audio files
for (int i = 0; i < playlist.AudioFiles.Length; i++)
{
long currentOffset = data.Position;
var entryHeader = PlayJAudio.DeserializeStream(data, currentOffset);
if (entryHeader == null)
continue;
playlist.AudioFiles[i] = entryHeader;
}
#endregion
return playlist;
}
catch
{
// Ignore the actual error
return null;
}
#endregion
return playlist;
}
/// <summary>

View File

@@ -20,269 +20,273 @@ 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
{
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new executable to fill
var executable = new Executable();
#region MS-DOS Stub
// Parse the MS-DOS stub
var stub = new MSDOS().Deserialize(data);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
#endregion
#region Signature
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
byte[] signature = data.ReadBytes(4);
executable.Signature = Encoding.ASCII.GetString(signature);
if (executable.Signature != SignatureString)
return null;
#endregion
#region COFF File Header
// Try to parse the COFF file header
var coffFileHeader = data.ReadType<COFFFileHeader>();
if (coffFileHeader == null)
return null;
// Set the COFF file header
executable.COFFFileHeader = coffFileHeader;
#endregion
#region Optional Header
// Try to parse the optional header
var optionalHeader = ParseOptionalHeader(data, coffFileHeader.SizeOfOptionalHeader);
if (optionalHeader == null)
return null;
// Set the optional header
executable.OptionalHeader = optionalHeader;
#endregion
#region Section Table
// Try to parse the section table
var sectionTable = ParseSectionTable(data, coffFileHeader.NumberOfSections);
if (sectionTable == null)
return null;
// Set the section table
executable.SectionTable = sectionTable;
#endregion
#region COFF Symbol Table and COFF String Table
// TODO: Validate that this is correct with an "old" PE
if (coffFileHeader.PointerToSymbolTable.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the COFF symbol table doesn't exist
int symbolTableAddress = initialOffset
+ (int)coffFileHeader.PointerToSymbolTable.ConvertVirtualAddress(executable.SectionTable);
if (symbolTableAddress >= data.Length)
return executable;
// Try to parse the COFF symbol table
data.Seek(symbolTableAddress, SeekOrigin.Begin);
var coffSymbolTable = ParseCOFFSymbolTable(data, coffFileHeader.NumberOfSymbols);
if (coffSymbolTable == null)
return null;
// Set the COFF symbol table
executable.COFFSymbolTable = coffSymbolTable;
// Try to parse the COFF string table
var coffStringTable = ParseCOFFStringTable(data);
if (coffStringTable == null)
return null;
// Set the COFF string table
executable.COFFStringTable = coffStringTable;
}
#endregion
#region Attribute Certificate Table
if (optionalHeader.CertificateTable != null && optionalHeader.CertificateTable.VirtualAddress != 0)
{
// If the offset for the attribute certificate table doesn't exist
int certificateTableAddress = initialOffset
+ (int)optionalHeader.CertificateTable.VirtualAddress;
if (certificateTableAddress >= data.Length)
return executable;
// Try to parse the attribute certificate table
data.Seek(certificateTableAddress, SeekOrigin.Begin);
int endOffset = (int)(certificateTableAddress + optionalHeader.CertificateTable.Size);
var attributeCertificateTable = ParseAttributeCertificateTable(data, endOffset);
if (attributeCertificateTable == null)
return null;
// Set the attribute certificate table
executable.AttributeCertificateTable = attributeCertificateTable;
}
#endregion
#region Delay-Load Directory Table
if (optionalHeader.DelayImportDescriptor != null && optionalHeader.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the delay-load directory table doesn't exist
int delayLoadDirectoryTableAddress = initialOffset
+ (int)optionalHeader.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (delayLoadDirectoryTableAddress >= data.Length)
return executable;
// Try to parse the delay-load directory table
data.Seek(delayLoadDirectoryTableAddress, SeekOrigin.Begin);
var delayLoadDirectoryTable = data.ReadType<DelayLoadDirectoryTable>();
if (delayLoadDirectoryTable == null)
return null;
// Set the delay-load directory table
executable.DelayLoadDirectoryTable = delayLoadDirectoryTable;
}
#endregion
#region Base Relocation Table
// Should also be in a '.reloc' section
if (optionalHeader.BaseRelocationTable != null && optionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the base relocation table doesn't exist
int baseRelocationTableAddress = initialOffset
+ (int)optionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (baseRelocationTableAddress >= data.Length)
return executable;
// Try to parse the base relocation table
data.Seek(baseRelocationTableAddress, SeekOrigin.Begin);
int endOffset = (int)(baseRelocationTableAddress + optionalHeader.BaseRelocationTable.Size);
var baseRelocationTable = ParseBaseRelocationTable(data, endOffset, executable.SectionTable);
if (baseRelocationTable == null)
return null;
// Set the base relocation table
executable.BaseRelocationTable = baseRelocationTable;
}
#endregion
#region Debug Table
// Should also be in a '.debug' section
if (optionalHeader.Debug != null && optionalHeader.Debug.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the debug table doesn't exist
int debugTableAddress = initialOffset
+ (int)optionalHeader.Debug.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (debugTableAddress >= data.Length)
return executable;
// Try to parse the debug table
data.Seek(debugTableAddress, SeekOrigin.Begin);
int endOffset = (int)(debugTableAddress + optionalHeader.Debug.Size);
var debugTable = ParseDebugTable(data, endOffset);
if (debugTable == null)
return null;
// Set the debug table
executable.DebugTable = debugTable;
}
#endregion
#region Export Table
// Should also be in a '.edata' section
if (optionalHeader.ExportTable != null && optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the export table doesn't exist
int exportTableAddress = initialOffset
+ (int)optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (exportTableAddress >= data.Length)
return executable;
// Try to parse the export table
data.Seek(exportTableAddress, SeekOrigin.Begin);
var exportTable = ParseExportTable(data, executable.SectionTable);
if (exportTable == null)
return null;
// Set the export table
executable.ExportTable = exportTable;
}
#endregion
#region Import Table
// Should also be in a '.idata' section
if (optionalHeader.ImportTable != null && optionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the import table doesn't exist
int importTableAddress = initialOffset
+ (int)optionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (importTableAddress >= data.Length)
return executable;
// Try to parse the import table
data.Seek(importTableAddress, SeekOrigin.Begin);
var importTable = ParseImportTable(data, optionalHeader.Magic, executable.SectionTable);
if (importTable == null)
return null;
// Set the import table
executable.ImportTable = importTable;
}
#endregion
#region Resource Directory Table
// Should also be in a '.rsrc' section
if (optionalHeader.ResourceTable != null && optionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the resource directory table doesn't exist
int resourceTableAddress = initialOffset
+ (int)optionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (resourceTableAddress >= data.Length)
return executable;
// Try to parse the resource directory table
data.Seek(resourceTableAddress, SeekOrigin.Begin);
var resourceDirectoryTable = ParseResourceDirectoryTable(data, data.Position, executable.SectionTable, true);
if (resourceDirectoryTable == null)
return null;
// Set the resource directory table
executable.ResourceDirectoryTable = resourceDirectoryTable;
}
#endregion
// TODO: Finish implementing PE parsing
return executable;
}
catch
{
// Ignore the actual error
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new executable to fill
var executable = new Executable();
#region MS-DOS Stub
// Parse the MS-DOS stub
var stub = new MSDOS().Deserialize(data);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
#endregion
#region Signature
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
byte[] signature = data.ReadBytes(4);
executable.Signature = Encoding.ASCII.GetString(signature);
if (executable.Signature != SignatureString)
return null;
#endregion
#region COFF File Header
// Try to parse the COFF file header
var coffFileHeader = data.ReadType<COFFFileHeader>();
if (coffFileHeader == null)
return null;
// Set the COFF file header
executable.COFFFileHeader = coffFileHeader;
#endregion
#region Optional Header
// Try to parse the optional header
var optionalHeader = ParseOptionalHeader(data, coffFileHeader.SizeOfOptionalHeader);
if (optionalHeader == null)
return null;
// Set the optional header
executable.OptionalHeader = optionalHeader;
#endregion
#region Section Table
// Try to parse the section table
var sectionTable = ParseSectionTable(data, coffFileHeader.NumberOfSections);
if (sectionTable == null)
return null;
// Set the section table
executable.SectionTable = sectionTable;
#endregion
#region COFF Symbol Table and COFF String Table
// TODO: Validate that this is correct with an "old" PE
if (coffFileHeader.PointerToSymbolTable.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the COFF symbol table doesn't exist
int symbolTableAddress = initialOffset
+ (int)coffFileHeader.PointerToSymbolTable.ConvertVirtualAddress(executable.SectionTable);
if (symbolTableAddress >= data.Length)
return executable;
// Try to parse the COFF symbol table
data.Seek(symbolTableAddress, SeekOrigin.Begin);
var coffSymbolTable = ParseCOFFSymbolTable(data, coffFileHeader.NumberOfSymbols);
if (coffSymbolTable == null)
return null;
// Set the COFF symbol table
executable.COFFSymbolTable = coffSymbolTable;
// Try to parse the COFF string table
var coffStringTable = ParseCOFFStringTable(data);
if (coffStringTable == null)
return null;
// Set the COFF string table
executable.COFFStringTable = coffStringTable;
}
#endregion
#region Attribute Certificate Table
if (optionalHeader.CertificateTable != null && optionalHeader.CertificateTable.VirtualAddress != 0)
{
// If the offset for the attribute certificate table doesn't exist
int certificateTableAddress = initialOffset
+ (int)optionalHeader.CertificateTable.VirtualAddress;
if (certificateTableAddress >= data.Length)
return executable;
// Try to parse the attribute certificate table
data.Seek(certificateTableAddress, SeekOrigin.Begin);
int endOffset = (int)(certificateTableAddress + optionalHeader.CertificateTable.Size);
var attributeCertificateTable = ParseAttributeCertificateTable(data, endOffset);
if (attributeCertificateTable == null)
return null;
// Set the attribute certificate table
executable.AttributeCertificateTable = attributeCertificateTable;
}
#endregion
#region Delay-Load Directory Table
if (optionalHeader.DelayImportDescriptor != null && optionalHeader.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the delay-load directory table doesn't exist
int delayLoadDirectoryTableAddress = initialOffset
+ (int)optionalHeader.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (delayLoadDirectoryTableAddress >= data.Length)
return executable;
// Try to parse the delay-load directory table
data.Seek(delayLoadDirectoryTableAddress, SeekOrigin.Begin);
var delayLoadDirectoryTable = data.ReadType<DelayLoadDirectoryTable>();
if (delayLoadDirectoryTable == null)
return null;
// Set the delay-load directory table
executable.DelayLoadDirectoryTable = delayLoadDirectoryTable;
}
#endregion
#region Base Relocation Table
// Should also be in a '.reloc' section
if (optionalHeader.BaseRelocationTable != null && optionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the base relocation table doesn't exist
int baseRelocationTableAddress = initialOffset
+ (int)optionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (baseRelocationTableAddress >= data.Length)
return executable;
// Try to parse the base relocation table
data.Seek(baseRelocationTableAddress, SeekOrigin.Begin);
int endOffset = (int)(baseRelocationTableAddress + optionalHeader.BaseRelocationTable.Size);
var baseRelocationTable = ParseBaseRelocationTable(data, endOffset, executable.SectionTable);
if (baseRelocationTable == null)
return null;
// Set the base relocation table
executable.BaseRelocationTable = baseRelocationTable;
}
#endregion
#region Debug Table
// Should also be in a '.debug' section
if (optionalHeader.Debug != null && optionalHeader.Debug.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the debug table doesn't exist
int debugTableAddress = initialOffset
+ (int)optionalHeader.Debug.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (debugTableAddress >= data.Length)
return executable;
// Try to parse the debug table
data.Seek(debugTableAddress, SeekOrigin.Begin);
int endOffset = (int)(debugTableAddress + optionalHeader.Debug.Size);
var debugTable = ParseDebugTable(data, endOffset);
if (debugTable == null)
return null;
// Set the debug table
executable.DebugTable = debugTable;
}
#endregion
#region Export Table
// Should also be in a '.edata' section
if (optionalHeader.ExportTable != null && optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the export table doesn't exist
int exportTableAddress = initialOffset
+ (int)optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (exportTableAddress >= data.Length)
return executable;
// Try to parse the export table
data.Seek(exportTableAddress, SeekOrigin.Begin);
var exportTable = ParseExportTable(data, executable.SectionTable);
if (exportTable == null)
return null;
// Set the export table
executable.ExportTable = exportTable;
}
#endregion
#region Import Table
// Should also be in a '.idata' section
if (optionalHeader.ImportTable != null && optionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the import table doesn't exist
int importTableAddress = initialOffset
+ (int)optionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (importTableAddress >= data.Length)
return executable;
// Try to parse the import table
data.Seek(importTableAddress, SeekOrigin.Begin);
var importTable = ParseImportTable(data, optionalHeader.Magic, executable.SectionTable);
if (importTable == null)
return null;
// Set the import table
executable.ImportTable = importTable;
}
#endregion
#region Resource Directory Table
// Should also be in a '.rsrc' section
if (optionalHeader.ResourceTable != null && optionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
{
// If the offset for the resource directory table doesn't exist
int resourceTableAddress = initialOffset
+ (int)optionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
if (resourceTableAddress >= data.Length)
return executable;
// Try to parse the resource directory table
data.Seek(resourceTableAddress, SeekOrigin.Begin);
var resourceDirectoryTable = ParseResourceDirectoryTable(data, data.Position, executable.SectionTable, true);
if (resourceDirectoryTable == null)
return null;
// Set the resource directory table
executable.ResourceDirectoryTable = resourceDirectoryTable;
}
#endregion
// TODO: Finish implementing PE parsing
return executable;
}
/// <summary>

View File

@@ -15,49 +15,53 @@ 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<Header>();
if (header?.Signature != SignatureString)
return null;
// Set the archive header
archive.Header = header;
#endregion
#region File List
// If we have any files
var fileDescriptors = new FileDescriptor[header.FileCount];
// Read all entries in turn
for (int i = 0; i < header.FileCount; i++)
try
{
var file = ParseFileDescriptor(data, header.MinorVersion);
if (file == null)
// Create a new archive to fill
var archive = new Archive();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Signature != SignatureString)
return null;
fileDescriptors[i] = file;
// Set the archive header
archive.Header = header;
#endregion
#region File List
// If we have any files
var fileDescriptors = new FileDescriptor[header.FileCount];
// Read all entries in turn
for (int i = 0; i < header.FileCount; i++)
{
var file = ParseFileDescriptor(data, header.MinorVersion);
if (file == null)
return null;
fileDescriptors[i] = file;
}
// Set the file list
archive.FileList = fileDescriptors;
#endregion
// Cache the compressed data offset
archive.CompressedDataOffset = data.Position;
return archive;
}
catch
{
// Ignore the actual error
return null;
}
// Set the file list
archive.FileList = fileDescriptors;
#endregion
// Cache the compressed data offset
archive.CompressedDataOffset = data.Position;
return archive;
}
/// <summary>

View File

@@ -11,176 +11,173 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc/>
public override MetadataFile? Deserialize(Stream? data)
{
// 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;
}
catch
{
// Ignore errors in getting position for compressed streams
}
// Setup the reader and output
var reader = new IniReader(data, Encoding.UTF8)
{
ValidateRows = false,
};
var dat = new MetadataFile();
// Loop through and parse out the values
var roms = new List<Rom>();
while (!reader.EndOfStream)
{
// If we have no next line
if (!reader.ReadNextLine())
break;
// Ignore certain row types
switch (reader.RowType)
// Setup the reader and output
var reader = new IniReader(data, Encoding.UTF8)
{
case IniRowType.None:
case IniRowType.Comment:
continue;
case IniRowType.SectionHeader:
switch (reader.Section?.ToLowerInvariant())
ValidateRows = false,
};
var dat = new MetadataFile();
// Loop through and parse out the values
var roms = new List<Rom>();
while (!reader.EndOfStream)
{
// If we have no next line
if (!reader.ReadNextLine())
break;
// Ignore certain row types
switch (reader.RowType)
{
case IniRowType.None:
case IniRowType.Comment:
continue;
case IniRowType.SectionHeader:
switch (reader.Section?.ToLowerInvariant())
{
case "credits":
dat.Credits ??= new Credits();
break;
case "dat":
dat.Dat ??= new Dat();
break;
case "emulator":
dat.Emulator ??= new Emulator();
break;
case "games":
dat.Games ??= new Games();
break;
}
continue;
}
// If we're in credits
if (reader.Section?.ToLowerInvariant() == "credits")
{
// Create the section if we haven't already
dat.Credits ??= new Credits();
switch (reader.KeyValuePair?.Key?.ToLowerInvariant())
{
case "credits":
dat.Credits ??= new Credits();
case "author":
dat.Credits.Author = reader.KeyValuePair?.Value;
break;
case "dat":
dat.Dat ??= new Dat();
case "version":
dat.Credits.Version = reader.KeyValuePair?.Value;
break;
case "emulator":
dat.Emulator ??= new Emulator();
case "email":
dat.Credits.Email = reader.KeyValuePair?.Value;
break;
case "games":
dat.Games ??= new Games();
case "homepage":
dat.Credits.Homepage = reader.KeyValuePair?.Value;
break;
case "url":
dat.Credits.Url = reader.KeyValuePair?.Value;
break;
case "date":
dat.Credits.Date = reader.KeyValuePair?.Value;
break;
case "comment":
dat.Credits.Comment = reader.KeyValuePair?.Value;
break;
}
continue;
}
// If we're in credits
if (reader.Section?.ToLowerInvariant() == "credits")
{
// Create the section if we haven't already
dat.Credits ??= new Credits();
switch (reader.KeyValuePair?.Key?.ToLowerInvariant())
{
case "author":
dat.Credits.Author = reader.KeyValuePair?.Value;
break;
case "version":
dat.Credits.Version = reader.KeyValuePair?.Value;
break;
case "email":
dat.Credits.Email = reader.KeyValuePair?.Value;
break;
case "homepage":
dat.Credits.Homepage = reader.KeyValuePair?.Value;
break;
case "url":
dat.Credits.Url = reader.KeyValuePair?.Value;
break;
case "date":
dat.Credits.Date = reader.KeyValuePair?.Value;
break;
case "comment":
dat.Credits.Comment = reader.KeyValuePair?.Value;
break;
}
}
// If we're in dat
else if (reader.Section?.ToLowerInvariant() == "dat")
{
// Create the section if we haven't already
dat.Dat ??= new Dat();
switch (reader.KeyValuePair?.Key?.ToLowerInvariant())
// If we're in dat
else if (reader.Section?.ToLowerInvariant() == "dat")
{
case "version":
dat.Dat.Version = reader.KeyValuePair?.Value;
break;
case "plugin":
dat.Dat.Plugin = reader.KeyValuePair?.Value;
break;
case "split":
dat.Dat.Split = reader.KeyValuePair?.Value;
break;
case "merge":
dat.Dat.Merge = reader.KeyValuePair?.Value;
break;
// Create the section if we haven't already
dat.Dat ??= new Dat();
switch (reader.KeyValuePair?.Key?.ToLowerInvariant())
{
case "version":
dat.Dat.Version = reader.KeyValuePair?.Value;
break;
case "plugin":
dat.Dat.Plugin = reader.KeyValuePair?.Value;
break;
case "split":
dat.Dat.Split = reader.KeyValuePair?.Value;
break;
case "merge":
dat.Dat.Merge = reader.KeyValuePair?.Value;
break;
}
}
}
// If we're in emulator
else if (reader.Section?.ToLowerInvariant() == "emulator")
{
// Create the section if we haven't already
dat.Emulator ??= new Emulator();
switch (reader.KeyValuePair?.Key?.ToLowerInvariant())
// If we're in emulator
else if (reader.Section?.ToLowerInvariant() == "emulator")
{
case "refname":
dat.Emulator.RefName = reader.KeyValuePair?.Value;
break;
case "version":
dat.Emulator.Version = reader.KeyValuePair?.Value;
break;
// Create the section if we haven't already
dat.Emulator ??= new Emulator();
switch (reader.KeyValuePair?.Key?.ToLowerInvariant())
{
case "refname":
dat.Emulator.RefName = reader.KeyValuePair?.Value;
break;
case "version":
dat.Emulator.Version = reader.KeyValuePair?.Value;
break;
}
}
}
// If we're in games
else if (reader.Section?.ToLowerInvariant() == "games")
{
// Create the section if we haven't already
dat.Games ??= new Games();
// If we're in games
else if (reader.Section?.ToLowerInvariant() == "games")
{
// Create the section if we haven't already
dat.Games ??= new Games();
// If the line doesn't contain the delimiter
// If the line doesn't contain the delimiter
#if NETFRAMEWORK
if (!(reader.CurrentLine?.Contains("¬") ?? false))
#else
if (!(reader.CurrentLine?.Contains('¬') ?? false))
if (!(reader.CurrentLine?.Contains('¬') ?? false))
#endif
continue;
continue;
// Otherwise, separate out the line
string[] splitLine = reader.CurrentLine.Split('¬');
var rom = new Rom
{
// EMPTY = splitLine[0]
ParentName = splitLine[1],
ParentDescription = splitLine[2],
GameName = splitLine[3],
GameDescription = splitLine[4],
RomName = splitLine[5],
RomCRC = splitLine[6],
RomSize = splitLine[7],
RomOf = splitLine[8],
MergeName = splitLine[9],
// EMPTY = splitLine[10]
};
// Otherwise, separate out the line
string[] splitLine = reader.CurrentLine.Split('¬');
var rom = new Rom
{
// EMPTY = splitLine[0]
ParentName = splitLine[1],
ParentDescription = splitLine[2],
GameName = splitLine[3],
GameDescription = splitLine[4],
RomName = splitLine[5],
RomCRC = splitLine[6],
RomSize = splitLine[7],
RomOf = splitLine[8],
MergeName = splitLine[9],
// EMPTY = splitLine[10]
};
roms.Add(rom);
roms.Add(rom);
}
}
}
// Add extra pieces and return
if (dat.Games != null && roms.Count > 0)
// Add extra pieces and return
if (dat.Games != null && roms.Count > 0)
{
dat.Games.Rom = [.. roms];
return dat;
}
return null;
}
catch
{
dat.Games.Rom = [.. roms];
return dat;
// Ignore the actual error
return null;
}
return null;
}
}
}

View File

@@ -13,20 +13,24 @@ 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;
try
{
// Deserialize the SFB
var sfb = data.ReadType<Models.PlayStation3.SFB>();
if (sfb?.Magic == null)
return null;
// Deserialize the SFB
var sfb = data.ReadType<Models.PlayStation3.SFB>();
if (sfb?.Magic == null)
return null;
string magic = Encoding.ASCII.GetString(sfb.Magic);
if (magic != ".SFB")
return null;
string magic = Encoding.ASCII.GetString(sfb.Magic);
if (magic != ".SFB")
return sfb;
}
catch
{
// Ignore the actual error
return null;
return sfb;
}
}
}
}

View File

@@ -13,47 +13,51 @@ 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 SFO to fill
var sfo = new Models.PlayStation3.SFO();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Assign the header
sfo.Header = header;
#endregion
#region Index Table
// TODO: Determine how many entries are in the index table
#endregion
#region Key Table
// TODO: Finish implementation
#endregion
// Padding
// TODO: Finish implementation
#region Data Table
// TODO: Finish implementation
#endregion
return sfo;
}
catch
{
// Ignore the actual error
return null;
// Create a new SFO to fill
var sfo = new Models.PlayStation3.SFO();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Assign the header
sfo.Header = header;
#endregion
#region Index Table
// TODO: Determine how many entries are in the index table
#endregion
#region Key Table
// TODO: Finish implementation
#endregion
// Padding
// TODO: Finish implementation
#region Data Table
// TODO: Finish implementation
#endregion
return sfo;
}
}
/// <summary>
@@ -61,10 +65,9 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SFO header on success, null on error</returns>
public Models.PlayStation3.SFOHeader? ParseHeader(Stream data)
public static Models.PlayStation3.SFOHeader? ParseHeader(Stream data)
{
var sfoHeader = data.ReadType<Models.PlayStation3.SFOHeader>();
if (sfoHeader == null)
return null;
@@ -74,13 +77,13 @@ namespace SabreTools.Serialization.Deserializers
return sfoHeader;
}
/// <summary>
/// Parse a Stream into an SFO index table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SFO index table entry on success, null on error</returns>
public Models.PlayStation3.SFOIndexTableEntry? ParseIndexTableEntry(Stream data)
public static Models.PlayStation3.SFOIndexTableEntry? ParseIndexTableEntry(Stream data)
{
return data.ReadType<Models.PlayStation3.SFOIndexTableEntry>();
}

View File

@@ -16,37 +16,42 @@ 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 SGA to fill
var file = new Models.SGA.File();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the SGA header
file.Header = header;
#endregion
#region Directory
// Try to parse the directory
var directory = ParseDirectory(data, header.MajorVersion);
if (directory == null)
return null;
// Set the SGA directory
file.Directory = directory;
#endregion
return file;
}
catch
{
// Ignore the actual error
return null;
// Create a new SGA to fill
var file = new Models.SGA.File();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the SGA header
file.Header = header;
#endregion
#region Directory
// Try to parse the directory
var directory = ParseDirectory(data, header.MajorVersion);
if (directory == null)
return null;
// Set the SGA directory
file.Directory = directory;
#endregion
return file;
}
}
/// <summary>

View File

@@ -85,100 +85,97 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public MetadataFile? Deserialize(Stream? data, char delim)
{
// 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 SeparatedValueReader(data, Encoding.UTF8)
{
Header = true,
Separator = delim,
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<Row>();
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 < HeaderWithExtendedHashesCount)
{
row = new Row
{
FileName = reader.Line[0],
InternalName = reader.Line[1],
Description = reader.Line[2],
GameName = reader.Line[3],
GameDescription = reader.Line[4],
Type = reader.Line[5],
RomName = reader.Line[6],
DiskName = reader.Line[7],
Size = reader.Line[8],
CRC = reader.Line[9],
MD5 = reader.Line[10],
SHA1 = reader.Line[11],
SHA256 = reader.Line[12],
Status = reader.Line[13],
};
}
else
{
row = new Row
{
FileName = reader.Line[0],
InternalName = reader.Line[1],
Description = reader.Line[2],
GameName = reader.Line[3],
GameDescription = reader.Line[4],
Type = reader.Line[5],
RomName = reader.Line[6],
DiskName = reader.Line[7],
Size = reader.Line[8],
CRC = reader.Line[9],
MD5 = reader.Line[10],
SHA1 = reader.Line[11],
SHA256 = reader.Line[12],
SHA384 = reader.Line[13],
SHA512 = reader.Line[14],
SpamSum = reader.Line[15],
Status = 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)
{
Header = true,
Separator = delim,
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<Row>();
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 < HeaderWithExtendedHashesCount)
{
row = new Row
{
FileName = reader.Line[0],
InternalName = reader.Line[1],
Description = reader.Line[2],
GameName = reader.Line[3],
GameDescription = reader.Line[4],
Type = reader.Line[5],
RomName = reader.Line[6],
DiskName = reader.Line[7],
Size = reader.Line[8],
CRC = reader.Line[9],
MD5 = reader.Line[10],
SHA1 = reader.Line[11],
SHA256 = reader.Line[12],
Status = reader.Line[13],
};
}
else
{
row = new Row
{
FileName = reader.Line[0],
InternalName = reader.Line[1],
Description = reader.Line[2],
GameName = reader.Line[3],
GameDescription = reader.Line[4],
Type = reader.Line[5],
RomName = reader.Line[6],
DiskName = reader.Line[7],
Size = reader.Line[8],
CRC = reader.Line[9],
MD5 = reader.Line[10],
SHA1 = reader.Line[11],
SHA256 = reader.Line[12],
SHA384 = reader.Line[13],
SHA512 = reader.Line[14],
SpamSum = reader.Line[15],
Status = 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

View File

@@ -17,248 +17,252 @@ 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 2 Level to fill
var file = new VbspFile();
#region Header
// Try to parse the header
var header = data.ReadType<VbspHeader>();
if (header?.Signature != SignatureString)
return null;
if (Array.IndexOf([17, 18, 19, 20, 21, 22, 23, 25, 27, 29, 0x00040014], header.Version) > -1)
return null;
if (header.Lumps == null || header.Lumps.Length != VBSP_HEADER_LUMPS)
return null;
// Set the package header
file.Header = header;
#endregion
#region Lumps
for (int l = 0; l < VBSP_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 2 Level to fill
var file = new VbspFile();
// 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<VbspHeader>();
if (header?.Signature != SignatureString)
return null;
if (Array.IndexOf([17, 18, 19, 20, 21, 22, 23, 25, 27, 29, 0x00040014], header.Version) > -1)
return null;
if (header.Lumps == null || header.Lumps.Length != VBSP_HEADER_LUMPS)
return null;
// Set the package header
file.Header = header;
#endregion
#region Lumps
for (int l = 0; l < VBSP_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.TexdataLump = ParseTexdataLump(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.OcclusionLump = ParseOcclusionLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAVES:
file.LeavesLump = ParseLeavesLump(data, lumpEntry.Version, 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;
case LumpType.LUMP_WORLDLIGHTS:
file.LDRWorldLightsLump = ParseWorldLightsLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAFFACES:
file.LeafFacesLump = ParseLeafFacesLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAFBRUSHES:
file.LeafBrushesLump = ParseLeafBrushesLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_BRUSHES:
file.BrushesLump = ParseBrushesLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_BRUSHSIDES:
file.BrushsidesLump = ParseBrushsidesLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_AREAS:
// TODO: Support LUMP_AREAS [20] when in Models
break;
case LumpType.LUMP_AREAPORTALS:
// TODO: Support LUMP_AREAPORTALS [21] when in Models
break;
case LumpType.LUMP_PORTALS:
// TODO: Support LUMP_PORTALS / LUMP_UNUSED0 / LUMP_PROPCOLLISION [22] when in Models
break;
case LumpType.LUMP_CLUSTERS:
// TODO: Support LUMP_CLUSTERS / LUMP_UNUSED1 / LUMP_PROPHULLS [23] when in Models
break;
case LumpType.LUMP_PORTALVERTS:
// TODO: Support LUMP_PORTALVERTS / LUMP_UNUSED2 / LUMP_FAKEENTITIES / LUMP_PROPHULLVERTS [24] when in Models
break;
case LumpType.LUMP_CLUSTERPORTALS:
// TODO: Support LUMP_CLUSTERPORTALS / LUMP_UNUSED3 / LUMP_PROPTRIS [25] when in Models
break;
case LumpType.LUMP_DISPINFO:
file.DispInfosLump = ParseDispInfosLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_ORIGINALFACES:
file.OriginalFacesLump = ParseFacesLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_PHYSDISP:
// TODO: Support LUMP_PHYSDISP [28] when in Models
break;
case LumpType.LUMP_PHYSCOLLIDE:
file.PhysCollideLump = ParsePhysCollideLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_VERTNORMALS:
// TODO: Support LUMP_VERTNORMALS [30] when in Models
break;
case LumpType.LUMP_VERTNORMALINDICES:
// TODO: Support LUMP_VERTNORMALINDICES [31] when in Models
break;
case LumpType.LUMP_DISP_LIGHTMAP_ALPHAS:
// TODO: Support LUMP_DISP_LIGHTMAP_ALPHAS [32] when in Models
break;
case LumpType.LUMP_DISP_VERTS:
file.DispVertsLump = ParseDispVertsLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS:
// TODO: Support LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS [34] when in Models
break;
case LumpType.LUMP_GAME_LUMP:
file.GameLump = ParseGameLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAFWATERDATA:
// TODO: Support LUMP_LEAFWATERDATA [36] when in Models
break;
case LumpType.LUMP_PRIMITIVES:
// TODO: Support LUMP_PRIMITIVES [37] when in Models
break;
case LumpType.LUMP_PRIMVERTS:
// TODO: Support LUMP_PRIMVERTS [38] when in Models
break;
case LumpType.LUMP_PRIMINDICES:
// TODO: Support LUMP_PRIMINDICES [39] when in Models
break;
case LumpType.LUMP_PAKFILE:
file.PakfileLump = ParsePakfileLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_CLIPPORTALVERTS:
// TODO: Support LUMP_CLIPPORTALVERTS [41] when in Models
break;
case LumpType.LUMP_CUBEMAPS:
file.CubemapsLump = ParseCubemapsLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_TEXDATA_STRING_DATA:
file.TexdataStringData = ParseTexdataStringData(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_TEXDATA_STRING_TABLE:
file.TexdataStringTable = ParseTexdataStringTable(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_OVERLAYS:
file.OverlaysLump = ParseOverlaysLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAFMINDISTTOWATER:
// TODO: Support LUMP_LEAFMINDISTTOWATER [46] when in Models
break;
case LumpType.LUMP_FACE_MACRO_TEXTURE_INFO:
// TODO: Support LUMP_FACE_MACRO_TEXTURE_INFO [47] when in Models
break;
case LumpType.LUMP_DISP_TRIS:
file.DispTrisLump = ParseDispTrisLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_PHYSCOLLIDESURFACE:
// TODO: Support LUMP_PHYSCOLLIDESURFACE / LUMP_PROP_BLOB [49] when in Models
break;
case LumpType.LUMP_WATEROVERLAYS:
// TODO: Support LUMP_WATEROVERLAYS [50] when in Models
break;
case LumpType.LUMP_LIGHTMAPPAGES:
file.HDRAmbientIndexLump = ParseAmbientIndexLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LIGHTMAPPAGEINFOS:
file.LDRAmbientIndexLump = ParseAmbientIndexLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LIGHTING_HDR:
// TODO: Support LUMP_LIGHTING_HDR [53] when in Models
break;
case LumpType.LUMP_WORLDLIGHTS_HDR:
file.HDRWorldLightsLump = ParseWorldLightsLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAF_AMBIENT_LIGHTING_HDR:
file.HDRAmbientLightingLump = ParseAmbientLightingLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAF_AMBIENT_LIGHTING:
file.LDRAmbientLightingLump = ParseAmbientLightingLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_XZIPPAKFILE:
// TODO: Support LUMP_XZIPPAKFILE [57] when in Models
break;
case LumpType.LUMP_FACES_HDR:
// TODO: Support LUMP_FACES_HDR [58] when in Models
break;
case LumpType.LUMP_MAP_FLAGS:
// TODO: Support LUMP_MAP_FLAGS [59] when in Models
break;
case LumpType.LUMP_OVERLAY_FADES:
// TODO: Support LUMP_OVERLAY_FADES [60] when in Models
break;
case LumpType.LUMP_OVERLAY_SYSTEM_LEVELS:
// TODO: Support LUMP_OVERLAY_SYSTEM_LEVELS [61] when in Models
break;
case LumpType.LUMP_PHYSLEVEL:
// TODO: Support LUMP_PHYSLEVEL [62] when in Models
break;
case LumpType.LUMP_DISP_MULTIBLEND:
// TODO: Support LUMP_DISP_MULTIBLEND [63] when in Models
break;
// Get the next lump entry
var lumpEntry = header.Lumps[l];
if (lumpEntry == null)
continue;
if (lumpEntry.Offset == 0 || lumpEntry.Length == 0)
continue;
default:
// Unsupported LumpType value, ignore
break;
// 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.TexdataLump = ParseTexdataLump(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.OcclusionLump = ParseOcclusionLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAVES:
file.LeavesLump = ParseLeavesLump(data, lumpEntry.Version, 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;
case LumpType.LUMP_WORLDLIGHTS:
file.LDRWorldLightsLump = ParseWorldLightsLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAFFACES:
file.LeafFacesLump = ParseLeafFacesLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAFBRUSHES:
file.LeafBrushesLump = ParseLeafBrushesLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_BRUSHES:
file.BrushesLump = ParseBrushesLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_BRUSHSIDES:
file.BrushsidesLump = ParseBrushsidesLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_AREAS:
// TODO: Support LUMP_AREAS [20] when in Models
break;
case LumpType.LUMP_AREAPORTALS:
// TODO: Support LUMP_AREAPORTALS [21] when in Models
break;
case LumpType.LUMP_PORTALS:
// TODO: Support LUMP_PORTALS / LUMP_UNUSED0 / LUMP_PROPCOLLISION [22] when in Models
break;
case LumpType.LUMP_CLUSTERS:
// TODO: Support LUMP_CLUSTERS / LUMP_UNUSED1 / LUMP_PROPHULLS [23] when in Models
break;
case LumpType.LUMP_PORTALVERTS:
// TODO: Support LUMP_PORTALVERTS / LUMP_UNUSED2 / LUMP_FAKEENTITIES / LUMP_PROPHULLVERTS [24] when in Models
break;
case LumpType.LUMP_CLUSTERPORTALS:
// TODO: Support LUMP_CLUSTERPORTALS / LUMP_UNUSED3 / LUMP_PROPTRIS [25] when in Models
break;
case LumpType.LUMP_DISPINFO:
file.DispInfosLump = ParseDispInfosLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_ORIGINALFACES:
file.OriginalFacesLump = ParseFacesLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_PHYSDISP:
// TODO: Support LUMP_PHYSDISP [28] when in Models
break;
case LumpType.LUMP_PHYSCOLLIDE:
file.PhysCollideLump = ParsePhysCollideLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_VERTNORMALS:
// TODO: Support LUMP_VERTNORMALS [30] when in Models
break;
case LumpType.LUMP_VERTNORMALINDICES:
// TODO: Support LUMP_VERTNORMALINDICES [31] when in Models
break;
case LumpType.LUMP_DISP_LIGHTMAP_ALPHAS:
// TODO: Support LUMP_DISP_LIGHTMAP_ALPHAS [32] when in Models
break;
case LumpType.LUMP_DISP_VERTS:
file.DispVertsLump = ParseDispVertsLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS:
// TODO: Support LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS [34] when in Models
break;
case LumpType.LUMP_GAME_LUMP:
file.GameLump = ParseGameLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAFWATERDATA:
// TODO: Support LUMP_LEAFWATERDATA [36] when in Models
break;
case LumpType.LUMP_PRIMITIVES:
// TODO: Support LUMP_PRIMITIVES [37] when in Models
break;
case LumpType.LUMP_PRIMVERTS:
// TODO: Support LUMP_PRIMVERTS [38] when in Models
break;
case LumpType.LUMP_PRIMINDICES:
// TODO: Support LUMP_PRIMINDICES [39] when in Models
break;
case LumpType.LUMP_PAKFILE:
file.PakfileLump = ParsePakfileLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_CLIPPORTALVERTS:
// TODO: Support LUMP_CLIPPORTALVERTS [41] when in Models
break;
case LumpType.LUMP_CUBEMAPS:
file.CubemapsLump = ParseCubemapsLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_TEXDATA_STRING_DATA:
file.TexdataStringData = ParseTexdataStringData(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_TEXDATA_STRING_TABLE:
file.TexdataStringTable = ParseTexdataStringTable(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_OVERLAYS:
file.OverlaysLump = ParseOverlaysLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAFMINDISTTOWATER:
// TODO: Support LUMP_LEAFMINDISTTOWATER [46] when in Models
break;
case LumpType.LUMP_FACE_MACRO_TEXTURE_INFO:
// TODO: Support LUMP_FACE_MACRO_TEXTURE_INFO [47] when in Models
break;
case LumpType.LUMP_DISP_TRIS:
file.DispTrisLump = ParseDispTrisLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_PHYSCOLLIDESURFACE:
// TODO: Support LUMP_PHYSCOLLIDESURFACE / LUMP_PROP_BLOB [49] when in Models
break;
case LumpType.LUMP_WATEROVERLAYS:
// TODO: Support LUMP_WATEROVERLAYS [50] when in Models
break;
case LumpType.LUMP_LIGHTMAPPAGES:
file.HDRAmbientIndexLump = ParseAmbientIndexLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LIGHTMAPPAGEINFOS:
file.LDRAmbientIndexLump = ParseAmbientIndexLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LIGHTING_HDR:
// TODO: Support LUMP_LIGHTING_HDR [53] when in Models
break;
case LumpType.LUMP_WORLDLIGHTS_HDR:
file.HDRWorldLightsLump = ParseWorldLightsLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAF_AMBIENT_LIGHTING_HDR:
file.HDRAmbientLightingLump = ParseAmbientLightingLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_LEAF_AMBIENT_LIGHTING:
file.LDRAmbientLightingLump = ParseAmbientLightingLump(data, lumpEntry.Offset, lumpEntry.Length);
break;
case LumpType.LUMP_XZIPPAKFILE:
// TODO: Support LUMP_XZIPPAKFILE [57] when in Models
break;
case LumpType.LUMP_FACES_HDR:
// TODO: Support LUMP_FACES_HDR [58] when in Models
break;
case LumpType.LUMP_MAP_FLAGS:
// TODO: Support LUMP_MAP_FLAGS [59] when in Models
break;
case LumpType.LUMP_OVERLAY_FADES:
// TODO: Support LUMP_OVERLAY_FADES [60] when in Models
break;
case LumpType.LUMP_OVERLAY_SYSTEM_LEVELS:
// TODO: Support LUMP_OVERLAY_SYSTEM_LEVELS [61] when in Models
break;
case LumpType.LUMP_PHYSLEVEL:
// TODO: Support LUMP_PHYSLEVEL [62] when in Models
break;
case LumpType.LUMP_DISP_MULTIBLEND:
// TODO: Support LUMP_DISP_MULTIBLEND [63] when in Models
break;
default:
// Unsupported LumpType value, ignore
break;
}
}
#endregion
return file;
}
catch
{
// Ignore the actual error
return null;
}
#endregion
return file;
}
/// <summary>

View File

@@ -15,84 +15,87 @@ 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 Valve Package to fill
var file = new Models.VPK.File();
#region Header
// Try to parse the header
// The original version had no signature.
var header = data.ReadType<Header>();
if (header?.Signature != SignatureUInt32)
return null;
if (header.Version > 2)
return null;
// Set the package header
file.Header = header;
#endregion
#region Extended Header
if (header.Version == 2)
try
{
// Try to parse the extended header
var extendedHeader = data.ReadType<ExtendedHeader>();
if (extendedHeader == null)
// Create a new Valve Package to fill
var file = new Models.VPK.File();
#region Header
// The original version had no signature.
var header = data.ReadType<Header>();
if (header?.Signature != SignatureUInt32)
return null;
if (header.Version > 2)
return null;
// Set the package extended header
file.ExtendedHeader = extendedHeader;
}
// Set the package header
file.Header = header;
#endregion
#endregion
#region Directory Items
#region Extended Header
// Create the directory items tree
var directoryItems = ParseDirectoryItemTree(data);
if (directoryItems == null)
return null;
// Set the directory items
file.DirectoryItems = directoryItems;
#endregion
#region Archive Hashes
if (header?.Version == 2
&& file.ExtendedHeader != null
&& file.ExtendedHeader.ArchiveMD5SectionSize > 0
&& data.Position + file.ExtendedHeader.ArchiveMD5SectionSize <= data.Length)
{
// Create the archive hashes list
var archiveHashes = new List<ArchiveHash>();
// Cache the current offset
long initialOffset = data.Position;
// Try to parse the directory items
while (data.Position < initialOffset + file.ExtendedHeader.ArchiveMD5SectionSize)
if (header.Version == 2)
{
var archiveHash = data.ReadType<ArchiveHash>();
if (archiveHash == null)
// Try to parse the extended header
var extendedHeader = data.ReadType<ExtendedHeader>();
if (extendedHeader == null)
return null;
archiveHashes.Add(archiveHash);
// Set the package extended header
file.ExtendedHeader = extendedHeader;
}
file.ArchiveHashes = [.. archiveHashes];
#endregion
#region Directory Items
// Create the directory items tree
var directoryItems = ParseDirectoryItemTree(data);
if (directoryItems == null)
return null;
// Set the directory items
file.DirectoryItems = directoryItems;
#endregion
#region Archive Hashes
if (header?.Version == 2
&& file.ExtendedHeader != null
&& file.ExtendedHeader.ArchiveMD5SectionSize > 0
&& data.Position + file.ExtendedHeader.ArchiveMD5SectionSize <= data.Length)
{
// Create the archive hashes list
var archiveHashes = new List<ArchiveHash>();
// Cache the current offset
long initialOffset = data.Position;
// Try to parse the directory items
while (data.Position < initialOffset + file.ExtendedHeader.ArchiveMD5SectionSize)
{
var archiveHash = data.ReadType<ArchiveHash>();
if (archiveHash == null)
return null;
archiveHashes.Add(archiveHash);
}
file.ArchiveHashes = [.. archiveHashes];
}
#endregion
return file;
}
catch
{
// Ignore the actual error
return null;
}
#endregion
return file;
}
/// <summary>

View File

@@ -15,79 +15,83 @@ 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 Texture Package to fill
var file = new Models.WAD3.File();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Signature != SignatureString)
return null;
// Set the package header
file.Header = header;
#endregion
#region Directory Entries
// Get the directory offset
uint dirOffset = header.DirOffset;
if (dirOffset < 0 || dirOffset >= data.Length)
return null;
// Seek to the lump offset
data.Seek(dirOffset, SeekOrigin.Begin);
// Create the lump array
file.DirEntries = new DirEntry[header.NumDirs];
for (int i = 0; i < header.NumDirs; i++)
try
{
var lump = data.ReadType<DirEntry>();
if (lump == null)
// Create a new Half-Life Texture Package to fill
var file = new Models.WAD3.File();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Signature != SignatureString)
return null;
file.DirEntries[i] = lump;
// Set the package header
file.Header = header;
#endregion
#region Directory Entries
// Get the directory offset
uint dirOffset = header.DirOffset;
if (dirOffset < 0 || dirOffset >= data.Length)
return null;
// Seek to the lump offset
data.Seek(dirOffset, SeekOrigin.Begin);
// Create the lump array
file.DirEntries = new DirEntry[header.NumDirs];
for (int i = 0; i < header.NumDirs; i++)
{
var lump = data.ReadType<DirEntry>();
if (lump == null)
return null;
file.DirEntries[i] = lump;
}
#endregion
#region File Entries
// Create the file entry array
file.FileEntries = new FileEntry?[header.NumDirs];
for (int i = 0; i < header.NumDirs; i++)
{
var dirEntry = file.DirEntries[i];
if (dirEntry == null)
continue;
// TODO: Handle compressed entries
if (dirEntry.Compression != 0)
continue;
// Get the file entry offset
uint fileEntryOffset = dirEntry.Offset;
if (fileEntryOffset < 0 || fileEntryOffset >= data.Length)
continue;
// Seek to the file entry offset
data.Seek(fileEntryOffset, SeekOrigin.Begin);
// Try to parse the file entry
var fileEntry = ParseFileEntry(data, dirEntry.Type);
if (fileEntry != null)
file.FileEntries[i] = fileEntry;
}
#endregion
return file;
}
#endregion
#region File Entries
// Create the file entry array
file.FileEntries = new FileEntry?[header.NumDirs];
for (int i = 0; i < header.NumDirs; i++)
catch
{
var dirEntry = file.DirEntries[i];
if (dirEntry == null)
continue;
// TODO: Handle compressed entries
if (dirEntry.Compression != 0)
continue;
// Get the file entry offset
uint fileEntryOffset = dirEntry.Offset;
if (fileEntryOffset < 0 || fileEntryOffset >= data.Length)
continue;
// Seek to the file entry offset
data.Seek(fileEntryOffset, SeekOrigin.Begin);
// Try to parse the file entry
var fileEntry = ParseFileEntry(data, dirEntry.Type);
if (fileEntry != null)
file.FileEntries[i] = fileEntry;
// Ignore the actual error
return null;
}
#endregion
return file;
}
/// <summary>

View File

@@ -14,125 +14,129 @@ 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 XBox Package File to fill
var file = new Models.XZP.File();
#region Header
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Signature != HeaderSignatureString)
return null;
if (header.Version != 6)
return null;
// Set the package header
file.Header = header;
#endregion
#region Directory Entries
// Create the directory entry array
file.DirectoryEntries = new DirectoryEntry[header.DirectoryEntryCount];
// Try to parse the directory entries
for (int i = 0; i < file.DirectoryEntries.Length; i++)
try
{
var directoryEntry = data.ReadType<DirectoryEntry>();
if (directoryEntry == null)
continue;
// Create a new XBox Package File to fill
var file = new Models.XZP.File();
file.DirectoryEntries[i] = directoryEntry;
}
#region Header
#endregion
// Try to parse the header
var header = data.ReadType<Header>();
if (header?.Signature != HeaderSignatureString)
return null;
if (header.Version != 6)
return null;
#region Preload Directory Entries
// Set the package header
file.Header = header;
if (header.PreloadBytes > 0)
{
// Create the preload directory entry array
file.PreloadDirectoryEntries = new DirectoryEntry[header.PreloadDirectoryEntryCount];
#endregion
// Try to parse the preload directory entries
for (int i = 0; i < file.PreloadDirectoryEntries.Length; i++)
#region Directory Entries
// Create the directory entry array
file.DirectoryEntries = new DirectoryEntry[header.DirectoryEntryCount];
// Try to parse the directory entries
for (int i = 0; i < file.DirectoryEntries.Length; i++)
{
var directoryEntry = data.ReadType<DirectoryEntry>();
if (directoryEntry == null)
continue;
file.PreloadDirectoryEntries[i] = directoryEntry;
file.DirectoryEntries[i] = directoryEntry;
}
}
#endregion
#endregion
#region Preload Directory Mappings
#region Preload Directory Entries
if (header.PreloadBytes > 0)
{
// Create the preload directory mapping array
file.PreloadDirectoryMappings = new DirectoryMapping[header.PreloadDirectoryEntryCount];
// Try to parse the preload directory mappings
for (int i = 0; i < file.PreloadDirectoryMappings.Length; i++)
if (header.PreloadBytes > 0)
{
var directoryMapping = data.ReadType<DirectoryMapping>();
if (directoryMapping == null)
continue;
// Create the preload directory entry array
file.PreloadDirectoryEntries = new DirectoryEntry[header.PreloadDirectoryEntryCount];
file.PreloadDirectoryMappings[i] = directoryMapping;
// Try to parse the preload directory entries
for (int i = 0; i < file.PreloadDirectoryEntries.Length; i++)
{
var directoryEntry = data.ReadType<DirectoryEntry>();
if (directoryEntry == null)
continue;
file.PreloadDirectoryEntries[i] = directoryEntry;
}
}
}
#endregion
#endregion
#region Directory Items
#region Preload Directory Mappings
if (header.DirectoryItemCount > 0)
{
// Get the directory item offset
uint directoryItemOffset = header.DirectoryItemOffset;
if (directoryItemOffset < 0 || directoryItemOffset >= data.Length)
if (header.PreloadBytes > 0)
{
// Create the preload directory mapping array
file.PreloadDirectoryMappings = new DirectoryMapping[header.PreloadDirectoryEntryCount];
// Try to parse the preload directory mappings
for (int i = 0; i < file.PreloadDirectoryMappings.Length; i++)
{
var directoryMapping = data.ReadType<DirectoryMapping>();
if (directoryMapping == null)
continue;
file.PreloadDirectoryMappings[i] = directoryMapping;
}
}
#endregion
#region Directory Items
if (header.DirectoryItemCount > 0)
{
// Get the directory item offset
uint directoryItemOffset = header.DirectoryItemOffset;
if (directoryItemOffset < 0 || directoryItemOffset >= data.Length)
return null;
// Seek to the directory items
data.Seek(directoryItemOffset, SeekOrigin.Begin);
// Create the directory item array
file.DirectoryItems = new DirectoryItem[header.DirectoryItemCount];
// Try to parse the directory items
for (int i = 0; i < file.DirectoryItems.Length; i++)
{
var directoryItem = ParseDirectoryItem(data);
file.DirectoryItems[i] = directoryItem;
}
}
#endregion
#region Footer
// Seek to the footer
data.Seek(-8, SeekOrigin.End);
// Try to parse the footer
var footer = data.ReadType<Footer>();
if (footer?.Signature != FooterSignatureString)
return null;
// Seek to the directory items
data.Seek(directoryItemOffset, SeekOrigin.Begin);
// Set the package footer
file.Footer = footer;
// Create the directory item array
file.DirectoryItems = new DirectoryItem[header.DirectoryItemCount];
#endregion
// Try to parse the directory items
for (int i = 0; i < file.DirectoryItems.Length; i++)
{
var directoryItem = ParseDirectoryItem(data);
file.DirectoryItems[i] = directoryItem;
}
return file;
}
#endregion
#region Footer
// Seek to the footer
data.Seek(-8, SeekOrigin.End);
// Try to parse the footer
var footer = data.ReadType<Footer>();
if (footer?.Signature != FooterSignatureString)
catch
{
// Ignore the actual error
return null;
// Set the package footer
file.Footer = footer;
#endregion
return file;
}
}
/// <summary>

View File

@@ -20,37 +20,26 @@ namespace SabreTools.Serialization.Deserializers
try
{
// If the stream length and offset are invalid
if (data.Length == 0 || data.Position < 0 || data.Position >= data.Length)
return default;
}
catch
{
// Ignore errors in getting position for compressed streams
}
// Setup the serializer and the reader
var serializer = new XmlSerializer(typeof(T));
var settings = new XmlReaderSettings
{
CheckCharacters = false,
// Setup the serializer and the reader
var serializer = new XmlSerializer(typeof(T));
var settings = new XmlReaderSettings
{
CheckCharacters = false,
#if NET40_OR_GREATER || NETCOREAPP
DtdProcessing = DtdProcessing.Ignore,
DtdProcessing = DtdProcessing.Ignore,
#endif
ValidationFlags = XmlSchemaValidationFlags.None,
ValidationType = ValidationType.None,
};
var streamReader = new StreamReader(data);
var xmlReader = XmlReader.Create(streamReader, settings);
ValidationFlags = XmlSchemaValidationFlags.None,
ValidationType = ValidationType.None,
};
var streamReader = new StreamReader(data);
var xmlReader = XmlReader.Create(streamReader, settings);
// Perform the deserialization and return
try
{
// Perform the deserialization and return
return (T?)serializer.Deserialize(xmlReader);
}
catch
{
// Absorb all exceptions
// Ignore the actual error
return default;
}
}