mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-04-28 01:00:06 +00:00
Update Models to 1.5.2
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.5.1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.5.2" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
@@ -6,10 +7,10 @@ using static SabreTools.Models.BSP.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class BSP : BaseBinaryDeserializer<Models.BSP.File>
|
||||
public class BSP : BaseBinaryDeserializer<BspFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Models.BSP.File? Deserialize(Stream? data)
|
||||
public override BspFile? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
@@ -23,13 +24,13 @@ namespace SabreTools.Serialization.Deserializers
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new Half-Life Level to fill
|
||||
var file = new Models.BSP.File();
|
||||
var file = new BspFile();
|
||||
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
if (header?.Lumps == null)
|
||||
return null;
|
||||
|
||||
// Set the level header
|
||||
@@ -39,59 +40,326 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
#region Lumps
|
||||
|
||||
// Create the lump array
|
||||
file.Lumps = new Lump[HL_BSP_LUMP_COUNT];
|
||||
|
||||
// Try to parse the lumps
|
||||
for (int i = 0; i < HL_BSP_LUMP_COUNT; i++)
|
||||
// LUMP_ENTITIES [0]
|
||||
var lumpEntry = header.Lumps[(int)LumpType.LUMP_ENTITIES];
|
||||
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
|
||||
{
|
||||
var lump = ParseLump(data);
|
||||
if (lump == null)
|
||||
return null;
|
||||
// Seek to the lump offset
|
||||
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
|
||||
|
||||
file.Lumps[i] = lump;
|
||||
// Read the lump data
|
||||
var entities = new List<Entity>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
var lump = new EntitiesLump();
|
||||
lump.Entities = [.. entities];
|
||||
|
||||
file.Entities = lump;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Texture header
|
||||
|
||||
// Try to get the texture header lump
|
||||
var textureDataLump = file.Lumps[HL_BSP_LUMP_TEXTUREDATA];
|
||||
if (textureDataLump == null || textureDataLump.Offset == 0 || textureDataLump.Length == 0)
|
||||
return null;
|
||||
|
||||
// Seek to the texture header
|
||||
data.Seek(textureDataLump.Offset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the texture header
|
||||
var textureHeader = ParseTextureHeader(data);
|
||||
if (textureHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the texture header
|
||||
file.TextureHeader = textureHeader;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Textures
|
||||
|
||||
// Create the texture array
|
||||
file.Textures = new Texture[textureHeader.TextureCount];
|
||||
|
||||
// Try to parse the textures
|
||||
for (int i = 0; i < textureHeader.TextureCount; i++)
|
||||
// LUMP_PLANES [1]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_PLANES];
|
||||
if (lumpEntry != null && lumpEntry.Offset > 0 && lumpEntry.Length > 0)
|
||||
{
|
||||
// Get the texture offset
|
||||
int offset = (int)(textureHeader.Offsets![i] + file.Lumps[HL_BSP_LUMP_TEXTUREDATA]!.Offset);
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
continue;
|
||||
// Seek to the lump offset
|
||||
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
|
||||
|
||||
// Seek to the texture
|
||||
data.Seek(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 texture = ParseTexture(data);
|
||||
file.Textures[i] = texture;
|
||||
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
|
||||
@@ -105,28 +373,24 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Level header on success, null on error</returns>
|
||||
/// <remarks>Only recognized versions are 29 and 30</remarks>
|
||||
private static Header? ParseHeader(Stream data)
|
||||
private static BspHeader? ParseHeader(Stream data)
|
||||
{
|
||||
var header = data.ReadType<Header>();
|
||||
// TODO: Use marshalling here later
|
||||
var header = new BspHeader();
|
||||
|
||||
if (header == null)
|
||||
return null;
|
||||
if (header.Version != 29 && header.Version != 30)
|
||||
header.Version = data.ReadInt32();
|
||||
if (header.Version < 29 || header.Version > 30)
|
||||
return null;
|
||||
|
||||
header.Lumps = new BspLumpEntry[BSP_HEADER_LUMPS];
|
||||
for (int i = 0; i < BSP_HEADER_LUMPS; i++)
|
||||
{
|
||||
header.Lumps[i] = data.ReadType<BspLumpEntry>()!;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a lump
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled lump on success, null on error</returns>
|
||||
private static Lump? ParseLump(Stream data)
|
||||
{
|
||||
return data.ReadType<Lump>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Level texture header
|
||||
/// </summary>
|
||||
@@ -137,80 +401,14 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// TODO: Use marshalling here instead of building
|
||||
var textureHeader = new TextureHeader();
|
||||
|
||||
textureHeader.TextureCount = data.ReadUInt32();
|
||||
|
||||
var offsets = new uint[textureHeader.TextureCount];
|
||||
|
||||
for (int i = 0; i < textureHeader.TextureCount; i++)
|
||||
textureHeader.MipTextureCount = data.ReadUInt32();
|
||||
textureHeader.Offsets = new int[textureHeader.MipTextureCount];
|
||||
for (int i = 0; i < textureHeader.Offsets.Length; i++)
|
||||
{
|
||||
offsets[i] = data.ReadUInt32();
|
||||
if (data.Position >= data.Length)
|
||||
break;
|
||||
textureHeader.Offsets[i] = data.ReadInt32();
|
||||
}
|
||||
|
||||
textureHeader.Offsets = offsets;
|
||||
|
||||
return textureHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a texture
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="mipmap">Mipmap level</param>
|
||||
/// <returns>Filled texture on success, null on error</returns>
|
||||
private static Texture ParseTexture(Stream data, uint mipmap = 0)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var texture = new Texture();
|
||||
|
||||
byte[]? name = data.ReadBytes(16);
|
||||
if (name != null)
|
||||
texture.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
texture.Width = data.ReadUInt32();
|
||||
texture.Height = data.ReadUInt32();
|
||||
texture.Offsets = new uint[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
texture.Offsets[i] = data.ReadUInt32();
|
||||
}
|
||||
|
||||
// Get the size of the pixel data
|
||||
uint pixelSize = 0;
|
||||
for (int i = 0; i < HL_BSP_MIPMAP_COUNT; i++)
|
||||
{
|
||||
if (texture.Offsets[i] != 0)
|
||||
{
|
||||
pixelSize += (texture.Width >> i) * (texture.Height >> i);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no pixel data
|
||||
if (pixelSize == 0)
|
||||
return texture;
|
||||
|
||||
texture.TextureData = data.ReadBytes((int)pixelSize);
|
||||
texture.PaletteSize = data.ReadUInt16();
|
||||
texture.PaletteData = data.ReadBytes((int)(texture.PaletteSize * 3));
|
||||
|
||||
// Adjust the dimensions based on mipmap level
|
||||
switch (mipmap)
|
||||
{
|
||||
case 1:
|
||||
texture.Width /= 2;
|
||||
texture.Height /= 2;
|
||||
break;
|
||||
case 2:
|
||||
texture.Width /= 4;
|
||||
texture.Height /= 4;
|
||||
break;
|
||||
case 3:
|
||||
texture.Width /= 8;
|
||||
texture.Height /= 8;
|
||||
break;
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.VBSP;
|
||||
using static SabreTools.Models.VBSP.Constants;
|
||||
using SabreTools.Models.BSP;
|
||||
using static SabreTools.Models.BSP.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class VBSP : BaseBinaryDeserializer<Models.VBSP.File>
|
||||
public class VBSP : BaseBinaryDeserializer<VbspFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Models.VBSP.File? Deserialize(Stream? data)
|
||||
public override VbspFile? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
@@ -23,13 +25,13 @@ namespace SabreTools.Serialization.Deserializers
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// Create a new Half-Life 2 Level to fill
|
||||
var file = new Models.VBSP.File();
|
||||
var file = new VbspFile();
|
||||
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
if (header?.Lumps == null)
|
||||
return null;
|
||||
|
||||
// Set the package header
|
||||
@@ -37,6 +39,808 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lumps
|
||||
|
||||
// LUMP_ENTITIES [0]
|
||||
var lumpEntry = header.Lumps[(int)LumpType.LUMP_ENTITIES];
|
||||
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 entities = new List<Entity>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
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_TEXDATA [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 lump data
|
||||
var texdatas = new List<Texdata>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var texdata = data.ReadType<Texdata>();
|
||||
if (texdata != null)
|
||||
texdatas.Add(texdata);
|
||||
}
|
||||
|
||||
var lump = new TexdataLump();
|
||||
lump.Texdatas = [.. texdatas];
|
||||
|
||||
file.TexdataLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_VERTEXES [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)
|
||||
{
|
||||
// Seek to the lump offset
|
||||
data.Seek(lumpEntry.Offset, SeekOrigin.Begin);
|
||||
|
||||
// Read the lump data
|
||||
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.ReadByteValue();
|
||||
}
|
||||
|
||||
file.VisibilityLump = lump;
|
||||
}
|
||||
|
||||
// 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<VbspNode>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var node = data.ReadType<VbspNode>();
|
||||
if (node != null)
|
||||
nodes.Add(node);
|
||||
}
|
||||
|
||||
var lump = new VbspNodesLump();
|
||||
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<VbspTexinfo>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var texinfo = data.ReadType<VbspTexinfo>();
|
||||
if (texinfo != null)
|
||||
texinfos.Add(texinfo);
|
||||
}
|
||||
|
||||
var lump = new VbspTexinfoLump();
|
||||
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<VbspFace>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var face = data.ReadType<VbspFace>();
|
||||
if (face != null)
|
||||
faces.Add(face);
|
||||
}
|
||||
|
||||
var lump = new VbspFacesLump();
|
||||
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_OCCLUSION [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 lump = new OcclusionLump();
|
||||
|
||||
lump.Count = data.ReadInt32();
|
||||
lump.Data = new OccluderData[lump.Count];
|
||||
for (int i = 0; i < lump.Count; i++)
|
||||
{
|
||||
var occluderData = data.ReadType<OccluderData>();
|
||||
if (occluderData != null)
|
||||
lump.Data[i] = occluderData;
|
||||
}
|
||||
lump.PolyDataCount = data.ReadInt32();
|
||||
lump.PolyData = new OccluderPolyData[lump.Count];
|
||||
for (int i = 0; i < lump.Count; i++)
|
||||
{
|
||||
var polyData = data.ReadType<OccluderPolyData>();
|
||||
if (polyData != null)
|
||||
lump.PolyData[i] = polyData;
|
||||
}
|
||||
lump.VertexIndexCount = data.ReadInt32();
|
||||
lump.VertexIndices = new int[lump.VertexIndexCount];
|
||||
for (int i = 0; i < lump.VertexIndexCount; i++)
|
||||
{
|
||||
lump.VertexIndices[i] = data.ReadInt32();
|
||||
}
|
||||
|
||||
file.OcclusionLump = 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<VbspLeaf>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
// TODO: Fix parsing between V0 and V1+
|
||||
var leaf = data.ReadType<VbspLeaf>();
|
||||
if (leaf != null)
|
||||
leaves.Add(leaf);
|
||||
}
|
||||
|
||||
var lump = new VbspLeavesLump();
|
||||
lump.Leaves = [.. leaves];
|
||||
|
||||
file.LeavesLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_FACEIDS [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<VbspModel>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var model = data.ReadType<VbspModel>();
|
||||
if (model != null)
|
||||
models.Add(model);
|
||||
}
|
||||
|
||||
var lump = new VbspModelsLump();
|
||||
lump.Models = [.. models];
|
||||
|
||||
file.ModelsLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_WORLDLIGHTS [15]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_WORLDLIGHTS];
|
||||
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 worldLights = new List<WorldLight>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var worldLight = data.ReadType<WorldLight>();
|
||||
if (worldLight != null)
|
||||
worldLights.Add(worldLight);
|
||||
}
|
||||
|
||||
var lump = new WorldLightsLump();
|
||||
lump.WorldLights = [.. worldLights];
|
||||
|
||||
file.LDRWorldLightsLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_LEAFFACES [16]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_LEAFFACES];
|
||||
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 map = new List<ushort>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
map.Add(data.ReadUInt16());
|
||||
}
|
||||
|
||||
var lump = new LeafFacesLump();
|
||||
lump.Map = [.. map];
|
||||
|
||||
file.LeafFacesLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_LEAFBRUSHES [17]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_LEAFBRUSHES];
|
||||
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 map = new List<ushort>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
map.Add(data.ReadUInt16());
|
||||
}
|
||||
|
||||
var lump = new LeafBrushesLump();
|
||||
lump.Map = [.. map];
|
||||
|
||||
file.LeafBrushesLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_BRUSHES [18]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_BRUSHES];
|
||||
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 brushes = new List<Brush>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var brush = data.ReadType<Brush>();
|
||||
if (brush != null)
|
||||
brushes.Add(brush);
|
||||
}
|
||||
|
||||
var lump = new BrushesLump();
|
||||
lump.Brushes = [.. brushes];
|
||||
|
||||
file.BrushesLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_BRUSHSIDES [19]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_BRUSHSIDES];
|
||||
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 brushsides = new List<Brushside>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var brushside = data.ReadType<Brushside>();
|
||||
if (brushside != null)
|
||||
brushsides.Add(brushside);
|
||||
}
|
||||
|
||||
var lump = new BrushsidesLump();
|
||||
lump.Brushsides = [.. brushsides];
|
||||
|
||||
file.BrushsidesLump = lump;
|
||||
}
|
||||
|
||||
// TODO: Support LUMP_AREAS [20] when in Models
|
||||
// TODO: Support LUMP_AREAPORTALS [21] when in Models
|
||||
// TODO: Support LUMP_PORTALS / LUMP_UNUSED0 / LUMP_PROPCOLLISION [22] when in Models
|
||||
// TODO: Support LUMP_CLUSTERS / LUMP_UNUSED1 / LUMP_PROPHULLS [23] when in Models
|
||||
// TODO: Support LUMP_PORTALVERTS / LUMP_UNUSED2 / LUMP_FAKEENTITIES / LUMP_PROPHULLVERTS [24] when in Models
|
||||
// TODO: Support LUMP_CLUSTERPORTALS / LUMP_UNUSED3 / LUMP_PROPTRIS [25] when in Models
|
||||
|
||||
// LUMP_DISPINFO [26]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_DISPINFO];
|
||||
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 dispInfos = new List<DispInfo>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var dispInfo = data.ReadType<DispInfo>();
|
||||
if (dispInfo != null)
|
||||
dispInfos.Add(dispInfo);
|
||||
}
|
||||
|
||||
var lump = new DispInfosLump();
|
||||
lump.Infos = [.. dispInfos];
|
||||
|
||||
file.DispInfoLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_ORIGINALFACES [27]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_ORIGINALFACES];
|
||||
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<VbspFace>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var face = data.ReadType<VbspFace>();
|
||||
if (face != null)
|
||||
faces.Add(face);
|
||||
}
|
||||
|
||||
var lump = new VbspFacesLump();
|
||||
lump.Faces = [.. faces];
|
||||
|
||||
file.OriginalFacesLump = lump;
|
||||
}
|
||||
|
||||
// TODO: Support LUMP_PHYSDISP [28] when in Models
|
||||
// TODO: Support LUMP_PHYSCOLLIDE [29] when in Models
|
||||
// TODO: Support LUMP_VERTNORMALS [30] when in Models
|
||||
// TODO: Support LUMP_VERTNORMALINDICES [31] when in Models
|
||||
// TODO: Support LUMP_DISP_LIGHTMAP_ALPHAS [32] when in Models
|
||||
|
||||
// LUMP_DISP_VERTS [33]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_DISP_VERTS];
|
||||
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 verts = new List<DispVert>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var vert = data.ReadType<DispVert>();
|
||||
if (vert != null)
|
||||
verts.Add(vert);
|
||||
}
|
||||
|
||||
var lump = new DispVertsLump();
|
||||
lump.Verts = [.. verts];
|
||||
|
||||
file.DispVertLump = lump;
|
||||
}
|
||||
|
||||
// TODO: Support LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS [34] when in Models
|
||||
|
||||
// LUMP_GAME_LUMP [35]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_GAME_LUMP];
|
||||
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 GameLump();
|
||||
|
||||
lump.LumpCount = data.ReadInt32();
|
||||
lump.Directories = new GameLumpDirectory[lump.LumpCount];
|
||||
for (int i = 0; i < lump.LumpCount; i++)
|
||||
{
|
||||
var dir = data.ReadType<GameLumpDirectory>();
|
||||
if (dir != null)
|
||||
lump.Directories[i] = dir;
|
||||
}
|
||||
|
||||
file.GameLump = lump;
|
||||
}
|
||||
|
||||
// TODO: Support LUMP_LEAFWATERDATA [36] when in Models
|
||||
// TODO: Support LUMP_PRIMITIVES [37] when in Models
|
||||
// TODO: Support LUMP_PRIMVERTS [38] when in Models
|
||||
// TODO: Support LUMP_PRIMINDICES [39] when in Models
|
||||
// TODO: Support LUMP_PAKFILE [40] when in Models
|
||||
// TODO: Support LUMP_CLIPPORTALVERTS [41] when in Models
|
||||
|
||||
// LUMP_CUBEMAPS [42]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_CUBEMAPS];
|
||||
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 cubemaps = new List<Cubemap>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var cubemap = data.ReadType<Cubemap>();
|
||||
if (cubemap != null)
|
||||
cubemaps.Add(cubemap);
|
||||
}
|
||||
|
||||
var lump = new CubemapsLump();
|
||||
lump.Cubemaps = [.. cubemaps];
|
||||
|
||||
file.CubemapLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_TEXDATA_STRING_DATA [43]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_TEXDATA_STRING_DATA];
|
||||
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 strings = new List<string>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var str = data.ReadNullTerminatedAnsiString();
|
||||
if (str != null)
|
||||
strings.Add(str);
|
||||
}
|
||||
|
||||
var lump = new TexdataStringData();
|
||||
lump.Strings = [.. strings];
|
||||
|
||||
file.TexdataStringData = lump;
|
||||
}
|
||||
|
||||
// LUMP_TEXDATA_STRING_TABLE [44]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_TEXDATA_STRING_TABLE];
|
||||
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 offsets = new List<int>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
offsets.Add(data.ReadInt32());
|
||||
}
|
||||
|
||||
var lump = new TexdataStringTable();
|
||||
lump.Offsets = [.. offsets];
|
||||
|
||||
file.TexdataStringTable = lump;
|
||||
}
|
||||
|
||||
// LUMP_OVERLAYS [45]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_OVERLAYS];
|
||||
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 overlays = new List<Overlay>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var overlay = data.ReadType<Overlay>();
|
||||
if (overlay != null)
|
||||
overlays.Add(overlay);
|
||||
}
|
||||
|
||||
var lump = new OverlaysLump();
|
||||
lump.Overlays = [.. overlays];
|
||||
|
||||
file.OverlaysLump = lump;
|
||||
}
|
||||
|
||||
// TODO: Support LUMP_LEAFMINDISTTOWATER [46] when in Models
|
||||
// TODO: Support LUMP_FACE_MACRO_TEXTURE_INFO [47] when in Models
|
||||
|
||||
// LUMP_DISP_TRIS [48]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_DISP_TRIS];
|
||||
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 tris = new List<DispTri>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var tri = data.ReadType<DispTri>();
|
||||
if (tri != null)
|
||||
tris.Add(tri);
|
||||
}
|
||||
|
||||
var lump = new DispTrisLump();
|
||||
lump.Tris = [.. tris];
|
||||
|
||||
file.DispTrisLump = lump;
|
||||
}
|
||||
|
||||
// TODO: Support LUMP_PHYSCOLLIDESURFACE / LUMP_PROP_BLOB [49] when in Models
|
||||
// TODO: Support LUMP_WATEROVERLAYS [50] when in Models
|
||||
|
||||
// LUMP_LIGHTMAPPAGES / LUMP_LEAF_AMBIENT_INDEX_HDR [51]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_LIGHTMAPPAGES];
|
||||
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 indicies = new List<LeafAmbientIndex>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var index = data.ReadType<LeafAmbientIndex>();
|
||||
if (index != null)
|
||||
indicies.Add(index);
|
||||
}
|
||||
|
||||
var lump = new AmbientIndexLump();
|
||||
lump.Indicies = [.. indicies];
|
||||
|
||||
file.HDRAmbientIndexLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_LIGHTMAPPAGEINFOS / LUMP_LEAF_AMBIENT_INDEX [52]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_LIGHTMAPPAGEINFOS];
|
||||
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 indicies = new List<LeafAmbientIndex>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var index = data.ReadType<LeafAmbientIndex>();
|
||||
if (index != null)
|
||||
indicies.Add(index);
|
||||
}
|
||||
|
||||
var lump = new AmbientIndexLump();
|
||||
lump.Indicies = [.. indicies];
|
||||
|
||||
file.LDRAmbientIndexLump = lump;
|
||||
}
|
||||
|
||||
// TODO: Support LUMP_LIGHTING_HDR [53] when in Models
|
||||
|
||||
// LUMP_WORLDLIGHTS_HDR [54]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_WORLDLIGHTS_HDR];
|
||||
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 worldLights = new List<WorldLight>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var worldLight = data.ReadType<WorldLight>();
|
||||
if (worldLight != null)
|
||||
worldLights.Add(worldLight);
|
||||
}
|
||||
|
||||
var lump = new WorldLightsLump();
|
||||
lump.WorldLights = [.. worldLights];
|
||||
|
||||
file.WorldLightsLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_LEAF_AMBIENT_LIGHTING_HDR [55]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_LEAF_AMBIENT_LIGHTING_HDR];
|
||||
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 lightings = new List<LeafAmbientLighting>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var lighting = data.ReadType<LeafAmbientLighting>();
|
||||
if (lighting != null)
|
||||
lightings.Add(lighting);
|
||||
}
|
||||
|
||||
var lump = new AmbientLightingLump();
|
||||
lump.Lightings = [.. lightings];
|
||||
|
||||
file.HDRAmbientLightingLump = lump;
|
||||
}
|
||||
|
||||
// LUMP_LEAF_AMBIENT_LIGHTING [56]
|
||||
lumpEntry = header.Lumps[(int)LumpType.LUMP_LEAF_AMBIENT_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 lightings = new List<LeafAmbientLighting>();
|
||||
while (data.Position < lumpEntry.Offset + lumpEntry.Length)
|
||||
{
|
||||
var lighting = data.ReadType<LeafAmbientLighting>();
|
||||
if (lighting != null)
|
||||
lightings.Add(lighting);
|
||||
}
|
||||
|
||||
var lump = new AmbientLightingLump();
|
||||
lump.Lightings = [.. lightings];
|
||||
|
||||
file.LDRAmbientLightingLump = lump;
|
||||
}
|
||||
|
||||
// TODO: Support LUMP_XZIPPAKFILE [57] when in Models
|
||||
// TODO: Support LUMP_FACES_HDR [58] when in Models
|
||||
// TODO: Support LUMP_MAP_FLAGS [59] when in Models
|
||||
// TODO: Support LUMP_OVERLAY_FADES [60] when in Models
|
||||
// TODO: Support LUMP_OVERLAY_SYSTEM_LEVELS [61] when in Models
|
||||
// TODO: Support LUMP_PHYSLEVEL [62] when in Models
|
||||
// TODO: Support LUMP_DISP_MULTIBLEND [63] when in Models
|
||||
|
||||
#endregion
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -45,61 +849,16 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life 2 Level header on success, null on error</returns>
|
||||
private static Header? ParseHeader(Stream data)
|
||||
private static VbspHeader? ParseHeader(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var header = new Header();
|
||||
var header = data.ReadType<VbspHeader>();
|
||||
|
||||
byte[]? signature = data.ReadBytes(4);
|
||||
if (signature == null)
|
||||
if (header?.Signature != SignatureString)
|
||||
return null;
|
||||
|
||||
header.Signature = Encoding.ASCII.GetString(signature);
|
||||
if (header.Signature != SignatureString)
|
||||
if (Array.IndexOf([17, 18, 19, 20, 21, 22, 23, 25, 27, 29, 0x00040014], header.Version) > -1)
|
||||
return null;
|
||||
|
||||
header.Version = data.ReadInt32();
|
||||
if ((header.Version < 19 || header.Version > 22) && header.Version != 0x00040014)
|
||||
return null;
|
||||
|
||||
header.Lumps = new Lump[HL_VBSP_LUMP_COUNT];
|
||||
for (int i = 0; i < HL_VBSP_LUMP_COUNT; i++)
|
||||
{
|
||||
var lump = ParseLump(data, header.Version);
|
||||
if (lump == null)
|
||||
return null;
|
||||
|
||||
header.Lumps[i] = lump;
|
||||
}
|
||||
|
||||
header.MapRevision = data.ReadInt32();
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life 2 Level lump
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="version">VBSP version</param>
|
||||
/// <returns>Filled Half-Life 2 Level lump on success, null on error</returns>
|
||||
private static Lump? ParseLump(Stream data, int version)
|
||||
{
|
||||
return data.ReadType<Lump>();
|
||||
|
||||
// This block was commented out because test VBSPs with header
|
||||
// version 21 had the values in the "right" order already and
|
||||
// were causing decompression issues
|
||||
|
||||
//if (version >= 21 && version != 0x00040014)
|
||||
//{
|
||||
// uint temp = lump.Version;
|
||||
// lump.Version = lump.Offset;
|
||||
// lump.Offset = lump.Length;
|
||||
// lump.Length = temp;
|
||||
//}
|
||||
//
|
||||
//return lump
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
#region Extended Header
|
||||
|
||||
if (header?.Version == 2)
|
||||
if (header.Version == 2)
|
||||
{
|
||||
// Try to parse the extended header
|
||||
var extendedHeader = ParseExtendedHeader(data);
|
||||
@@ -69,8 +69,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
if (header?.Version == 2
|
||||
&& file.ExtendedHeader != null
|
||||
&& file.ExtendedHeader.ArchiveHashLength > 0
|
||||
&& data.Position + file.ExtendedHeader.ArchiveHashLength <= data.Length)
|
||||
&& file.ExtendedHeader.ArchiveMD5SectionSize > 0
|
||||
&& data.Position + file.ExtendedHeader.ArchiveMD5SectionSize <= data.Length)
|
||||
{
|
||||
// Create the archive hashes list
|
||||
var archiveHashes = new List<ArchiveHash>();
|
||||
@@ -79,7 +79,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
initialOffset = data.Position;
|
||||
|
||||
// Try to parse the directory items
|
||||
while (data.Position < initialOffset + file.ExtendedHeader.ArchiveHashLength)
|
||||
while (data.Position < initialOffset + file.ExtendedHeader.ArchiveMD5SectionSize)
|
||||
{
|
||||
var archiveHash = ParseArchiveHash(data);
|
||||
if (archiveHash == null)
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.WAD;
|
||||
using static SabreTools.Models.WAD.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class WAD : BaseBinaryDeserializer<Models.WAD.File>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Models.WAD.File? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !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
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// Create a new Half-Life Texture Package to fill
|
||||
var file = new Models.WAD.File();
|
||||
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
// Set the package header
|
||||
file.Header = header;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lumps
|
||||
|
||||
// Get the lump offset
|
||||
uint lumpOffset = header.LumpOffset;
|
||||
if (lumpOffset < 0 || lumpOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the lump offset
|
||||
data.Seek(lumpOffset, SeekOrigin.Begin);
|
||||
|
||||
// Create the lump array
|
||||
file.Lumps = new Lump[header.LumpCount];
|
||||
for (int i = 0; i < header.LumpCount; i++)
|
||||
{
|
||||
var lump = ParseLump(data);
|
||||
if (lump == null)
|
||||
return null;
|
||||
|
||||
file.Lumps[i] = lump;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lump Infos
|
||||
|
||||
// Create the lump info array
|
||||
file.LumpInfos = new LumpInfo?[header.LumpCount];
|
||||
for (int i = 0; i < header.LumpCount; i++)
|
||||
{
|
||||
var lump = file.Lumps[i];
|
||||
if (lump == null)
|
||||
{
|
||||
file.LumpInfos[i] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lump.Compression != 0)
|
||||
{
|
||||
file.LumpInfos[i] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the lump info offset
|
||||
uint lumpInfoOffset = lump.Offset;
|
||||
if (lumpInfoOffset < 0 || lumpInfoOffset >= data.Length)
|
||||
{
|
||||
file.LumpInfos[i] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Seek to the lump info offset
|
||||
data.Seek(lumpInfoOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the lump info -- TODO: Do we ever set the mipmap level?
|
||||
var lumpInfo = ParseLumpInfo(data, lump.Type);
|
||||
file.LumpInfos[i] = lumpInfo;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package header on success, null on error</returns>
|
||||
private static Header? ParseHeader(Stream data)
|
||||
{
|
||||
var header = data.ReadType<Header>();
|
||||
|
||||
if (header == null)
|
||||
return null;
|
||||
if (header.Signature != SignatureString)
|
||||
return null;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package lump
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package lump on success, null on error</returns>
|
||||
private static Lump? ParseLump(Stream data)
|
||||
{
|
||||
return data.ReadType<Lump>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package lump info
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="type">Lump type</param>
|
||||
/// <param name="mipmap">Mipmap level</param>
|
||||
/// <returns>Filled Half-Life Texture Package lump info on success, null on error</returns>
|
||||
private static LumpInfo? ParseLumpInfo(Stream data, byte type, uint mipmap = 0)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
LumpInfo lumpInfo = new LumpInfo();
|
||||
|
||||
// Cache the initial offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// Type 0x42 has no name, type 0x43 does. Are these flags?
|
||||
if (type == 0x42)
|
||||
{
|
||||
if (mipmap > 0)
|
||||
return null;
|
||||
|
||||
lumpInfo.Width = data.ReadUInt32();
|
||||
lumpInfo.Height = data.ReadUInt32();
|
||||
lumpInfo.PixelData = data.ReadBytes((int)(lumpInfo.Width * lumpInfo.Height));
|
||||
lumpInfo.PaletteSize = data.ReadUInt16();
|
||||
}
|
||||
else if (type == 0x43)
|
||||
{
|
||||
if (mipmap > 3)
|
||||
return null;
|
||||
|
||||
byte[]? name = data.ReadBytes(16);
|
||||
if (name != null)
|
||||
lumpInfo.Name = Encoding.ASCII.GetString(name);
|
||||
lumpInfo.Width = data.ReadUInt32();
|
||||
lumpInfo.Height = data.ReadUInt32();
|
||||
lumpInfo.PixelOffset = data.ReadUInt32();
|
||||
lumpInfo.UnknownData = data.ReadBytes(12);
|
||||
|
||||
// Cache the current offset
|
||||
long currentOffset = data.Position;
|
||||
|
||||
// Seek to the pixel data
|
||||
data.Seek(initialOffset + lumpInfo.PixelOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the pixel data
|
||||
lumpInfo.PixelData = data.ReadBytes((int)(lumpInfo.Width * lumpInfo.Height));
|
||||
|
||||
// Seek back to the offset
|
||||
data.Seek(currentOffset, SeekOrigin.Begin);
|
||||
|
||||
uint pixelSize = lumpInfo.Width * lumpInfo.Height;
|
||||
|
||||
// Mipmap data -- TODO: How do we determine this during initial parsing?
|
||||
switch (mipmap)
|
||||
{
|
||||
case 1: _ = data.ReadBytes((int)pixelSize); break;
|
||||
case 2: _ = data.ReadBytes((int)(pixelSize + (pixelSize / 4))); break;
|
||||
case 3: _ = data.ReadBytes((int)(pixelSize + (pixelSize / 4) + (pixelSize / 16))); break;
|
||||
default: return null;
|
||||
}
|
||||
|
||||
_ = data.ReadBytes((int)(pixelSize + (pixelSize / 4) + (pixelSize / 16) + (pixelSize / 64))); // Pixel data
|
||||
lumpInfo.PaletteSize = data.ReadUInt16();
|
||||
lumpInfo.PaletteData = data.ReadBytes((int)lumpInfo.PaletteSize * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Adjust based on mipmap level
|
||||
switch (mipmap)
|
||||
{
|
||||
case 1:
|
||||
lumpInfo.Width /= 2;
|
||||
lumpInfo.Height /= 2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
lumpInfo.Width /= 4;
|
||||
lumpInfo.Height /= 4;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
lumpInfo.Width /= 8;
|
||||
lumpInfo.Height /= 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return lumpInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
272
SabreTools.Serialization/Deserializers/WAD3.cs
Normal file
272
SabreTools.Serialization/Deserializers/WAD3.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.WAD3;
|
||||
using static SabreTools.Models.WAD3.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class WAD3 : BaseBinaryDeserializer<Models.WAD3.File>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Models.WAD3.File? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !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
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// Create a new Half-Life Texture Package to fill
|
||||
var file = new Models.WAD3.File();
|
||||
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
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++)
|
||||
{
|
||||
var lump = ParseDirEntry(data);
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package header on success, null on error</returns>
|
||||
private static Header? ParseHeader(Stream data)
|
||||
{
|
||||
var header = data.ReadType<Header>();
|
||||
|
||||
if (header == null)
|
||||
return null;
|
||||
if (header.Signature != SignatureString)
|
||||
return null;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package directory entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package directory entry on success, null on error</returns>
|
||||
private static DirEntry? ParseDirEntry(Stream data)
|
||||
{
|
||||
return data.ReadType<DirEntry>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package file entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="type">File entry type</param>
|
||||
/// <returns>Filled Half-Life Texture Package file entry on success, null on error</returns>
|
||||
private static FileEntry? ParseFileEntry(Stream data, FileType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
FileType.Spraydecal
|
||||
or FileType.Miptex => ParseMipTex(data),
|
||||
FileType.Qpic => ParseQpicImage(data),
|
||||
FileType.Font => ParseFont(data),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package MipTex
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package MipTex on success, null on error</returns>
|
||||
private static MipTex ParseMipTex(Stream data)
|
||||
{
|
||||
var miptex = new MipTex();
|
||||
|
||||
byte[] nameBytes = data.ReadBytes(16);
|
||||
miptex.Name = Encoding.ASCII.GetString(nameBytes).TrimEnd('\0');
|
||||
miptex.Width = data.ReadUInt32();
|
||||
miptex.Height = data.ReadUInt32();
|
||||
miptex.MipOffsets = new uint[4];
|
||||
for (int i = 0; i < miptex.MipOffsets.Length; i++)
|
||||
{
|
||||
miptex.MipOffsets[i] = data.ReadUInt32();
|
||||
}
|
||||
miptex.MipImages = new MipMap[4];
|
||||
for (int i = 0; i < miptex.MipImages.Length; i++)
|
||||
{
|
||||
miptex.MipImages[i] = ParseMipMap(data, miptex.Width, miptex.Height);
|
||||
}
|
||||
miptex.ColorsUsed = data.ReadUInt16();
|
||||
miptex.Palette = new byte[miptex.ColorsUsed, 3];
|
||||
for (int i = 0; i < miptex.ColorsUsed; i++)
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
miptex.Palette[i, j] = data.ReadByteValue();
|
||||
}
|
||||
|
||||
return miptex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package MipMap
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package MipMap on success, null on error</returns>
|
||||
private static MipMap ParseMipMap(Stream data, uint width, uint height)
|
||||
{
|
||||
var mipmap = new MipMap();
|
||||
|
||||
mipmap.Data = new byte[width, height];
|
||||
for (int i = 0; i < width; i++)
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
mipmap.Data[i, j] = data.ReadByteValue();
|
||||
}
|
||||
|
||||
return mipmap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package Qpic image
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package Qpic image on success, null on error</returns>
|
||||
private static QpicImage ParseQpicImage(Stream data)
|
||||
{
|
||||
var qpic = new QpicImage();
|
||||
|
||||
qpic.Width = data.ReadUInt32();
|
||||
qpic.Height = data.ReadUInt32();
|
||||
qpic.Data = new byte[qpic.Height, qpic.Width];
|
||||
for (int i = 0; i < qpic.Height; i++)
|
||||
for (int j = 0; j < qpic.Width; j++)
|
||||
{
|
||||
qpic.Data[i, j] = data.ReadByteValue();
|
||||
}
|
||||
qpic.ColorsUsed = data.ReadUInt16();
|
||||
qpic.Palette = new byte[qpic.ColorsUsed, 3];
|
||||
for (int i = 0; i < qpic.ColorsUsed; i++)
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
qpic.Palette[i, j] = data.ReadByteValue();
|
||||
}
|
||||
|
||||
return qpic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package font
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package font on success, null on error</returns>
|
||||
private static Font ParseFont(Stream data)
|
||||
{
|
||||
var font = new Font();
|
||||
|
||||
font.Width = data.ReadUInt32();
|
||||
font.Height = data.ReadUInt32();
|
||||
font.RowCount = data.ReadUInt32();
|
||||
font.RowHeight = data.ReadUInt32();
|
||||
font.FontInfo = new CharInfo[256];
|
||||
for (int i = 0; i < font.FontInfo.Length; i++)
|
||||
{
|
||||
font.FontInfo[i] = ParseCharInfo(data);
|
||||
}
|
||||
font.Data = new byte[font.Height, font.Width];
|
||||
for (int i = 0; i < font.Height; i++)
|
||||
for (int j = 0; j < font.Width; j++)
|
||||
{
|
||||
font.Data[i, j] = data.ReadByteValue();
|
||||
}
|
||||
font.ColorsUsed = data.ReadUInt16();
|
||||
font.Palette = new byte[font.ColorsUsed, 3];
|
||||
for (int i = 0; i < font.ColorsUsed; i++)
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
font.Palette[i, j] = data.ReadByteValue();
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package CharInfo
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package CharInfo on success, null on error</returns>
|
||||
private static CharInfo ParseCharInfo(Stream data)
|
||||
{
|
||||
var charinfo = new CharInfo();
|
||||
|
||||
charinfo.StartOffset = data.ReadUInt16();
|
||||
charinfo.CharWidth = data.ReadUInt16();
|
||||
|
||||
return charinfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ namespace SabreTools.Serialization
|
||||
Wrapper.SGA item => item.PrettyPrint(),
|
||||
Wrapper.VBSP item => item.PrettyPrint(),
|
||||
Wrapper.VPK item => item.PrettyPrint(),
|
||||
Wrapper.WAD item => item.PrettyPrint(),
|
||||
Wrapper.WAD3 item => item.PrettyPrint(),
|
||||
Wrapper.XeMID item => item.PrettyPrint(),
|
||||
Wrapper.XMID item => item.PrettyPrint(),
|
||||
Wrapper.XZP item => item.PrettyPrint(),
|
||||
@@ -108,7 +108,7 @@ namespace SabreTools.Serialization
|
||||
Wrapper.SGA item => item.ExportJSON(),
|
||||
Wrapper.VBSP item => item.ExportJSON(),
|
||||
Wrapper.VPK item => item.ExportJSON(),
|
||||
Wrapper.WAD item => item.ExportJSON(),
|
||||
Wrapper.WAD3 item => item.ExportJSON(),
|
||||
Wrapper.XeMID item => item.ExportJSON(),
|
||||
Wrapper.XMID item => item.ExportJSON(),
|
||||
Wrapper.XZP item => item.ExportJSON(),
|
||||
@@ -412,10 +412,10 @@ namespace SabreTools.Serialization
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
private static StringBuilder PrettyPrint(this Wrapper.WAD item)
|
||||
private static StringBuilder PrettyPrint(this Wrapper.WAD3 item)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
WAD.Print(builder, item.Model);
|
||||
WAD3.Print(builder, item.Model);
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.BSP;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
using static SabreTools.Models.BSP.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class BSP : IPrinter<File>
|
||||
public class BSP : IPrinter<BspFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, File model)
|
||||
public void PrintInformation(StringBuilder builder, BspFile model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, File file)
|
||||
public static void Print(StringBuilder builder, BspFile file)
|
||||
{
|
||||
builder.AppendLine("BSP Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, file.Header);
|
||||
Print(builder, file.Lumps);
|
||||
Print(builder, file.TextureHeader);
|
||||
Print(builder, file.Textures);
|
||||
PrintLumps(builder, file);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Header? header)
|
||||
private static void Print(StringBuilder builder, BspHeader? header)
|
||||
{
|
||||
builder.AppendLine(" Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
@@ -38,30 +35,21 @@ namespace SabreTools.Serialization.Printers
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Lump?[]? lumps)
|
||||
private static void PrintLumps(StringBuilder builder, BspFile? model)
|
||||
{
|
||||
builder.AppendLine(" Lumps Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (lumps == null || lumps.Length == 0)
|
||||
if (model?.Header?.Lumps == null || model.Header.Lumps.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No lumps");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < lumps.Length; i++)
|
||||
for (int i = 0; i < model.Header.Lumps.Length; i++)
|
||||
{
|
||||
var lump = lumps[i];
|
||||
string specialLumpName = string.Empty;
|
||||
switch (i)
|
||||
{
|
||||
case HL_BSP_LUMP_ENTITIES:
|
||||
specialLumpName = " (entities)";
|
||||
break;
|
||||
case HL_BSP_LUMP_TEXTUREDATA:
|
||||
specialLumpName = " (texture data)";
|
||||
break;
|
||||
}
|
||||
var lump = model.Header.Lumps[i];
|
||||
string specialLumpName = GetLumpName(i);
|
||||
|
||||
builder.AppendLine($" Lump {i}{specialLumpName}");
|
||||
if (lump == null)
|
||||
@@ -72,79 +60,291 @@ namespace SabreTools.Serialization.Printers
|
||||
|
||||
builder.AppendLine(lump.Offset, " Offset");
|
||||
builder.AppendLine(lump.Length, " Length");
|
||||
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");
|
||||
}
|
||||
}
|
||||
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})");
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
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}");
|
||||
}
|
||||
}
|
||||
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");
|
||||
// }
|
||||
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");
|
||||
}
|
||||
}
|
||||
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})");
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
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...");
|
||||
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");
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
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})");
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
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})");
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, TextureHeader? header)
|
||||
private static string GetLumpName(int i)
|
||||
{
|
||||
builder.AppendLine(" Texture Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header == null)
|
||||
return (LumpType)i switch
|
||||
{
|
||||
builder.AppendLine(" No texture header");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.TextureCount, " Texture count");
|
||||
builder.AppendLine(" Offsets:");
|
||||
if (header.Offsets == null || header.Offsets.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No offsets");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < header.Offsets.Length; i++)
|
||||
{
|
||||
builder.AppendLine(header.Offsets[i], $" Offset {i}");
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Texture?[]? textures)
|
||||
{
|
||||
builder.AppendLine(" Textures Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (textures == null || textures.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No textures");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < textures.Length; i++)
|
||||
{
|
||||
var texture = textures[i];
|
||||
builder.AppendLine($" Texture {i}");
|
||||
if (texture == null)
|
||||
{
|
||||
builder.AppendLine(" [NULL]");
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.AppendLine(texture.Name, " Name");
|
||||
builder.AppendLine(texture.Width, " Width");
|
||||
builder.AppendLine(texture.Height, " Height");
|
||||
builder.AppendLine(" Offsets:");
|
||||
if (texture.Offsets == null || texture.Offsets.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No offsets");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < texture.Offsets.Length; j++)
|
||||
{
|
||||
builder.AppendLine(texture.Offsets[i], $" Offset {j}");
|
||||
}
|
||||
}
|
||||
// Skip texture data
|
||||
builder.AppendLine(texture.PaletteSize, " Palette size");
|
||||
// Skip palette data
|
||||
}
|
||||
builder.AppendLine();
|
||||
LumpType.LUMP_ENTITIES => " - LUMP_ENTITIES",
|
||||
LumpType.LUMP_PLANES => " - LUMP_PLANES",
|
||||
LumpType.LUMP_TEXTURES => " - LUMP_TEXTURES",
|
||||
LumpType.LUMP_VERTICES => " - LUMP_VERTICES",
|
||||
LumpType.LUMP_VISIBILITY => " - LUMP_VISIBILITY",
|
||||
LumpType.LUMP_NODES => " - LUMP_NODES",
|
||||
LumpType.LUMP_TEXINFO => " - LUMP_TEXINFO",
|
||||
LumpType.LUMP_FACES => " - LUMP_FACES",
|
||||
LumpType.LUMP_LIGHTING => " - LUMP_LIGHTING",
|
||||
LumpType.LUMP_CLIPNODES => " - LUMP_CLIPNODES",
|
||||
LumpType.LUMP_LEAVES => " - LUMP_LEAVES",
|
||||
LumpType.LUMP_MARKSURFACES => " - LUMP_MARKSURFACES",
|
||||
LumpType.LUMP_EDGES => " - LUMP_EDGES",
|
||||
LumpType.LUMP_SURFEDGES => " - LUMP_SURFEDGES",
|
||||
LumpType.LUMP_MODELS => " - LUMP_MODELS",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,26 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.VBSP;
|
||||
using SabreTools.Models.BSP;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
using static SabreTools.Models.VBSP.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class VBSP : IPrinter<File>
|
||||
public class VBSP : IPrinter<VbspFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, File model)
|
||||
public void PrintInformation(StringBuilder builder, VbspFile model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, File file)
|
||||
public static void Print(StringBuilder builder, VbspFile file)
|
||||
{
|
||||
builder.AppendLine("VBSP Information:");
|
||||
builder.AppendLine("BSP Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, file.Header);
|
||||
PrintLumps(builder, file);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Header? header)
|
||||
private static void Print(StringBuilder builder, VbspHeader? header)
|
||||
{
|
||||
builder.AppendLine(" Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
@@ -35,34 +35,23 @@ namespace SabreTools.Serialization.Printers
|
||||
builder.AppendLine(header.Version, " Version");
|
||||
builder.AppendLine(header.MapRevision, " Map revision");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, header.Lumps);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Lump?[]? lumps)
|
||||
private static void PrintLumps(StringBuilder builder, VbspFile? model)
|
||||
{
|
||||
builder.AppendLine(" Lumps Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (lumps == null || lumps.Length == 0)
|
||||
if (model?.Header?.Lumps == null || model.Header.Lumps.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No lumps");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < lumps.Length; i++)
|
||||
for (int i = 0; i < model.Header.Lumps.Length; i++)
|
||||
{
|
||||
var lump = lumps[i];
|
||||
string specialLumpName = string.Empty;
|
||||
switch (i)
|
||||
{
|
||||
case HL_VBSP_LUMP_ENTITIES:
|
||||
specialLumpName = " (entities)";
|
||||
break;
|
||||
case HL_VBSP_LUMP_PAKFILE:
|
||||
specialLumpName = " (pakfile)";
|
||||
break;
|
||||
}
|
||||
var lump = model.Header.Lumps[i];
|
||||
string specialLumpName = GetLumpName(i);
|
||||
|
||||
builder.AppendLine($" Lump {i}{specialLumpName}");
|
||||
if (lump == null)
|
||||
@@ -73,10 +62,396 @@ namespace SabreTools.Serialization.Printers
|
||||
|
||||
builder.AppendLine(lump.Offset, " Offset");
|
||||
builder.AppendLine(lump.Length, " Length");
|
||||
builder.AppendLine(lump.Version, " Version");
|
||||
builder.AppendLine(lump.FourCC, " 4CC");
|
||||
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");
|
||||
}
|
||||
}
|
||||
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})");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LumpType.LUMP_TEXTURES:
|
||||
if (model.TexdataLump?.Texdatas == null || model.TexdataLump.Texdatas.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No data");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < model.TexdataLump.Texdatas.Length; j++)
|
||||
{
|
||||
var texdata = model.TexdataLump.Texdatas[j];
|
||||
builder.AppendLine($" Texture {j}");
|
||||
builder.AppendLine($" Reflectivity: {texdata.Reflectivity.X}, {texdata.Reflectivity.Y}, {texdata.Reflectivity.Z}");
|
||||
builder.AppendLine(texdata.NameStringTableID, " Name string table ID");
|
||||
builder.AppendLine(texdata.Width, " Width");
|
||||
builder.AppendLine(texdata.Height, " Height");
|
||||
builder.AppendLine(texdata.ViewWidth, " View width");
|
||||
builder.AppendLine(texdata.ViewHeight, " View height");
|
||||
}
|
||||
}
|
||||
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}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LumpType.LUMP_VISIBILITY:
|
||||
if (model.VisibilityLump == null)
|
||||
{
|
||||
builder.AppendLine(" No data");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(model.VisibilityLump.NumClusters, " Cluster count");
|
||||
builder.AppendLine(" Byte offsets skipped...");
|
||||
}
|
||||
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");
|
||||
builder.AppendLine(node.Area, " Area");
|
||||
builder.AppendLine(node.Padding, " Padding");
|
||||
}
|
||||
}
|
||||
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($" Texture S-Vector: {texinfo.TextureSVector.X}, {texinfo.TextureSVector.Y}, {texinfo.TextureSVector.Z}");
|
||||
builder.AppendLine(texinfo.TextureSShift, " Texture shift in S direction");
|
||||
builder.AppendLine($" Texture T-Vector: {texinfo.TextureTVector.X}, {texinfo.TextureTVector.Y}, {texinfo.TextureTVector.Z}");
|
||||
builder.AppendLine(texinfo.TextureTShift, " Texture shift in T direction");
|
||||
builder.AppendLine($" Lightmap S-Vector: {texinfo.LightmapSVector.X}, {texinfo.LightmapSVector.Y}, {texinfo.LightmapSVector.Z}");
|
||||
builder.AppendLine(texinfo.TextureSShift, " Lightmap shift in S direction");
|
||||
builder.AppendLine($" Lightmap T-Vector: {texinfo.LightmapTVector.X}, {texinfo.LightmapTVector.Y}, {texinfo.LightmapTVector.Z}");
|
||||
builder.AppendLine(texinfo.TextureTShift, " Lightmap shift in T direction");
|
||||
builder.AppendLine($" Flags: {texinfo.Flags} (0x{texinfo.Flags:X})");
|
||||
builder.AppendLine(texinfo.TexData, " Pointer to texdata");
|
||||
}
|
||||
}
|
||||
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.PlaneNum, " Plane number");
|
||||
builder.AppendLine(face.Side, " Side");
|
||||
builder.AppendLine(face.OnNode, " On node");
|
||||
builder.AppendLine(face.FirstEdgeIndex, " First surfedge index");
|
||||
builder.AppendLine(face.NumberOfEdges, " Surfedge count");
|
||||
builder.AppendLine(face.TextureInfoIndex, " Texture info index");
|
||||
builder.AppendLine(face.DisplacementInfoIndex, " Displacement info index");
|
||||
builder.AppendLine(face.SurfaceFogVolumeID, " Surface fog volume ID");
|
||||
builder.AppendLine(face.Styles, " Styles");
|
||||
builder.AppendLine(face.LightmapOffset, " Lightmap offset");
|
||||
builder.AppendLine(face.Area, " Area");
|
||||
builder.AppendLine(face.LightmapTextureMinsInLuxels, " Lightmap texture mins in Luxels");
|
||||
builder.AppendLine(face.LightmapTextureSizeInLuxels, " Lightmap texture size in Luxels");
|
||||
builder.AppendLine(face.OrigFace, " Original face index");
|
||||
builder.AppendLine(face.PrimitiveCount, " Primitive count");
|
||||
builder.AppendLine(face.FirstPrimitiveID, " First primitive ID");
|
||||
builder.AppendLine(face.SmoothingGroups, " Smoothing groups");
|
||||
}
|
||||
}
|
||||
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...");
|
||||
break;
|
||||
case LumpType.LUMP_CLIPNODES:
|
||||
if (model.OcclusionLump == null)
|
||||
{
|
||||
builder.AppendLine(" No data");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(model.OcclusionLump.Count, " Count");
|
||||
if (model.OcclusionLump.Data == null || model.OcclusionLump.Data.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No occluder data");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < model.OcclusionLump.Data.Length; j++)
|
||||
{
|
||||
var data = model.OcclusionLump.Data[j];
|
||||
builder.AppendLine($" Occluder Data {j}");
|
||||
builder.AppendLine(data.Flags, " Flags");
|
||||
builder.AppendLine(data.FirstPoly, " First poly");
|
||||
builder.AppendLine(data.PolyCount, " Poly count");
|
||||
builder.AppendLine($" Mins: {data.Mins.X}, {data.Mins.Y}, {data.Mins.Z}");
|
||||
builder.AppendLine($" Maxs: {data.Maxs.X}, {data.Maxs.Y}, {data.Maxs.Z}");
|
||||
builder.AppendLine(data.Area, " Area");
|
||||
}
|
||||
}
|
||||
builder.AppendLine(model.OcclusionLump.PolyDataCount, " Polydata count");
|
||||
if (model.OcclusionLump.PolyData == null || model.OcclusionLump.PolyData.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No occluder polydata");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < model.OcclusionLump.PolyData.Length; j++)
|
||||
{
|
||||
var polydata = model.OcclusionLump.PolyData[j];
|
||||
builder.AppendLine($" Occluder Polydata {j}");
|
||||
builder.AppendLine(polydata.FirstVertexIndex, " First vertex index");
|
||||
builder.AppendLine(polydata.VertexCount, " Vertex count");
|
||||
builder.AppendLine(polydata.PlanEnum, " Plan enum");
|
||||
}
|
||||
}
|
||||
builder.AppendLine(model.OcclusionLump.VertexIndexCount, " Vertex index count");
|
||||
if (model.OcclusionLump.VertexIndices == null || model.OcclusionLump.VertexIndices.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No vertex indicies");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < model.OcclusionLump.VertexIndices.Length; j++)
|
||||
{
|
||||
builder.AppendLine($" Vertex Index {j}: {model.OcclusionLump.VertexIndices[j]}");
|
||||
}
|
||||
}
|
||||
}
|
||||
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.Cluster, " Cluster");
|
||||
builder.AppendLine(leaf.AreaFlags, " AreaFlags");
|
||||
builder.AppendLine(leaf.Mins, " Mins");
|
||||
builder.AppendLine(leaf.Maxs, " Maxs");
|
||||
builder.AppendLine(leaf.FirstLeafFace, " First leaf face");
|
||||
builder.AppendLine(leaf.NumLeafFaces, " Leaf faces count");
|
||||
builder.AppendLine(leaf.FirstLeafBrush, " First leaf brush");
|
||||
builder.AppendLine(leaf.NumLeafBrushes, " Leaf brushes count");
|
||||
builder.AppendLine(leaf.LeafWaterDataID, " Leaf water data ID");
|
||||
if (lump.Version == 0)
|
||||
{
|
||||
// TODO: Figure out how to print the colors array
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(leaf.Padding, " Padding");
|
||||
}
|
||||
}
|
||||
}
|
||||
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})");
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
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})");
|
||||
}
|
||||
}
|
||||
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.HeadNode, " Headnode index");
|
||||
builder.AppendLine(bmodel.FirstFaceIndex, " First face index");
|
||||
builder.AppendLine(bmodel.FacesCount, " Faces count");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// TODO: Implement remaining printed lump types
|
||||
}
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static string GetLumpName(int i)
|
||||
{
|
||||
return (LumpType)i switch
|
||||
{
|
||||
LumpType.LUMP_ENTITIES => " - LUMP_ENTITIES",
|
||||
LumpType.LUMP_PLANES => " - LUMP_PLANES",
|
||||
LumpType.LUMP_TEXTURES => " - LUMP_TEXDATA",
|
||||
LumpType.LUMP_VERTICES => " - LUMP_VERTEXES",
|
||||
LumpType.LUMP_VISIBILITY => " - LUMP_VISIBILITY",
|
||||
LumpType.LUMP_NODES => " - LUMP_NODES",
|
||||
LumpType.LUMP_TEXINFO => " - LUMP_TEXINFO",
|
||||
LumpType.LUMP_FACES => " - LUMP_FACES",
|
||||
LumpType.LUMP_LIGHTING => " - LUMP_LIGHTING",
|
||||
LumpType.LUMP_CLIPNODES => " - LUMP_OCCLUSION",
|
||||
LumpType.LUMP_LEAVES => " - LUMP_LEAVES",
|
||||
LumpType.LUMP_MARKSURFACES => " - LUMP_FACEIDS",
|
||||
LumpType.LUMP_EDGES => " - LUMP_EDGES",
|
||||
LumpType.LUMP_SURFEDGES => " - LUMP_SURFEDGES",
|
||||
LumpType.LUMP_MODELS => " - LUMP_MODELS",
|
||||
LumpType.LUMP_WORLDLIGHTS => " - LUMP_WORLDLIGHTS",
|
||||
LumpType.LUMP_LEAFFACES => " - LUMP_LEAFFACES",
|
||||
LumpType.LUMP_LEAFBRUSHES => " - LUMP_LEAFBRUSHES",
|
||||
LumpType.LUMP_BRUSHES => " - LUMP_BRUSHES",
|
||||
LumpType.LUMP_BRUSHSIDES => " - LUMP_BRUSHSIDES",
|
||||
LumpType.LUMP_AREAS => " - LUMP_AREAS",
|
||||
LumpType.LUMP_AREAPORTALS => " - LUMP_AREAPORTALS",
|
||||
LumpType.LUMP_PORTALS => " - LUMP_PORTALS / LUMP_UNUSED0 / LUMP_PROPCOLLISION",
|
||||
LumpType.LUMP_CLUSTERS => " - LUMP_CLUSTERS / LUMP_UNUSED1 / LUMP_PROPHULLS",
|
||||
LumpType.LUMP_PORTALVERTS => " - LUMP_PORTALVERTS / LUMP_UNUSED2 / LUMP_FAKEENTITIES / LUMP_PROPHULLVERTS",
|
||||
LumpType.LUMP_CLUSTERPORTALS => " - LUMP_CLUSTERPORTALS / LUMP_UNUSED3 / LUMP_PROPTRIS",
|
||||
LumpType.LUMP_DISPINFO => " - LUMP_DISPINFO",
|
||||
LumpType.LUMP_ORIGINALFACES => " - LUMP_ORIGINALFACES",
|
||||
LumpType.LUMP_PHYSDISP => " - LUMP_PHYSDISP",
|
||||
LumpType.LUMP_PHYSCOLLIDE => " - LUMP_PHYSCOLLIDE",
|
||||
LumpType.LUMP_VERTNORMALS => " - LUMP_VERTNORMALS",
|
||||
LumpType.LUMP_VERTNORMALINDICES => " - LUMP_VERTNORMALINDICES",
|
||||
LumpType.LUMP_DISP_LIGHTMAP_ALPHAS => " - LUMP_DISP_LIGHTMAP_ALPHAS",
|
||||
LumpType.LUMP_DISP_VERTS => " - LUMP_DISP_VERTS",
|
||||
LumpType.LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS => " - LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS",
|
||||
LumpType.LUMP_GAME_LUMP => " - LUMP_GAME_LUMP",
|
||||
LumpType.LUMP_LEAFWATERDATA => " - LUMP_LEAFWATERDATA",
|
||||
LumpType.LUMP_PRIMITIVES => " - LUMP_PRIMITIVES",
|
||||
LumpType.LUMP_PRIMVERTS => " - LUMP_PRIMVERTS",
|
||||
LumpType.LUMP_PRIMINDICES => " - LUMP_PRIMINDICES",
|
||||
LumpType.LUMP_PAKFILE => " - LUMP_PAKFILE",
|
||||
LumpType.LUMP_CLIPPORTALVERTS => " - LUMP_CLIPPORTALVERTS",
|
||||
LumpType.LUMP_CUBEMAPS => " - LUMP_CUBEMAPS",
|
||||
LumpType.LUMP_TEXDATA_STRING_DATA => " - LUMP_TEXDATA_STRING_DATA",
|
||||
LumpType.LUMP_TEXDATA_STRING_TABLE => " - LUMP_TEXDATA_STRING_TABLE",
|
||||
LumpType.LUMP_OVERLAYS => " - LUMP_OVERLAYS",
|
||||
LumpType.LUMP_LEAFMINDISTTOWATER => " - LUMP_LEAFMINDISTTOWATER",
|
||||
LumpType.LUMP_FACE_MACRO_TEXTURE_INFO => " - LUMP_FACE_MACRO_TEXTURE_INFO",
|
||||
LumpType.LUMP_DISP_TRIS => " - LUMP_DISP_TRIS",
|
||||
LumpType.LUMP_PHYSCOLLIDESURFACE => " - LUMP_PHYSCOLLIDESURFACE / LUMP_PROP_BLOB",
|
||||
LumpType.LUMP_WATEROVERLAYS => " - LUMP_WATEROVERLAYS",
|
||||
LumpType.LUMP_LIGHTMAPPAGES => " - LUMP_LIGHTMAPPAGES / LUMP_LEAF_AMBIENT_INDEX_HDR",
|
||||
LumpType.LUMP_LIGHTMAPPAGEINFOS => " - LUMP_LIGHTMAPPAGEINFOS / LUMP_LEAF_AMBIENT_INDEX",
|
||||
LumpType.LUMP_LIGHTING_HDR => " - LUMP_LIGHTING_HDR",
|
||||
LumpType.LUMP_WORLDLIGHTS_HDR => " - LUMP_WORLDLIGHTS_HDR",
|
||||
LumpType.LUMP_LEAF_AMBIENT_LIGHTING_HDR => " - LUMP_LEAF_AMBIENT_LIGHTING_HDR",
|
||||
LumpType.LUMP_LEAF_AMBIENT_LIGHTING => " - LUMP_LEAF_AMBIENT_LIGHTING",
|
||||
LumpType.LUMP_XZIPPAKFILE => " - LUMP_XZIPPAKFILE",
|
||||
LumpType.LUMP_FACES_HDR => " - LUMP_FACES_HDR",
|
||||
LumpType.LUMP_MAP_FLAGS => " - LUMP_MAP_FLAGS",
|
||||
LumpType.LUMP_OVERLAY_FADES => " - LUMP_OVERLAY_FADES",
|
||||
LumpType.LUMP_OVERLAY_SYSTEM_LEVELS => " - LUMP_OVERLAY_SYSTEM_LEVELS",
|
||||
LumpType.LUMP_PHYSLEVEL => " - LUMP_PHYSLEVEL",
|
||||
LumpType.LUMP_DISP_MULTIBLEND => " - LUMP_DISP_MULTIBLEND",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ namespace SabreTools.Serialization.Printers
|
||||
|
||||
builder.AppendLine(header.Signature, " Signature");
|
||||
builder.AppendLine(header.Version, " Version");
|
||||
builder.AppendLine(header.DirectoryLength, " Directory length");
|
||||
builder.AppendLine(header.TreeSize, " Tree size");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
@@ -50,10 +50,10 @@ namespace SabreTools.Serialization.Printers
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.Dummy0, " Dummy 0");
|
||||
builder.AppendLine(header.ArchiveHashLength, " Archive hash length");
|
||||
builder.AppendLine(header.ExtraLength, " Extra length");
|
||||
builder.AppendLine(header.Dummy1, " Dummy 1");
|
||||
builder.AppendLine(header.FileDataSectionSize, " File data section size");
|
||||
builder.AppendLine(header.ArchiveMD5SectionSize, " Archive MD5 section size");
|
||||
builder.AppendLine(header.OtherMD5SectionSize, " Other MD5 section size");
|
||||
builder.AppendLine(header.SignatureSectionSize, " Signature section size");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.WAD;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class WAD : IPrinter<File>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, File model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, File file)
|
||||
{
|
||||
builder.AppendLine("WAD Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, file.Header);
|
||||
Print(builder, file.Lumps);
|
||||
Print(builder, file.LumpInfos);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Header? header)
|
||||
{
|
||||
builder.AppendLine(" Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header == null)
|
||||
{
|
||||
builder.AppendLine(" No header");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.Signature, " Signature");
|
||||
builder.AppendLine(header.LumpCount, " Lump count");
|
||||
builder.AppendLine(header.LumpOffset, " Lump offset");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Lump?[]? entries)
|
||||
{
|
||||
builder.AppendLine(" Lumps Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (entries == null || entries.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No lumps");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
builder.AppendLine($" Lump {i}");
|
||||
if (entry == null)
|
||||
{
|
||||
builder.AppendLine(" [NULL]");
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.AppendLine(entry.Offset, " Offset");
|
||||
builder.AppendLine(entry.DiskLength, " Disk length");
|
||||
builder.AppendLine(entry.Length, " Length");
|
||||
builder.AppendLine(entry.Type, " Type");
|
||||
builder.AppendLine(entry.Compression, " Compression");
|
||||
builder.AppendLine(entry.Padding0, " Padding 0");
|
||||
builder.AppendLine(entry.Padding1, " Padding 1");
|
||||
builder.AppendLine(entry.Name, " Name");
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, LumpInfo?[]? entries)
|
||||
{
|
||||
builder.AppendLine(" Lump Infos Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (entries == null || entries.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No lump infos");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
builder.AppendLine($" Lump Info {i}");
|
||||
if (entry == null)
|
||||
{
|
||||
builder.AppendLine(" Lump is compressed");
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.AppendLine(entry.Name, " Name");
|
||||
builder.AppendLine(entry.Width, " Width");
|
||||
builder.AppendLine(entry.Height, " Height");
|
||||
builder.AppendLine(entry.PixelOffset, " Pixel offset");
|
||||
// TODO: Print unknown data?
|
||||
// TODO: Print pixel data?
|
||||
builder.AppendLine(entry.PaletteSize, " Palette size");
|
||||
// TODO: Print palette data?
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
131
SabreTools.Serialization/Printers/WAD3.cs
Normal file
131
SabreTools.Serialization/Printers/WAD3.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.WAD3;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class WAD3 : IPrinter<File>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, File model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, File file)
|
||||
{
|
||||
builder.AppendLine("WAD Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, file.Header);
|
||||
Print(builder, file.DirEntries);
|
||||
Print(builder, file.FileEntries);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Header? header)
|
||||
{
|
||||
builder.AppendLine(" Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header == null)
|
||||
{
|
||||
builder.AppendLine(" No header");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.Signature, " Signature");
|
||||
builder.AppendLine(header.NumDirs, " Number of directory entries");
|
||||
builder.AppendLine(header.DirOffset, " Offset to first directory entry");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, DirEntry?[]? entries)
|
||||
{
|
||||
builder.AppendLine(" Directory Entries Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (entries == null || entries.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No directory entries");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
builder.AppendLine($" Directory Entry {i}");
|
||||
if (entry == null)
|
||||
{
|
||||
builder.AppendLine(" [NULL]");
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.AppendLine(entry.Offset, " Offset");
|
||||
builder.AppendLine(entry.DiskLength, " Disk length");
|
||||
builder.AppendLine(entry.Length, " Length");
|
||||
builder.AppendLine($" File type: {entry.Type} (0x{entry.Type:X})");
|
||||
builder.AppendLine(entry.Compression, " Compression");
|
||||
builder.AppendLine(entry.Padding, " Padding");
|
||||
builder.AppendLine(entry.Name, " Name");
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, FileEntry?[]? entries)
|
||||
{
|
||||
builder.AppendLine(" File Entries Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (entries == null || entries.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No file entries");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
builder.AppendLine($" File Entry {i}");
|
||||
if (entry == null)
|
||||
{
|
||||
builder.AppendLine(" [NULL]");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry is MipTex mipTex)
|
||||
{
|
||||
builder.AppendLine(mipTex.Name, " Name");
|
||||
builder.AppendLine(mipTex.Width, " Width");
|
||||
builder.AppendLine(mipTex.Height, " Height");
|
||||
builder.AppendLine(mipTex.MipOffsets, " Mipmap Offsets");
|
||||
builder.AppendLine(" Mipmap Images skipped...");
|
||||
builder.AppendLine(mipTex.ColorsUsed, " Colors used");
|
||||
builder.AppendLine(" Palette skipped...");
|
||||
}
|
||||
else if (entry is QpicImage qpic)
|
||||
{
|
||||
builder.AppendLine(qpic.Width, " Width");
|
||||
builder.AppendLine(qpic.Height, " Height");
|
||||
builder.AppendLine(" Image data skipped...");
|
||||
builder.AppendLine(qpic.ColorsUsed, " Colors used");
|
||||
builder.AppendLine(" Palette skipped...");
|
||||
}
|
||||
else if (entry is Font font)
|
||||
{
|
||||
builder.AppendLine(font.Width, " Width");
|
||||
builder.AppendLine(font.Height, " Height");
|
||||
builder.AppendLine(font.RowCount, " Row count");
|
||||
builder.AppendLine(font.RowHeight, " Row height");
|
||||
builder.AppendLine(" Font info skipped...");
|
||||
builder.AppendLine(" Image data skipped...");
|
||||
builder.AppendLine(font.ColorsUsed, " Colors used");
|
||||
builder.AppendLine(" Palette skipped...");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(" Unrecognized entry type");
|
||||
}
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
<PackageReference Include="SabreTools.ASN1" Version="1.4.1" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.4.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.5.1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.5.1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.5.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -84,6 +84,16 @@ namespace SabreTools.Serialization
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append a line containing a Single to a StringBuilder
|
||||
/// </summary>
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, float? value, string prefixString)
|
||||
{
|
||||
value ??= 0;
|
||||
string valueString = $"{value} (0x{value:X8})";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append a line containing a Int64 to a StringBuilder
|
||||
/// </summary>
|
||||
@@ -104,6 +114,16 @@ namespace SabreTools.Serialization
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append a line containing a Double to a StringBuilder
|
||||
/// </summary>
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, double? value, string prefixString)
|
||||
{
|
||||
value ??= 0;
|
||||
string valueString = $"{value} (0x{value:X16})";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append a line containing a string to a StringBuilder
|
||||
/// </summary>
|
||||
|
||||
@@ -2,7 +2,7 @@ using System.IO;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class BSP : WrapperBase<Models.BSP.File>
|
||||
public class BSP : WrapperBase<Models.BSP.BspFile>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
@@ -14,14 +14,14 @@ namespace SabreTools.Serialization.Wrappers
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public BSP(Models.BSP.File? model, byte[]? data, int offset)
|
||||
public BSP(Models.BSP.BspFile? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public BSP(Models.BSP.File? model, Stream? data)
|
||||
public BSP(Models.BSP.BspFile? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
|
||||
@@ -2,7 +2,7 @@ using System.IO;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class VBSP : WrapperBase<Models.VBSP.File>
|
||||
public class VBSP : WrapperBase<Models.BSP.VbspFile>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
@@ -14,14 +14,14 @@ namespace SabreTools.Serialization.Wrappers
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public VBSP(Models.VBSP.File? model, byte[]? data, int offset)
|
||||
public VBSP(Models.BSP.VbspFile? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public VBSP(Models.VBSP.File? model, Stream? data)
|
||||
public VBSP(Models.BSP.VbspFile? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
|
||||
@@ -2,38 +2,38 @@ using System.IO;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class WAD : WrapperBase<Models.WAD.File>
|
||||
public class WAD3 : WrapperBase<Models.WAD3.File>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DescriptionString => "Half-Life Texture Package File (WAD)";
|
||||
public override string DescriptionString => "Half-Life Texture Package File (WAD3)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WAD(Models.WAD.File? model, byte[]? data, int offset)
|
||||
public WAD3(Models.WAD3.File? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WAD(Models.WAD.File? model, Stream? data)
|
||||
public WAD3(Models.WAD3.File? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a WAD from a byte array and offset
|
||||
/// Create a WAD3 from a byte array and offset
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the WAD</param>
|
||||
/// <param name="data">Byte array representing the WAD3</param>
|
||||
/// <param name="offset">Offset within the array to parse</param>
|
||||
/// <returns>A WAD wrapper on success, null on failure</returns>
|
||||
public static WAD? Create(byte[]? data, int offset)
|
||||
/// <returns>A WAD3 wrapper on success, null on failure</returns>
|
||||
public static WAD3? Create(byte[]? data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
@@ -49,23 +49,23 @@ namespace SabreTools.Serialization.Wrappers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a WAD from a Stream
|
||||
/// Create a WAD3 from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the WAD</param>
|
||||
/// <returns>An WAD wrapper on success, null on failure</returns>
|
||||
public static WAD? Create(Stream? data)
|
||||
/// <param name="data">Stream representing the WAD3</param>
|
||||
/// <returns>An WAD3 wrapper on success, null on failure</returns>
|
||||
public static WAD3? Create(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
var file = Deserializers.WAD.DeserializeStream(data);
|
||||
var file = Deserializers.WAD3.DeserializeStream(data);
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return new WAD(file, data);
|
||||
return new WAD3(file, data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -54,7 +54,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
WrapperType.Textfile => null,// TODO: Implement wrapper
|
||||
WrapperType.VBSP => VBSP.Create(data),
|
||||
WrapperType.VPK => VPK.Create(data),
|
||||
WrapperType.WAD => WAD.Create(data),
|
||||
WrapperType.WAD => WAD3.Create(data),
|
||||
WrapperType.XZ => null,// TODO: Implement wrapper
|
||||
WrapperType.XZP => XZP.Create(data),
|
||||
_ => null,
|
||||
|
||||
Reference in New Issue
Block a user