Miscellaneous cleanup

This commit is contained in:
Matt Nadareski
2024-11-19 12:40:32 -05:00
parent 4d2fbbae04
commit c995ec1dca
33 changed files with 2946 additions and 2069 deletions

View File

@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
@@ -20,9 +19,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new media key block to fill
var mediaKeyBlock = new MediaKeyBlock();
@@ -73,38 +69,27 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled record on success, null on error</returns>
private static Record? ParseRecord(Stream data)
{
// TODO: Use marshalling here instead of building
// The first 4 bytes make up the type and length
byte[]? typeAndLength = data.ReadBytes(4);
if (typeAndLength == null)
return null;
RecordType type = (RecordType)typeAndLength[0];
// Remove the first byte and parse as big-endian
typeAndLength[0] = 0x00;
Array.Reverse(typeAndLength);
uint length = BitConverter.ToUInt32(typeAndLength, 0);
RecordType type = (RecordType)data.ReadByteValue();
uint length = data.ReadUInt24();
// Create a record based on the type
switch (type)
return type switch
{
// Recognized record types
case RecordType.EndOfMediaKeyBlock: return ParseEndOfMediaKeyBlockRecord(data, type, length);
case RecordType.ExplicitSubsetDifference: return ParseExplicitSubsetDifferenceRecord(data, type, length);
case RecordType.MediaKeyData: return ParseMediaKeyDataRecord(data, type, length);
case RecordType.SubsetDifferenceIndex: return ParseSubsetDifferenceIndexRecord(data, type, length);
case RecordType.TypeAndVersion: return ParseTypeAndVersionRecord(data, type, length);
case RecordType.DriveRevocationList: return ParseDriveRevocationListRecord(data, type, length);
case RecordType.HostRevocationList: return ParseHostRevocationListRecord(data, type, length);
case RecordType.VerifyMediaKey: return ParseVerifyMediaKeyRecord(data, type, length);
case RecordType.Copyright: return ParseCopyrightRecord(data, type, length);
// Unrecognized record type
default:
return null;
}
// Known record types
RecordType.EndOfMediaKeyBlock => ParseEndOfMediaKeyBlockRecord(data, type, length),
RecordType.ExplicitSubsetDifference => ParseExplicitSubsetDifferenceRecord(data, type, length),
RecordType.MediaKeyData => ParseMediaKeyDataRecord(data, type, length),
RecordType.SubsetDifferenceIndex => ParseSubsetDifferenceIndexRecord(data, type, length),
RecordType.TypeAndVersion => ParseTypeAndVersionRecord(data, type, length),
RecordType.DriveRevocationList => ParseDriveRevocationListRecord(data, type, length),
RecordType.HostRevocationList => ParseHostRevocationListRecord(data, type, length),
RecordType.VerifyMediaKey => ParseVerifyMediaKeyRecord(data, type, length),
RecordType.Copyright => ParseCopyrightRecord(data, type, length),
// Unknown record type
_ => null,
};
}
/// <summary>
@@ -118,7 +103,6 @@ namespace SabreTools.Serialization.Deserializers
if (type != RecordType.EndOfMediaKeyBlock)
return null;
// TODO: Use marshalling here instead of building
var record = new EndOfMediaKeyBlockRecord();
record.RecordType = type;
@@ -140,7 +124,6 @@ namespace SabreTools.Serialization.Deserializers
if (type != RecordType.ExplicitSubsetDifference)
return null;
// TODO: Use marshalling here instead of building
var record = new ExplicitSubsetDifferenceRecord();
record.RecordType = type;
@@ -184,7 +167,6 @@ namespace SabreTools.Serialization.Deserializers
if (type != RecordType.MediaKeyData)
return null;
// TODO: Use marshalling here instead of building
var record = new MediaKeyDataRecord();
record.RecordType = type;
@@ -221,7 +203,6 @@ namespace SabreTools.Serialization.Deserializers
if (type != RecordType.SubsetDifferenceIndex)
return null;
// TODO: Use marshalling here instead of building
var record = new SubsetDifferenceIndexRecord();
record.RecordType = type;
@@ -259,7 +240,6 @@ namespace SabreTools.Serialization.Deserializers
if (type != RecordType.TypeAndVersion)
return null;
// TODO: Use marshalling here instead of building
var record = new TypeAndVersionRecord();
record.RecordType = type;
@@ -281,7 +261,6 @@ namespace SabreTools.Serialization.Deserializers
if (type != RecordType.DriveRevocationList)
return null;
// TODO: Use marshalling here instead of building
var record = new DriveRevocationListRecord();
record.RecordType = type;
@@ -342,7 +321,6 @@ namespace SabreTools.Serialization.Deserializers
if (type != RecordType.HostRevocationList)
return null;
// TODO: Use marshalling here instead of building
var record = new HostRevocationListRecord();
record.RecordType = type;
@@ -403,7 +381,6 @@ namespace SabreTools.Serialization.Deserializers
if (type != RecordType.VerifyMediaKey)
return null;
// TODO: Use marshalling here instead of building
var record = new VerifyMediaKeyRecord();
record.RecordType = type;
@@ -424,7 +401,6 @@ namespace SabreTools.Serialization.Deserializers
if (type != RecordType.Copyright)
return null;
// TODO: Use marshalling here instead of building
var record = new CopyrightRecord();
record.RecordType = type;

View File

@@ -56,8 +56,8 @@ namespace SabreTools.Serialization.Deserializers
svm.Unknown2 = data.ReadBytes(4);
svm.Length = data.ReadUInt32();
// if (svm.Length > 0)
// svm.Data = data.ReadBytes((int)svm.Length);
if (svm.Length > 0)
svm.Data = data.ReadBytes((int)svm.Length);
return svm;
}

View File

