Ridiculously massive refactor:

All lump structures store their source data as a byte array now, so even unknown parts of structures are stored when they were discarded before. This means lumps can be read and written without losing any information.
New extension methods for Vector2d, 3d and 4d, Vertex, Plane and Color to get their components as bytes for easy storage.
Add support for most Call of Duty 2 structures. There's still some missing.
This commit is contained in:
Will
2018-11-22 19:28:03 -07:00
parent cab099b6f1
commit 76feb5046e
32 changed files with 6374 additions and 1158 deletions

View File

@@ -49,6 +49,9 @@
<Compile Include="Source\Extensions\CustomAttributeExtensions.cs" /> <Compile Include="Source\Extensions\CustomAttributeExtensions.cs" />
<Compile Include="Source\Extensions\PlaneExtensions.cs" /> <Compile Include="Source\Extensions\PlaneExtensions.cs" />
<Compile Include="Source\Extensions\StringExtensions.cs" /> <Compile Include="Source\Extensions\StringExtensions.cs" />
<Compile Include="Source\Extensions\Vector2dExtensions.cs" />
<Compile Include="Source\Extensions\Vector3dExtensions.cs" />
<Compile Include="Source\Extensions\Vector4dExtensions.cs" />
<Compile Include="Source\Extensions\VertexExtensions.cs" /> <Compile Include="Source\Extensions\VertexExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Source\Structs\BSP\Brush.cs" /> <Compile Include="Source\Structs\BSP\Brush.cs" />

View File

@@ -30,5 +30,26 @@ namespace LibBSP {
#endif #endif
} }
/// <summary>
/// Gets this <c>Color</c> as R8G8B8A8.
/// </summary>
/// <param name="color">This <c>Color</c>.</param>
/// <returns>A <c>byte</c> array with four members, RGBA.</returns>
public static byte[] GetBytes(this Color color) {
byte[] bytes = new byte[4];
#if UNITY
bytes[0] = color.r;
bytes[1] = color.g;
bytes[2] = color.b;
bytes[3] = color.a;
#else
bytes[0] = color.R;
bytes[1] = color.G;
bytes[2] = color.B;
bytes[3] = color.A;
#endif
return bytes;
}
} }
} }

View File

@@ -18,6 +18,18 @@ namespace LibBSP {
/// </summary> /// </summary>
public static class PlaneExtensions { public static class PlaneExtensions {
/// <summary>
/// Array of base texture axes. When referenced properly, provides a good default texture axis for any given plane.
/// </summary>
public static readonly Vector3d[] baseAxes = new Vector3d[] {
new Vector3d(0, 0, 1), new Vector3d(1, 0, 0), new Vector3d(0, -1, 0),
new Vector3d(0, 0, -1), new Vector3d(1, 0, 0), new Vector3d(0, -1, 0),
new Vector3d(1, 0, 0), new Vector3d(0, 1, 0), new Vector3d(0, 0, -1),
new Vector3d(-1, 0, 0), new Vector3d(0, 1, 0), new Vector3d(0, 0, -1),
new Vector3d(0, 1, 0), new Vector3d(1, 0, 0), new Vector3d(0, 0, -1),
new Vector3d(0, -1, 0), new Vector3d(1, 0, 0), new Vector3d(0, 0, -1)
};
/// <summary> /// <summary>
/// Intersects three <see cref="Plane"/>s at a <see cref="Vector3d"/>. Returns NaN for all components if two or more <see cref="Plane"/>s are parallel. /// Intersects three <see cref="Plane"/>s at a <see cref="Vector3d"/>. Returns NaN for all components if two or more <see cref="Plane"/>s are parallel.
/// </summary> /// </summary>
@@ -262,14 +274,32 @@ namespace LibBSP {
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <returns>A <c>List</c> of <see cref="Plane"/> objects.</returns> /// <returns>A <c>List</c> of <see cref="Plane"/> objects.</returns>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was null.</exception> /// <exception cref="ArgumentNullException"><paramref name="data" /> was null.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
/// <remarks>This function goes here since it can't go into Unity's Plane class, and so can't depend /// <remarks>This function goes here since it can't go into Unity's Plane class, and so can't depend
/// on having a constructor taking a byte array.</remarks> /// on having a constructor taking a byte array.</remarks>
public static List<Plane> LumpFactory(byte[] data, MapType type, int version = 0) { public static List<Plane> LumpFactory(byte[] data, MapType type, int version = 0) {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
int structLength = 0; int structLength = GetStructLength(type, version);
int numObjects = data.Length / structLength;
List<Plane> lump = new List<Plane>(numObjects);
for (int i = 0; i < numObjects; ++i) {
Vector3d normal = new Vector3d(BitConverter.ToSingle(data, structLength * i), BitConverter.ToSingle(data, (structLength * i) + 4), BitConverter.ToSingle(data, (structLength * i) + 8));
float distance = BitConverter.ToSingle(data, (structLength * i) + 12);
lump.Add(new Plane(normal, distance));
}
return lump;
}
/// <summary>
/// Gets this <see cref="Plane"/> as a <c>byte</c> array to be used in a BSP of type <see cref="type"/>.
/// </summary>
/// <param name="p">This <see cref="Plane"/>.</param>
/// <param name="type">The <see cref="MapType"/> of BSP this <see cref="Plane"/> is from.</param>
/// <param name="version">The version of the planes lump in the BSP.</param>
/// <returns><c>byte</c> array representing this <see cref="Plane"/>'s components.</returns>
public static byte[] GetBytes(this Plane p, MapType type, int version = 0) {
byte[] bytes = new byte[GetStructLength(type, version)];
switch (type) { switch (type) {
case MapType.Quake: case MapType.Quake:
case MapType.Nightfire: case MapType.Nightfire:
@@ -289,33 +319,39 @@ namespace LibBSP {
case MapType.Quake2: case MapType.Quake2:
case MapType.Daikatana: case MapType.Daikatana:
case MapType.TacticalInterventionEncrypted: { case MapType.TacticalInterventionEncrypted: {
structLength = 20; BitConverter.GetBytes(BestAxis(p)).CopyTo(bytes, 16);
break; break;
} }
case MapType.STEF2: }
case MapType.MOHAA: p.normal.GetBytes().CopyTo(bytes, 0);
case MapType.STEF2Demo: BitConverter.GetBytes(p.distance).CopyTo(bytes, 12);
case MapType.Raven: return bytes;
case MapType.Quake3: }
case MapType.FAKK:
case MapType.CoD: /// <summary>
case MapType.CoD2: /// Gets the axis this <see cref="Plane"/>'s normal is closest to (the <see cref="Plane"/>'s normal
case MapType.CoD4: { /// and the axis have the largest dot product).
structLength = 16; /// 0 = Positive Z
break; /// 1 = Negative Z
} /// 2 = Positive X
default: { /// 3 = Negative X
throw new ArgumentException("Map type " + type + " doesn't use a Plane lump or the lump is unknown."); /// 4 = Positive Y
/// 5 = Negative Y
/// </summary>
/// <param name="p">This <see cref="Plane"/>.</param>
/// <returns>The best-match axis for this <see cref="Plane"/>.</returns>
public static int BestAxis(this Plane p) {
int bestaxis = 0;
double best = 0; // "Best" dot product so far
for (int i = 0; i < 6; ++i) {
// For all possible axes, positive and negative
double dot = Vector3d.Dot(p.normal, baseAxes[i * 3]);
if (dot > best) {
best = dot;
bestaxis = i;
} }
} }
int numObjects = data.Length / structLength; return bestaxis;
List<Plane> lump = new List<Plane>(numObjects);
for (int i = 0; i < numObjects; ++i) {
Vector3d normal = new Vector3d(BitConverter.ToSingle(data, structLength * i), BitConverter.ToSingle(data, (structLength * i) + 4), BitConverter.ToSingle(data, (structLength * i) + 8));
float distance = BitConverter.ToSingle(data, (structLength * i) + 12);
lump.Add(new Plane(normal, distance));
}
return lump;
} }
/// <summary> /// <summary>
@@ -363,5 +399,45 @@ namespace LibBSP {
} }
} }
} }
public static int GetStructLength(MapType type, int version) {
int structLength = 0;
switch (type) {
case MapType.Quake:
case MapType.Nightfire:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.DMoMaM:
case MapType.Vindictus:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.TacticalInterventionEncrypted: {
structLength = 20;
break;
}
case MapType.STEF2:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
structLength = 16;
break;
}
}
return structLength;
}
} }
} }

View File

@@ -0,0 +1,36 @@
#if UNITY_3_4 || UNITY_3_5 || UNITY_4_0 || UNITY_4_0_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_5_3_OR_NEWER
#define UNITY
#endif
using System;
using System.Collections.Generic;
#if UNITY
using UnityEngine;
# endif
namespace LibBSP {
#if UNITY
using Vector2d = Vector2;
#endif
/// <summary>
/// Class containing helper methods for <see cref="Vector2d"/> objects.
/// </summary>
public static class Vector2dExtensions {
/// <summary>
/// Gets a <c>byte</c> array representing the components of this <see cref="Vector2d"/> as <c>float</c>s.
/// </summary>
/// <param name="vector">This <see cref="Vector2d"/>.</param>
/// <returns><c>byte</c> array with the components' bytes.</returns>
public static byte[] GetBytes(this Vector2d vector) {
byte[] ret = new byte[8];
byte[] bytes = BitConverter.GetBytes((float)vector.x);
bytes.CopyTo(ret, 0);
bytes = BitConverter.GetBytes((float)vector.y);
bytes.CopyTo(ret, 4);
return ret;
}
}
}

View File

@@ -0,0 +1,38 @@
#if UNITY_3_4 || UNITY_3_5 || UNITY_4_0 || UNITY_4_0_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_5_3_OR_NEWER
#define UNITY
#endif
using System;
using System.Collections.Generic;
#if UNITY
using UnityEngine;
#endif
namespace LibBSP {
#if UNITY
using Vector3d = Vector3;
#endif
/// <summary>
/// Class containing helper methods for <see cref="Vector3d"/> objects.
/// </summary>
public static class Vector3dExtensions {
/// <summary>
/// Gets a <c>byte</c> array representing the components of this <see cref="Vector3d"/> as <c>float</c>s.
/// </summary>
/// <param name="vector">This <see cref="Vector3d"/>.</param>
/// <returns><c>byte</c> array with the components' bytes.</returns>
public static byte[] GetBytes(this Vector3d vector) {
byte[] ret = new byte[12];
byte[] bytes = BitConverter.GetBytes((float)vector.x);
bytes.CopyTo(ret, 0);
bytes = BitConverter.GetBytes((float)vector.y);
bytes.CopyTo(ret, 4);
bytes = BitConverter.GetBytes((float)vector.z);
bytes.CopyTo(ret, 8);
return ret;
}
}
}

View File

@@ -0,0 +1,40 @@
#if UNITY_3_4 || UNITY_3_5 || UNITY_4_0 || UNITY_4_0_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_5_3_OR_NEWER
#define UNITY
#endif
using System;
using System.Collections.Generic;
#if UNITY
using UnityEngine;
#endif
namespace LibBSP {
#if UNITY
using Vector4d = Vector4;
#endif
/// <summary>
/// Class containing helper methods for <see cref="Vector4d"/> objects.
/// </summary>
public static class Vector4dExtensions {
/// <summary>
/// Gets a <c>byte</c> array representing the components of this <see cref="Vector4d"/> as <c>float</c>s.
/// </summary>
/// <param name="vector">This <see cref="Vector4d"/>.</param>
/// <returns><c>byte</c> array with the components' bytes.</returns>
public static byte[] GetBytes(this Vector4d vector) {
byte[] ret = new byte[16];
byte[] bytes = BitConverter.GetBytes((float)vector.x);
bytes.CopyTo(ret, 0);
bytes = BitConverter.GetBytes((float)vector.y);
bytes.CopyTo(ret, 4);
bytes = BitConverter.GetBytes((float)vector.z);
bytes.CopyTo(ret, 8);
bytes = BitConverter.GetBytes((float)vector.w);
bytes.CopyTo(ret, 12);
return ret;
}
}
}

View File