@@ -40,24 +40,21 @@ namespace SabreTools.Serialization.Deserializers
#region Files
// If we have any files
if (header.Files > 0)
var files = new FileEntry[header.Files];
// Read all entries in turn
for (int i = 0; i < header.Files; i++)
{
var files = new FileEntry[header.Files];
var file = ParseFileEntry(data);
if (file == null)
return null;
// Read all entries in turn
for (int i = 0; i < header.Files; i++)
{
var file = ParseFileEntry(data);
if (file == null)
return null;
files[i] = file;
}
// Set the files
archive.Files = files;
files[i] = file;
}
// Set the files
archive.Files = files;
#endregion
return archive;
@@ -72,9 +69,7 @@ namespace SabreTools.Serialization.Deserializers
{
var header = data.ReadType<Header>();
if (header == null)
return null;
if (header.Magic != SignatureString)
if (header?.Magic != SignatureString)
return null;
return header;

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
@@ -20,9 +21,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new Half-Life Level to fill
var file = new BspFile();
@@ -40,326 +38,71 @@ namespace SabreTools.Serialization.Deserializers
#region Lumps
// LUMP_ENTITIES [0]
var lumpEntry = header.Lumps[(int)LumpType.LUMP_ENTITIES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
for (int l = 0; l < BSP_HEADER_LUMPS; l++)
{
// 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 the lump data
var entities = new List<Entity>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
// Read according to the lump type
switch ((LumpType)l)
{
// TODO: Read this into sets of key-value pairs
var sb = new StringBuilder();
char c = '\0';
do
{
c = (char)data.ReadByteValue();
sb.Append(c);
} while (c != '}');
var entity = new Entity();
entity.Attributes = new List<KeyValuePair<string, string>>
{
new("REPLACE", sb.ToString()),
};
entities.Add(entity);
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:
// TODO: Assign when Models supports it
_ = 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;
}
var lump = new EntitiesLump();
lump.Entities = [.. entities];
file.Entities = lump;
}
// LUMP_PLANES [1]
lumpEntry = header.Lumps[(int)LumpType.LUMP_PLANES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var planes = new List<Plane>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
var plane = data.ReadType<Plane>();
if (plane != null)
planes.Add(plane);
}
var lump = new PlanesLump();
lump.Planes = [.. planes];
file.PlanesLump = lump;
}
// LUMP_TEXTURES [2]
lumpEntry = header.Lumps[(int)LumpType.LUMP_TEXTURES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the header
var lump = new TextureLump();
lump.Header = ParseTextureHeader(data);
// Read the lump data
var textures = new List<MipTexture>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
var texture = data.ReadType<MipTexture>();
if (texture != null)
textures.Add(texture);
}
lump.Textures = [.. textures];
file.TextureLump = lump;
}
// LUMP_VERTICES [3]
lumpEntry = header.Lumps[(int)LumpType.LUMP_VERTICES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var vertices = new List<Vector3D>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
vertices.Add(data.ReadType<Vector3D>());
}
var lump = new VerticesLump();
lump.Vertices = [.. vertices];
file.VerticesLump = lump;
}
// LUMP_VISIBILITY [4]
lumpEntry = header.Lumps[(int)LumpType.LUMP_VISIBILITY];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// TODO: Parse LUMP_VISIBILITY when added to model
}
// LUMP_NODES [5]
lumpEntry = header.Lumps[(int)LumpType.LUMP_NODES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var nodes = new List<BspNode>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
var node = data.ReadType<BspNode>();
if (node != null)
nodes.Add(node);
}
var lump = new BspNodesLump();
lump.Nodes = [.. nodes];
file.NodesLump = lump;
}
// LUMP_TEXINFO [6]
lumpEntry = header.Lumps[(int)LumpType.LUMP_TEXINFO];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var texinfos = new List<BspTexinfo>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
var texinfo = data.ReadType<BspTexinfo>();
if (texinfo != null)
texinfos.Add(texinfo);
}
var lump = new BspTexinfoLump();
lump.Texinfos = [.. texinfos];
file.TexinfoLump = lump;
}
// LUMP_FACES [7]
lumpEntry = header.Lumps[(int)LumpType.LUMP_FACES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var faces = new List<BspFace>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
var face = data.ReadType<BspFace>();
if (face != null)
faces.Add(face);
}
var lump = new BspFacesLump();
lump.Faces = [.. faces];
file.FacesLump = lump;
}
// LUMP_LIGHTING [8]
lumpEntry = header.Lumps[(int)LumpType.LUMP_LIGHTING];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var lump = new LightmapLump();
lump.Lightmap = new byte[lumpEntry.Length / 3, 3];
for (int i = 0; i < lumpEntry.Length / 3; i++)
for (int j = 0; j < 3; j++)
{
lump.Lightmap[i, j] = data.ReadByteValue();
}
file.LightmapLump = lump;
}
// LUMP_CLIPNODES [9]
lumpEntry = header.Lumps[(int)LumpType.LUMP_CLIPNODES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var clipnodes = new List<Clipnode>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
var clipnode = data.ReadType<Clipnode>();
if (clipnode != null)
clipnodes.Add(clipnode);
}
var lump = new ClipnodesLump();
lump.Clipnodes = [.. clipnodes];
file.ClipnodesLump = lump;
}
// LUMP_LEAVES [10]
lumpEntry = header.Lumps[(int)LumpType.LUMP_LEAVES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var leaves = new List<BspLeaf>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
var leaf = data.ReadType<BspLeaf>();
if (leaf != null)
leaves.Add(leaf);
}
var lump = new BspLeavesLump();
lump.Leaves = [.. leaves];
file.LeavesLump = lump;
}
// LUMP_MARKSURFACES [11]
lumpEntry = header.Lumps[(int)LumpType.LUMP_MARKSURFACES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var marksurfaces = new List<ushort>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
marksurfaces.Add(data.ReadUInt16());
}
var lump = new MarksurfacesLump();
lump.Marksurfaces = [.. marksurfaces];
file.MarksurfacesLump = lump;
}
// LUMP_EDGES [12]
lumpEntry = header.Lumps[(int)LumpType.LUMP_EDGES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var edges = new List<Edge>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
var edge = data.ReadType<Edge>();
if (edge != null)
edges.Add(edge);
}
var lump = new EdgesLump();
lump.Edges = [.. edges];
file.EdgesLump = lump;
}
// LUMP_SURFEDGES [13]
lumpEntry = header.Lumps[(int)LumpType.LUMP_SURFEDGES];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var surfedges = new List<int>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
surfedges.Add(data.ReadInt32());
}
var lump = new SurfedgesLump();
lump.Surfedges = [.. surfedges];
file.SurfedgesLump = lump;
}
// LUMP_MODELS [14]
lumpEntry = header.Lumps[(int)LumpType.LUMP_MODELS];
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
{
// Seek to the lump offset
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
// Read the lump data
var models = new List<BspModel>();
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
{
var model = data.ReadType<BspModel>();
if (model != null)
models.Add(model);
}
var lump = new BspModelsLump();
lump.Models = [.. models];
file.ModelsLump = lump;
}
#endregion
@@ -391,6 +134,88 @@ namespace SabreTools.Serialization.Deserializers
return header;
}
/// <summary>
/// Parse a Stream into LUMP_ENTITIES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_ENTITIES on success, null on error</returns>
private static EntitiesLump? ParseEntitiesLump(Stream data, int offset, int length)
{
var entities = new List<Entity>();
// Read the entire lump as text
byte[] lumpData = data.ReadBytes(length);
string lumpText = Encoding.ASCII.GetString(lumpData);
// Break the text by ending curly braces
string[] lumpSections = lumpText.Split('}');
Array.ForEach(lumpSections, s => s.Trim('{', '}'));
// Loop through all sections
for (int i = 0; i < lumpSections.Length; i++)
{
// Prepare an attributes list
var attributes = new List<KeyValuePair<string, string>>();
// Split the section by newlines
string section = lumpSections[i];
string[] lines = section.Split('\n');
Array.ForEach(lines, l => l.Trim());
// Convert each line into a key-value pair and add
for (int j = 0; j < lines.Length; j++)
{
// TODO: Split lines and add
}
// Create a new entity and add
var entity = new Entity { Attributes = attributes };
entities.Add(entity);
}
return new EntitiesLump { Entities = [.. entities] };
}
/// <summary>
/// Parse a Stream into LUMP_PLANES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_PLANES on success, null on error</returns>
private static PlanesLump? ParsePlanesLump(Stream data, int offset, int length)
{
var planes = new List<Plane>();
while (data.Position < offset + length)
{
var plane = data.ReadType<Plane>();
if (plane != null)
planes.Add(plane);
}
return new PlanesLump { Planes = [.. planes] };
}
/// <summary>
/// Parse a Stream into LUMP_TEXTURES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_TEXTURES on success, null on error</returns>
private static TextureLump? ParseTextureLump(Stream data, int offset, int length)
{
var lump = new TextureLump();
lump.Header = ParseTextureHeader(data);
var textures = new List<MipTexture>();
while (data.Position < offset + length)
{
var texture = data.ReadType<MipTexture>();
if (texture != null)
textures.Add(texture);
}
lump.Textures = [.. textures];
return lump;
}
/// <summary>
/// Parse a Stream into a Half-Life Level texture header
/// </summary>
@@ -410,5 +235,222 @@ namespace SabreTools.Serialization.Deserializers
return textureHeader;
}
/// <summary>
/// Parse a Stream into LUMP_VERTICES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_VERTICES on success, null on error</returns>
private static VerticesLump? ParseVerticesLump(Stream data, int offset, int length)
{
var vertices = new List<Vector3D>();
while (data.Position < offset + length)
{
vertices.Add(data.ReadType<Vector3D>());
}
return new VerticesLump { Vertices = [.. vertices] };
}
/// <summary>
/// Parse a Stream into LUMP_VISIBILITY
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_VISIBILITY on success, null on error</returns>
private static VisibilityLump? ParseVisibilityLump(Stream data, int offset, int length)
{
var lump = new VisibilityLump();
lump.NumClusters = data.ReadInt32();
lump.ByteOffsets = new int[lump.NumClusters, 2];
for (int i = 0; i < lump.NumClusters; i++)
{
for (int j = 0; j < 2; j++)
{
lump.ByteOffsets[i, j] = data.ReadInt32();
}
}
return lump;
}
/// <summary>
/// Parse a Stream into LUMP_NODES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_NODES on success, null on error</returns>
private static BspNodesLump? ParseNodesLump(Stream data, int offset, int length)
{
var nodes = new List<BspNode>();
while (data.Position < offset + length)
{
var node = data.ReadType<BspNode>();
if (node != null)
nodes.Add(node);
}
return new BspNodesLump { Nodes = [.. nodes] };
}
/// <summary>
/// Parse a Stream into LUMP_TEXINFO
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_TEXINFO on success, null on error</returns>
private static BspTexinfoLump? ParseTexinfoLump(Stream data, int offset, int length)
{
var texinfos = new List<BspTexinfo>();
while (data.Position < offset + length)
{
var texinfo = data.ReadType<BspTexinfo>();
if (texinfo != null)
texinfos.Add(texinfo);
}
return new BspTexinfoLump { Texinfos = [.. texinfos] };
}
/// <summary>
/// Parse a Stream into LUMP_FACES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_FACES on success, null on error</returns>
private static BspFacesLump? ParseFacesLump(Stream data, int offset, int length)
{
var faces = new List<BspFace>();
while (data.Position < offset + length)
{
var face = data.ReadType<BspFace>();
if (face != null)
faces.Add(face);
}
return new BspFacesLump { Faces = [.. faces] };
}
/// <summary>
/// Parse a Stream into LUMP_LIGHTING
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_LIGHTING on success, null on error</returns>
private static LightmapLump? ParseLightmapLump(Stream data, int offset, int length)
{
var lump = new LightmapLump();
lump.Lightmap = new byte[length / 3, 3];
for (int i = 0; i < length / 3; i++)
{
for (int j = 0; j < 3; j++)
{
lump.Lightmap[i, j] = data.ReadByteValue();
}
}
return lump;
}
/// <summary>
/// Parse a Stream into LUMP_CLIPNODES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_CLIPNODES on success, null on error</returns>
private static ClipnodesLump? ParseClipnodesLump(Stream data, int offset, int length)
{
var clipnodes = new List<Clipnode>();
while (data.Position < offset + length)
{
var clipnode = data.ReadType<Clipnode>();
if (clipnode != null)
clipnodes.Add(clipnode);
}
return new ClipnodesLump { Clipnodes = [.. clipnodes] };
}
/// <summary>
/// Parse a Stream into LUMP_LEAVES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_LEAVES on success, null on error</returns>
private static BspLeavesLump? ParseLeavesLump(Stream data, int offset, int length)
{
var leaves = new List<BspLeaf>();
while (data.Position < offset + length)
{
var leaf = data.ReadType<BspLeaf>();
if (leaf != null)
leaves.Add(leaf);
}
return new BspLeavesLump { Leaves = [.. leaves] };
}
/// <summary>
/// Parse a Stream into LUMP_MARKSURFACES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_MARKSURFACES on success, null on error</returns>
private static MarksurfacesLump? ParseMarksurfacesLump(Stream data, int offset, int length)
{
var marksurfaces = new List<ushort>();
while (data.Position < offset + length)
{
marksurfaces.Add(data.ReadUInt16());
}
return new MarksurfacesLump { Marksurfaces = [.. marksurfaces] };
}
/// <summary>
/// Parse a Stream into LUMP_EDGES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_EDGES on success, null on error</returns>
private static EdgesLump? ParseEdgesLump(Stream data, int offset, int length)
{
var edges = new List<Edge>();
while (data.Position < offset + length)
{
var edge = data.ReadType<Edge>();
if (edge != null)
edges.Add(edge);
}
return new EdgesLump { Edges = [.. edges] };
}
/// <summary>
/// Parse a Stream into LUMP_SURFEDGES
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_SURFEDGES on success, null on error</returns>
private static SurfedgesLump? ParseSurfedgesLump(Stream data, int offset, int length)
{
var surfedges = new List<int>();
while (data.Position < offset + length)
{
surfedges.Add(data.ReadInt32());
}
return new SurfedgesLump { Surfedges = [.. surfedges] };
}
/// <summary>
/// Parse a Stream into LUMP_MODELS
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled LUMP_MODELS on success, null on error</returns>
private static BspModelsLump? ParseModelsLump(Stream data, int offset, int length)
{
var models = new List<BspModel>();
while (data.Position < offset + length)
{
var model = data.ReadType<BspModel>();
if (model != null)
models.Add(model);
}
return new BspModelsLump { Models = [.. models] };
}
}
}

View File

@@ -21,9 +21,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new binary to fill
var binary = new Binary();
@@ -235,9 +232,7 @@ namespace SabreTools.Serialization.Deserializers
{
var header = data.ReadType<FileHeader>();
if (header == null)
return null;
if (header.Signature != SignatureUInt64)
if (header?.Signature != SignatureUInt64)
return null;
if (header.ByteOrder != 0xFFFE)
return null;

View File

@@ -19,9 +19,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new CIA archive to fill
var cia = new Models.N3DS.CIA();

View File

@@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
@@ -173,7 +172,7 @@ namespace SabreTools.Serialization.Deserializers
long directoryNamesEnd = data.Position + directoryHeader.NameSize;
// Create the string dictionary
file.DirectoryNames = new Dictionary<long, string?>();
file.DirectoryNames = [];
// Loop and read the null-terminated strings
while (data.Position < directoryNamesEnd)
@@ -184,10 +183,7 @@ namespace SabreTools.Serialization.Deserializers
{
data.Seek(-directoryName?.Length ?? 0, SeekOrigin.Current);
byte[]? endingData = data.ReadBytes((int)(directoryNamesEnd - data.Position));
if (endingData != null)
directoryName = Encoding.ASCII.GetString(endingData);
else
directoryName = null;
directoryName = endingData != null ? Encoding.ASCII.GetString(endingData) : null;
}
file.DirectoryNames[nameOffset] = directoryName;

View File

@@ -88,6 +88,8 @@ namespace SabreTools.Serialization.Deserializers
// Create lists for each hash type
var sfvList = new List<SFV>();
var md2List = new List<MD2>();
var md4List = new List<MD4>();
var md5List = new List<MD5>();
var sha1List = new List<SHA1>();
var sha256List = new List<SHA256>();
@@ -115,6 +117,22 @@ namespace SabreTools.Serialization.Deserializers
};
sfvList.Add(sfv);
break;
case HashType.MD2:
var md2 = new MD2
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
md2List.Add(md2);
break;
case HashType.MD4:
var md4 = new MD4
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
md4List.Add(md4);
break;
case HashType.MD5:
var md5 = new MD5
{
@@ -172,6 +190,12 @@ namespace SabreTools.Serialization.Deserializers
case HashType.CRC32:
dat.SFV = [.. sfvList];
break;
case HashType.MD2:
dat.MD2 = [.. md2List];
break;
case HashType.MD4:
dat.MD4 = [.. md4List];
break;
case HashType.MD5:
dat.MD5 = [.. md5List];
break;

View File

@@ -17,9 +17,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new IRD to fill
var ird = new Models.IRD.File();

View File

@@ -18,9 +18,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new archive to fill
var archive = new Archive();
@@ -100,7 +97,7 @@ namespace SabreTools.Serialization.Deserializers
if (header == null)
return null;
if (header.Signature1 != 0x8C655D13) // TODO: Move constant to Models
if (header?.Signature1 != Constants.HeaderSignature)
return null;
if (header.TocAddress >= data.Length)
return null;

View File

@@ -21,9 +21,6 @@ namespace SabreTools.Serialization.Deserializers
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();
@@ -337,9 +334,7 @@ namespace SabreTools.Serialization.Deserializers
{
var commonHeader = data.ReadType<CommonHeader>();
if (commonHeader == null)
return null;
if (commonHeader.Signature != SignatureString)
if (commonHeader?.Signature != SignatureString)
return null;
return commonHeader;
@@ -353,7 +348,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled volume header on success, null on error</returns>
public static VolumeHeader ParseVolumeHeader(Stream data, int majorVersion)
{
VolumeHeader volumeHeader = new VolumeHeader();
var volumeHeader = new VolumeHeader();
// Read the descriptor based on version
if (majorVersion <= 5)
@@ -649,7 +644,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled file descriptor on success, null on error</returns>
public static FileDescriptor ParseFileDescriptor(Stream data, int majorVersion, uint descriptorOffset)
{
FileDescriptor fileDescriptor = new FileDescriptor();
var fileDescriptor = new FileDescriptor();
// Read the descriptor based on version
if (majorVersion <= 5)

View File

@@ -427,9 +427,7 @@ namespace SabreTools.Serialization.Deserializers
{
var informationBlock = data.ReadType<InformationBlock>();
if (informationBlock == null)
return null;
if (informationBlock.Signature != LESignatureString && informationBlock.Signature != LXSignatureString)
if (informationBlock?.Signature != LESignatureString && informationBlock?.Signature != LXSignatureString)
return null;
return informationBlock;

View File

@@ -21,9 +21,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new archive to fill
var archive = new Archive();
@@ -32,7 +29,7 @@ namespace SabreTools.Serialization.Deserializers
// Check for User Data
uint possibleSignature = data.ReadUInt32();
data.Seek(-4, SeekOrigin.Current);
if (possibleSignature == 0x1B51504D)
if (possibleSignature == UserDataSignatureUInt32)
{
// Save the current position for offset correction
long basePtr = data.Position;
@@ -56,7 +53,7 @@ namespace SabreTools.Serialization.Deserializers
// Check for the Header
possibleSignature = data.ReadUInt32();
data.Seek(-4, SeekOrigin.Current);
if (possibleSignature == 0x1A51504D)
if (possibleSignature == ArchiveHeaderSignatureUInt32)
{
// Try to parse the archive header
var archiveHeader = ParseArchiveHeader(data);
@@ -406,9 +403,7 @@ namespace SabreTools.Serialization.Deserializers
{
var userData = data.ReadType<UserData>();
if (userData == null)
return null;
if (userData.Signature != UserDataSignatureString)
if (userData?.Signature != UserDataSignatureString)
return null;
return userData;

View File

@@ -20,9 +20,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new cart image to fill
var cart = new Cart();

View File

@@ -91,10 +91,7 @@ namespace SabreTools.Serialization.Deserializers
{
data.Seek(-directoryName?.Length ?? 0, SeekOrigin.Current);
byte[]? endingData = data.ReadBytes((int)(directoryNamesEnd - data.Position));
if (endingData != null)
directoryName = Encoding.ASCII.GetString(endingData);
else
directoryName = null;
directoryName = endingData != null ? Encoding.ASCII.GetString(endingData) : null;
}
file.DirectoryNames[nameOffset] = directoryName;

View File

@@ -214,9 +214,7 @@ namespace SabreTools.Serialization.Deserializers
{
var header = data.ReadType<ExecutableHeader>();
if (header == null)
return null;
if (header.Magic != SignatureString)
if (header?.Magic != SignatureString)
return null;
return header;

View File

@@ -19,9 +19,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new cart image to fill
var cart = new Cart();

View File

@@ -18,9 +18,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new Half-Life Package to fill
var file = new Models.PAK.File();
@@ -73,9 +70,7 @@ namespace SabreTools.Serialization.Deserializers
{
var header = data.ReadType<Header>();
if (header == null)
return null;
if (header.Signature != SignatureString)
if (header?.Signature != SignatureString)
return null;
return header;

View File

@@ -1,5 +1,4 @@
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.PFF;
@@ -20,9 +19,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new archive to fill
var archive = new Archive();
@@ -56,7 +52,7 @@ namespace SabreTools.Serialization.Deserializers
{
var file = ParseSegment(data, header.FileSegmentSize);
if (file == null)
return null;
continue;
archive.Segments[i] = file;
}

View File

@@ -168,7 +168,7 @@ namespace SabreTools.Serialization.Deserializers
#region Local File
// Setup all of the collections
var localFileHeaders = new List<LocalFileHeader?>();
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?>();
@@ -200,7 +200,7 @@ namespace SabreTools.Serialization.Deserializers
if (localFileHeader == null)
{
// Add a placeholder null item
localFileHeaders.Add(null);
localFileHeaders.Add(new LocalFileHeader());
encryptionHeaders.Add(null);
fileData.Add([]);
dataDescriptors.Add(null);

View File

@@ -303,7 +303,7 @@ namespace SabreTools.Serialization.Deserializers
private static UnknownBlock1 ParseUnknownBlock1(Stream data)
{
// TODO: Use marshalling here instead of building
UnknownBlock1 unknownBlock1 = new UnknownBlock1();
var unknownBlock1 = new UnknownBlock1();
unknownBlock1.Length = data.ReadUInt32();
unknownBlock1.Data = data.ReadBytes((int)unknownBlock1.Length);
@@ -319,7 +319,7 @@ namespace SabreTools.Serialization.Deserializers
private static UnknownBlock3 ParseUnknownBlock3(Stream data)
{
// TODO: Use marshalling here instead of building
UnknownBlock3 unknownBlock3 = new UnknownBlock3();
var unknownBlock3 = new UnknownBlock3();
// No-op because we don't even know the length
@@ -334,7 +334,7 @@ namespace SabreTools.Serialization.Deserializers
private static DataFile ParseDataFile(Stream data)
{
// TODO: Use marshalling here instead of building
DataFile dataFile = new DataFile();
var dataFile = new DataFile();
dataFile.FileNameLength = data.ReadUInt16();
byte[]? fileName = data.ReadBytes(dataFile.FileNameLength);

View File

@@ -17,9 +17,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new playlist to fill
var playlist = new Playlist();
@@ -46,7 +43,7 @@ namespace SabreTools.Serialization.Deserializers
long currentOffset = data.Position;
var entryHeader = PlayJAudio.DeserializeStream(data, currentOffset);
if (entryHeader == null)
return null;
continue;
playlist.AudioFiles[i] = entryHeader;
}
@@ -64,7 +61,7 @@ namespace SabreTools.Serialization.Deserializers
private static PlaylistHeader ParsePlaylistHeader(Stream data)
{
// TODO: Use marshalling here instead of building
PlaylistHeader playlistHeader = new PlaylistHeader();
var playlistHeader = new PlaylistHeader();
playlistHeader.TrackCount = data.ReadUInt32();
playlistHeader.Data = data.ReadBytes(52);

View File

@@ -19,9 +19,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new archive to fill
var archive = new Archive();
@@ -40,24 +37,21 @@ namespace SabreTools.Serialization.Deserializers
#region File List
// If we have any files
if (header.FileCount > 0)
var fileDescriptors = new FileDescriptor[header.FileCount];
// Read all entries in turn
for (int i = 0; i < header.FileCount; i++)
{
var fileDescriptors = new FileDescriptor[header.FileCount];
var file = ParseFileDescriptor(data, header.MinorVersion);
if (file == null)
return null;
// 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;
fileDescriptors[i] = file;
}
// Set the file list
archive.FileList = fileDescriptors;
#endregion
// Cache the compressed data offset
@@ -75,9 +69,7 @@ namespace SabreTools.Serialization.Deserializers
{
var header = data.ReadType<Header>();
if (header == null)
return null;
if (header.Signature != SignatureString)
if (header?.Signature != SignatureString)
return null;
return header;

View File

@@ -17,9 +17,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Deserialize the SFB
var sfb = data.ReadType<Models.PlayStation3.SFB>();
if (sfb == null)

View File

@@ -17,9 +17,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new SFO to fill
var sfo = new Models.PlayStation3.SFO();

View File

@@ -20,9 +20,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new SGA to fill
var file = new Models.SGA.File();
@@ -78,7 +75,7 @@ namespace SabreTools.Serialization.Deserializers
// Versions 4 and 5 share the same header
case 4:
case 5:
Header4 header4 = new Header4();
var header4 = new Header4();
header4.Signature = signature;
header4.MajorVersion = majorVersion;
@@ -97,7 +94,7 @@ namespace SabreTools.Serialization.Deserializers
// Versions 6 and 7 share the same header
case 6:
case 7:
Header6 header6 = new Header6();
var header6 = new Header6();
header6.Signature = signature;
header6.MajorVersion = majorVersion;
@@ -125,20 +122,24 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA directory on success, null on error</returns>
private static Models.SGA.Directory? ParseDirectory(Stream data, ushort majorVersion)
{
#region Directory
// Create the appropriate type of directory
Models.SGA.Directory directory;
switch (majorVersion)
return majorVersion switch
{
case 4: directory = new Directory4(); break;
case 5: directory = new Directory5(); break;
case 6: directory = new Directory6(); break;
case 7: directory = new Directory7(); break;
default: return null;
}
4 => ParseDirectory4(data),
5 => ParseDirectory5(data),
6 => ParseDirectory6(data),
7 => ParseDirectory7(data),
_ => null,
};
}
#endregion
/// <summary>
/// Parse a Stream into an SGA directory
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SGA directory on success, null on error</returns>
private static Directory4? ParseDirectory4(Stream data)
{
var directory = new Directory4();
// Cache the current offset
long currentOffset = data.Position;
@@ -146,36 +147,19 @@ namespace SabreTools.Serialization.Deserializers
#region Directory Header
// Try to parse the directory header
var directoryHeader = ParseDirectoryHeader(data, majorVersion);
var directoryHeader = ParseDirectory4Header(data);
if (directoryHeader == null)
return null;
// Set the directory header
switch (majorVersion)
{
case 4: (directory as Directory4)!.DirectoryHeader = directoryHeader as DirectoryHeader4; break;
case 5: (directory as Directory5)!.DirectoryHeader = directoryHeader as DirectoryHeader5; break;
case 6: (directory as Directory6)!.DirectoryHeader = directoryHeader as DirectoryHeader5; break;
case 7: (directory as Directory7)!.DirectoryHeader = directoryHeader as DirectoryHeader7; break;
default: return null;
}
directory.DirectoryHeader = directoryHeader;
#endregion
#region Sections
// Get the sections offset
long sectionOffset;
switch (majorVersion)
{
case 4: sectionOffset = (directoryHeader as DirectoryHeader4)!.SectionOffset; break;
case 5:
case 6: sectionOffset = (directoryHeader as DirectoryHeader5)!.SectionOffset; break;
case 7: sectionOffset = (directoryHeader as DirectoryHeader7)!.SectionOffset; break;
default: return null;
}
// Adjust the sections offset based on the directory
// Get and adjust the sections offset
long sectionOffset = directoryHeader.SectionOffset;
sectionOffset += currentOffset;
// Validate the offset
@@ -185,67 +169,21 @@ namespace SabreTools.Serialization.Deserializers
// Seek to the sections
data.Seek(sectionOffset, SeekOrigin.Begin);
// Get the section count
uint sectionCount;
switch (majorVersion)
{
case 4: sectionCount = (directoryHeader as DirectoryHeader4)!.SectionCount; break;
case 5:
case 6: sectionCount = (directoryHeader as DirectoryHeader5)!.SectionCount; break;
case 7: sectionCount = (directoryHeader as DirectoryHeader7)!.SectionCount; break;
default: return null;
}
// Create the sections array
object[] sections;
switch (majorVersion)
{
case 4: sections = new Section4[sectionCount]; break;
case 5:
case 6:
case 7: sections = new Section5[sectionCount]; break;
default: return null;
}
directory.Sections = new Section4[directoryHeader.SectionCount];
// Try to parse the sections
for (int i = 0; i < sections.Length; i++)
for (int i = 0; i < directory.Sections.Length; i++)
{
switch (majorVersion)
{
case 4: sections[i] = ParseSection4(data); break;
case 5:
case 6:
case 7: sections[i] = ParseSection5(data); break;
default: return null;
}
}
// Assign the sections
switch (majorVersion)
{
case 4: (directory as Directory4)!.Sections = sections as Section4[]; break;
case 5: (directory as Directory5)!.Sections = sections as Section5[]; break;
case 6: (directory as Directory6)!.Sections = sections as Section5[]; break;
case 7: (directory as Directory7)!.Sections = sections as Section5[]; break;
default: return null;
directory.Sections[i] = ParseSection4(data);
}
#endregion
#region Folders
// Get the folders offset
long folderOffset;
switch (majorVersion)
{
case 4: folderOffset = (directoryHeader as DirectoryHeader4)!.FolderOffset; break;
case 5: folderOffset = (directoryHeader as DirectoryHeader5)!.FolderOffset; break;
case 6: folderOffset = (directoryHeader as DirectoryHeader5)!.FolderOffset; break;
case 7: folderOffset = (directoryHeader as DirectoryHeader7)!.FolderOffset; break;
default: return null;
}
// Adjust the folders offset based on the directory
// Get and adjust the folders offset
long folderOffset = directoryHeader.FolderOffset;
folderOffset += currentOffset;
// Validate the offset
@@ -255,67 +193,21 @@ namespace SabreTools.Serialization.Deserializers
// Seek to the folders
data.Seek(folderOffset, SeekOrigin.Begin);
// Get the folder count
uint folderCount;
switch (majorVersion)
{
case 4: folderCount = (directoryHeader as DirectoryHeader4)!.FolderCount; break;
case 5: folderCount = (directoryHeader as DirectoryHeader5)!.FolderCount; break;
case 6: folderCount = (directoryHeader as DirectoryHeader5)!.FolderCount; break;
case 7: folderCount = (directoryHeader as DirectoryHeader7)!.FolderCount; break;
default: return null;
}
// Create the folders array
object[] folders;
switch (majorVersion)
{
case 4: folders = new Folder4[folderCount]; break;
case 5: folders = new Folder5[folderCount]; break;
case 6: folders = new Folder5[folderCount]; break;
case 7: folders = new Folder5[folderCount]; break;
default: return null;
}
directory.Folders = new Folder4[directoryHeader.FolderCount];
// Try to parse the folders
for (int i = 0; i < folders.Length; i++)
for (int i = 0; i < directory.Folders.Length; i++)
{
switch (majorVersion)
{
case 4: folders[i] = ParseFolder4(data); break;
case 5: folders[i] = ParseFolder5(data); break;
case 6: folders[i] = ParseFolder5(data); break;
case 7: folders[i] = ParseFolder5(data); break;
default: return null;
}
}
// Assign the folders
switch (majorVersion)
{
case 4: (directory as Directory4)!.Folders = folders as Folder4[]; break;
case 5: (directory as Directory5)!.Folders = folders as Folder5[]; break;
case 6: (directory as Directory6)!.Folders = folders as Folder5[]; break;
case 7: (directory as Directory7)!.Folders = folders as Folder5[]; break;
default: return null;
directory.Folders[i] = ParseFolder4(data);
}
#endregion
#region Files
// Get the files offset
long fileOffset;
switch (majorVersion)
{
case 4: fileOffset = (directoryHeader as DirectoryHeader4)!.FileOffset; break;
case 5: fileOffset = (directoryHeader as DirectoryHeader5)!.FileOffset; break;
case 6: fileOffset = (directoryHeader as DirectoryHeader5)!.FileOffset; break;
case 7: fileOffset = (directoryHeader as DirectoryHeader7)!.FileOffset; break;
default: return null;
}
// Adjust the files offset based on the directory
// Get and adjust the files offset
long fileOffset = directoryHeader.FileOffset;
fileOffset += currentOffset;
// Validate the offset
@@ -326,66 +218,23 @@ namespace SabreTools.Serialization.Deserializers
data.Seek(fileOffset, SeekOrigin.Begin);
// Get the file count
uint fileCount;
switch (majorVersion)
{
case 4: fileCount = (directoryHeader as DirectoryHeader4)!.FileCount; break;
case 5: fileCount = (directoryHeader as DirectoryHeader5)!.FileCount; break;
case 6: fileCount = (directoryHeader as DirectoryHeader5)!.FileCount; break;
case 7: fileCount = (directoryHeader as DirectoryHeader7)!.FileCount; break;
default: return null;
}
uint fileCount = directoryHeader.FileCount;
// Create the files array
object[] files;
switch (majorVersion)
{
case 4: files = new File4[fileCount]; break;
case 5: files = new File4[fileCount]; break;
case 6: files = new File6[fileCount]; break;
case 7: files = new File7[fileCount]; break;
default: return null;
}
directory.Files = new File4[fileCount];
// Try to parse the files
for (int i = 0; i < files.Length; i++)
for (int i = 0; i < directory.Files.Length; i++)
{
switch (majorVersion)
{
case 4: files[i] = ParseFile4(data); break;
case 5: files[i] = ParseFile4(data); break;
case 6: files[i] = ParseFile6(data); break;
case 7: files[i] = ParseFile7(data); break;
default: return null;
}
}
// Assign the files
switch (majorVersion)
{
case 4: (directory as Directory4)!.Files = files as File4[]; break;
case 5: (directory as Directory5)!.Files = files as File4[]; break;
case 6: (directory as Directory6)!.Files = files as File6[]; break;
case 7: (directory as Directory7)!.Files = files as File7[]; break;
default: return null;
directory.Files[i] = ParseFile4(data);
}
#endregion
#region String Table
// Get the string table offset
long stringTableOffset;
switch (majorVersion)
{
case 4: stringTableOffset = (directoryHeader as DirectoryHeader4)!.StringTableOffset; break;
case 5: stringTableOffset = (directoryHeader as DirectoryHeader5)!.StringTableOffset; break;
case 6: stringTableOffset = (directoryHeader as DirectoryHeader5)!.StringTableOffset; break;
case 7: stringTableOffset = (directoryHeader as DirectoryHeader7)!.StringTableOffset; break;
default: return null;
}
// Adjust the string table offset based on the directory
// Get and adjust the string table offset
long stringTableOffset = directoryHeader.StringTableOffset;
stringTableOffset += currentOffset;
// Validate the offset
@@ -395,87 +244,40 @@ namespace SabreTools.Serialization.Deserializers
// Seek to the string table
data.Seek(stringTableOffset, SeekOrigin.Begin);
// Get the string table count
uint stringCount;
switch (majorVersion)
{
case 4: stringCount = (directoryHeader as DirectoryHeader4)!.StringTableCount; break;
case 5: stringCount = (directoryHeader as DirectoryHeader5)!.StringTableCount; break;
case 6: stringCount = (directoryHeader as DirectoryHeader5)!.StringTableCount; break;
case 7: stringCount = (directoryHeader as DirectoryHeader7)!.StringTableCount; break;
default: return null;
}
// TODO: Are these strings actually indexed by number and not position?
// TODO: If indexed by position, I think it needs to be adjusted by start of table
// Create the strings dictionary
Dictionary<long, string?> strings = new Dictionary<long, string?>((int)stringCount);
directory.StringTable = new Dictionary<long, string?>((int)directoryHeader.StringTableCount);
// Get the current position to adjust the offsets
long stringTableStart = data.Position;
// Try to parse the strings
for (int i = 0; i < stringCount; i++)
for (int i = 0; i < directoryHeader.StringTableCount; i++)
{
long currentPosition = data.Position - stringTableStart;
strings[currentPosition] = data.ReadNullTerminatedAnsiString();
}
// Assign the files
switch (majorVersion)
{
case 4: (directory as Directory4)!.StringTable = strings; break;
case 5: (directory as Directory5)!.StringTable = strings; break;
case 6: (directory as Directory6)!.StringTable = strings; break;
case 7: (directory as Directory7)!.StringTable = strings; break;
default: return null;
directory.StringTable[currentPosition] = data.ReadNullTerminatedAnsiString();
}
// Loop through all folders to assign names
for (int i = 0; i < folderCount; i++)
for (int i = 0; i < directory.Folders.Length; i++)
{
uint nameOffset;
switch (majorVersion)
{
case 4: nameOffset = (directory as Directory4)!.Folders![i]!.NameOffset; break;
case 5: nameOffset = (directory as Directory5)!.Folders![i]!.NameOffset; break;
case 6: nameOffset = (directory as Directory6)!.Folders![i]!.NameOffset; break;
case 7: nameOffset = (directory as Directory7)!.Folders![i]!.NameOffset; break;
default: return null;
}
var folder = directory.Folders[i];
if (folder == null)
continue;
switch (majorVersion)
{
case 4: (directory as Directory4)!.Folders![i]!.Name = strings[nameOffset]; break;
case 5: (directory as Directory5)!.Folders![i]!.Name = strings[nameOffset]; break;
case 6: (directory as Directory6)!.Folders![i]!.Name = strings[nameOffset]; break;
case 7: (directory as Directory7)!.Folders![i]!.Name = strings[nameOffset]; break;
default: return null;
}
folder.Name = directory.StringTable[folder.NameOffset];
}
// Loop through all files to assign names
for (int i = 0; i < fileCount; i++)
for (int i = 0; i < directory.Files.Length; i++)
{
uint nameOffset;
switch (majorVersion)
{
case 4: nameOffset = (directory as Directory4)!.Files![i]!.NameOffset; break;
case 5: nameOffset = (directory as Directory5)!.Files![i]!.NameOffset; break;
case 6: nameOffset = (directory as Directory6)!.Files![i]!.NameOffset; break;
case 7: nameOffset = (directory as Directory7)!.Files![i]!.NameOffset; break;
default: return null;
}
var file = directory.Files[i];
if (file == null)
continue;
switch (majorVersion)
{
case 4: (directory as Directory4)!.Files![i]!.Name = strings[nameOffset]; break;
case 5: (directory as Directory5)!.Files![i]!.Name = strings[nameOffset]; break;
case 6: (directory as Directory6)!.Files![i]!.Name = strings[nameOffset]; break;
case 7: (directory as Directory7)!.Files![i]!.Name = strings[nameOffset]; break;
default: return null;
}
file.Name = directory.StringTable[file.NameOffset];
}
#endregion
@@ -484,21 +286,453 @@ namespace SabreTools.Serialization.Deserializers
}
/// <summary>
/// Parse a Stream into an SGA directory header
/// Parse a Stream into an SGA directory
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="majorVersion">SGA major version</param>
/// <returns>Filled SGA directory header on success, null on error</returns>
private static object? ParseDirectoryHeader(Stream data, ushort majorVersion)
/// <returns>Filled SGA directory on success, null on error</returns>
private static Directory5? ParseDirectory5(Stream data)
{
switch (majorVersion)
var directory = new Directory5();
// Cache the current offset
long currentOffset = data.Position;
#region Directory Header
// Try to parse the directory header
var directoryHeader = ParseDirectory5Header(data);
if (directoryHeader == null)
return null;
// Set the directory header
directory.DirectoryHeader = directoryHeader;
#endregion
#region Sections
// Get and adjust the sections offset
long sectionOffset = directoryHeader.SectionOffset;
sectionOffset += currentOffset;
// Validate the offset
if (sectionOffset < 0 || sectionOffset >= data.Length)
return null;
// Seek to the sections
data.Seek(sectionOffset, SeekOrigin.Begin);
// Create the sections array
directory.Sections = new Section5[directoryHeader.SectionCount];
// Try to parse the sections
for (int i = 0; i < directory.Sections.Length; i++)
{
case 4: return ParseDirectory4Header(data);
case 5: return ParseDirectory5Header(data);
case 6: return ParseDirectory5Header(data);
case 7: return ParseDirectory7Header(data);
default: return null;
directory.Sections[i] = ParseSection5(data);
}
#endregion
#region Folders
// Get and adjust the folders offset
long folderOffset = directoryHeader.FolderOffset;
folderOffset += currentOffset;
// Validate the offset
if (folderOffset < 0 || folderOffset >= data.Length)
return null;
// Seek to the folders
data.Seek(folderOffset, SeekOrigin.Begin);
// Create the folders array
directory.Folders = new Folder5[directoryHeader.FolderCount];
// Try to parse the folders
for (int i = 0; i < directory.Folders.Length; i++)
{
directory.Folders[i] = ParseFolder5(data);
}
#endregion
#region Files
// Get and adjust the files offset
long fileOffset = directoryHeader.FileOffset;
fileOffset += currentOffset;
// Validate the offset
if (fileOffset < 0 || fileOffset >= data.Length)
return null;
// Seek to the files
data.Seek(fileOffset, SeekOrigin.Begin);
// Create the files array
directory.Files = new File4[directoryHeader.FileCount];
// Try to parse the files
for (int i = 0; i < directory.Files.Length; i++)
{
directory.Files[i] = ParseFile4(data);
}
#endregion
#region String Table
// Get and adjust the string table offset
long stringTableOffset = directoryHeader.StringTableOffset;
stringTableOffset += currentOffset;
// Validate the offset
if (stringTableOffset < 0 || stringTableOffset >= data.Length)
return null;
// Seek to the string table
data.Seek(stringTableOffset, SeekOrigin.Begin);
// TODO: Are these strings actually indexed by number and not position?
// TODO: If indexed by position, I think it needs to be adjusted by start of table
// Create the strings dictionary
directory.StringTable = new Dictionary<long, string?>((int)directoryHeader.StringTableCount);
// Get the current position to adjust the offsets
long stringTableStart = data.Position;
// Try to parse the strings
for (int i = 0; i < directoryHeader.StringTableCount; i++)
{
long currentPosition = data.Position - stringTableStart;
directory.StringTable[currentPosition] = data.ReadNullTerminatedAnsiString();
}
// Loop through all folders to assign names
for (int i = 0; i < directory.Folders.Length; i++)
{
var folder = directory.Folders[i];
if (folder == null)
continue;
folder.Name = directory.StringTable[folder.NameOffset];
}
// Loop through all files to assign names
for (int i = 0; i < directory.Files.Length; i++)
{
var file = directory.Files[i];
if (file == null)
continue;
file.Name = directory.StringTable[file.NameOffset];
}
#endregion
return directory;
}
/// <summary>
/// Parse a Stream into an SGA directory
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SGA directory on success, null on error</returns>
private static Directory6? ParseDirectory6(Stream data)
{
var directory = new Directory6();
// Cache the current offset
long currentOffset = data.Position;
#region Directory Header
// Try to parse the directory header
var directoryHeader = ParseDirectory5Header(data);
if (directoryHeader == null)
return null;
// Set the directory header
directory.DirectoryHeader = directoryHeader;
#endregion
#region Sections
// Get and adjust the sections offset
long sectionOffset = directoryHeader.SectionOffset;
sectionOffset += currentOffset;
// Validate the offset
if (sectionOffset < 0 || sectionOffset >= data.Length)
return null;
// Seek to the sections
data.Seek(sectionOffset, SeekOrigin.Begin);
// Create the sections array
directory.Sections = new Section5[directoryHeader.SectionCount];
// Try to parse the sections
for (int i = 0; i < directory.Sections.Length; i++)
{
directory.Sections[i] = ParseSection5(data);
}
#endregion
#region Folders
// Get and adjust the folders offset
long folderOffset = directoryHeader.FolderOffset;
folderOffset += currentOffset;
// Validate the offset
if (folderOffset < 0 || folderOffset >= data.Length)
return null;
// Seek to the folders
data.Seek(folderOffset, SeekOrigin.Begin);
// Create the folders array
directory.Folders = new Folder5[directoryHeader.FolderCount];
// Try to parse the folders
for (int i = 0; i < directory.Folders.Length; i++)
{
directory.Folders[i] = ParseFolder5(data);
}
#endregion
#region Files
// Get and adjust the files offset
long fileOffset = directoryHeader.FileOffset;
fileOffset += currentOffset;
// Validate the offset
if (fileOffset < 0 || fileOffset >= data.Length)
return null;
// Seek to the files
data.Seek(fileOffset, SeekOrigin.Begin);
// Create the files array
directory.Files = new File6[directoryHeader.FileCount];
// Try to parse the files
for (int i = 0; i < directory.Files.Length; i++)
{
directory.Files[i] = ParseFile6(data);
}
#endregion
#region String Table
// Get and adjust the string table offset
long stringTableOffset = directoryHeader.StringTableOffset;
stringTableOffset += currentOffset;
// Validate the offset
if (stringTableOffset < 0 || stringTableOffset >= data.Length)
return null;
// Seek to the string table
data.Seek(stringTableOffset, SeekOrigin.Begin);
// TODO: Are these strings actually indexed by number and not position?
// TODO: If indexed by position, I think it needs to be adjusted by start of table
// Create the strings dictionary
directory.StringTable = new Dictionary<long, string?>((int)directoryHeader.StringTableCount);
// Get the current position to adjust the offsets
long stringTableStart = data.Position;
// Try to parse the strings
for (int i = 0; i < directoryHeader.StringTableCount; i++)
{
long currentPosition = data.Position - stringTableStart;
directory.StringTable[currentPosition] = data.ReadNullTerminatedAnsiString();
}
// Loop through all folders to assign names
for (int i = 0; i < directory.Folders.Length; i++)
{
var folder = directory.Folders[i];
if (folder == null)
continue;
folder.Name = directory.StringTable[folder.NameOffset];
}
// Loop through all files to assign names
for (int i = 0; i < directory.Files.Length; i++)
{
var file = directory.Files[i];
if (file == null)
continue;
file.Name = directory.StringTable[file.NameOffset];
}
#endregion
return directory;
}
/// <summary>
/// Parse a Stream into an SGA directory
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SGA directory on success, null on error</returns>
private static Directory7? ParseDirectory7(Stream data)
{
var directory = new Directory7();
// Cache the current offset
long currentOffset = data.Position;
#region Directory Header
// Try to parse the directory header
var directoryHeader = ParseDirectory7Header(data);
if (directoryHeader == null)
return null;
// Set the directory header
directory.DirectoryHeader = directoryHeader;
#endregion
#region Sections
// Get and adjust the sections offset
long sectionOffset = directoryHeader.SectionOffset;
sectionOffset += currentOffset;
// Validate the offset
if (sectionOffset < 0 || sectionOffset >= data.Length)
return null;
// Seek to the sections
data.Seek(sectionOffset, SeekOrigin.Begin);
// Create the sections array
directory.Sections = new Section5[directoryHeader.SectionCount];
// Try to parse the sections
for (int i = 0; i < directory.Sections.Length; i++)
{
directory.Sections[i] = ParseSection5(data);
}
#endregion
#region Folders
// Get and adjust the folders offset
long folderOffset = directoryHeader.FolderOffset;
folderOffset += currentOffset;
// Validate the offset
if (folderOffset < 0 || folderOffset >= data.Length)
return null;
// Seek to the folders
data.Seek(folderOffset, SeekOrigin.Begin);
// Create the folders array
directory.Folders = new Folder5[directoryHeader.FolderCount];
// Try to parse the folders
for (int i = 0; i < directory.Folders.Length; i++)
{
directory.Folders[i] = ParseFolder5(data);
}
#endregion
#region Files
// Get and adjust the files offset
long fileOffset = directoryHeader.FileOffset;
fileOffset += currentOffset;
// Validate the offset
if (fileOffset < 0 || fileOffset >= data.Length)
return null;
// Seek to the files
data.Seek(fileOffset, SeekOrigin.Begin);
// Create the files array
directory.Files = new File7[directoryHeader.FileCount];
// Try to parse the files
for (int i = 0; i < directory.Files.Length; i++)
{
directory.Files[i] = ParseFile7(data);
}
#endregion
#region String Table
// Get and adjust the string table offset
long stringTableOffset = directoryHeader.StringTableOffset;
stringTableOffset += currentOffset;
// Validate the offset
if (stringTableOffset < 0 || stringTableOffset >= data.Length)
return null;
// Seek to the string table
data.Seek(stringTableOffset, SeekOrigin.Begin);
// TODO: Are these strings actually indexed by number and not position?
// TODO: If indexed by position, I think it needs to be adjusted by start of table
// Create the strings dictionary
directory.StringTable = new Dictionary<long, string?>((int)directoryHeader.StringTableCount);
// Get the current position to adjust the offsets
long stringTableStart = data.Position;
// Try to parse the strings
for (int i = 0; i < directoryHeader.StringTableCount; i++)
{
long currentPosition = data.Position - stringTableStart;
directory.StringTable[currentPosition] = data.ReadNullTerminatedAnsiString();
}
// Loop through all folders to assign names
for (int i = 0; i < directory.Folders.Length; i++)
{
var folder = directory.Folders[i];
if (folder == null)
continue;
folder.Name = directory.StringTable[folder.NameOffset];
}
// Loop through all files to assign names
for (int i = 0; i < directory.Files.Length; i++)
{
var file = directory.Files[i];
if (file == null)
continue;
file.Name = directory.StringTable[file.NameOffset];
}
#endregion
return directory;
}
/// <summary>
@@ -508,7 +742,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA directory header version 4 on success, null on error</returns>
private static DirectoryHeader4 ParseDirectory4Header(Stream data)
{
DirectoryHeader4 directoryHeader4 = new DirectoryHeader4();
var directoryHeader4 = new DirectoryHeader4();
directoryHeader4.SectionOffset = data.ReadUInt32();
directoryHeader4.SectionCount = data.ReadUInt16();
@@ -529,7 +763,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA directory header version 5 on success, null on error</returns>
private static DirectoryHeader5 ParseDirectory5Header(Stream data)
{
DirectoryHeader5 directoryHeader5 = new DirectoryHeader5();
var directoryHeader5 = new DirectoryHeader5();
directoryHeader5.SectionOffset = data.ReadUInt32();
directoryHeader5.SectionCount = data.ReadUInt32();
@@ -550,7 +784,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA directory header version 7 on success, null on error</returns>
private static DirectoryHeader7 ParseDirectory7Header(Stream data)
{
DirectoryHeader7 directoryHeader7 = new DirectoryHeader7();
var directoryHeader7 = new DirectoryHeader7();
directoryHeader7.SectionOffset = data.ReadUInt32();
directoryHeader7.SectionCount = data.ReadUInt32();
@@ -574,7 +808,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA section version 4 on success, null on error</returns>
private static Section4 ParseSection4(Stream data)
{
Section4 section4 = new Section4();
var section4 = new Section4();
byte[]? section4Alias = data.ReadBytes(64);
if (section4Alias != null)
@@ -599,7 +833,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA section version 5 on success, null on error</returns>
private static Section5 ParseSection5(Stream data)
{
Section5 section5 = new Section5();
var section5 = new Section5();
byte[]? section5Alias = data.ReadBytes(64);
if (section5Alias != null)
@@ -624,7 +858,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA folder version 4 on success, null on error</returns>
private static Folder4 ParseFolder4(Stream data)
{
Folder4 folder4 = new Folder4();
var folder4 = new Folder4();
folder4.NameOffset = data.ReadUInt32();
folder4.Name = null; // Read from string table
@@ -644,7 +878,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA folder version 5 on success, null on error</returns>
private static Folder5 ParseFolder5(Stream data)
{
Folder5 folder5 = new Folder5();
var folder5 = new Folder5();
folder5.NameOffset = data.ReadUInt32();
folder5.Name = null; // Read from string table
@@ -664,7 +898,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA file version 4 on success, null on error</returns>
private static File4 ParseFile4(Stream data)
{
File4 file4 = new File4();
var file4 = new File4();
file4.NameOffset = data.ReadUInt32();
file4.Name = null; // Read from string table
@@ -686,7 +920,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA file version 6 on success, null on error</returns>
private static File6 ParseFile6(Stream data)
{
File6 file6 = new File6();
var file6 = new File6();
file6.NameOffset = data.ReadUInt32();
file6.Name = null; // Read from string table
@@ -709,7 +943,7 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled SGA file version 7 on success, null on error</returns>
private static File7 ParseFile7(Stream data)
{
File7 file7 = new File7();
var file7 = new File7();
file7.NameOffset = data.ReadUInt32();
file7.Name = null; // Read from string table

File diff suppressed because it is too large Load Diff

View File

@@ -19,9 +19,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new Half-Life Texture Package to fill
var file = new Models.WAD3.File();

View File

@@ -1,5 +1,4 @@
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.XZP;
using static SabreTools.Models.XZP.Constants;
@@ -19,9 +18,6 @@ namespace SabreTools.Serialization.Deserializers
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new XBox Package File to fill
var file = new Models.XZP.File();
@@ -43,11 +39,11 @@ namespace SabreTools.Serialization.Deserializers
file.DirectoryEntries = new DirectoryEntry[header.DirectoryEntryCount];
// Try to parse the directory entries
for (int i = 0; i < header.DirectoryEntryCount; i++)
for (int i = 0; i < file.DirectoryEntries.Length; i++)
{
var directoryEntry = ParseDirectoryEntry(data);
if (directoryEntry == null)
return null;
continue;
file.DirectoryEntries[i] = directoryEntry;
}
@@ -62,11 +58,11 @@ namespace SabreTools.Serialization.Deserializers
file.PreloadDirectoryEntries = new DirectoryEntry[header.PreloadDirectoryEntryCount];
// Try to parse the preload directory entries
for (int i = 0; i < header.PreloadDirectoryEntryCount; i++)
for (int i = 0; i < file.PreloadDirectoryEntries.Length; i++)
{
var directoryEntry = ParseDirectoryEntry(data);
if (directoryEntry == null)
return null;
continue;
file.PreloadDirectoryEntries[i] = directoryEntry;
}
@@ -82,11 +78,11 @@ namespace SabreTools.Serialization.Deserializers
file.PreloadDirectoryMappings = new DirectoryMapping[header.PreloadDirectoryEntryCount];
// Try to parse the preload directory mappings
for (int i = 0; i < header.PreloadDirectoryEntryCount; i++)
for (int i = 0; i < file.PreloadDirectoryMappings.Length; i++)
{
var directoryMapping = ParseDirectoryMapping(data);
if (directoryMapping == null)
return null;
continue;
file.PreloadDirectoryMappings[i] = directoryMapping;
}
@@ -110,7 +106,7 @@ namespace SabreTools.Serialization.Deserializers
file.DirectoryItems = new DirectoryItem[header.DirectoryItemCount];
// Try to parse the directory items
for (int i = 0; i < header.DirectoryItemCount; i++)
for (int i = 0; i < file.DirectoryItems.Length; i++)
{
var directoryItem = ParseDirectoryItem(data);
file.DirectoryItems[i] = directoryItem;
@@ -146,9 +142,7 @@ namespace SabreTools.Serialization.Deserializers
{
var header = data.ReadType<Header>();
if (header == null)
return null;
if (header.Signature != HeaderSignatureString)
if (header?.Signature != HeaderSignatureString)
return null;
if (header.Version != 6)
return null;
@@ -214,9 +208,7 @@ namespace SabreTools.Serialization.Deserializers
{
var footer = data.ReadType<Footer>();
if (footer == null)
return null;
if (footer.Signature != FooterSignatureString)
if (footer?.Signature != FooterSignatureString)
return null;
return footer;

View File

@@ -21,7 +21,7 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine(svm.Day, "Day");
builder.AppendLine(svm.Unknown2, "Unknown 2");
builder.AppendLine(svm.Length, "Length");
//builder.AppendLine(svm.Data, "Data");
builder.AppendLine(svm.Length, "Data skipped...");
builder.AppendLine();
}
}

View File

@@ -63,261 +63,53 @@ namespace SabreTools.Serialization.Printers
switch ((LumpType)i)
{
case LumpType.LUMP_ENTITIES:
if (model.Entities?.Entities == null || model.Entities.Entities.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.Entities.Entities.Length; j++)
{
// TODO: Implement entity printing
var entity = model.Entities.Entities[j];
builder.AppendLine($" Entity {j}");
builder.AppendLine(" Entity data is not parsed properly");
}
}
Print(builder, model.Entities);
break;
case LumpType.LUMP_PLANES:
if (model.PlanesLump?.Planes == null || model.PlanesLump.Planes.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.PlanesLump.Planes.Length; j++)
{
var plane = model.PlanesLump.Planes[j];
builder.AppendLine($" Plane {j}");
builder.AppendLine($" Normal vector: {plane.NormalVector.X}, {plane.NormalVector.Y}, {plane.NormalVector.Z}");
builder.AppendLine(plane.Distance, " Distance");
builder.AppendLine($" Plane type: {plane.PlaneType} (0x{plane.PlaneType:X})");
}
}
Print(builder, model.PlanesLump);
break;
case LumpType.LUMP_TEXTURES:
if (model.TextureLump?.Textures == null || model.TextureLump.Textures.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
var header = model.TextureLump.Header;
if (header == null)
{
builder.AppendLine(" No texture header");
}
else
{
builder.AppendLine(" Texture Header:");
builder.AppendLine(header.MipTextureCount, " MipTexture count");
builder.AppendLine(header.Offsets, " Offsets");
}
builder.AppendLine(" Textures:");
for (int j = 0; j < model.TextureLump.Textures.Length; j++)
{
var texture = model.TextureLump.Textures[j];
builder.AppendLine($" Texture {j}");
builder.AppendLine(texture.Name, " Name");
builder.AppendLine(texture.Width, " Width");
builder.AppendLine(texture.Height, " Height");
builder.AppendLine(texture.Offsets, " Offsets");
}
}
Print(builder, model.TextureLump);
break;
case LumpType.LUMP_VERTICES:
if (model.VerticesLump?.Vertices == null || model.VerticesLump.Vertices.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.VerticesLump.Vertices.Length; j++)
{
var vertex = model.VerticesLump.Vertices[j];
builder.AppendLine($" Vertex {j}: {vertex.X}, {vertex.Y}, {vertex.Z}");
}
}
Print(builder, model.VerticesLump);
break;
case LumpType.LUMP_VISIBILITY:
// TODO: Implement when added to Models
// if (model.VisibilityLump == null)
// {
// builder.AppendLine(" No data");
// }
// else
// {
// builder.AppendLine(model.VisibilityLump.NumClusters, " Cluster count");
// builder.AppendLine(model.VisibilityLump.ByteOffsets, " Byte offsets");
// }
// Print(builder, model.VisibilityLump);
break;
case LumpType.LUMP_NODES:
if (model.NodesLump?.Nodes == null || model.NodesLump.Nodes.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.NodesLump.Nodes.Length; j++)
{
var node = model.NodesLump.Nodes[j];
builder.AppendLine($" Node {j}");
builder.AppendLine(node.Children, " Children");
builder.AppendLine(node.Mins, " Mins");
builder.AppendLine(node.Maxs, " Maxs");
builder.AppendLine(node.FirstFace, " First face index");
builder.AppendLine(node.FaceCount, " Count of faces");
}
}
Print(builder, model.NodesLump);
break;
case LumpType.LUMP_TEXINFO:
if (model.TexinfoLump?.Texinfos == null || model.TexinfoLump.Texinfos.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.TexinfoLump.Texinfos.Length; j++)
{
var texinfo = model.TexinfoLump.Texinfos[j];
builder.AppendLine($" Texinfo {j}");
builder.AppendLine($" S-Vector: {texinfo.SVector.X}, {texinfo.SVector.Y}, {texinfo.SVector.Z}");
builder.AppendLine(texinfo.TextureSShift, " Texture shift in S direction");
builder.AppendLine($" T-Vector: {texinfo.TVector.X}, {texinfo.TVector.Y}, {texinfo.TVector.Z}");
builder.AppendLine(texinfo.TextureTShift, " Texture shift in T direction");
builder.AppendLine(texinfo.MiptexIndex, " Miptex index");
builder.AppendLine($" Flags: {texinfo.Flags} (0x{texinfo.Flags:X})");
}
}
Print(builder, model.TexinfoLump);
break;
case LumpType.LUMP_FACES:
if (model.FacesLump?.Faces == null || model.FacesLump.Faces.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.FacesLump.Faces.Length; j++)
{
var face = model.FacesLump.Faces[j];
builder.AppendLine($" Face {j}");
builder.AppendLine(face.PlaneIndex, " Plane index");
builder.AppendLine(face.PlaneSideCount, " Plane side count");
builder.AppendLine(face.FirstEdgeIndex, " First surfedge index");
builder.AppendLine(face.NumberOfEdges, " Surfedge count");
builder.AppendLine(face.TextureInfoIndex, " Texture info index");
builder.AppendLine(face.LightingStyles, " Lighting styles");
builder.AppendLine(face.LightmapOffset, " Lightmap offset");
}
}
Print(builder, model.FacesLump);
break;
case LumpType.LUMP_LIGHTING:
if (model.LightmapLump?.Lightmap == null || model.LightmapLump.Lightmap.Length == 0)
builder.AppendLine(" No data");
else
builder.AppendLine(" Lightmap data skipped...");
Print(builder, model.LightmapLump);
break;
case LumpType.LUMP_CLIPNODES:
if (model.ClipnodesLump?.Clipnodes == null || model.ClipnodesLump.Clipnodes.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.ClipnodesLump.Clipnodes.Length; j++)
{
var clipnode = model.ClipnodesLump.Clipnodes[j];
builder.AppendLine($" Clipnode {j}");
builder.AppendLine(clipnode.PlaneIndex, " Plane index");
builder.AppendLine(clipnode.ChildrenIndices, " Children indices");
}
}
Print(builder, model.ClipnodesLump);
break;
case LumpType.LUMP_LEAVES:
if (model.LeavesLump?.Leaves == null || model.LeavesLump.Leaves.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.LeavesLump.Leaves.Length; j++)
{
var leaf = model.LeavesLump.Leaves[j];
builder.AppendLine($" Leaf {j}");
builder.AppendLine($" Contents: {leaf.Contents} (0x{leaf.Contents:X})");
builder.AppendLine(leaf.VisOffset, " Visibility offset");
builder.AppendLine(leaf.Mins, " Mins");
builder.AppendLine(leaf.Maxs, " Maxs");
builder.AppendLine(leaf.FirstMarkSurfaceIndex, " First marksurface index");
builder.AppendLine(leaf.MarkSurfacesCount, " Marksurfaces count");
builder.AppendLine(leaf.AmbientLevels, " Ambient sound levels");
}
}
Print(builder, model.LeavesLump);
break;
case LumpType.LUMP_MARKSURFACES:
if (model.MarksurfacesLump?.Marksurfaces == null || model.MarksurfacesLump.Marksurfaces.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.MarksurfacesLump.Marksurfaces.Length; j++)
{
var marksurface = model.MarksurfacesLump.Marksurfaces[j];
builder.AppendLine($" Marksurface {j}: {marksurface} (0x{marksurface:X4})");
}
}
Print(builder, model.MarksurfacesLump);
break;
case LumpType.LUMP_EDGES:
if (model.EdgesLump?.Edges == null || model.EdgesLump.Edges.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.EdgesLump.Edges.Length; j++)
{
var edge = model.EdgesLump.Edges[j];
builder.AppendLine($" Edge {j}");
builder.AppendLine(edge.VertexIndices, " Vertex indices");
}
}
Print(builder, model.EdgesLump);
break;
case LumpType.LUMP_SURFEDGES:
if (model.SurfedgesLump?.Surfedges == null || model.SurfedgesLump.Surfedges.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.SurfedgesLump.Surfedges.Length; j++)
{
var surfedge = model.SurfedgesLump.Surfedges[j];
builder.AppendLine($" Surfedge {j}: {surfedge} (0x{surfedge:X4})");
}
}
Print(builder, model.SurfedgesLump);
break;
case LumpType.LUMP_MODELS:
if (model.ModelsLump?.Models == null || model.ModelsLump.Models.Length == 0)
{
builder.AppendLine(" No data");
}
else
{
for (int j = 0; j < model.ModelsLump.Models.Length; j++)
{
var bmodel = model.ModelsLump.Models[j];
builder.AppendLine($" Model {j}");
builder.AppendLine($" Mins: {bmodel.Mins.X}, {bmodel.Mins.Y}, {bmodel.Mins.Z}");
builder.AppendLine($" Maxs: {bmodel.Maxs.X}, {bmodel.Maxs.Y}, {bmodel.Maxs.Z}");
builder.AppendLine($" Origin vector: {bmodel.OriginVector.X}, {bmodel.OriginVector.Y}, {bmodel.OriginVector.Z}");
builder.AppendLine(bmodel.HeadnodesIndex, " Headnodes index");
builder.AppendLine(bmodel.VisLeafsCount, " ??? (VisLeafsCount)");
builder.AppendLine(bmodel.FirstFaceIndex, " First face index");
builder.AppendLine(bmodel.FacesCount, " Faces count");
}
}
Print(builder, model.ModelsLump);
break;
default:
builder.AppendLine($" Unsupported lump type: {(LumpType)i} (0x{i:X4})");
break;
}
}
@@ -346,5 +138,283 @@ namespace SabreTools.Serialization.Printers
_ => string.Empty,
};
}
private static void Print(StringBuilder builder, EntitiesLump? lump)
{
if (lump?.Entities == null || lump.Entities.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Entities.Length; j++)
{
// TODO: Implement entity printing
var entity = lump.Entities[j];
builder.AppendLine($" Entity {j}");
builder.AppendLine(" Entity data is not parsed properly");
}
}
private static void Print(StringBuilder builder, PlanesLump? lump)
{
if (lump?.Planes == null || lump.Planes.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Planes.Length; j++)
{
var plane = lump.Planes[j];
builder.AppendLine($" Plane {j}");
builder.AppendLine($" Normal vector: ({plane.NormalVector.X}, {plane.NormalVector.Y}, {plane.NormalVector.Z})");
builder.AppendLine(plane.Distance, " Distance");
builder.AppendLine($" Plane type: {plane.PlaneType} (0x{plane.PlaneType:X})");
}
}
private static void Print(StringBuilder builder, TextureLump? lump)
{
if (lump == null)
{
builder.AppendLine(" No data");
return;
}
if (lump?.Header == null)
{
builder.AppendLine(" No texture header");
}
else
{
builder.AppendLine(" Texture Header:");
builder.AppendLine(lump.Header.MipTextureCount, " MipTexture count");
builder.AppendLine(lump.Header.Offsets, " Offsets");
}
if (lump?.Textures == null || lump.Textures.Length == 0)
{
builder.AppendLine(" No texture data");
}
else
{
builder.AppendLine(" Textures:");
for (int j = 0; j < lump.Textures.Length; j++)
{
var texture = lump.Textures[j];
builder.AppendLine($" Texture {j}");
builder.AppendLine(texture.Name, " Name");
builder.AppendLine(texture.Width, " Width");
builder.AppendLine(texture.Height, " Height");
builder.AppendLine(texture.Offsets, " Offsets");
}
}
}
private static void Print(StringBuilder builder, VerticesLump? lump)
{
if (lump?.Vertices == null || lump.Vertices.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Vertices.Length; j++)
{
var vertex = lump.Vertices[j];
builder.AppendLine($" Vertex {j}: ({vertex.X}, {vertex.Y}, {vertex.Z})");
}
}
private static void Print(StringBuilder builder, VisibilityLump? lump)
{
if (lump == null)
{
builder.AppendLine(" No data");
return;
}
builder.AppendLine(lump.NumClusters, " Cluster count");
builder.AppendLine(" Byte offsets skipped...");
}
private static void Print(StringBuilder builder, BspNodesLump? lump)
{
if (lump?.Nodes == null || lump.Nodes.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Nodes.Length; j++)
{
var node = lump.Nodes[j];
builder.AppendLine($" Node {j}");
builder.AppendLine(node.Children, " Children");
builder.AppendLine(node.Mins, " Mins");
builder.AppendLine(node.Maxs, " Maxs");
builder.AppendLine(node.FirstFace, " First face index");
builder.AppendLine(node.FaceCount, " Count of faces");
}
}
private static void Print(StringBuilder builder, BspTexinfoLump? lump)
{
if (lump?.Texinfos == null || lump.Texinfos.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Texinfos.Length; j++)
{
var texinfo = lump.Texinfos[j];
builder.AppendLine($" Texinfo {j}");
builder.AppendLine($" S-Vector: ({texinfo.SVector.X}, {texinfo.SVector.Y}, {texinfo.SVector.Z})");
builder.AppendLine(texinfo.TextureSShift, " Texture shift in S direction");
builder.AppendLine($" T-Vector: ({texinfo.TVector.X}, {texinfo.TVector.Y}, {texinfo.TVector.Z})");
builder.AppendLine(texinfo.TextureTShift, " Texture shift in T direction");
builder.AppendLine(texinfo.MiptexIndex, " Miptex index");
builder.AppendLine($" Flags: {texinfo.Flags} (0x{texinfo.Flags:X})");
}
}
private static void Print(StringBuilder builder, BspFacesLump? lump)
{
if (lump?.Faces == null || lump.Faces.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Faces.Length; j++)
{
var face = lump.Faces[j];
builder.AppendLine($" Face {j}");
builder.AppendLine(face.PlaneIndex, " Plane index");
builder.AppendLine(face.PlaneSideCount, " Plane side count");
builder.AppendLine(face.FirstEdgeIndex, " First surfedge index");
builder.AppendLine(face.NumberOfEdges, " Surfedge count");
builder.AppendLine(face.TextureInfoIndex, " Texture info index");
builder.AppendLine(face.LightingStyles, " Lighting styles");
builder.AppendLine(face.LightmapOffset, " Lightmap offset");
}
}
private static void Print(StringBuilder builder, LightmapLump? lump)
{
if (lump?.Lightmap == null || lump.Lightmap.Length == 0)
builder.AppendLine(" No data");
else
builder.AppendLine(" Lightmap data skipped...");
}
private static void Print(StringBuilder builder, ClipnodesLump? lump)
{
if (lump?.Clipnodes == null || lump.Clipnodes.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Clipnodes.Length; j++)
{
var clipnode = lump.Clipnodes[j];
builder.AppendLine($" Clipnode {j}");
builder.AppendLine(clipnode.PlaneIndex, " Plane index");
builder.AppendLine(clipnode.ChildrenIndices, " Children indices");
}
}
private static void Print(StringBuilder builder, BspLeavesLump? lump)
{
if (lump?.Leaves == null || lump.Leaves.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Leaves.Length; j++)
{
var leaf = lump.Leaves[j];
builder.AppendLine($" Leaf {j}");
builder.AppendLine($" Contents: {leaf.Contents} (0x{leaf.Contents:X})");
builder.AppendLine(leaf.VisOffset, " Visibility offset");
builder.AppendLine(leaf.Mins, " Mins");
builder.AppendLine(leaf.Maxs, " Maxs");
builder.AppendLine(leaf.FirstMarkSurfaceIndex, " First marksurface index");
builder.AppendLine(leaf.MarkSurfacesCount, " Marksurfaces count");
builder.AppendLine(leaf.AmbientLevels, " Ambient sound levels");
}
}
private static void Print(StringBuilder builder, MarksurfacesLump? lump)
{
if (lump?.Marksurfaces == null || lump.Marksurfaces.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Marksurfaces.Length; j++)
{
var marksurface = lump.Marksurfaces[j];
builder.AppendLine(marksurface, $" Marksurface {j}");
}
}
private static void Print(StringBuilder builder, EdgesLump? lump)
{
if (lump?.Edges == null || lump.Edges.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Edges.Length; j++)
{
var edge = lump.Edges[j];
builder.AppendLine($" Edge {j}");
builder.AppendLine(edge.VertexIndices, " Vertex indices");
}
}
private static void Print(StringBuilder builder, SurfedgesLump? lump)
{
if (lump?.Surfedges == null || lump.Surfedges.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Surfedges.Length; j++)
{
var surfedge = lump.Surfedges[j];
builder.AppendLine(surfedge, $" Surfedge {j}");
}
}
private static void Print(StringBuilder builder, BspModelsLump? lump)
{
if (lump?.Models == null || lump.Models.Length == 0)
{
builder.AppendLine(" No data");
return;
}
for (int j = 0; j < lump.Models.Length; j++)
{
var bmodel = lump.Models[j];
builder.AppendLine($" Model {j}");
builder.AppendLine($" Mins: {bmodel.Mins.X}, {bmodel.Mins.Y}, {bmodel.Mins.Z}");
builder.AppendLine($" Maxs: {bmodel.Maxs.X}, {bmodel.Maxs.Y}, {bmodel.Maxs.Z}");
builder.AppendLine($" Origin vector: {bmodel.OriginVector.X}, {bmodel.OriginVector.Y}, {bmodel.OriginVector.Z}");
builder.AppendLine(bmodel.HeadnodesIndex, " Headnodes index");
builder.AppendLine(bmodel.VisLeafsCount, " ??? (VisLeafsCount)");
builder.AppendLine(bmodel.FirstFaceIndex, " First face index");
builder.AppendLine(bmodel.FacesCount, " Faces count");
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -236,6 +236,21 @@ namespace SabreTools.Serialization
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Single[] value to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, float[]? value, string prefixString)
{
string valueString = "[NULL]";
if (value != null)
{
var valueArr = Array.ConvertAll(value, u => u.ToString());
valueString = string.Join(", ", valueArr);
}
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Int64[] value to a StringBuilder
/// </summary>
@@ -266,6 +281,21 @@ namespace SabreTools.Serialization
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Double[] value to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, double[]? value, string prefixString)
{
string valueString = "[NULL]";
if (value != null)
{
var valueArr = Array.ConvertAll(value, u => u.ToString());
valueString = string.Join(", ", valueArr);
}
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a UInt64[] value to a StringBuilder
/// </summary>