@@ -15,6 +15,7 @@ namespace LibBSP {
#if UNITY #if UNITY
using Vector2d = Vector2; using Vector2d = Vector2;
using Vector3d = Vector3; using Vector3d = Vector3;
using Vector4d = Vector4;
#if !OLDUNITY #if !OLDUNITY
using Vertex = UIVertex; using Vertex = UIVertex;
#endif #endif
@@ -122,6 +123,9 @@ namespace LibBSP {
result.color = ColorExtensions.FromArgb(data[27], data[24], data[25], data[26]); result.color = ColorExtensions.FromArgb(data[27], data[24], data[25], data[26]);
result.uv0 = new Vector2d(BitConverter.ToSingle(data, 28), BitConverter.ToSingle(data, 32)); result.uv0 = new Vector2d(BitConverter.ToSingle(data, 28), BitConverter.ToSingle(data, 32));
result.uv1 = new Vector2d(BitConverter.ToSingle(data, 36), BitConverter.ToSingle(data, 40)); result.uv1 = new Vector2d(BitConverter.ToSingle(data, 36), BitConverter.ToSingle(data, 40));
// Use these fields to store additional unknown information
result.tangent = new Vector4d(BitConverter.ToSingle(data, 44), BitConverter.ToSingle(data, 48), BitConverter.ToSingle(data, 52), BitConverter.ToSingle(data, 56));
result.uv3 = new Vector2d(BitConverter.ToSingle(data, 60), BitConverter.ToSingle(data, 64));
goto case MapType.Quake; goto case MapType.Quake;
} }
case MapType.MOHAA: case MapType.MOHAA:
@@ -137,14 +141,20 @@ namespace LibBSP {
case MapType.Raven: { case MapType.Raven: {
result.uv0 = new Vector2d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 16)); result.uv0 = new Vector2d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 16));
result.uv1 = new Vector2d(BitConverter.ToSingle(data, 20), BitConverter.ToSingle(data, 24)); result.uv1 = new Vector2d(BitConverter.ToSingle(data, 20), BitConverter.ToSingle(data, 24));
result.uv2 = new Vector2d(BitConverter.ToSingle(data, 28), BitConverter.ToSingle(data, 32));
result.uv3 = new Vector2d(BitConverter.ToSingle(data, 36), BitConverter.ToSingle(data, 40));
result.normal = new Vector3d(BitConverter.ToSingle(data, 52), BitConverter.ToSingle(data, 56), BitConverter.ToSingle(data, 60)); result.normal = new Vector3d(BitConverter.ToSingle(data, 52), BitConverter.ToSingle(data, 56), BitConverter.ToSingle(data, 60));
result.color = ColorExtensions.FromArgb(data[67], data[64], data[65], data[66]); result.color = ColorExtensions.FromArgb(data[67], data[64], data[65], data[66]);
// Use for two more float fields and two more colors.
// There's actually another field that seems to be color but I've only ever seen it be FFFFFFFF.
result.tangent = new Vector4d(BitConverter.ToSingle(data, 44), BitConverter.ToSingle(data, 48), BitConverter.ToSingle(data, 68), BitConverter.ToSingle(data, 72));
goto case MapType.Quake; goto case MapType.Quake;
} }
case MapType.STEF2: case MapType.STEF2:
case MapType.STEF2Demo: { case MapType.STEF2Demo: {
result.uv0 = new Vector2d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 16)); result.uv0 = new Vector2d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 16));
result.uv1 = new Vector2d(BitConverter.ToSingle(data, 20), BitConverter.ToSingle(data, 24)); result.uv1 = new Vector2d(BitConverter.ToSingle(data, 20), BitConverter.ToSingle(data, 24));
result.uv2 = new Vector2d(BitConverter.ToSingle(data, 28), 0);
result.color = ColorExtensions.FromArgb(data[35], data[32], data[33], data[34]); result.color = ColorExtensions.FromArgb(data[35], data[32], data[33], data[34]);
result.normal = new Vector3d(BitConverter.ToSingle(data, 36), BitConverter.ToSingle(data, 40), BitConverter.ToSingle(data, 44)); result.normal = new Vector3d(BitConverter.ToSingle(data, 36), BitConverter.ToSingle(data, 40), BitConverter.ToSingle(data, 44));
goto case MapType.Quake; goto case MapType.Quake;
@@ -186,66 +196,13 @@ namespace LibBSP {
/// <returns>A <c>List</c> of <see cref="Vertex"/> objects.</returns> /// <returns>A <c>List</c> of <see cref="Vertex"/> objects.</returns>
/// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception> /// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
/// <remarks>This function goes here since I can't put it into Unity's <c>UIVertex</c> class, and so I can't /// <remarks>This function goes here since it can't be in Unity's <c>UIVertex</c> class, and so I can't
/// depend on having a constructor taking a byte array.</remarks> /// depend on having a constructor taking a byte array.</remarks>
public static List<Vertex> LumpFactory(byte[] data, MapType type, int version = 0) { public static List<Vertex> LumpFactory(byte[] data, MapType type, int version = 0) {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
int structLength = 0; int structLength = GetStructLength(type, version);
switch (type) {
case MapType.Quake:
case MapType.Nightfire:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.Vindictus:
case MapType.DMoMaM: {
structLength = 12;
break;
}
case MapType.CoD: {
if (version == 0) {
goto case MapType.Quake3;
} else if (version == 1) {
goto case MapType.Quake;
}
break;
}
case MapType.CoD2: {
structLength = 68;
break;
}
case MapType.MOHAA:
case MapType.Quake3:
case MapType.FAKK: {
structLength = 44;
break;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
structLength = 48;
break;
}
case MapType.Raven: {
structLength = 80;
break;
}
default: {
throw new ArgumentException("Map type " + type + " doesn't use a Vertex lump or the lump is unknown.");
}
}
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<Vertex> lump = new List<Vertex>(numObjects); List<Vertex> lump = new List<Vertex>(numObjects);
byte[] bytes = new byte[structLength]; byte[] bytes = new byte[structLength];
@@ -325,5 +282,164 @@ namespace LibBSP {
} }
} }
/// <summary>
/// Gets the length of the <see cref="Vertex"/> struct for the given <see cref="MapType"/> and <paramref name="version"/>.
/// </summary>
/// <param name="type">The type of BSP to get struct length for.</param>
/// <param name="version">Version of the lump.</param>
/// <returns>The length of the struct for the given <see cref="MapType"/> of the given <paramref name="version"/>.</returns>
public static int GetStructLength(MapType type, int version) {
int structLength = 0;
switch (type) {
case MapType.Quake:
case MapType.Nightfire:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.Vindictus:
case MapType.DMoMaM: {
structLength = 12;
break;
}
case MapType.CoD: {
if (version == 0) {
goto case MapType.Quake3;
} else if (version == 1) {
goto case MapType.Quake;
}
break;
}
case MapType.CoD2: {
structLength = 68;
break;
}
case MapType.MOHAA:
case MapType.Quake3:
case MapType.FAKK: {
structLength = 44;
break;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
structLength = 48;
break;
}
case MapType.Raven: {
structLength = 80;
break;
}
default: {
throw new ArgumentException("Map type " + type + " doesn't use a Vertex lump or the lump is unknown.");
}
}
return structLength;
}
/// <summary>
/// Gets a byte array for this <see cref="Vertex"/> to be inserted into a map of type <paramref name="type"/> with lump version <paramref name="version"/>.
/// </summary>
/// <param name="v">This <see cref="Vertex"/>.</param>
/// <param name="type">The <see cref="MapType"/> this <see cref="Vertex"/> is from.</param>
/// <param name="version">The version of the lump this <see cref="Vertex"/> is from.</param>
/// <returns><c>byte</c> array of the data for this <see cref="Vertex"/>.</returns>
public static byte[] GetBytes(this Vertex v, MapType type, int version) {
byte[] bytes = new byte[GetStructLength(type, version)];
switch (type) {
case MapType.Quake:
case MapType.Nightfire:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.Vindictus:
case MapType.DMoMaM: {
v.position.GetBytes().CopyTo(bytes, 0);
break;
}
case MapType.CoD: {
if (version == 0) {
goto case MapType.Quake3;
} else if (version == 1) {
goto case MapType.Quake;
}
break;
}
case MapType.CoD2: {
v.normal.GetBytes().CopyTo(bytes, 12);
v.color.GetBytes().CopyTo(bytes, 24);
v.uv0.GetBytes().CopyTo(bytes, 28);
v.uv1.GetBytes().CopyTo(bytes, 36);
// Use these fields to store additional unknown information
v.tangent.GetBytes().CopyTo(bytes, 44);
v.uv3.GetBytes().CopyTo(bytes, 60);
break;
}
case MapType.MOHAA:
case MapType.Quake3:
case MapType.CoD4:
case MapType.FAKK: {
v.uv0.GetBytes().CopyTo(bytes, 12);
v.uv1.GetBytes().CopyTo(bytes, 20);
v.normal.GetBytes().CopyTo(bytes, 28);
v.color.GetBytes().CopyTo(bytes, 40);
goto case MapType.Quake;
}
case MapType.Raven: {
v.uv0.GetBytes().CopyTo(bytes, 12);
v.uv1.GetBytes().CopyTo(bytes, 20);
v.uv2.GetBytes().CopyTo(bytes, 28);
v.uv3.GetBytes().CopyTo(bytes, 36);
BitConverter.GetBytes((float)v.tangent.x).CopyTo(bytes, 44);
BitConverter.GetBytes((float)v.tangent.y).CopyTo(bytes, 48);
v.normal.GetBytes().CopyTo(bytes, 52);
v.color.GetBytes().CopyTo(bytes, 64);
BitConverter.GetBytes((float)v.tangent.z).CopyTo(bytes, 68);
BitConverter.GetBytes((float)v.tangent.w).CopyTo(bytes, 72);
// There's actually another field that seems to be a color but I've only ever seen it be FFFFFFFF.
bytes[76] = 255;
bytes[77] = 255;
bytes[78] = 255;
bytes[79] = 255;
goto case MapType.Quake;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
v.uv0.GetBytes().CopyTo(bytes, 12);
v.uv1.GetBytes().CopyTo(bytes, 20);
BitConverter.GetBytes(v.uv2.x).CopyTo(bytes, 28);
v.color.GetBytes().CopyTo(bytes, 32);
v.normal.GetBytes().CopyTo(bytes, 36);
goto case MapType.Quake;
}
default: {
bytes = new byte[0];
break;
}
}
return bytes;
}
} }
} }

View File

@@ -606,8 +606,19 @@ namespace LibBSP {
if (_staticProps == null) { if (_staticProps == null) {
if (gameLump != null && gameLump.ContainsKey(GameLumpType.sprp)) { if (gameLump != null && gameLump.ContainsKey(GameLumpType.sprp)) {
LumpInfo info = gameLump[GameLumpType.sprp]; LumpInfo info = gameLump[GameLumpType.sprp];
byte[] thisLump = new byte[info.length]; byte[] thisLump;
Array.Copy(gameLump.rawData, info.offset - gameLump.gameLumpOffset, thisLump, 0, info.length); // GameLump lumps may have their offset specified from either the beginning of the GameLump, or the beginning of the file.
if (gameLump.GetLowestLumpOffset() < this[GameLump.GetIndexForLump(version)].offset) {
thisLump = reader.ReadLump(new LumpInfo() {
ident = info.ident,
flags = info.flags,
version = info.version,
offset = info.offset + this[GameLump.GetIndexForLump(version)].offset,
length = info.length
});
} else {
thisLump = reader.ReadLump(info);
}
_staticProps = StaticProp.LumpFactory(thisLump, version, info.version); _staticProps = StaticProp.LumpFactory(thisLump, version, info.version);
} }
} }
@@ -832,9 +843,16 @@ namespace LibBSP {
int count = (int)(countProperty.GetGetMethod().Invoke(o, null)); int count = (int)(countProperty.GetGetMethod().Invoke(o, null));
// Get the lump from this class // Get the lump from this class
List<T> theLump = targetLump.GetGetMethod().Invoke(this, null) as List<T>; IList<T> theLump = targetLump.GetGetMethod().Invoke(this, null) as IList<T>;
return theLump.GetRange(index, count); // Copy items from the lump into a return list.
// IList<T> lacks AddRange and this is faster and creates less garbage than any Linq trickery I could come up with.
// Passing references to IList<T> out of this method just eats obscene amounts of memory until the system runs out.
List<T> ret = new List<T>(count);
for (int i = 0; i < count; ++i) {
ret.Add(theLump[index + i]);
}
return ret;
} }
} }

View File

@@ -8,10 +8,270 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct Brush { public struct Brush {
[Index("brushSides")] public int firstSide { get; private set; } public byte[] data;
[Count("brushSides")] public int numSides { get; private set; } public MapType type;
public int texture { get; private set; } public int version;
public int contents { get; private set; }
[Index("brushSides")] public int firstSide {
get {
switch (type) {
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK: {
return BitConverter.ToInt32(data, 0);
}
case MapType.Nightfire:
case MapType.STEF2: {
return BitConverter.ToInt32(data, 4);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK: {
bytes.CopyTo(data, 0);
break;
}
case MapType.Nightfire:
case MapType.STEF2: {
bytes.CopyTo(data, 4);
break;
}
}
}
}
[Count("brushSides")] public int numSides {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
return BitConverter.ToInt16(data, 0);
}
case MapType.STEF2: {
return BitConverter.ToInt32(data, 0);
}
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK: {
return BitConverter.ToInt32(data, 4);
}
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 8);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
data[0] = bytes[0];
data[1] = bytes[1];
break;
}
case MapType.STEF2: {
bytes.CopyTo(data, 0);
break;
}
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK: {
bytes.CopyTo(data, 4);
break;
}
case MapType.Nightfire: {
bytes.CopyTo(data, 8);
break;
}
}
}
}
public int texture {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
return BitConverter.ToInt16(data, 2);
}
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2: {
return BitConverter.ToInt32(data, 8);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
bytes.CopyTo(data, 2);
break;
}
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2: {
bytes.CopyTo(data, 8);
break;
}
}
}
}
public int contents {
get {
switch (type) {
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 0);
}
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
return BitConverter.ToInt32(data, 8);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Nightfire: {
bytes.CopyTo(data, 0);
break;
}
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
bytes.CopyTo(data, 8);
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="Brush"/> object from a <c>byte</c> array. /// Creates a new <see cref="Brush"/> object from a <c>byte</c> array.
@@ -20,70 +280,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public Brush(byte[] data, MapType type, int version = 0) : this() { public Brush(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
firstSide = -1; this.data = data;
numSides = -1; this.type = type;
texture = -1; this.version = version;
contents = -1;
switch (type) {
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
firstSide = BitConverter.ToInt32(data, 0);
numSides = BitConverter.ToInt32(data, 4);
contents = BitConverter.ToInt32(data, 8);
break;
}
case MapType.Nightfire: {
contents = BitConverter.ToInt32(data, 0);
firstSide = BitConverter.ToInt32(data, 4);
numSides = BitConverter.ToInt32(data, 8);
break;
}
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK: {
firstSide = BitConverter.ToInt32(data, 0);
numSides = BitConverter.ToInt32(data, 4);
texture = BitConverter.ToInt32(data, 8);
break;
}
case MapType.STEF2: {
numSides = BitConverter.ToInt32(data, 0);
firstSide = BitConverter.ToInt32(data, 4);
texture = BitConverter.ToInt32(data, 8);
break;
}
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
numSides = BitConverter.ToInt16(data, 0);
texture = BitConverter.ToInt16(data, 2);
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Brush class.");
}
}
} }
/// <summary> /// <summary>
@@ -139,8 +342,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<Brush> lump = new List<Brush>(numObjects); List<Brush> lump = new List<Brush>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new Brush(bytes, type, version)); lump.Add(new Brush(bytes, type, version));
} }

View File

@@ -1,23 +1,374 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace LibBSP { namespace LibBSP {
/// <summary> /// <summary>
/// Holds the data used by the brush side structures of all formats of BSP. /// Holds the data used by the brush side structures of all formats of BSP.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct BrushSide { public struct BrushSide {
// Call of Duty's format sucks. The first field is either a float or an int public byte[] data;
// depending on whether or not it's one of the first six sides in a brush. public MapType type;
[FieldOffset(0)] public int plane; public int version;
[FieldOffset(0)] public float dist;
[FieldOffset(4)] public int texture; // -1 is a valid texture index in Quake 2. However it means "unused" there public int plane {
[FieldOffset(8)] public int face; get {
[FieldOffset(12)] public int displacement; // In theory, this should always point to the side's displacement info. In practice, displacement brushes are removed on compile, leaving only the faces. switch (type) {
[FieldOffset(16)] public bool bevel; case MapType.SiN:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
return BitConverter.ToUInt16(data, 0);
}
case MapType.Raven:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.MOHAA:
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 0);
}
case MapType.STEF2:
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 4);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.SiN:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
data[0] = bytes[0];
data[1] = bytes[1];
break;
}
case MapType.Raven:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.MOHAA:
case MapType.Vindictus: {
bytes.CopyTo(data, 0);
break;
}
case MapType.STEF2:
case MapType.Nightfire: {
bytes.CopyTo(data, 4);
break;
}
}
}
}
public float dist {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
return BitConverter.ToSingle(data, 0);
}
default: {
return float.NaN;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
bytes.CopyTo(data, 0);
break;
}
}
}
}
public int texture {
get {
switch (type) {
case MapType.STEF2: {
return BitConverter.ToInt32(data, 0);
}
case MapType.SiN:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
return BitConverter.ToInt16(data, 2);
}
case MapType.Vindictus:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.MOHAA:
case MapType.Raven: {
return BitConverter.ToInt32(data, 4);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.STEF2: {
bytes.CopyTo(data, 0);
break;
}
case MapType.SiN:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
data[2] = bytes[0];
data[3] = bytes[1];
break;
}
case MapType.Vindictus:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.MOHAA:
case MapType.Raven: {
bytes.CopyTo(data, 4);
break;
}
}
}
}
public int face {
get {
switch (type) {
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 0);
}
case MapType.Raven: {
return BitConverter.ToInt32(data, 8);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Nightfire: {
bytes.CopyTo(data, 0);
break;
}
case MapType.Raven: {
bytes.CopyTo(data, 8);
break;
}
}
}
}
public int displacement {
get {
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
return BitConverter.ToInt16(data, 4);
}
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 8);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
data[4] = bytes[0];
data[5] = bytes[1];
break;
}
case MapType.Vindictus: {
bytes.CopyTo(data, 8);
break;
}
}
}
}
public bool bevel {
get {
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
return data[6] > 0;
}
case MapType.Vindictus: {
return data[12] > 0;
}
default: {
return false;
}
}
}
set {
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
data[6] = (byte)(value ? 1 : 0);
break;
}
case MapType.Vindictus: {
data[12] = (byte)(value ? 1 : 0);
break;
}
}
}
}
public bool thin {
get {
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
return data[7] > 0;
}
default: {
return false;
}
}
}
set {
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
data[7] = (byte)(value ? 1 : 0);
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="BrushSide"/> object from a <c>byte</c> array. /// Creates a new <see cref="BrushSide"/> object from a <c>byte</c> array.
@@ -26,80 +377,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public BrushSide(byte[] data, MapType type, int version = 0) : this() { public BrushSide(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
plane = -1; this.data = data;
texture = -1; this.type = type;
face = -1; this.version = version;
displacement = -1;
bevel = false;
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.MOHAA: {
plane = BitConverter.ToInt32(data, 0);
texture = BitConverter.ToInt32(data, 4);
break;
}
case MapType.STEF2: {
texture = BitConverter.ToInt32(data, 0);
plane = BitConverter.ToInt32(data, 4);
break;
}
case MapType.Raven: {
plane = BitConverter.ToInt32(data, 0);
texture = BitConverter.ToInt32(data, 4);
face = BitConverter.ToInt32(data, 8);
break;
}
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
this.displacement = BitConverter.ToInt16(data, 4);
// In little endian format, this byte takes the least significant bits of a short
// and can therefore be used for all Source engine formats, including Portal 2.
this.bevel = data[6] > 0;
goto case MapType.SiN;
}
case MapType.SiN:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SoF: {
plane = BitConverter.ToUInt16(data, 0);
texture = BitConverter.ToInt16(data, 2);
break;
}
case MapType.Vindictus: {
plane = BitConverter.ToInt32(data, 0);
texture = BitConverter.ToInt32(data, 4);
displacement = BitConverter.ToInt32(data, 8);
bevel = data[12] > 0;
break;
}
case MapType.Nightfire: {
face = BitConverter.ToInt32(data, 0);
plane = BitConverter.ToInt32(data, 4);
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the BrushSide class.");
}
}
} }
/// <summary> /// <summary>
@@ -161,8 +445,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<BrushSide> lump = new List<BrushSide>(numObjects); List<BrushSide> lump = new List<BrushSide>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; i++) { for (int i = 0; i < numObjects; i++) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new BrushSide(bytes, type, version)); lump.Add(new BrushSide(bytes, type, version));
} }

View File

@@ -18,8 +18,96 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct Cubemap { public struct Cubemap {
public Vector3d origin { get; private set; } public byte[] data;
public int size { get; private set; } public MapType type;
public int version;
public Vector3d origin {
get {
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalInterventionEncrypted:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.DMoMaM: {
return new Vector3d(BitConverter.ToInt32(data, 0), BitConverter.ToInt32(data, 4), BitConverter.ToInt32(data, 8));
}
default: {
return new Vector3d(float.NaN, float.NaN, float.NaN);
}
}
}
set {
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalInterventionEncrypted:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.DMoMaM: {
value.GetBytes().CopyTo(data, 0);
break;
}
}
}
}
public int size {
get {
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalInterventionEncrypted:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.DMoMaM: {
return BitConverter.ToInt32(data, 12);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalInterventionEncrypted:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.DMoMaM: {
bytes.CopyTo(data, 12);
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="Cubemap"/> object from a <c>byte</c> array. /// Creates a new <see cref="Cubemap"/> object from a <c>byte</c> array.
@@ -28,32 +116,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public Cubemap(byte[] data, MapType type, int version = 0) : this() { public Cubemap(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
switch (type) { this.data = data;
case MapType.Source17: this.type = type;
case MapType.Source18: this.version = version;
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalInterventionEncrypted:
case MapType.L4D2:
case MapType.Vindictus:
case MapType.DMoMaM: {
origin = new Vector3d(BitConverter.ToInt32(data, 0), BitConverter.ToInt32(data, 4), BitConverter.ToInt32(data, 8));
size = BitConverter.ToInt32(data, 12);
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the SourceCubemap class.");
}
}
} }
/// <summary> /// <summary>
@@ -92,8 +161,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<Cubemap> lump = new List<Cubemap>(numObjects); List<Cubemap> lump = new List<Cubemap>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new Cubemap(bytes, type, version)); lump.Add(new Cubemap(bytes, type, version));
} }

View File

@@ -18,17 +18,175 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct DisplacementInfo { public struct DisplacementInfo {
public Vector3d startPosition { get; private set; } public byte[] data;
public int dispVertStart { get; private set; } public MapType type;
public int dispTriStart { get; private set; } public int version;
public int power { get; private set; }
public int minTess { get; private set; } public Vector3d startPosition {
public float smoothingAngle { get; private set; } get {
public int contents { get; private set; } return new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8));
public ushort mapFace { get; private set; } }
public int lightmapAlphaStart { get; private set; } set {
public int lightmapSamplePositionStart { get; private set; } value.GetBytes().CopyTo(data, 0);
public uint[] allowedVerts { get; private set; } }
}
public int dispVertStart {
get {
return BitConverter.ToInt32(data, 12);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 12);
}
}
public int dispTriStart {
get {
return BitConverter.ToInt32(data, 16);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 16);
}
}
public int power {
get {
return BitConverter.ToInt32(data, 20);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 20);
}
}
public int minTess {
get {
return BitConverter.ToInt32(data, 24);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 24);
}
}
public float smoothingAngle {
get {
return BitConverter.ToSingle(data, 28);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 28);
}
}
public int contents {
get {
return BitConverter.ToInt32(data, 32);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 32);
}
}
public ushort mapFace {
get {
return BitConverter.ToUInt16(data, 36);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 36);
}
}
public int lightmapAlphaStart {
get {
return BitConverter.ToInt32(data, 38);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 38);
}
}
public int lightmapSamplePositionStart {
get {
return BitConverter.ToInt32(data, 42);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 42);
}
}
public uint[] allowedVerts {
get {
uint[] allowedVerts = new uint[10];
int offset = -1;
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
offset = 136;
break;
}
case MapType.Source22: {
offset = 140;
break;
}
case MapType.Source23: {
offset = 144;
break;
}
case MapType.Vindictus: {
offset = 192;
break;
}
}
if (offset >= 0) {
for (int i = 0; i < 10; ++i) {
allowedVerts[i] = BitConverter.ToUInt32(data, offset + (i * 4));
}
}
return allowedVerts;
}
set {
if (value.Length != 10) {
throw new ArgumentException("AllowedVerts array must have 10 elements.");
}
int offset = -1;
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
offset = 136;
break;
}
case MapType.Source22: {
offset = 140;
break;
}
case MapType.Source23: {
offset = 144;
break;
}
case MapType.Vindictus: {
offset = 192;
break;
}
}
if (offset >= 0) {
for (int i = 0; i < value.Length; ++i) {
BitConverter.GetBytes(value[i]).CopyTo(data, offset + (i * 4));
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="DisplacementInfo"/> object from a <c>byte</c> array. /// Creates a new <see cref="DisplacementInfo"/> object from a <c>byte</c> array.
@@ -37,55 +195,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public DisplacementInfo(byte[] data, MapType type, int version = 0) : this() { public DisplacementInfo(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
startPosition = new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)); this.data = data;
dispVertStart = BitConverter.ToInt32(data, 12); this.type = type;
dispTriStart = BitConverter.ToInt32(data, 16); this.version = version;
power = BitConverter.ToInt32(data, 20);
minTess = BitConverter.ToInt32(data, 24);
smoothingAngle = BitConverter.ToSingle(data, 28);
contents = BitConverter.ToInt32(data, 32);
mapFace = BitConverter.ToUInt16(data, 36);
lightmapAlphaStart = BitConverter.ToInt32(data, 38);
lightmapSamplePositionStart = BitConverter.ToInt32(data, 42);
allowedVerts = new uint[10];
int offset = 0;
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
offset = 136;
break;
}
case MapType.Source22: {
offset = 140;
break;
}
case MapType.Source23: {
offset = 144;
break;
}
case MapType.Vindictus: {
offset = 192;
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the SourceDispInfo class.");
}
}
for (int i = 0; i < 10; ++i) {
allowedVerts[i] = BitConverter.ToUInt32(data, offset + (i * 4));
}
} }
/// <summary> /// <summary>
@@ -133,8 +249,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<DisplacementInfo> lump = new List<DisplacementInfo>(numObjects); List<DisplacementInfo> lump = new List<DisplacementInfo>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new DisplacementInfo(bytes, type, version)); lump.Add(new DisplacementInfo(bytes, type, version));
} }

View File

@@ -17,9 +17,45 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct DisplacementVertex { public struct DisplacementVertex {
public Vector3d normal { get; private set; } // The normalized vector direction this vertex points from "flat" public byte[] data;
public float dist { get; private set; } // Magnitude of normal, before normalization public MapType type;
public float alpha { get; private set; } // Alpha value of texture at this vertex public int version;
/// <summary>
/// The normalized vector direction this vertex points from "flat".
/// </summary>
public Vector3d normal {
get {
return new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8));
}
set {
value.GetBytes().CopyTo(data, 0);
}
}
/// <summary>
/// Magnitude of normal, before normalization.
/// </summary>
public float dist {
get {
return BitConverter.ToSingle(data, 12);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 12);
}
}
/// <summary>
/// Alpha value of texture at this vertex.
/// </summary>
public float alpha {
get {
return BitConverter.ToSingle(data, 16);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 16);
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="DisplacementVertex"/> object from a <c>byte</c> array. /// Creates a new <see cref="DisplacementVertex"/> object from a <c>byte</c> array.
@@ -32,9 +68,9 @@ namespace LibBSP {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
normal = new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)); this.data = data;
dist = BitConverter.ToSingle(data, 12); this.type = type;
alpha = BitConverter.ToSingle(data, 16); this.version = version;
} }
/// <summary> /// <summary>

View File

@@ -8,8 +8,129 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct Edge { public struct Edge {
public int firstVertex { get; private set; } public byte[] data;
public int secondVertex { get; private set; } public MapType type;
public int version;
public int firstVertex {
get {
switch (type) {
case MapType.Quake:
case MapType.SiN:
case MapType.Daikatana:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Quake2:
case MapType.SoF: {
return BitConverter.ToUInt16(data, 0);
}
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 0);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Quake:
case MapType.SiN:
case MapType.Daikatana:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Quake2:
case MapType.SoF: {
data[0] = bytes[0];
data[1] = bytes[1];
break;
}
case MapType.Vindictus: {
bytes.CopyTo(data, 0);
break;
}
}
}
}
public int secondVertex {
get {
switch (type) {
case MapType.Quake:
case MapType.SiN:
case MapType.Daikatana:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Quake2:
case MapType.SoF: {
return BitConverter.ToUInt16(data, 2);
}
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 4);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Quake:
case MapType.SiN:
case MapType.Daikatana:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Quake2:
case MapType.SoF: {
data[2] = bytes[0];
data[3] = bytes[1];
break;
}
case MapType.Vindictus: {
BitConverter.GetBytes(value).CopyTo(data, 4);
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="Edge"/> object from a <c>byte</c> array. /// Creates a new <see cref="Edge"/> object from a <c>byte</c> array.
@@ -18,41 +139,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public Edge(byte[] data, MapType type, int version = 0) : this() { public Edge(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
switch (type) { this.data = data;
case MapType.Quake: this.type = type;
case MapType.SiN: this.version = version;
case MapType.Daikatana:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Quake2:
case MapType.SoF: {
firstVertex = BitConverter.ToUInt16(data, 0);
secondVertex = BitConverter.ToUInt16(data, 2);
break;
}
case MapType.Vindictus: {
firstVertex = BitConverter.ToInt32(data, 0);
secondVertex = BitConverter.ToInt32(data, 4);
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Edge class.");
}
}
} }
/// <summary> /// <summary>
@@ -99,8 +192,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<Edge> lump = new List<Edge>(numObjects); List<Edge> lump = new List<Edge>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new Edge(bytes, type, version)); lump.Add(new Edge(bytes, type, version));
} }

File diff suppressed because it is too large Load Diff

View File

@@ -8,17 +8,274 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct LODTerrain { public struct LODTerrain {
public byte flags { get; private set; } public byte[] data;
public byte scale { get; private set; } public MapType type;
public byte[] lightmapCoords { get; private set; } public int version;
public float[] textureCoords { get; private set; }
public sbyte x { get; private set; } public byte flags {
public sbyte y { get; private set; } get {
public short baseZ { get; private set; } switch (type) {
public ushort texture { get; private set; } case MapType.MOHAA: {
public short lightmap { get; private set; } return data[0];
public short[][] vertexFlags { get; private set; } }
public byte[][] heightmap { get; private set; } default: {
return 0;
}
}
}
set {
switch (type) {
case MapType.MOHAA: {
data[0] = value;
break;
}
}
}
}
public byte scale {
get {
switch (type) {
case MapType.MOHAA: {
return data[1];
}
default: {
return 0;
}
}
}
set {
switch (type) {
case MapType.MOHAA: {
data[1] = value;
break;
}
}
}
}
public byte[] lightmapCoords {
get {
switch (type) {
case MapType.MOHAA: {
return new byte[] { data[2], data[3] };
}
default: {
return null;
}
}
}
set {
if (value.Length != 2) {
throw new ArgumentException("LightmapCoords array must have 2 elements.");
}
switch (type) {
case MapType.MOHAA: {
data[2] = value[0];
data[3] = value[1];
break;
}
}
}
}
public float[] textureCoords {
get {
switch (type) {
case MapType.MOHAA: {
return new float[] {
BitConverter.ToSingle(data, 4),
BitConverter.ToSingle(data, 8),
BitConverter.ToSingle(data, 12),
BitConverter.ToSingle(data, 16),
BitConverter.ToSingle(data, 20),
BitConverter.ToSingle(data, 24),
BitConverter.ToSingle(data, 28),
BitConverter.ToSingle(data, 32),
};
}
default: {
return null;
}
}
}
set {
if (value.Length != 8) {
throw new ArgumentException("TextureCoords array must have 8 elements.");
}
switch (type) {
case MapType.MOHAA: {
int offset = 4;
for (int i = 0; i < value.Length; ++i) {
BitConverter.GetBytes(value[i]).CopyTo(data, offset + (i * 4));
}
break;
}
}
}
}
public sbyte x {
get {
switch (type) {
case MapType.MOHAA: {
return (sbyte)data[36];
}
default: {
return 0;
}
}
}
set {
switch (type) {
case MapType.MOHAA: {
data[36] = (byte)value;
break;
}
}
}
}
public sbyte y {
get {
switch (type) {
case MapType.MOHAA: {
return (sbyte)data[37];
}
default: {
return 0;
}
}
}
set {
switch (type) {
case MapType.MOHAA: {
data[37] = (byte)value;
break;
}
}
}
}
public short baseZ {
get {
switch (type) {
case MapType.MOHAA: {
return BitConverter.ToInt16(data, 38);
}
default: {
return 0;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.MOHAA: {
bytes.CopyTo(data, 38);
break;
}
}
}
}
public ushort texture {
get {
switch (type) {
case MapType.MOHAA: {
return BitConverter.ToUInt16(data, 40);
}
default: {
return 0;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.MOHAA: {
bytes.CopyTo(data, 40);
break;
}
}
}
}
public short lightmap {
get {
switch (type) {
case MapType.MOHAA: {
return BitConverter.ToInt16(data, 42);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.MOHAA: {
bytes.CopyTo(data, 42);
break;
}
}
}
}
public ushort[,] vertexFlags {
get {
ushort[,] ret = new ushort[2,63];
switch (type) {
case MapType.MOHAA: {
for (int i = 0; i < ret.GetLength(0); ++i) {
for (int j = 0; j < ret.GetLength(1); ++j) {
ret[i, j] = BitConverter.ToUInt16(data, 52 + (i * 126) + (j * 2));
}
}
break;
}
}
return ret;
}
set {
if (value.GetLength(0) != 2 || value.GetLength(1) != 63) {
throw new ArgumentException("VertexFlags array must be size (2, 63) elements.");
}
for (int i = 0; i < value.GetLength(0); ++i) {
for (int j = 0; j < value.GetLength(1); ++j) {
BitConverter.GetBytes(value[i, j]).CopyTo(data, 52 + (i * 126) + (j * 2));
}
}
}
}
public byte[,] heightmap {
get {
byte[,] ret = new byte[9, 9];
switch (type) {
case MapType.MOHAA: {
for (int i = 0; i < ret.GetLength(0); ++i) {
for (int j = 0; j < ret.GetLength(1); ++j) {
ret[i, j] = data[304 + (i * 9) + j];
}
}
break;
}
}
return ret;
}
set {
if (value.GetLength(0) != 9 || value.GetLength(1) != 9) {
throw new ArgumentException("Heightmap array must be size (9, 9) elements.");
}
for (int i = 0; i < value.GetLength(0); ++i) {
for (int j = 0; j < value.GetLength(1); ++j) {
data[304 + (i * 9) + j] = value[i, j];
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="LODTerrain"/> object from a <c>byte</c> array. /// Creates a new <see cref="LODTerrain"/> object from a <c>byte</c> array.
@@ -27,47 +284,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public LODTerrain(byte[] data, MapType type, int version = 0) : this() { public LODTerrain(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
lightmapCoords = new byte[2]; this.data = data;
textureCoords = new float[8]; this.type = type;
vertexFlags = new short[2][]; this.version = version;
heightmap = new byte[9][];
switch (type) {
case MapType.MOHAA: {
flags = data[0];
scale = data[1];
lightmapCoords[0] = data[2];
lightmapCoords[1] = data[3];
for (int i = 0; i < 8; ++i) {
textureCoords[i] = BitConverter.ToSingle(data, 4 + (4 * i));
}
x = (sbyte)data[36];
y = (sbyte)data[37];
baseZ = BitConverter.ToInt16(data, 38);
texture = BitConverter.ToUInt16(data, 40);
lightmap = BitConverter.ToInt16(data, 42);
for (int i = 0; i < vertexFlags.Length; ++i) {
vertexFlags[i] = new short[63];
for (int j = 0; j < vertexFlags[i].Length; ++j) {
vertexFlags[i][j] = BitConverter.ToInt16(data, 52 + (i * 126) + (j * 2));
}
}
for (int i = 0; i < heightmap.Length; ++i) {
heightmap[i] = new byte[9];
for (int j = 0; j < heightmap[i].Length; ++j) {
heightmap[i][j] = data[304 + (i * 9) + j];
}
}
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the LODTerrain class.");
}
}
} }
/// <summary> /// <summary>
@@ -95,8 +318,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<LODTerrain> lump = new List<LODTerrain>(numObjects); List<LODTerrain> lump = new List<LODTerrain>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new LODTerrain(bytes, type, version)); lump.Add(new LODTerrain(bytes, type, version));
} }

View File

@@ -8,12 +8,457 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct Leaf { public struct Leaf {
public int contents { get; private set; } public byte[] data;
[Index("markBrushes")] public int firstMarkBrush { get; private set; } public MapType type;
[Count("markBrushes")] public int numMarkBrushes { get; private set; } public int version;
[Index("markSurfaces")] public int firstMarkFace { get; private set; }
[Count("markSurfaces")] public int numMarkFaces { get; private set; } public int contents {
public int pvs { get; private set; } get {
switch (type) {
case MapType.SoF:
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana:
case MapType.Vindictus:
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 0);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.SoF:
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana:
case MapType.Vindictus:
case MapType.Nightfire: {
bytes.CopyTo(data, 0);
break;
}
}
}
}
[Index("markBrushes")] public int firstMarkBrush {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
return BitConverter.ToInt32(data, 16);
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana: {
return BitConverter.ToUInt16(data, 24);
}
case MapType.SoF: {
return BitConverter.ToUInt16(data, 26);
}
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.Raven:
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 40);
}
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 44);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
bytes.CopyTo(data, 16);
break;
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana: {
data[24] = bytes[0];
data[25] = bytes[1];
break;
}
case MapType.SoF: {
data[26] = bytes[0];
data[27] = bytes[1];
break;
}
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.Raven:
case MapType.Nightfire: {
bytes.CopyTo(data, 40);
break;
}
case MapType.Vindictus: {
bytes.CopyTo(data, 44);
break;
}
}
}
}
[Count("markBrushes")] public int numMarkBrushes {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
return BitConverter.ToInt32(data, 20);
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana: {
return BitConverter.ToUInt16(data, 26);
}
case MapType.SoF: {
return BitConverter.ToUInt16(data, 28);
}
case MapType.Nightfire:
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.Raven: {
return BitConverter.ToInt32(data, 44);
}
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 48);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
bytes.CopyTo(data, 20);
break;
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana: {
data[26] = bytes[0];
data[27] = bytes[1];
break;
}
case MapType.SoF: {
data[28] = bytes[0];
data[29] = bytes[1];
break;
}
case MapType.Nightfire:
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.Raven: {
bytes.CopyTo(data, 44);
break;
}
case MapType.Vindictus: {
bytes.CopyTo(data, 48);
break;
}
}
}
}
[Index("markSurfaces")] public int firstMarkFace {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
return BitConverter.ToInt32(data, 8);
}
case MapType.Quake:
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana: {
return BitConverter.ToUInt16(data, 20);
}
case MapType.SoF: {
return BitConverter.ToUInt16(data, 22);
}
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.Raven:
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 32);
}
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 36);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
bytes.CopyTo(data, 8);
break;
}
case MapType.Quake:
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana: {
data[20] = bytes[0];
data[21] = bytes[1];
break;
}
case MapType.SoF: {
data[22] = bytes[0];
data[23] = bytes[1];
break;
}
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.Raven:
case MapType.Nightfire: {
bytes.CopyTo(data, 32);
break;
}
case MapType.Vindictus: {
bytes.CopyTo(data, 36);
break;
}
}
}
}
[Count("markSurfaces")] public int numMarkFaces {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
return BitConverter.ToInt32(data, 12);
}
case MapType.Quake:
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana: {
return BitConverter.ToUInt16(data, 22);
}
case MapType.SoF: {
return BitConverter.ToUInt16(data, 24);
}
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.Raven:
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 36);
}
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 40);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
bytes.CopyTo(data, 12);
break;
}
case MapType.Quake:
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana: {
data[22] = bytes[0];
data[23] = bytes[1];
break;
}
case MapType.SoF: {
data[24] = bytes[0];
data[25] = bytes[1];
break;
}
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.Raven:
case MapType.Nightfire: {
bytes.CopyTo(data, 36);
break;
}
case MapType.Vindictus: {
bytes.CopyTo(data, 40);
break;
}
}
}
}
public int pvs {
get {
switch (type) {
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 4);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Nightfire: {
bytes.CopyTo(data, 4);
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="Leaf"/> object from a <c>byte</c> array. /// Creates a new <see cref="Leaf"/> object from a <c>byte</c> array.
@@ -22,86 +467,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public Leaf(byte[] data, MapType type, int version = 0) : this() { public Leaf(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
contents = -1; this.data = data;
firstMarkBrush = -1; this.type = type;
numMarkBrushes = -1; this.version = version;
firstMarkFace = -1;
numMarkFaces = -1;
pvs = -1;
switch (type) {
case MapType.SoF: {
contents = BitConverter.ToInt32(data, 0);
firstMarkFace = BitConverter.ToUInt16(data, 22);
numMarkFaces = BitConverter.ToUInt16(data, 24);
firstMarkBrush = BitConverter.ToUInt16(data, 26);
numMarkBrushes = BitConverter.ToUInt16(data, 28);
break;
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana: {
contents = BitConverter.ToInt32(data, 0);
firstMarkBrush = BitConverter.ToUInt16(data, 24);
numMarkBrushes = BitConverter.ToUInt16(data, 26);
goto case MapType.Quake;
}
case MapType.Quake: {
firstMarkFace = BitConverter.ToUInt16(data, 20);
numMarkFaces = BitConverter.ToUInt16(data, 22);
break;
}
case MapType.Vindictus: {
contents = BitConverter.ToInt32(data, 0);
firstMarkFace = BitConverter.ToInt32(data, 36);
numMarkFaces = BitConverter.ToInt32(data, 40);
firstMarkBrush = BitConverter.ToInt32(data, 44);
numMarkBrushes = BitConverter.ToInt32(data, 48);
break;
}
case MapType.Nightfire: {
contents = BitConverter.ToInt32(data, 0);
pvs = BitConverter.ToInt32(data, 4);
goto case MapType.Raven;
}
case MapType.Quake3:
case MapType.FAKK:
case MapType.STEF2Demo:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.Raven: {
firstMarkFace = BitConverter.ToInt32(data, 32);
numMarkFaces = BitConverter.ToInt32(data, 36);
firstMarkBrush = BitConverter.ToInt32(data, 40);
numMarkBrushes = BitConverter.ToInt32(data, 44);
break;
}
case MapType.CoD: {
firstMarkFace = BitConverter.ToInt32(data, 8);
numMarkFaces = BitConverter.ToInt32(data, 12);
firstMarkBrush = BitConverter.ToInt32(data, 16);
numMarkBrushes = BitConverter.ToInt32(data, 20);
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Leaf class.");
}
}
} }
/// <summary> /// <summary>
@@ -143,7 +515,8 @@ namespace LibBSP {
structLength = 56; structLength = 56;
break; break;
} }
case MapType.CoD: { case MapType.CoD:
case MapType.CoD2: {
structLength = 36; structLength = 36;
break; break;
} }
@@ -171,8 +544,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<Leaf> lump = new List<Leaf>(numObjects); List<Leaf> lump = new List<Leaf>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new Leaf(bytes, type, version)); lump.Add(new Leaf(bytes, type, version));
} }
@@ -221,6 +594,9 @@ namespace LibBSP {
case MapType.CoD: { case MapType.CoD: {
return 21; return 21;
} }
case MapType.CoD2: {
return 26;
}
default: { default: {
return -1; return -1;
} }

View File

@@ -20,9 +20,9 @@ namespace LibBSP {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
int structLength = 20; int structLength = 20;
byte[] bytes = new byte[structLength];
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
Add(new DisplacementVertex(bytes, type, version)); Add(new DisplacementVertex(bytes, type, version));
} }

View File

@@ -19,27 +19,6 @@ namespace LibBSP {
/// </summary> /// </summary>
public class GameLump : Dictionary<GameLumpType, LumpInfo> { public class GameLump : Dictionary<GameLumpType, LumpInfo> {
private byte[] _rawData;
private int _gameLumpOffset;
/// <summary>
/// Byte array representing the raw data read from the BSP for the game lump.
/// </summary>
public byte[] rawData {
get {
return _rawData;
}
}
/// <summary>
/// The amount to subtract from all the <see cref="LumpInfo.offset"/> values to find the offset relative to the start of the Game Lump data. May be 0.
/// </summary>
public int gameLumpOffset {
get {
return _gameLumpOffset;
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="GameLump"/> object by parsing a <c>byte</c> array into a <c>Dictionary</c> of <see cref="LumpInfo"/> objects. /// Creates a new <see cref="GameLump"/> object by parsing a <c>byte</c> array into a <c>Dictionary</c> of <see cref="LumpInfo"/> objects.
/// These objects contain offsets, lengths and versions of the GameLump lumps. /// These objects contain offsets, lengths and versions of the GameLump lumps.
@@ -53,7 +32,6 @@ namespace LibBSP {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
_rawData = data;
int structLength = 0; int structLength = 0;
switch (type) { switch (type) {
@@ -82,25 +60,25 @@ namespace LibBSP {
int numGameLumps = BitConverter.ToInt32(data, 0); int numGameLumps = BitConverter.ToInt32(data, 0);
if (numGameLumps > 0) { if (numGameLumps > 0) {
int headerLength = 4 + (numGameLumps * structLength); int lumpDictionaryOffset = (type == MapType.DMoMaM) ? 8 : 4;
int lowestLumpOffset = int.MaxValue; int lowestLumpOffset = int.MaxValue;
for (int i = 0; i < numGameLumps; ++i) { for (int i = 0; i < numGameLumps; ++i) {
int lumpIdent = BitConverter.ToInt32(data, (i * structLength) + 4); int lumpIdent = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset);
int lumpFlags; int lumpFlags;
int lumpVersion; int lumpVersion;
int lumpOffset; int lumpOffset;
int lumpLength; int lumpLength;
if (type == MapType.Vindictus) { if (type == MapType.Vindictus) {
lumpFlags = BitConverter.ToInt32(data, (i * structLength) + 8); lumpFlags = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 4);
lumpVersion = BitConverter.ToInt32(data, (i * structLength) + 12); lumpVersion = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 8);
lumpOffset = BitConverter.ToInt32(data, (i * structLength) + 16); lumpOffset = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 12);
lumpLength = BitConverter.ToInt32(data, (i * structLength) + 20); lumpLength = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 16);
} else { } else {
lumpFlags = BitConverter.ToUInt16(data, (i * structLength) + 8); lumpFlags = BitConverter.ToUInt16(data, (i * structLength) + lumpDictionaryOffset + 4);
lumpVersion = BitConverter.ToUInt16(data, (i * structLength) + 10); lumpVersion = BitConverter.ToUInt16(data, (i * structLength) + lumpDictionaryOffset + 6);
lumpOffset = BitConverter.ToInt32(data, (i * structLength) + 12); lumpOffset = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 8);
lumpLength = BitConverter.ToInt32(data, (i * structLength) + 16); lumpLength = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 12);
} }
LumpInfo info = new LumpInfo { LumpInfo info = new LumpInfo {
ident = lumpIdent, ident = lumpIdent,
@@ -115,9 +93,6 @@ namespace LibBSP {
lowestLumpOffset = info.offset; lowestLumpOffset = info.offset;
} }
} }
// If the offset values are given relative to the beginning of the lump, this should give 0.
_gameLumpOffset = lowestLumpOffset - headerLength;
} }
} }
@@ -159,5 +134,19 @@ namespace LibBSP {
} }
} }
} }
/// <summary>
/// Gets the lowest offset used for a Game Lump.
/// </summary>
/// <returns>The lowest offset used for a Game Lump, or <c>int.MaxValue</c> if no Game Lumps exist.</returns>
public int GetLowestLumpOffset() {
int lowest = int.MaxValue;
foreach (LumpInfo info in Values) {
if (info.offset < lowest) {
lowest = info.offset;
}
}
return lowest;
}
} }
} }

View File

@@ -43,8 +43,8 @@ namespace LibBSP {
offset += 4; offset += 4;
if (numProps > 0) { if (numProps > 0) {
structLength = (data.Length - offset) / numProps; structLength = (data.Length - offset) / numProps;
byte[] bytes = new byte[structLength];
for (int i = 0; i < numProps; ++i) { for (int i = 0; i < numProps; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, offset, bytes, 0, structLength); Array.Copy(data, offset, bytes, 0, structLength);
Add(new StaticProp(bytes, type, version)); Add(new StaticProp(bytes, type, version));
offset += structLength; offset += structLength;

View File

@@ -65,11 +65,10 @@ namespace LibBSP {
case MapType.Vindictus: case MapType.Vindictus:
case MapType.DMoMaM: { case MapType.DMoMaM: {
int offset = 0; int offset = 0;
byte[] myBytes;
for (int i = 0; i < data.Length; ++i) { for (int i = 0; i < data.Length; ++i) {
if (data[i] == (byte)0x00) { if (data[i] == (byte)0x00) {
// They are null-terminated strings, of non-constant length (not padded) // They are null-terminated strings, of non-constant length (not padded)
myBytes = new byte[i - offset]; byte[] myBytes = new byte[i - offset];
Array.Copy(data, offset, myBytes, 0, i - offset); Array.Copy(data, offset, myBytes, 0, i - offset);
Add(new Texture(myBytes, type, version)); Add(new Texture(myBytes, type, version));
offset = i + 1; offset = i + 1;
@@ -80,8 +79,8 @@ namespace LibBSP {
case MapType.Quake: { case MapType.Quake: {
int numElements = BitConverter.ToInt32(data, 0); int numElements = BitConverter.ToInt32(data, 0);
structLength = 40; structLength = 40;
byte[] myBytes = new byte[structLength];
for (int i = 0; i < numElements; ++i) { for (int i = 0; i < numElements; ++i) {
byte[] myBytes = new byte[structLength];
Array.Copy(data, BitConverter.ToInt32(data, (i + 1) * 4), myBytes, 0, structLength); Array.Copy(data, BitConverter.ToInt32(data, (i + 1) * 4), myBytes, 0, structLength);
Add(new Texture(myBytes, type, version)); Add(new Texture(myBytes, type, version));
} }
@@ -93,8 +92,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
Add(new Texture(bytes, type, version)); Add(new Texture(bytes, type, version));
} }

View File

@@ -16,15 +16,431 @@ namespace LibBSP {
/// </remarks> /// </remarks>
public struct Model { public struct Model {
public int headNode { get; private set; } // Quake, Half-life, Quake 2, SiN public byte[] data;
[Index("leaves")] public int firstLeaf { get; private set; } // 007 nightfire public MapType type;
[Count("leaves")] public int numLeaves { get; private set; } public int version;
[Index("brushes")] public int firstBrush { get; private set; } // Quake 3 and derivatives
[Count("brushes")] public int numBrushes { get; private set; } [Index("nodes")] public int headNode {
[Index("faces")] public int firstFace { get; private set; } // Quake/GoldSrc get {
[Count("faces")] public int numFaces { get; private set; } switch (type) {
[Index("markSurfaces")] public int firstLeafPatch { get; private set; } // CoD case MapType.Quake:
[Count("markSurfaces")] public int numLeafPatches { get; private set; } case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 36);
}
case MapType.DMoMaM: {
return BitConverter.ToInt32(data, 40);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Quake:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
bytes.CopyTo(data, 36);
break;
}
case MapType.DMoMaM: {
bytes.CopyTo(data, 40);
break;
}
}
}
}
[Index("leaves")] public int firstLeaf {
get {
switch (type) {
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 40);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Nightfire: {
bytes.CopyTo(data, 40);
break;
}
}
}
}
[Count("leaves")] public int numLeaves {
get {
switch (type) {
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 44);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Nightfire: {
bytes.CopyTo(data, 44);
break;
}
}
}
}
[Index("brushes")] public int firstBrush {
get {
switch (type) {
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake3:
case MapType.MOHAA:
case MapType.Raven:
case MapType.FAKK: {
return BitConverter.ToInt32(data, 32);
}
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
return BitConverter.ToInt32(data, 40);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake3:
case MapType.MOHAA:
case MapType.Raven:
case MapType.FAKK: {
bytes.CopyTo(data, 32);
break;
}
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
bytes.CopyTo(data, 40);
break;
}
}
}
}
[Count("brushes")] public int numBrushes {
get {
switch (type) {
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake3:
case MapType.MOHAA:
case MapType.Raven:
case MapType.FAKK: {
return BitConverter.ToInt32(data, 36);
}
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
return BitConverter.ToInt32(data, 44);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake3:
case MapType.MOHAA:
case MapType.Raven:
case MapType.FAKK: {
bytes.CopyTo(data, 36);
break;
}
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4: {
bytes.CopyTo(data, 44);
break;
}
}
}
}
[Index("faces")] public int firstFace {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake3:
case MapType.MOHAA:
case MapType.Raven:
case MapType.FAKK: {
return BitConverter.ToInt32(data, 24);
}
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 40);
}
case MapType.DMoMaM: {
return BitConverter.ToInt32(data, 44);
}
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 48);
}
case MapType.Quake: {
return BitConverter.ToInt32(data, 56);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake3:
case MapType.MOHAA:
case MapType.Raven:
case MapType.FAKK: {
bytes.CopyTo(data, 24);
break;
}
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
bytes.CopyTo(data, 40);
break;
}
case MapType.DMoMaM: {
bytes.CopyTo(data, 44);
break;
}
case MapType.Nightfire: {
bytes.CopyTo(data, 48);
break;
}
case MapType.Quake: {
bytes.CopyTo(data, 56);
break;
}
}
}
}
[Count("faces")] public int numFaces {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake3:
case MapType.MOHAA:
case MapType.Raven:
case MapType.FAKK: {
return BitConverter.ToInt32(data, 28);
}
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 44);
}
case MapType.DMoMaM: {
return BitConverter.ToInt32(data, 48);
}
case MapType.Nightfire: {
return BitConverter.ToInt32(data, 52);
}
case MapType.Quake: {
return BitConverter.ToInt32(data, 60);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake3:
case MapType.MOHAA:
case MapType.Raven:
case MapType.FAKK: {
bytes.CopyTo(data, 28);
break;
}
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
bytes.CopyTo(data, 44);
break;
}
case MapType.DMoMaM: {
bytes.CopyTo(data, 48);
break;
}
case MapType.Nightfire: {
bytes.CopyTo(data, 52);
break;
}
case MapType.Quake: {
bytes.CopyTo(data, 60);
break;
}
}
}
}
[Index("markSurfaces")]
public int firstLeafPatch {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
return BitConverter.ToInt32(data, 32);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
bytes.CopyTo(data, 32);
break;
}
}
}
}
[Count("markSurfaces")] public int numLeafPatches {
get {
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
return BitConverter.ToInt32(data, 36);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD:
case MapType.CoD2: {
bytes.CopyTo(data, 36);
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="Model"/> object from a <c>byte</c> array. /// Creates a new <see cref="Model"/> object from a <c>byte</c> array.
@@ -33,89 +449,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public Model(byte[] data, MapType type, int version = 0) : this() { public Model(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
headNode = -1; this.data = data;
firstLeaf = -1; this.type = type;
numLeaves = -1; this.version = version;
firstBrush = -1;
numBrushes = -1;
firstFace = -1;
numFaces = -1;
firstLeafPatch = -1;
numLeafPatches = -1;
switch (type) {
case MapType.Quake: {
headNode = BitConverter.ToInt32(data, 36);
firstFace = BitConverter.ToInt32(data, 56);
numFaces = BitConverter.ToInt32(data, 60);
break;
}
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
headNode = BitConverter.ToInt32(data, 36);
firstFace = BitConverter.ToInt32(data, 40);
numFaces = BitConverter.ToInt32(data, 44);
break;
}
case MapType.DMoMaM: {
headNode = BitConverter.ToInt32(data, 40);
firstFace = BitConverter.ToInt32(data, 44);
numFaces = BitConverter.ToInt32(data, 48);
break;
}
case MapType.Nightfire: {
firstLeaf = BitConverter.ToInt32(data, 40);
numLeaves = BitConverter.ToInt32(data, 44);
firstFace = BitConverter.ToInt32(data, 48);
numFaces = BitConverter.ToInt32(data, 52);
break;
}
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake3:
case MapType.MOHAA:
case MapType.Raven:
case MapType.FAKK: {
firstFace = BitConverter.ToInt32(data, 24);
numFaces = BitConverter.ToInt32(data, 28);
firstBrush = BitConverter.ToInt32(data, 32);
numBrushes = BitConverter.ToInt32(data, 36);
break;
}
case MapType.CoD: {
firstFace = BitConverter.ToInt32(data, 24);
numFaces = BitConverter.ToInt32(data, 28);
firstLeafPatch = BitConverter.ToInt32(data, 32);
numLeafPatches = BitConverter.ToInt32(data, 36);
goto case MapType.CoD2;
}
case MapType.CoD2:
case MapType.CoD4: {
firstBrush = BitConverter.ToInt32(data, 40);
numBrushes = BitConverter.ToInt32(data, 44);
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Model class.");
}
}
} }
/// <summary> /// <summary>
@@ -181,8 +521,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<Model> lump = new List<Model>(numObjects); List<Model> lump = new List<Model>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new Model(bytes, type, version)); lump.Add(new Model(bytes, type, version));
} }

View File

@@ -8,9 +8,174 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct Node { public struct Node {
public int plane { get; private set; } public byte[] data;
public int child1 { get; private set; } // Negative values are valid here. However, the child can never be zero, public MapType type;
public int child2 { get; private set; } // since that would reference the head node causing an infinite loop. public int version;
public int plane {
get {
return BitConverter.ToInt32(data, 0);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 0);
}
}
public int child1 {
get {
switch (type) {
case MapType.Quake: {
return BitConverter.ToInt16(data, 4);
}
case MapType.SiN:
case MapType.SoF:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.Nightfire:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK:
case MapType.CoD:
case MapType.CoD2: {
return BitConverter.ToInt32(data, 4);
}
default: {
return 0;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Quake: {
data[4] = bytes[0];
data[5] = bytes[1];
break;
}
case MapType.SiN:
case MapType.SoF:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.Nightfire:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK:
case MapType.CoD:
case MapType.CoD2: {
bytes.CopyTo(data, 4);
break;
}
}
}
}
public int child2 {
get {
switch (type) {
case MapType.Quake: {
return BitConverter.ToInt16(data, 6);
}
case MapType.SiN:
case MapType.SoF:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.Nightfire:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK:
case MapType.CoD:
case MapType.CoD2: {
return BitConverter.ToInt32(data, 8);
}
default: {
return 0;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Quake: {
data[6] = bytes[0];
data[7] = bytes[1];
break;
}
case MapType.SiN:
case MapType.SoF:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.Nightfire:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK:
case MapType.CoD:
case MapType.CoD2: {
bytes.CopyTo(data, 8);
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="Node"/> object from a <c>byte</c> array. /// Creates a new <see cref="Node"/> object from a <c>byte</c> array.
@@ -19,51 +184,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public Node(byte[] data, MapType type, int version = 0) : this() { public Node(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
plane = BitConverter.ToInt32(data, 0); // All formats I've seen use the first 4 bytes as an int, plane index this.data = data;
switch (type) { this.type = type;
case MapType.Quake: { this.version = version;
child1 = BitConverter.ToInt16(data, 4);
child2 = BitConverter.ToInt16(data, 6);
break;
}
// These all use the first three ints for planenum and children
case MapType.SiN:
case MapType.SoF:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.Nightfire:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM:
case MapType.STEF2:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.FAKK:
case MapType.CoD: {
child1 = BitConverter.ToInt32(data, 4);
child2 = BitConverter.ToInt32(data, 8);
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Node class.");
}
}
} }
/// <summary> /// <summary>
@@ -113,6 +240,7 @@ namespace LibBSP {
case MapType.Quake3: case MapType.Quake3:
case MapType.FAKK: case MapType.FAKK:
case MapType.CoD: case MapType.CoD:
case MapType.CoD2:
case MapType.STEF2: case MapType.STEF2:
case MapType.STEF2Demo: case MapType.STEF2Demo:
case MapType.MOHAA: case MapType.MOHAA:
@@ -127,8 +255,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<Node> lump = new List<Node>(numObjects); List<Node> lump = new List<Node>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new Node(bytes, type, version)); lump.Add(new Node(bytes, type, version));
} }
@@ -181,6 +309,9 @@ namespace LibBSP {
case MapType.CoD: { case MapType.CoD: {
return 20; return 20;
} }
case MapType.CoD2: {
return 25;
}
default: { default: {
return -1; return -1;
} }

View File

@@ -18,14 +18,229 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct Patch { public struct Patch {
public short shader { get; private set; } public byte[] data;
public short type { get; private set; } public MapType type;
public Vector2d dimensions { get; private set; } public int version;
public int flags { get; private set; }
[Index("patchVerts")] public int firstVertex { get; private set; } public short shader {
[Count("patchVerts")] public int numVertices { get; private set; } get {
[Count("patchIndices")] public int numIndices { get; private set; } switch (type) {
[Index("patchIndices")] public int firstIndex { get; private set; } case MapType.CoD: {
return BitConverter.ToInt16(data, 0);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD: {
bytes.CopyTo(data, 0);
break;
}
}
}
}
public short patchType {
get {
switch (type) {
case MapType.CoD: {
return BitConverter.ToInt16(data, 2);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD: {
bytes.CopyTo(data, 2);
break;
}
}
}
}
public Vector2d dimensions {
get {
switch (type) {
case MapType.CoD: {
if (patchType == 0) {
return new Vector2d(BitConverter.ToInt16(data, 4), BitConverter.ToInt16(data, 6));
} else {
return new Vector2d(float.NaN, float.NaN);
}
}
default: {
return new Vector2d(float.NaN, float.NaN);
}
}
}
set {
switch (type) {
case MapType.CoD: {
if (patchType == 0) {
BitConverter.GetBytes((short)value.x).CopyTo(data, 4);
BitConverter.GetBytes((short)value.y).CopyTo(data, 6);
}
break;
}
}
}
}
public int flags {
get {
switch (type) {
case MapType.CoD: {
if (patchType == 0) {
return BitConverter.ToInt32(data, 8);
} else {
return -1;
}
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD: {
if (patchType == 0) {
bytes.CopyTo(data, 8);
}
break;
}
}
}
}
[Index("patchVerts")] public int firstVertex {
get {
switch (type) {
case MapType.CoD: {
if (patchType == 0) {
return BitConverter.ToInt32(data, 12);
} else if (patchType == 1) {
return BitConverter.ToInt32(data, 8);
} else {
return -1;
}
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD: {
if (patchType == 0) {
bytes.CopyTo(data, 12);
} else if (patchType == 1) {
bytes.CopyTo(data, 8);
}
break;
}
}
}
}
[Count("patchVerts")] public int numVertices {
get {
switch (type) {
case MapType.CoD: {
if (patchType == 0) {
return BitConverter.ToInt16(data, 4) * BitConverter.ToInt16(data, 6);
} else if (patchType == 1) {
return BitConverter.ToInt16(data, 4);
} else {
return -1;
}
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD: {
if (patchType == 1) {
data[4] = bytes[0];
data[5] = bytes[1];
}
break;
}
}
}
}
[Count("patchIndices")] public int numIndices {
get {
switch (type) {
case MapType.CoD: {
if (patchType == 1) {
return BitConverter.ToInt16(data, 6);
} else {
return -1;
}
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD: {
if (patchType == 1) {
data[6] = bytes[0];
data[7] = bytes[1];
}
break;
}
}
}
}
[Index("patchIndices")] public int firstIndex {
get {
switch (type) {
case MapType.CoD: {
if (patchType == 1) {
return BitConverter.ToInt32(data, 12);
} else {
return -1;
}
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.CoD: {
if (patchType == 1) {
bytes.CopyTo(data, 12);
}
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="Patch"/> object from a <c>byte</c> array. /// Creates a new <see cref="Patch"/> object from a <c>byte</c> array.
@@ -34,34 +249,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public Patch(byte[] data, MapType type, int version = 0) : this() { public Patch(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
switch (type) { this.data = data;
case MapType.CoD: { this.type = type;
shader = BitConverter.ToInt16(data, 0); this.version = version;
this.type = BitConverter.ToInt16(data, 2);
if (this.type == 0) { // Patch
short x = BitConverter.ToInt16(data, 4);
short y = BitConverter.ToInt16(data, 6);
dimensions = new Vector2d(x, y);
flags = BitConverter.ToInt32(data, 8);
firstVertex = BitConverter.ToInt32(data, 12);
numVertices = x * y;
} else if (this.type == 1) { // Terrain
numVertices = BitConverter.ToInt16(data, 4);
numIndices = BitConverter.ToInt16(data, 6);
firstVertex = BitConverter.ToInt32(data, 8);
firstIndex = BitConverter.ToInt32(data, 12);
}
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Patch class.");
}
}
} }
/// <summary> /// <summary>
@@ -89,8 +283,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<Patch> lump = new List<Patch>(numObjects); List<Patch> lump = new List<Patch>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new Patch(bytes, type, version)); lump.Add(new Patch(bytes, type, version));
} }

View File

@@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
#if UNITY #if UNITY
using UnityEngine; using UnityEngine;
#endif #endif
@@ -18,12 +19,142 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct StaticModel { public struct StaticModel {
public string name { get; private set; } public byte[] data;
public Vector3d origin { get; private set; } public MapType type;
public Vector3d angles { get; private set; } public int version;
public float scale { get; private set; }
[Index("vertices")] public int firstVertex { get; private set; } public string name {
[Count("vertices")] public short numVertices { get; private set; } get {
switch (type) {
case MapType.MOHAA: {
return data.ToNullTerminatedString(0, 128);
}
default: {
return null;
}
}
}
set {
switch (type) {
case MapType.MOHAA: {
for (int i = 0; i < 128; ++i) {
data[i] = 0;
}
byte[] strBytes = Encoding.ASCII.GetBytes(value);
Array.Copy(strBytes, 0, data, 0, Math.Min(strBytes.Length, 127));
break;
}
}
}
}
public Vector3d origin {
get {
switch (type) {
case MapType.MOHAA: {
return new Vector3d(BitConverter.ToSingle(data, 128), BitConverter.ToSingle(data, 132), BitConverter.ToSingle(data, 136));
}
default: {
return new Vector3d(float.NaN, float.NaN, float.NaN);
}
}
}
set {
switch (type) {
case MapType.MOHAA: {
value.GetBytes().CopyTo(data, 128);
break;
}
}
}
}
public Vector3d angles {
get {
switch (type) {
case MapType.MOHAA: {
return new Vector3d(BitConverter.ToSingle(data, 140), BitConverter.ToSingle(data, 144), BitConverter.ToSingle(data, 148));
}
default: {
return new Vector3d(float.NaN, float.NaN, float.NaN);
}
}
}
set {
switch (type) {
case MapType.MOHAA: {
value.GetBytes().CopyTo(data, 140);
break;
}
}
}
}
public float scale {
get {
switch (type) {
case MapType.MOHAA: {
return BitConverter.ToSingle(data, 152);
}
default: {
return float.NaN;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.MOHAA: {
bytes.CopyTo(data, 152);
break;
}
}
}
}
[Index("vertices")] public int firstVertex {
get {
switch (type) {
case MapType.MOHAA: {
return BitConverter.ToInt32(data, 156);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.MOHAA: {
bytes.CopyTo(data, 156);
break;
}
}
}
}
[Count("vertices")] public short numVertices {
get {
switch (type) {
case MapType.MOHAA: {
return BitConverter.ToInt16(data, 160);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.MOHAA: {
bytes.CopyTo(data, 160);
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="StaticModel"/> object from a <c>byte</c> array. /// Creates a new <see cref="StaticModel"/> object from a <c>byte</c> array.
@@ -32,31 +163,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of static prop lump this object is a member of.</param> /// <param name="version">The version of static prop lump this object is a member of.</param>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public StaticModel(byte[] data, MapType type, int version = 0) : this() { public StaticModel(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
name = ""; this.data = data;
origin = Vector3d.zero; this.type = type;
angles = Vector3d.zero; this.version = version;
scale = 0;
switch (type) {
case MapType.MOHAA: {
byte[] nameBytes = new byte[128];
Array.Copy(data, 0, nameBytes, 0, 128);
name = nameBytes.ToNullTerminatedString();
origin = new Vector3d(BitConverter.ToSingle(data, 128), BitConverter.ToSingle(data, 132), BitConverter.ToSingle(data, 136));
angles = new Vector3d(BitConverter.ToSingle(data, 140), BitConverter.ToSingle(data, 144), BitConverter.ToSingle(data, 148));
scale = BitConverter.ToSingle(data, 152);
firstVertex = BitConverter.ToInt32(data, 156);
numVertices = BitConverter.ToInt16(data, 160);
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the StaticModel class.");
}
}
} }
/// <summary> /// <summary>
@@ -84,8 +197,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<StaticModel> lump = new List<StaticModel>(numObjects); List<StaticModel> lump = new List<StaticModel>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new StaticModel(bytes, type, version)); lump.Add(new StaticModel(bytes, type, version));
} }

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
#endif #endif
using System; using System;
using System.Text;
#if UNITY #if UNITY
using UnityEngine; using UnityEngine;
#endif #endif
@@ -25,11 +26,265 @@ namespace LibBSP {
/// </remarks> /// </remarks>
public struct Texture { public struct Texture {
public string name { get; private set; } public byte[] data;
public string mask { get; private set; } // Only used by MoHAA, "ignore" means it's unused public MapType type;
public int flags { get; private set; } public int version;
public int contents { get; private set; }
public TextureInfo texAxes { get; private set; } public string name {
get {
switch (type) {
case MapType.Quake: {
return data.ToNullTerminatedString(0, 16);
}
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.FAKK:
case MapType.Nightfire: {
return data.ToNullTerminatedString(0, 64);
}
case MapType.Quake2:
case MapType.SoF:
case MapType.Daikatana: {
return data.ToNullTerminatedString(40, 32);
}
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM: {
return data.ToRawString();
}
case MapType.SiN: {
return data.ToNullTerminatedString(36, 64);
}
default: {
return null;
}
}
}
set {
byte[] bytes = Encoding.ASCII.GetBytes(value);
switch (type) {
case MapType.Quake: {
for (int i = 0; i < 16; ++i) {
data[i] = 0;
}
Array.Copy(bytes, 0, data, 0, Math.Min(bytes.Length, 15));
break;
}
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.FAKK:
case MapType.Nightfire: {
for (int i = 0; i < 64; ++i) {
data[i] = 0;
}
Array.Copy(bytes, 0, data, 0, Math.Min(bytes.Length, 63));
break;
}
case MapType.Quake2:
case MapType.SoF:
case MapType.Daikatana: {
for (int i = 0; i < 32; ++i) {
data[i + 40] = 0;
}
Array.Copy(bytes, 0, data, 40, Math.Min(bytes.Length, 31));
break;
}
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM: {
data = bytes;
break;
}
case MapType.SiN: {
for (int i = 0; i < 64; ++i) {
data[i + 36] = 0;
}
Array.Copy(bytes, 0, data, 36, Math.Min(bytes.Length, 63));
break;
}
}
}
}
public string mask {
get {
switch (type) {
case MapType.MOHAA: {
return data.ToNullTerminatedString(76, 64);
}
default: {
return null;
}
}
}
set {
switch (type) {
case MapType.MOHAA: {
for (int i = 0; i < 64; ++i) {
data[i + 76] = 0;
}
byte[] strBytes = Encoding.ASCII.GetBytes(value);
Array.Copy(strBytes, 0, data, 76, Math.Min(strBytes.Length, 63));
break;
}
}
}
}
public int flags {
get {
switch (type) {
case MapType.Quake2:
case MapType.SoF:
case MapType.Daikatana:
case MapType.SiN: {
return BitConverter.ToInt32(data, 32);
}
case MapType.MOHAA:
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.FAKK: {
return BitConverter.ToInt32(data, 64);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Quake2:
case MapType.SoF:
case MapType.Daikatana:
case MapType.SiN: {
bytes.CopyTo(data, 32);
break;
}
case MapType.MOHAA:
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.FAKK: {
bytes.CopyTo(data, 64);
break;
}
}
}
}
public int contents {
get {
switch (type) {
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.FAKK:
case MapType.MOHAA: {
return BitConverter.ToInt32(data, 68);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.FAKK:
case MapType.MOHAA: {
bytes.CopyTo(data, 68);
break;
}
}
}
}
public TextureInfo texAxes {
get {
switch (type) {
case MapType.Quake2:
case MapType.SoF:
case MapType.Daikatana:
case MapType.SiN: {
return new TextureInfo(new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)),
new Vector3d(BitConverter.ToSingle(data, 16), BitConverter.ToSingle(data, 20), BitConverter.ToSingle(data, 24)),
new Vector2d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 28)),
Vector2d.one,
-1, -1, 0);
}
default: {
return null;
}
}
}
set {
switch (type) {
case MapType.Quake2:
case MapType.SoF:
case MapType.Daikatana:
case MapType.SiN: {
byte[] bytes = value.uAxis.GetBytes();
bytes.CopyTo(data, 0);
bytes = value.vAxis.GetBytes();
bytes.CopyTo(data, 16);
bytes = BitConverter.GetBytes(value.translation.x);
bytes.CopyTo(data, 12);
bytes = BitConverter.GetBytes(value.translation.y);
bytes.CopyTo(data, 28);
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="Texture"/> object from a <c>byte</c> array. /// Creates a new <see cref="Texture"/> object from a <c>byte</c> array.
@@ -38,79 +293,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public Texture(byte[] data, MapType type, int version = 0) : this() { public Texture(byte[] data, MapType type, int version = 0) : this() {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
name = ""; this.data = data;
mask = "ignore"; this.type = type;
flags = 0; this.version = version;
contents = 0;
switch (type) {
case MapType.Quake:
case MapType.Nightfire: {
name = data.ToNullTerminatedString();
break;
}
case MapType.Quake2:
case MapType.SoF:
case MapType.Daikatana: {
texAxes = new TextureInfo(new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)),
new Vector3d(BitConverter.ToSingle(data, 16), BitConverter.ToSingle(data, 20), BitConverter.ToSingle(data, 24)),
new Vector2d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 28)),
Vector2d.one,
-1, -1, 0);
flags = BitConverter.ToInt32(data, 32);
name = data.ToNullTerminatedString(40, 32);
break;
}
case MapType.MOHAA: {
mask = data.ToNullTerminatedString(76, 64);
goto case MapType.STEF2;
}
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Raven:
case MapType.Quake3:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.FAKK: {
name = data.ToNullTerminatedString(0, 64);
flags = BitConverter.ToInt32(data, 64);
contents = BitConverter.ToInt32(data, 68);
break;
}
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM: {
name = data.ToRawString();
break;
}
case MapType.SiN: {
texAxes = new TextureInfo(new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)),
new Vector3d(BitConverter.ToSingle(data, 16), BitConverter.ToSingle(data, 20), BitConverter.ToSingle(data, 24)),
new Vector2d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 28)),
Vector2d.one,
-1, -1, 0);
flags = BitConverter.ToInt32(data, 32);
name = data.ToNullTerminatedString(36, 64);
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Texture class.");
}
}
} }
/// <summary> /// <summary>

View File

@@ -18,12 +18,63 @@ namespace LibBSP {
/// </summary> /// </summary>
public struct TextureData { public struct TextureData {
public Vector3d reflectivity { get; private set; } public byte[] data;
public int stringTableIndex { get; private set; } public MapType type;
public int width { get; private set; } public int version;
public int height { get; private set; }
public int view_width { get; private set; } public Vector3d reflectivity {
public int view_height { get; private set; } get {
return new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8));
}
set {
value.GetBytes().CopyTo(data, 0);
}
}
public int stringTableIndex {
get {
return BitConverter.ToInt32(data, 12);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 12);
}
}
public int width {
get {
return BitConverter.ToInt32(data, 16);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 16);
}
}
public int height {
get {
return BitConverter.ToInt32(data, 20);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 20);
}
}
public int view_width {
get {
return BitConverter.ToInt32(data, 24);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 24);
}
}
public int view_height {
get {
return BitConverter.ToInt32(data, 28);
}
set {
BitConverter.GetBytes(value).CopyTo(data, 28);
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="TextureData"/> object from a <c>byte</c> array. /// Creates a new <see cref="TextureData"/> object from a <c>byte</c> array.
@@ -36,12 +87,9 @@ namespace LibBSP {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
reflectivity = new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)); this.data = data;
stringTableIndex = BitConverter.ToInt32(data, 12); this.type = type;
width = BitConverter.ToInt32(data, 16); this.version = version;
height = BitConverter.ToInt32(data, 20);
view_width = BitConverter.ToInt32(data, 24);
view_height = BitConverter.ToInt32(data, 28);
} }
/// <summary> /// <summary>
@@ -59,8 +107,8 @@ namespace LibBSP {
int structLength = 32; int structLength = 32;
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<TextureData> lump = new List<TextureData>(numObjects); List<TextureData> lump = new List<TextureData>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; i++) { for (int i = 0; i < numObjects; i++) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new TextureData(bytes, type, version)); lump.Add(new TextureData(bytes, type, version));
} }

View File

@@ -420,7 +420,7 @@ namespace LibBSP {
/// <summary> /// <summary>
/// Clears the bits in "spawnflags" which are set in <paramref name="bits"/>. /// Clears the bits in "spawnflags" which are set in <paramref name="bits"/>.
/// Equivalent to spawnflags = (<paramref name="bits"/> ^ 0xFFFFFFFF) & spawnflags. /// Equivalent to spawnflags = (<paramref name="bits"/> ^ 0xFFFFFFFF) &amp; spawnflags.
/// </summary> /// </summary>
/// <param name="bits">Bitmask of bits to clear.</param> /// <param name="bits">Bitmask of bits to clear.</param>
public void ClearSpawnflags(uint bits) { public void ClearSpawnflags(uint bits) {
@@ -524,9 +524,9 @@ namespace LibBSP {
#region ISerializable #region ISerializable
/// <summary> /// <summary>
/// Implements the <c>ISerializable</c> interface and returns the data needed /// Implements the <c>ISerializable</c> interface and returns the data needed
/// to serialize the <see cref="Entity"> instance. /// to serialize the <see cref="Entity"/> instance.
/// </summary> /// </summary>
/// <param name="info">A <c>SerializationInfo</c> object that contains the information required to serialize the <see cref="Entity"> instance.</param> /// <param name="info">A <c>SerializationInfo</c> object that contains the information required to serialize the <see cref="Entity"/> instance.</param>
/// <param name="context">A <c>StreamingContext</c> structure that contains the source and destination of the serialized stream associated with the <see cref="Entity"/> instance.</param> /// <param name="context">A <c>StreamingContext</c> structure that contains the source and destination of the serialized stream associated with the <see cref="Entity"/> instance.</param>
public override void GetObjectData(SerializationInfo info, StreamingContext context) { public override void GetObjectData(SerializationInfo info, StreamingContext context) {
base.GetObjectData(info, context); base.GetObjectData(info, context);

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
namespace LibBSP { namespace LibBSP {
@@ -6,8 +7,11 @@ namespace LibBSP {
/// <summary> /// <summary>
/// List class for numbers. Can handle any integer data type except <c>ulong</c>. /// List class for numbers. Can handle any integer data type except <c>ulong</c>.
/// </summary> /// </summary>
public class NumList : List<long> { public class NumList : IList<long>, ICollection<long>, IEnumerable<long>, IList, ICollection, IEnumerable {
/// <summary>
/// Enum of the types that may be used in this class.
/// </summary>
public enum DataType : int { public enum DataType : int {
Invalid = 0, Invalid = 0,
SByte = 1, SByte = 1,
@@ -19,6 +23,7 @@ namespace LibBSP {
Int64 = 7, Int64 = 7,
} }
public byte[] data;
public DataType type { get; private set; } public DataType type { get; private set; }
/// <summary> /// <summary>
@@ -27,61 +32,21 @@ namespace LibBSP {
/// <param name="data"><c>byte</c> array to parse.</param> /// <param name="data"><c>byte</c> array to parse.</param>
/// <param name="type">The type of number to store.</param> /// <param name="type">The type of number to store.</param>
/// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="type"/> was not a member of the DataType enum.</exception>
public NumList(byte[] data, DataType type) { public NumList(byte[] data, DataType type) {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
this.data = data;
this.type = type;
}
/// <summary>
/// Creates an empty <see cref="NumList"/> object.
/// </summary>
/// <param name="type">The type of number to store.</param>
public NumList(DataType type) {
this.data = new byte[0];
this.type = type; this.type = type;
switch (type) {
case DataType.SByte: {
unchecked {
for (int i = 0; i < data.Length; ++i) {
Add((long)((sbyte)data[i]));
}
}
break;
}
case DataType.Byte: {
for (int i = 0; i < data.Length; ++i) {
Add((long)(data[i]));
}
break;
}
case DataType.Int16: {
for (int i = 0; i < data.Length / 2; ++i) {
Add((long)BitConverter.ToInt16(data, i * 2));
}
break;
}
case DataType.UInt16: {
for (int i = 0; i < data.Length / 2; ++i) {
Add((long)BitConverter.ToUInt16(data, i * 2));
}
break;
}
case DataType.Int32: {
for (int i = 0; i < data.Length / 4; ++i) {
Add((long)BitConverter.ToInt32(data, i * 4));
}
break;
}
case DataType.UInt32: {
for (int i = 0; i < data.Length / 4; ++i) {
Add((long)BitConverter.ToUInt32(data, i * 4));
}
break;
}
case DataType.Int64: {
for (int i = 0; i < data.Length / 8; ++i) {
Add(BitConverter.ToInt64(data, i * 8));
}
break;
}
default: {
throw new ArgumentException(type + " isn't a member of the DataType enum.");
}
}
} }
/// <summary> /// <summary>
@@ -94,6 +59,34 @@ namespace LibBSP {
return new NumList(data, type); return new NumList(data, type);
} }
/// <summary>
/// Gets the length, in bytes, of the numerical primitive used by this instance of this class.
/// </summary>
public int StructLength {
get {
switch (type) {
case DataType.Byte:
case DataType.SByte: {
return 1;
}
case DataType.UInt16:
case DataType.Int16: {
return 2;
}
case DataType.UInt32:
case DataType.Int32: {
return 4;
}
case DataType.Int64: {
return 8;
}
default: {
return 0;
}
}
}
}
#region IndicesForLumps #region IndicesForLumps
/// <summary> /// <summary>
/// Gets the index for the Mark Surfaces lump in the BSP file for a specific map format, and the type of data the format uses. /// Gets the index for the Mark Surfaces lump in the BSP file for a specific map format, and the type of data the format uses.
@@ -248,6 +241,10 @@ namespace LibBSP {
dataType = DataType.UInt32; dataType = DataType.UInt32;
return 22; return 22;
} }
case MapType.CoD2: {
dataType = DataType.UInt32;
return 27;
}
} }
dataType = DataType.Invalid; dataType = DataType.Invalid;
return -1; return -1;
@@ -279,6 +276,10 @@ namespace LibBSP {
dataType = DataType.UInt16; dataType = DataType.UInt16;
return 8; return 8;
} }
case MapType.CoD2: {
dataType = DataType.UInt16;
return 9;
}
case MapType.Raven: case MapType.Raven:
case MapType.Quake3: { case MapType.Quake3: {
dataType = DataType.UInt32; dataType = DataType.UInt32;
@@ -362,5 +363,228 @@ namespace LibBSP {
return -1; return -1;
} }
#endregion #endregion
#region ICollection
public void Add(long value) {
byte[] temp = new byte[data.Length + StructLength];
Array.Copy(data, 0, temp, 0, data.Length);
byte[] bytes = BitConverter.GetBytes(value);
Array.Copy(bytes, 0, temp, data.Length, StructLength);
data = temp;
}
public void Clear() {
data = new byte[0];
}
public bool Contains(long value) {
foreach (long curr in this) {
if (curr == value) {
return true;
}
}
return false;
}
public void CopyTo(long[] array, int arrayIndex) {
for (int i = 0; i < Count; ++i) {
array[i + arrayIndex] = this[i];
}
}
void ICollection.CopyTo(Array array, int arrayIndex) {
for (int i = 0; i < Count; ++i) {
array.SetValue(this[i], i + arrayIndex);
}
}
public bool Remove(long value) {
for (int i = 0; i < Count; ++i) {
if (this[i] == value) {
RemoveAt(i);
return true;
}
}
return false;
}
public int Count {
get {
return data.Length / StructLength;
}
}
public bool IsReadOnly {
get {
return false;
}
}
public object SyncRoot {
get {
return data.SyncRoot;
}
}
public bool IsSynchronized {
get { return data.IsSynchronized; }
}
#endregion
#region IEnumerable
public IEnumerator<long> GetEnumerator() {
for (int i = 0; i < Count; ++i) {
yield return this[i];
}
}
IEnumerator IEnumerable.GetEnumerator() {
for (int i = 0; i < Count; ++i) {
yield return this[i];
}
}
#endregion
#region IList
public int Add(object obj) {
if (obj is byte || obj is sbyte || obj is short || obj is ushort || obj is int || obj is uint || obj is long) {
Add((long)obj);
return Count - 1;
}
return -1;
}
public bool Contains(object obj) {
if (obj is byte || obj is sbyte || obj is short || obj is ushort || obj is int || obj is uint || obj is long) {
return Contains((long)obj);
}
return false;
}
public int IndexOf(object obj) {
if (obj is byte || obj is sbyte || obj is short || obj is ushort || obj is int || obj is uint || obj is long) {
return IndexOf((long)obj);
}
return -1;
}
public void Insert(int index, object obj) {
if (obj is byte || obj is sbyte || obj is short || obj is ushort || obj is int || obj is uint || obj is long) {
Insert(index, (long)obj);
}
}
public void Remove(object obj) {
if (obj is byte || obj is sbyte || obj is short || obj is ushort || obj is int || obj is uint || obj is long) {
Remove((long)obj);
}
}
public int IndexOf(long value) {
for (int i = 0; i < Count; ++i) {
if (this[i] == value) {
return i;
}
}
return -1;
}
object IList.this[int index] {
get {
return this[index];
}
set {
if (value is byte || value is sbyte || value is short || value is ushort || value is int || value is uint || value is long) {
this[index] = (long)value;
}
}
}
public bool IsFixedSize {
get {
return false;
}
}
public void Insert(int index, long value) {
byte[] temp = new byte[data.Length + StructLength];
Array.Copy(data, 0, temp, 0, StructLength * index);
byte[] bytes = BitConverter.GetBytes(value);
Array.Copy(bytes, 0, temp, StructLength * index, StructLength);
Array.Copy(data, StructLength * index, temp, StructLength * (index + 1), data.Length - StructLength * index);
data = temp;
}
public void RemoveAt(int index) {
byte[] temp = new byte[data.Length - StructLength];
Array.Copy(data, 0, temp, 0, StructLength * index);
Array.Copy(data, StructLength * (index + 1), temp, StructLength * index, data.Length - (StructLength * (index + 1)));
data = temp;
}
public long this[int index] {
get {
switch (type) {
case DataType.SByte: {
return (sbyte)data[index];
}
case DataType.Byte: {
return data[index];
}
case DataType.Int16: {
return BitConverter.ToInt16(data, index * 2);
}
case DataType.UInt16: {
return BitConverter.ToUInt16(data, index * 2);
}
case DataType.Int32: {
return BitConverter.ToInt32(data, index * 4);
}
case DataType.UInt32: {
return BitConverter.ToUInt32(data, index * 4);
}
case DataType.Int64: {
return BitConverter.ToInt64(data, index * 8);
}
default: {
return 0;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case DataType.SByte:
case DataType.Byte: {
data[index] = bytes[0];
break;
}
case DataType.Int16:
case DataType.UInt16: {
data[index * 2] = bytes[0];
data[(index * 2) + 1] = bytes[1];
break;
}
case DataType.Int32:
case DataType.UInt32: {
data[index * 4] = bytes[0];
data[(index * 4) + 1] = bytes[1];
data[(index * 4) + 2] = bytes[2];
data[(index * 4) + 3] = bytes[3];
break;
}
case DataType.Int64: {
bytes.CopyTo(data, index * 8);
break;
}
}
}
}
#endregion
} }
} }

View File

@@ -21,36 +21,169 @@ namespace LibBSP {
/// </summary> /// </summary>
[Serializable] public class TextureInfo { [Serializable] public class TextureInfo {
public const int S = 0; public byte[] data;
public const int T = 1; public MapType type;
/// <summary> public int version;
/// Array of base texture axes. When referenced properly, provides a good default texture axis for any given plane.
/// </summary>
public static readonly Vector3d[] baseAxes = new Vector3d[] {
new Vector3d(0, 0, 1), new Vector3d(1, 0, 0), new Vector3d(0, -1, 0),
new Vector3d(0, 0, -1), new Vector3d(1, 0, 0), new Vector3d(0, -1, 0),
new Vector3d(1, 0, 0), new Vector3d(0, 1, 0), new Vector3d(0, 0, -1),
new Vector3d(-1, 0, 0), new Vector3d(0, 1, 0), new Vector3d(0, 0, -1),
new Vector3d(0, 1, 0), new Vector3d(1, 0, 0), new Vector3d(0, 0, -1),
new Vector3d(0, -1, 0), new Vector3d(1, 0, 0), new Vector3d(0, 0, -1)
};
public Vector3d[] axes; // No BSP format uses these so they are fields.
public Vector2d translation; public Vector2d scale = Vector2d.one;
public Vector2d scale; public double rotation = 0;
public int flags;
public int texture; public Vector3d uAxis {
public double rotation; get {
return new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8));
}
set {
value.GetBytes().CopyTo(data, 0);
}
}
public Vector3d vAxis {
get {
return new Vector3d(BitConverter.ToSingle(data, 16), BitConverter.ToSingle(data, 20), BitConverter.ToSingle(data, 24));
}
set {
value.GetBytes().CopyTo(data, 16);
}
}
public Vector2d translation {
get {
return new Vector2d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 28));
}
set {
BitConverter.GetBytes((float)value.x).CopyTo(data, 12);
BitConverter.GetBytes((float)value.y).CopyTo(data, 28);
}
}
public int flags {
get {
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 64);
}
case MapType.DMoMaM: {
return BitConverter.ToInt32(data, 88);
}
case MapType.Quake: {
return BitConverter.ToInt32(data, 36);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
bytes.CopyTo(data, 64);
break;
}
case MapType.DMoMaM: {
bytes.CopyTo(data, 88);
break;
}
case MapType.Quake: {
bytes.CopyTo(data, 36);
break;
}
}
}
}
public int texture {
get {
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
return BitConverter.ToInt32(data, 68);
}
case MapType.DMoMaM: {
return BitConverter.ToInt32(data, 92);
}
case MapType.Quake: {
return BitConverter.ToInt32(data, 32);
}
default: {
return -1;
}
}
}
set {
byte[] bytes = BitConverter.GetBytes(value);
switch (type) {
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
bytes.CopyTo(data, 68);
break;
}
case MapType.DMoMaM: {
bytes.CopyTo(data, 92);
break;
}
case MapType.Quake: {
bytes.CopyTo(data, 32);
break;
}
}
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="TextureInfo"/> object with sensible defaults. /// Creates a new <see cref="TextureInfo"/> object with sensible defaults.
/// </summary> /// </summary>
public TextureInfo() { public TextureInfo() {
axes = new Vector3d[2]; data = new byte[40];
type = MapType.Quake;
version = 0;
uAxis = Vector3d.zero;
vAxis = Vector3d.zero;
translation = Vector2d.zero; translation = Vector2d.zero;
scale = Vector2d.one;
flags = 0; flags = 0;
texture = 0; texture = -1;
scale = Vector2d.one;
rotation = 0; rotation = 0;
} }
@@ -61,70 +194,35 @@ namespace LibBSP {
/// <param name="type">The map type.</param> /// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param> /// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public TextureInfo(byte[] data, MapType type, int version = 0) { public TextureInfo(byte[] data, MapType type, int version = 0) {
if (data == null) { if (data == null) {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
texture = -1; this.data = data;
flags = -1; this.type = type;
axes = new Vector3d[2]; this.version = version;
translation = new Vector2d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 28));
axes[S] = new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8));
axes[T] = new Vector3d(BitConverter.ToSingle(data, 16), BitConverter.ToSingle(data, 20), BitConverter.ToSingle(data, 24));
// Texture scaling information is compiled into the axes by changing their length.
scale = Vector2d.one; scale = Vector2d.one;
rotation = 0; rotation = 0;
switch (type) {
// Excluded engines: Quake 2-based, Quake 3-based
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.L4D2:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
texture = BitConverter.ToInt32(data, 68);
flags = BitConverter.ToInt32(data, 64);
break;
}
case MapType.DMoMaM: {
texture = BitConverter.ToInt32(data, 92);
flags = BitConverter.ToInt32(data, 88);
break;
}
case MapType.Quake: {
texture = BitConverter.ToInt32(data, 32);
flags = BitConverter.ToInt32(data, 36);
break;
}
case MapType.Nightfire: {
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the TextureInfo class.");
}
}
} }
/// <summary> /// <summary>
/// Creates a new <see cref="TextureInfo"/> object using the passed data. /// Creates a new <see cref="TextureInfo"/> object using the passed data.
/// </summary> /// </summary>
/// <param name="s">The S texture axis.</param> /// <param name="u">The U texture axis.</param>
/// <param name="t">The T texture axis.</param> /// <param name="v">The V texture axis.</param>
/// <param name="translation">Texture translation along both axes (in pixels).</param> /// <param name="translation">Texture translation along both axes (in pixels).</param>
/// <param name="scale">Texture scale along both axes.</param> /// <param name="scale">Texture scale along both axes.</param>
/// <param name="flags">The flags for this <see cref="TextureInfo"/>.</param> /// <param name="flags">The flags for this <see cref="TextureInfo"/>.</param>
/// <param name="texture">Index into the texture list for the texture this <see cref="TextureInfo"/> uses.</param> /// <param name="texture">Index into the texture list for the texture this <see cref="TextureInfo"/> uses.</param>
/// <param name="rotation">Rotation of the texutre axes.</param> /// <param name="rotation">Rotation of the texutre axes.</param>
public TextureInfo(Vector3d s, Vector3d t, Vector2d translation, Vector2d scale, int flags, int texture, double rotation) { public TextureInfo(Vector3d u, Vector3d v, Vector2d translation, Vector2d scale, int flags, int texture, double rotation) {
axes = new Vector3d[2]; data = new byte[40];
axes[S] = s; type = MapType.Quake;
axes[T] = t; version = 0;
uAxis = u;
vAxis = v;
this.translation = translation; this.translation = translation;
this.scale = scale; this.scale = scale;
this.flags = flags; this.flags = flags;
@@ -138,19 +236,10 @@ namespace LibBSP {
/// <param name="p"><see cref="Plane"/> of the surface.</param> /// <param name="p"><see cref="Plane"/> of the surface.</param>
/// <returns>The best matching texture axes for the given <see cref="Plane"/>.</returns> /// <returns>The best matching texture axes for the given <see cref="Plane"/>.</returns>
public static Vector3d[] TextureAxisFromPlane(Plane p) { public static Vector3d[] TextureAxisFromPlane(Plane p) {
int bestaxis = 0; int bestaxis = p.BestAxis();
double best = 0; // "Best" dot product so far
for (int i = 0; i < 6; ++i) {
// For all possible axes, positive and negative
double dot = Vector3d.Dot(p.normal, new Vector3d(baseAxes[i * 3][0], baseAxes[i * 3][1], baseAxes[i * 3][2]));
if (dot > best) {
best = dot;
bestaxis = i;
}
}
Vector3d[] newAxes = new Vector3d[2]; Vector3d[] newAxes = new Vector3d[2];
newAxes[0] = new Vector3d(baseAxes[bestaxis * 3 + 1][0], baseAxes[bestaxis * 3 + 1][1], baseAxes[bestaxis * 3 + 1][2]); newAxes[0] = PlaneExtensions.baseAxes[bestaxis * 3 + 1];
newAxes[1] = new Vector3d(baseAxes[bestaxis * 3 + 2][0], baseAxes[bestaxis * 3 + 2][1], baseAxes[bestaxis * 3 + 2][2]); newAxes[1] = PlaneExtensions.baseAxes[bestaxis * 3 + 2];
return newAxes; return newAxes;
} }
@@ -201,8 +290,8 @@ namespace LibBSP {
} }
int numObjects = data.Length / structLength; int numObjects = data.Length / structLength;
List<TextureInfo> lump = new List<TextureInfo>(numObjects); List<TextureInfo> lump = new List<TextureInfo>(numObjects);
byte[] bytes = new byte[structLength];
for (int i = 0; i < numObjects; ++i) { for (int i = 0; i < numObjects; ++i) {
byte[] bytes = new byte[structLength];
Array.Copy(data, (i * structLength), bytes, 0, structLength); Array.Copy(data, (i * structLength), bytes, 0, structLength);
lump.Add(new TextureInfo(bytes, type, version)); lump.Add(new TextureInfo(bytes, type, version));
} }

View File

@@ -124,7 +124,7 @@ namespace LibBSP {
string[] split = tokens[1].SplitUnlessBetweenDelimiters(' ', '[', ']', StringSplitOptions.RemoveEmptyEntries); string[] split = tokens[1].SplitUnlessBetweenDelimiters(' ', '[', ']', StringSplitOptions.RemoveEmptyEntries);
textureInfo.scale = new Vector2d(float.Parse(split[1], _format), textureInfo.scale.y); textureInfo.scale = new Vector2d(float.Parse(split[1], _format), textureInfo.scale.y);
split = split[0].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); split = split[0].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
textureInfo.axes[0] = new Vector3d(float.Parse(split[0], _format), float.Parse(split[1], _format), float.Parse(split[2], _format)); textureInfo.uAxis = new Vector3d(float.Parse(split[0], _format), float.Parse(split[1], _format), float.Parse(split[2], _format));
textureInfo.translation = new Vector2d(float.Parse(split[3], _format), textureInfo.translation.y); textureInfo.translation = new Vector2d(float.Parse(split[3], _format), textureInfo.translation.y);
break; break;
} }
@@ -132,7 +132,7 @@ namespace LibBSP {
string[] split = tokens[1].SplitUnlessBetweenDelimiters(' ', '[', ']', StringSplitOptions.RemoveEmptyEntries); string[] split = tokens[1].SplitUnlessBetweenDelimiters(' ', '[', ']', StringSplitOptions.RemoveEmptyEntries);
textureInfo.scale = new Vector2d(textureInfo.scale.x, float.Parse(split[1], _format)); textureInfo.scale = new Vector2d(textureInfo.scale.x, float.Parse(split[1], _format));
split = split[0].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); split = split[0].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
textureInfo.axes[1] = new Vector3d(float.Parse(split[0], _format), float.Parse(split[1], _format), float.Parse(split[2], _format)); textureInfo.vAxis = new Vector3d(float.Parse(split[0], _format), float.Parse(split[1], _format), float.Parse(split[2], _format));
textureInfo.translation = new Vector2d(textureInfo.translation.x, float.Parse(split[3], _format)); textureInfo.translation = new Vector2d(textureInfo.translation.x, float.Parse(split[3], _format));
break; break;
} }