From 76feb5046e8a1b93947ddc9d4ee2da70b33fd2f3 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 22 Nov 2018 19:28:03 -0700 Subject: [PATCH] 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. --- LibBSP/LibBSP.csproj | 3 + LibBSP/Source/Extensions/ColorExtensions.cs | 21 + LibBSP/Source/Extensions/PlaneExtensions.cs | 126 +- .../Source/Extensions/Vector2dExtensions.cs | 36 + .../Source/Extensions/Vector3dExtensions.cs | 38 + .../Source/Extensions/Vector4dExtensions.cs | 40 + LibBSP/Source/Extensions/VertexExtensions.cs | 226 ++- LibBSP/Source/Structs/BSP/BSP.cs | 28 +- LibBSP/Source/Structs/BSP/Brush.cs | 333 +++- LibBSP/Source/Structs/BSP/BrushSide.cs | 446 ++++- LibBSP/Source/Structs/BSP/Cubemap.cs | 119 +- LibBSP/Source/Structs/BSP/DisplacementInfo.cs | 230 ++- .../Source/Structs/BSP/DisplacementVertex.cs | 48 +- LibBSP/Source/Structs/BSP/Edge.cs | 161 +- LibBSP/Source/Structs/BSP/Face.cs | 1010 +++++++++-- LibBSP/Source/Structs/BSP/LODTerrain.cs | 321 +++- LibBSP/Source/Structs/BSP/Leaf.cs | 544 +++++- .../Structs/BSP/Lumps/DisplacementVertices.cs | 2 +- LibBSP/Source/Structs/BSP/Lumps/GameLump.cs | 59 +- .../Source/Structs/BSP/Lumps/StaticProps.cs | 2 +- LibBSP/Source/Structs/BSP/Lumps/Textures.cs | 7 +- LibBSP/Source/Structs/BSP/Model.cs | 518 +++++- LibBSP/Source/Structs/BSP/Node.cs | 221 ++- LibBSP/Source/Structs/BSP/Patch.cs | 260 ++- LibBSP/Source/Structs/BSP/StaticModel.cs | 169 +- LibBSP/Source/Structs/BSP/StaticProp.cs | 1562 ++++++++++++++++- LibBSP/Source/Structs/BSP/Texture.cs | 337 +++- LibBSP/Source/Structs/BSP/TextureData.cs | 74 +- LibBSP/Source/Structs/Common/Entity.cs | 6 +- LibBSP/Source/Structs/Common/Lumps/NumList.cs | 326 +++- LibBSP/Source/Structs/Common/TextureInfo.cs | 255 ++- LibBSP/Source/Structs/MAP/MAPBrushSide.cs | 4 +- 32 files changed, 6374 insertions(+), 1158 deletions(-) create mode 100644 LibBSP/Source/Extensions/Vector2dExtensions.cs create mode 100644 LibBSP/Source/Extensions/Vector3dExtensions.cs create mode 100644 LibBSP/Source/Extensions/Vector4dExtensions.cs diff --git a/LibBSP/LibBSP.csproj b/LibBSP/LibBSP.csproj index 282ac8f..747a009 100644 --- a/LibBSP/LibBSP.csproj +++ b/LibBSP/LibBSP.csproj @@ -49,6 +49,9 @@ + + + diff --git a/LibBSP/Source/Extensions/ColorExtensions.cs b/LibBSP/Source/Extensions/ColorExtensions.cs index 3ed74f6..912a287 100644 --- a/LibBSP/Source/Extensions/ColorExtensions.cs +++ b/LibBSP/Source/Extensions/ColorExtensions.cs @@ -30,5 +30,26 @@ namespace LibBSP { #endif } + /// + /// Gets this Color as R8G8B8A8. + /// + /// This Color. + /// A byte array with four members, RGBA. + 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; + } + } } diff --git a/LibBSP/Source/Extensions/PlaneExtensions.cs b/LibBSP/Source/Extensions/PlaneExtensions.cs index 5a1dc28..8d5f940 100644 --- a/LibBSP/Source/Extensions/PlaneExtensions.cs +++ b/LibBSP/Source/Extensions/PlaneExtensions.cs @@ -18,6 +18,18 @@ namespace LibBSP { /// public static class PlaneExtensions { + /// + /// Array of base texture axes. When referenced properly, provides a good default texture axis for any given plane. + /// + 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) + }; + /// /// Intersects three s at a . Returns NaN for all components if two or more s are parallel. /// @@ -262,14 +274,32 @@ namespace LibBSP { /// The version of this lump. /// A List of objects. /// was null. - /// This structure is not implemented for the given maptype. /// 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. public static List LumpFactory(byte[] data, MapType type, int version = 0) { if (data == null) { throw new ArgumentNullException(); } - int structLength = 0; + int structLength = GetStructLength(type, version); + int numObjects = data.Length / structLength; + List lump = new List(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; + } + + /// + /// Gets this as a byte array to be used in a BSP of type . + /// + /// This . + /// The of BSP this is from. + /// The version of the planes lump in the BSP. + /// byte array representing this 's components. + public static byte[] GetBytes(this Plane p, MapType type, int version = 0) { + byte[] bytes = new byte[GetStructLength(type, version)]; switch (type) { case MapType.Quake: case MapType.Nightfire: @@ -289,33 +319,39 @@ namespace LibBSP { case MapType.Quake2: case MapType.Daikatana: case MapType.TacticalInterventionEncrypted: { - structLength = 20; + BitConverter.GetBytes(BestAxis(p)).CopyTo(bytes, 16); 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; - } - default: { - throw new ArgumentException("Map type " + type + " doesn't use a Plane lump or the lump is unknown."); + } + p.normal.GetBytes().CopyTo(bytes, 0); + BitConverter.GetBytes(p.distance).CopyTo(bytes, 12); + return bytes; + } + + /// + /// Gets the axis this 's normal is closest to (the 's normal + /// and the axis have the largest dot product). + /// 0 = Positive Z + /// 1 = Negative Z + /// 2 = Positive X + /// 3 = Negative X + /// 4 = Positive Y + /// 5 = Negative Y + /// + /// This . + /// The best-match axis for this . + 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; - List lump = new List(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; + return bestaxis; } /// @@ -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; + } } } diff --git a/LibBSP/Source/Extensions/Vector2dExtensions.cs b/LibBSP/Source/Extensions/Vector2dExtensions.cs new file mode 100644 index 0000000..b6e3ef6 --- /dev/null +++ b/LibBSP/Source/Extensions/Vector2dExtensions.cs @@ -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 + + /// + /// Class containing helper methods for objects. + /// + public static class Vector2dExtensions { + + /// + /// Gets a byte array representing the components of this as floats. + /// + /// This . + /// byte array with the components' bytes. + 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; + } + + } +} diff --git a/LibBSP/Source/Extensions/Vector3dExtensions.cs b/LibBSP/Source/Extensions/Vector3dExtensions.cs new file mode 100644 index 0000000..98ac3d2 --- /dev/null +++ b/LibBSP/Source/Extensions/Vector3dExtensions.cs @@ -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 + + /// + /// Class containing helper methods for objects. + /// + public static class Vector3dExtensions { + + /// + /// Gets a byte array representing the components of this as floats. + /// + /// This . + /// byte array with the components' bytes. + 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; + } + + } +} diff --git a/LibBSP/Source/Extensions/Vector4dExtensions.cs b/LibBSP/Source/Extensions/Vector4dExtensions.cs new file mode 100644 index 0000000..a54388f --- /dev/null +++ b/LibBSP/Source/Extensions/Vector4dExtensions.cs @@ -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 + + /// + /// Class containing helper methods for objects. + /// + public static class Vector4dExtensions { + + /// + /// Gets a byte array representing the components of this as floats. + /// + /// This . + /// byte array with the components' bytes. + 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; + } + + } +} diff --git a/LibBSP/Source/Extensions/VertexExtensions.cs b/LibBSP/Source/Extensions/VertexExtensions.cs index 7bfdc60..ec7ca9c 100644 --- a/LibBSP/Source/Extensions/VertexExtensions.cs +++ b/LibBSP/Source/Extensions/VertexExtensions.cs @@ -15,6 +15,7 @@ namespace LibBSP { #if UNITY using Vector2d = Vector2; using Vector3d = Vector3; + using Vector4d = Vector4; #if !OLDUNITY using Vertex = UIVertex; #endif @@ -122,6 +123,9 @@ namespace LibBSP { 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.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; } case MapType.MOHAA: @@ -137,14 +141,20 @@ namespace LibBSP { case MapType.Raven: { 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.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.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; } case MapType.STEF2: case MapType.STEF2Demo: { 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.uv2 = new Vector2d(BitConverter.ToSingle(data, 28), 0); 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)); goto case MapType.Quake; @@ -186,66 +196,13 @@ namespace LibBSP { /// A List of objects. /// was null. /// This structure is not implemented for the given maptype. - /// This function goes here since I can't put it into Unity's UIVertex class, and so I can't + /// This function goes here since it can't be in Unity's UIVertex class, and so I can't /// depend on having a constructor taking a byte array. public static List LumpFactory(byte[] data, MapType type, int version = 0) { if (data == null) { throw new ArgumentNullException(); } - 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."); - } - } + int structLength = GetStructLength(type, version); int numObjects = data.Length / structLength; List lump = new List(numObjects); byte[] bytes = new byte[structLength]; @@ -325,5 +282,164 @@ namespace LibBSP { } } + /// + /// Gets the length of the struct for the given and . + /// + /// The type of BSP to get struct length for. + /// Version of the lump. + /// The length of the struct for the given of the given . + 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; + } + + /// + /// Gets a byte array for this to be inserted into a map of type with lump version . + /// + /// This . + /// The this is from. + /// The version of the lump this is from. + /// byte array of the data for this . + 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; + } + } } diff --git a/LibBSP/Source/Structs/BSP/BSP.cs b/LibBSP/Source/Structs/BSP/BSP.cs index 848c7bb..9dace54 100644 --- a/LibBSP/Source/Structs/BSP/BSP.cs +++ b/LibBSP/Source/Structs/BSP/BSP.cs @@ -606,8 +606,19 @@ namespace LibBSP { if (_staticProps == null) { if (gameLump != null && gameLump.ContainsKey(GameLumpType.sprp)) { LumpInfo info = gameLump[GameLumpType.sprp]; - byte[] thisLump = new byte[info.length]; - Array.Copy(gameLump.rawData, info.offset - gameLump.gameLumpOffset, thisLump, 0, info.length); + byte[] thisLump; + // 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); } } @@ -832,9 +843,16 @@ namespace LibBSP { int count = (int)(countProperty.GetGetMethod().Invoke(o, null)); // Get the lump from this class - List theLump = targetLump.GetGetMethod().Invoke(this, null) as List; - - return theLump.GetRange(index, count); + IList theLump = targetLump.GetGetMethod().Invoke(this, null) as IList; + + // Copy items from the lump into a return list. + // IList lacks AddRange and this is faster and creates less garbage than any Linq trickery I could come up with. + // Passing references to IList out of this method just eats obscene amounts of memory until the system runs out. + List ret = new List(count); + for (int i = 0; i < count; ++i) { + ret.Add(theLump[index + i]); + } + return ret; } } diff --git a/LibBSP/Source/Structs/BSP/Brush.cs b/LibBSP/Source/Structs/BSP/Brush.cs index eaa118e..231d4e3 100644 --- a/LibBSP/Source/Structs/BSP/Brush.cs +++ b/LibBSP/Source/Structs/BSP/Brush.cs @@ -8,10 +8,270 @@ namespace LibBSP { /// public struct Brush { - [Index("brushSides")] public int firstSide { get; private set; } - [Count("brushSides")] public int numSides { get; private set; } - public int texture { get; private set; } - public int contents { get; private set; } + public byte[] data; + public MapType type; + public int version; + + [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; + } + } + } + } /// /// Creates a new object from a byte array. @@ -20,70 +280,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public Brush(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - firstSide = -1; - numSides = -1; - texture = -1; - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -139,8 +342,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new Brush(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/BrushSide.cs b/LibBSP/Source/Structs/BSP/BrushSide.cs index 9a3e228..ea6f08f 100644 --- a/LibBSP/Source/Structs/BSP/BrushSide.cs +++ b/LibBSP/Source/Structs/BSP/BrushSide.cs @@ -1,23 +1,374 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; namespace LibBSP { /// /// Holds the data used by the brush side structures of all formats of BSP. /// - [StructLayout(LayoutKind.Explicit)] public struct BrushSide { + + public byte[] data; + public MapType type; + public int version; + + public int plane { + get { + 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: { + 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; + } + } + } + } - // Call of Duty's format sucks. The first field is either a float or an int - // depending on whether or not it's one of the first six sides in a brush. - [FieldOffset(0)] public int plane; - [FieldOffset(0)] public float dist; - [FieldOffset(4)] public int texture; // -1 is a valid texture index in Quake 2. However it means "unused" there - [FieldOffset(8)] public int face; - [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. - [FieldOffset(16)] public bool bevel; + 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; + } + } + } + } /// /// Creates a new object from a byte array. @@ -26,80 +377,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public BrushSide(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - plane = -1; - texture = -1; - face = -1; - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -161,8 +445,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; i++) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new BrushSide(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/Cubemap.cs b/LibBSP/Source/Structs/BSP/Cubemap.cs index 2421ce1..55efcee 100644 --- a/LibBSP/Source/Structs/BSP/Cubemap.cs +++ b/LibBSP/Source/Structs/BSP/Cubemap.cs @@ -18,8 +18,96 @@ namespace LibBSP { /// public struct Cubemap { - public Vector3d origin { get; private set; } - public int size { get; private set; } + public byte[] data; + 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; + } + } + } + } /// /// Creates a new object from a byte array. @@ -28,32 +116,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public Cubemap(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - 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: { - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -92,8 +161,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new Cubemap(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/DisplacementInfo.cs b/LibBSP/Source/Structs/BSP/DisplacementInfo.cs index 9a199c4..f6340a9 100644 --- a/LibBSP/Source/Structs/BSP/DisplacementInfo.cs +++ b/LibBSP/Source/Structs/BSP/DisplacementInfo.cs @@ -18,17 +18,175 @@ namespace LibBSP { /// public struct DisplacementInfo { - public Vector3d startPosition { get; private set; } - public int dispVertStart { get; private set; } - public int dispTriStart { get; private set; } - public int power { get; private set; } - public int minTess { get; private set; } - public float smoothingAngle { get; private set; } - public int contents { get; private set; } - public ushort mapFace { get; private set; } - public int lightmapAlphaStart { get; private set; } - public int lightmapSamplePositionStart { get; private set; } - public uint[] allowedVerts { get; private set; } + public byte[] data; + public MapType type; + public int version; + + public Vector3d startPosition { + get { + return new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)); + } + set { + value.GetBytes().CopyTo(data, 0); + } + } + + 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)); + } + } + } + } /// /// Creates a new object from a byte array. @@ -37,55 +195,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public DisplacementInfo(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - startPosition = new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)); - dispVertStart = BitConverter.ToInt32(data, 12); - dispTriStart = BitConverter.ToInt32(data, 16); - 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)); - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -133,8 +249,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new DisplacementInfo(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/DisplacementVertex.cs b/LibBSP/Source/Structs/BSP/DisplacementVertex.cs index 7fcf213..b51d3dd 100644 --- a/LibBSP/Source/Structs/BSP/DisplacementVertex.cs +++ b/LibBSP/Source/Structs/BSP/DisplacementVertex.cs @@ -17,9 +17,45 @@ namespace LibBSP { /// public struct DisplacementVertex { - public Vector3d normal { get; private set; } // The normalized vector direction this vertex points from "flat" - public float dist { get; private set; } // Magnitude of normal, before normalization - public float alpha { get; private set; } // Alpha value of texture at this vertex + public byte[] data; + public MapType type; + public int version; + + /// + /// The normalized vector direction this vertex points from "flat". + /// + 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); + } + } + + /// + /// Magnitude of normal, before normalization. + /// + public float dist { + get { + return BitConverter.ToSingle(data, 12); + } + set { + BitConverter.GetBytes(value).CopyTo(data, 12); + } + } + + /// + /// Alpha value of texture at this vertex. + /// + public float alpha { + get { + return BitConverter.ToSingle(data, 16); + } + set { + BitConverter.GetBytes(value).CopyTo(data, 16); + } + } /// /// Creates a new object from a byte array. @@ -32,9 +68,9 @@ namespace LibBSP { if (data == null) { throw new ArgumentNullException(); } - normal = new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)); - dist = BitConverter.ToSingle(data, 12); - alpha = BitConverter.ToSingle(data, 16); + this.data = data; + this.type = type; + this.version = version; } /// diff --git a/LibBSP/Source/Structs/BSP/Edge.cs b/LibBSP/Source/Structs/BSP/Edge.cs index aa838b4..0299f3d 100644 --- a/LibBSP/Source/Structs/BSP/Edge.cs +++ b/LibBSP/Source/Structs/BSP/Edge.cs @@ -8,8 +8,129 @@ namespace LibBSP { /// public struct Edge { - public int firstVertex { get; private set; } - public int secondVertex { get; private set; } + public byte[] data; + 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; + } + } + } + } /// /// Creates a new object from a byte array. @@ -18,41 +139,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public Edge(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - 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: { - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -99,8 +192,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new Edge(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/Face.cs b/LibBSP/Source/Structs/BSP/Face.cs index a36d460..a805f9c 100644 --- a/LibBSP/Source/Structs/BSP/Face.cs +++ b/LibBSP/Source/Structs/BSP/Face.cs @@ -24,24 +24,870 @@ namespace LibBSP { /// public struct Face { - public int plane { get; private set; } - public int side { get; private set; } - [Index("edges")] public int firstEdge { get; private set; } - [Count("edges")] public int numEdges { get; private set; } - public int texture { get; private set; } - [Index("vertices")] public int firstVertex { get; private set; } - [Count("vertices")] public int numVertices { get; private set; } - public int material { get; private set; } - public int textureInfo { get; private set; } - public int displacement { get; private set; } - public int original { get; private set; } - public int flags { get; private set; } - [Index("indices")] public int firstIndex { get; private set; } - [Count("indices")] public int numIndices { get; private set; } - public int unknown { get; private set; } - public int lightStyles { get; private set; } - public int lightMaps { get; private set; } - public Vector2d patchSize { get; private set; } + public byte[] data; + public MapType type; + public int version; + + public int plane { + get { + switch (type) { + case MapType.Quake: + case MapType.Quake2: + case MapType.Daikatana: + case MapType.SiN: + case MapType.SoF: + 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.Source17: { + return BitConverter.ToUInt16(data, 32); + } + case MapType.Nightfire: + case MapType.Vindictus: { + return BitConverter.ToInt32(data, 0); + } + 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.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.Source17: { + data[32] = bytes[0]; + data[33] = bytes[1]; + break; + } + case MapType.Nightfire: + case MapType.Vindictus: { + bytes.CopyTo(data, 0); + break; + } + } + } + } + + public int side { + get { + switch (type) { + case MapType.Quake: + case MapType.Quake2: + case MapType.Daikatana: + case MapType.SiN: + case MapType.SoF: { + return BitConverter.ToUInt16(data, 2); + } + 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 (int)data[2]; + } + case MapType.Vindictus: { + return (int)data[4]; + } + case MapType.Source17: { + return (int)data[34]; + } + 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: { + data[2] = bytes[0]; + data[3] = bytes[1]; + break; + } + 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]; + break; + } + case MapType.Vindictus: { + data[4] = bytes[0]; + break; + } + case MapType.Source17: { + data[34] = bytes[0]; + break; + } + } + } + } + + [Index("edges")] public int firstEdge { + get { + switch (type) { + case MapType.Quake: + case MapType.Quake2: + case MapType.Daikatana: + case MapType.SiN: + case MapType.SoF: + 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.ToInt32(data, 4); + } + case MapType.Vindictus: { + return BitConverter.ToInt32(data, 8); + } + case MapType.Source17: { + return BitConverter.ToInt32(data, 36); + } + 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.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: { + bytes.CopyTo(data, 4); + break; + } + case MapType.Vindictus: { + bytes.CopyTo(data, 8); + break; + } + case MapType.Source17: { + bytes.CopyTo(data, 36); + break; + } + } + } + } + + [Count("edges")] public int numEdges { + get { + switch (type) { + case MapType.Quake: + case MapType.Quake2: + case MapType.Daikatana: + case MapType.SiN: + case MapType.SoF: + 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, 8); + } + case MapType.Vindictus: { + return BitConverter.ToInt32(data, 12); + } + case MapType.Source17: { + return BitConverter.ToUInt16(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.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[8] = bytes[0]; + data[9] = bytes[1]; + break; + } + case MapType.Vindictus: { + bytes.CopyTo(data, 12); + break; + } + case MapType.Source17: { + data[40] = bytes[0]; + data[41] = bytes[1]; + break; + } + } + } + } + + public int texture { + get { + switch (type) { + case MapType.CoD: + case MapType.CoD2: { + return BitConverter.ToInt16(data, 0); + } + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + return BitConverter.ToInt32(data, 0); + } + case MapType.Quake: + case MapType.Quake2: + case MapType.Daikatana: + case MapType.SiN: + case MapType.SoF: { + return BitConverter.ToUInt16(data, 10); + } + case MapType.Nightfire: { + return BitConverter.ToInt32(data, 24); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + case MapType.CoD: + case MapType.CoD2: { + data[0] = bytes[0]; + data[1] = bytes[1]; + break; + } + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + bytes.CopyTo(data, 0); + break; + } + case MapType.Quake: + case MapType.Quake2: + case MapType.Daikatana: + case MapType.SiN: + case MapType.SoF: { + data[10] = bytes[0]; + data[11] = bytes[1]; + break; + } + case MapType.Nightfire: { + bytes.CopyTo(data, 24); + break; + } + } + } + } + + [Index("vertices")] public int firstVertex { + get { + switch (type) { + case MapType.CoD: + case MapType.CoD2: + case MapType.Nightfire: { + return BitConverter.ToInt32(data, 4); + } + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + return BitConverter.ToInt32(data, 12); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + case MapType.CoD: + case MapType.CoD2: + case MapType.Nightfire: { + bytes.CopyTo(data, 4); + break; + } + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + bytes.CopyTo(data, 12); + break; + } + } + } + } + + [Count("vertices")] public int numVertices { + get { + switch (type) { + case MapType.CoD: + case MapType.CoD2: { + return BitConverter.ToInt16(data, 8); + } + case MapType.Nightfire: { + return BitConverter.ToInt32(data, 8); + } + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + return BitConverter.ToInt32(data, 16); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + case MapType.CoD: + case MapType.CoD2: { + data[8] = bytes[0]; + data[9] = bytes[1]; + break; + } + case MapType.Nightfire: { + bytes.CopyTo(data, 8); + break; + } + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + bytes.CopyTo(data, 16); + break; + } + } + } + } + + public int material { + get { + switch (type) { + case MapType.Nightfire: { + return BitConverter.ToInt32(data, 28); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + case MapType.Nightfire: { + bytes.CopyTo(data, 28); + break; + } + } + } + } + + public int textureInfo { + get { + switch (type) { + 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, 10); + } + case MapType.Vindictus: { + return BitConverter.ToInt32(data, 16); + } + case MapType.Nightfire: { + return BitConverter.ToInt32(data, 32); + } + case MapType.Source17: { + return BitConverter.ToUInt16(data, 42); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + 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[10] = bytes[0]; + data[11] = bytes[1]; + break; + } + case MapType.Vindictus: { + bytes.CopyTo(data, 16); + break; + } + case MapType.Nightfire: { + bytes.CopyTo(data, 32); + break; + } + case MapType.Source17: { + data[42] = bytes[0]; + data[43] = bytes[1]; + break; + } + } + } + } + + public int displacement { + get { + switch (type) { + 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, 12); + } + case MapType.Vindictus: { + return BitConverter.ToInt32(data, 20); + } + case MapType.Source17: { + return BitConverter.ToInt16(data, 44); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + 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[12] = bytes[0]; + data[13] = bytes[1]; + break; + } + case MapType.Vindictus: { + bytes.CopyTo(data, 20); + break; + } + case MapType.Source17: { + data[44] = bytes[0]; + data[45] = bytes[1]; + break; + } + } + } + } + + public int original { + get { + switch (type) { + 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.ToInt32(data, 44); + } + case MapType.Vindictus: { + if (version == 2) { + return BitConverter.ToInt32(data, 60); + } + else { + return BitConverter.ToInt32(data, 56); + } + } + case MapType.Source17: { + return BitConverter.ToInt32(data, 96); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + 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: { + bytes.CopyTo(data, 44); + break; + } + case MapType.Vindictus: { + if (version == 2) { + bytes.CopyTo(data, 60); + } + else { + bytes.CopyTo(data, 56); + } + break; + } + case MapType.Source17: { + bytes.CopyTo(data, 96); + break; + } + } + } + } + + public int flags { + get { + switch (type) { + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + return BitConverter.ToInt32(data, 8); + } + case MapType.Nightfire: { + return BitConverter.ToInt32(data, 20); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + bytes.CopyTo(data, 8); + break; + } + case MapType.Nightfire: { + bytes.CopyTo(data, 20); + break; + } + } + } + } + + [Index("indices")] public int firstIndex { + get { + switch (type) { + case MapType.CoD: + case MapType.CoD2: + case MapType.Nightfire: { + return BitConverter.ToInt32(data, 12); + } + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + return BitConverter.ToInt32(data, 20); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + case MapType.CoD: + case MapType.CoD2: + case MapType.Nightfire: { + bytes.CopyTo(data, 12); + break; + } + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + bytes.CopyTo(data, 20); + break; + } + } + } + } + + [Count("indices")] public int numIndices { + get { + switch (type) { + case MapType.CoD: + case MapType.CoD2: { + return BitConverter.ToInt16(data, 10); + } + case MapType.Nightfire: { + return BitConverter.ToInt32(data, 16); + } + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + return BitConverter.ToInt32(data, 24); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + case MapType.CoD: + case MapType.CoD2: { + data[10] = bytes[0]; + data[11] = bytes[1]; + return; + } + case MapType.Nightfire: { + bytes.CopyTo(data, 16); + break; + } + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + bytes.CopyTo(data, 24); + break; + } + } + } + } + + public int unknown { + get { + switch (type) { + case MapType.Nightfire: { + return BitConverter.ToInt32(data, 36); + } + default: { + return 0; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (type) { + case MapType.Nightfire: { + bytes.CopyTo(data, 36); + break; + } + } + } + } + + public int lightStyles { + 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; + } + } + } + } + + public int lightMaps { + 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; + } + } + } + } + + public Vector2d patchSize { + get { + switch (type) { + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + return new Vector2d(BitConverter.ToInt32(data, 96), BitConverter.ToInt32(data, 100)); + } + default: { + return new Vector2d(float.NaN, float.NaN); + } + } + } + set { + switch (type) { + case MapType.Quake3: + case MapType.Raven: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: { + byte[] bytes = BitConverter.GetBytes((int)value.x); + bytes.CopyTo(data, 96); + bytes = BitConverter.GetBytes((int)value.y); + bytes.CopyTo(data, 100); + break; + } + } + } + } /// /// Creates a new object from a byte array. @@ -50,127 +896,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public Face(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - plane = -1; - side = -1; - firstEdge = -1; - numEdges = -1; - texture = -1; - firstVertex = -1; - numVertices = -1; - material = -1; - textureInfo = -1; - displacement = -1; - original = -1; - flags = -1; - firstIndex = -1; - numIndices = -1; - unknown = -1; - lightStyles = -1; - lightMaps = -1; - patchSize = new Vector2d(float.NaN, float.NaN); - switch (type) { - case MapType.CoD: { - texture = BitConverter.ToInt16(data, 0); - firstVertex = BitConverter.ToInt32(data, 4); - numVertices = BitConverter.ToInt16(data, 8); - numIndices = BitConverter.ToInt16(data, 10); - firstIndex = BitConverter.ToInt32(data, 12); - break; - } - case MapType.Quake: - case MapType.Quake2: - case MapType.Daikatana: - case MapType.SiN: - case MapType.SoF: { - plane = BitConverter.ToUInt16(data, 0); - side = BitConverter.ToUInt16(data, 2); - firstEdge = BitConverter.ToInt32(data, 4); - numEdges = BitConverter.ToUInt16(data, 8); - texture = BitConverter.ToUInt16(data, 10); - break; - } - case MapType.Quake3: - case MapType.Raven: - case MapType.STEF2: - case MapType.STEF2Demo: - case MapType.MOHAA: - case MapType.FAKK: { - texture = BitConverter.ToInt32(data, 0); - flags = BitConverter.ToInt32(data, 8); - firstVertex = BitConverter.ToInt32(data, 12); - numVertices = BitConverter.ToInt32(data, 16); - firstIndex = BitConverter.ToInt32(data, 20); - numIndices = BitConverter.ToInt32(data, 24); - patchSize = new Vector2d(BitConverter.ToInt32(data, 96), BitConverter.ToInt32(data, 100)); - break; - } - case MapType.Source17: { - plane = BitConverter.ToUInt16(data, 32); - side = (int)data[34]; - firstEdge = BitConverter.ToInt32(data, 36); - numEdges = BitConverter.ToUInt16(data, 40); - textureInfo = BitConverter.ToUInt16(data, 42); - displacement = BitConverter.ToInt16(data, 44); - original = BitConverter.ToInt32(data, 96); - break; - } - 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: { - plane = BitConverter.ToUInt16(data, 0); - side = (int)data[2]; - firstEdge = BitConverter.ToInt32(data, 4); - numEdges = BitConverter.ToUInt16(data, 8); - textureInfo = BitConverter.ToUInt16(data, 10); - displacement = BitConverter.ToInt16(data, 12); - original = BitConverter.ToInt32(data, 44); - break; - } - case MapType.Vindictus: { - plane = BitConverter.ToInt32(data, 0); - side = (int)data[4]; - firstEdge = BitConverter.ToInt32(data, 8); - numEdges = BitConverter.ToInt32(data, 12); - textureInfo = BitConverter.ToInt32(data, 16); - displacement = BitConverter.ToInt32(data, 20); - if (version == 2) { - original = BitConverter.ToInt32(data, 60); - } else { - original = BitConverter.ToInt32(data, 56); - } - break; - } - case MapType.Nightfire: { - plane = BitConverter.ToInt32(data, 0); - firstVertex = BitConverter.ToInt32(data, 4); - numVertices = BitConverter.ToInt32(data, 8); - firstIndex = BitConverter.ToInt32(data, 12); - numIndices = BitConverter.ToInt32(data, 16); - flags = BitConverter.ToInt32(data, 20); - texture = BitConverter.ToInt32(data, 24); - material = BitConverter.ToInt32(data, 28); - textureInfo = BitConverter.ToInt32(data, 32); - unknown = BitConverter.ToInt32(data, 36); - lightStyles = BitConverter.ToInt32(data, 40); - lightMaps = BitConverter.ToInt32(data, 44); - break; - } - default: { - throw new ArgumentException("Map type " + type + " isn't supported by the Face class."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -188,7 +920,8 @@ namespace LibBSP { } int structLength = 0; switch (type) { - case MapType.CoD: { + case MapType.CoD: + case MapType.CoD2: { structLength = 16; break; } @@ -259,8 +992,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new Face(bytes, type, version)); } @@ -289,6 +1022,9 @@ namespace LibBSP { case MapType.CoD: { return 6; } + case MapType.CoD2: { + return 7; + } case MapType.Vindictus: case MapType.TacticalInterventionEncrypted: case MapType.Source17: diff --git a/LibBSP/Source/Structs/BSP/LODTerrain.cs b/LibBSP/Source/Structs/BSP/LODTerrain.cs index 1c4f9d8..4e2f90b 100644 --- a/LibBSP/Source/Structs/BSP/LODTerrain.cs +++ b/LibBSP/Source/Structs/BSP/LODTerrain.cs @@ -8,17 +8,274 @@ namespace LibBSP { /// public struct LODTerrain { - public byte flags { get; private set; } - public byte scale { get; private set; } - public byte[] lightmapCoords { get; private set; } - public float[] textureCoords { get; private set; } - public sbyte x { get; private set; } - public sbyte y { get; private set; } - public short baseZ { get; private set; } - public ushort texture { get; private set; } - public short lightmap { get; private set; } - public short[][] vertexFlags { get; private set; } - public byte[][] heightmap { get; private set; } + public byte[] data; + public MapType type; + public int version; + + public byte flags { + get { + switch (type) { + case MapType.MOHAA: { + return data[0]; + } + 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]; + } + } + } + } /// /// Creates a new object from a byte array. @@ -27,47 +284,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public LODTerrain(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - lightmapCoords = new byte[2]; - textureCoords = new float[8]; - vertexFlags = new short[2][]; - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -95,8 +318,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new LODTerrain(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/Leaf.cs b/LibBSP/Source/Structs/BSP/Leaf.cs index e82dfb7..486d9ba 100644 --- a/LibBSP/Source/Structs/BSP/Leaf.cs +++ b/LibBSP/Source/Structs/BSP/Leaf.cs @@ -8,12 +8,457 @@ namespace LibBSP { /// public struct Leaf { - public int contents { get; private set; } - [Index("markBrushes")] public int firstMarkBrush { get; private set; } - [Count("markBrushes")] public int numMarkBrushes { get; private set; } - [Index("markSurfaces")] public int firstMarkFace { get; private set; } - [Count("markSurfaces")] public int numMarkFaces { get; private set; } - public int pvs { get; private set; } + public byte[] data; + public MapType type; + public int version; + + public int contents { + 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; + } + } + } + } /// /// Creates a new object from a byte array. @@ -22,86 +467,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public Leaf(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - contents = -1; - firstMarkBrush = -1; - numMarkBrushes = -1; - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -143,7 +515,8 @@ namespace LibBSP { structLength = 56; break; } - case MapType.CoD: { + case MapType.CoD: + case MapType.CoD2: { structLength = 36; break; } @@ -171,8 +544,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new Leaf(bytes, type, version)); } @@ -221,6 +594,9 @@ namespace LibBSP { case MapType.CoD: { return 21; } + case MapType.CoD2: { + return 26; + } default: { return -1; } diff --git a/LibBSP/Source/Structs/BSP/Lumps/DisplacementVertices.cs b/LibBSP/Source/Structs/BSP/Lumps/DisplacementVertices.cs index acd783a..6e3543c 100644 --- a/LibBSP/Source/Structs/BSP/Lumps/DisplacementVertices.cs +++ b/LibBSP/Source/Structs/BSP/Lumps/DisplacementVertices.cs @@ -20,9 +20,9 @@ namespace LibBSP { throw new ArgumentNullException(); } int structLength = 20; - byte[] bytes = new byte[structLength]; int numObjects = data.Length / structLength; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); Add(new DisplacementVertex(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/Lumps/GameLump.cs b/LibBSP/Source/Structs/BSP/Lumps/GameLump.cs index 153e263..e37e18e 100644 --- a/LibBSP/Source/Structs/BSP/Lumps/GameLump.cs +++ b/LibBSP/Source/Structs/BSP/Lumps/GameLump.cs @@ -19,27 +19,6 @@ namespace LibBSP { /// public class GameLump : Dictionary { - private byte[] _rawData; - private int _gameLumpOffset; - - /// - /// Byte array representing the raw data read from the BSP for the game lump. - /// - public byte[] rawData { - get { - return _rawData; - } - } - - /// - /// The amount to subtract from all the values to find the offset relative to the start of the Game Lump data. May be 0. - /// - public int gameLumpOffset { - get { - return _gameLumpOffset; - } - } - /// /// Creates a new object by parsing a byte array into a Dictionary of objects. /// These objects contain offsets, lengths and versions of the GameLump lumps. @@ -53,7 +32,6 @@ namespace LibBSP { if (data == null) { throw new ArgumentNullException(); } - _rawData = data; int structLength = 0; switch (type) { @@ -82,25 +60,25 @@ namespace LibBSP { int numGameLumps = BitConverter.ToInt32(data, 0); if (numGameLumps > 0) { - int headerLength = 4 + (numGameLumps * structLength); + int lumpDictionaryOffset = (type == MapType.DMoMaM) ? 8 : 4; int lowestLumpOffset = int.MaxValue; 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 lumpVersion; int lumpOffset; int lumpLength; if (type == MapType.Vindictus) { - lumpFlags = BitConverter.ToInt32(data, (i * structLength) + 8); - lumpVersion = BitConverter.ToInt32(data, (i * structLength) + 12); - lumpOffset = BitConverter.ToInt32(data, (i * structLength) + 16); - lumpLength = BitConverter.ToInt32(data, (i * structLength) + 20); + lumpFlags = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 4); + lumpVersion = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 8); + lumpOffset = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 12); + lumpLength = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 16); } else { - lumpFlags = BitConverter.ToUInt16(data, (i * structLength) + 8); - lumpVersion = BitConverter.ToUInt16(data, (i * structLength) + 10); - lumpOffset = BitConverter.ToInt32(data, (i * structLength) + 12); - lumpLength = BitConverter.ToInt32(data, (i * structLength) + 16); + lumpFlags = BitConverter.ToUInt16(data, (i * structLength) + lumpDictionaryOffset + 4); + lumpVersion = BitConverter.ToUInt16(data, (i * structLength) + lumpDictionaryOffset + 6); + lumpOffset = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 8); + lumpLength = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 12); } LumpInfo info = new LumpInfo { ident = lumpIdent, @@ -115,9 +93,6 @@ namespace LibBSP { 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 { } } } + + /// + /// Gets the lowest offset used for a Game Lump. + /// + /// The lowest offset used for a Game Lump, or int.MaxValue if no Game Lumps exist. + public int GetLowestLumpOffset() { + int lowest = int.MaxValue; + foreach (LumpInfo info in Values) { + if (info.offset < lowest) { + lowest = info.offset; + } + } + return lowest; + } } } diff --git a/LibBSP/Source/Structs/BSP/Lumps/StaticProps.cs b/LibBSP/Source/Structs/BSP/Lumps/StaticProps.cs index be8912d..8961134 100644 --- a/LibBSP/Source/Structs/BSP/Lumps/StaticProps.cs +++ b/LibBSP/Source/Structs/BSP/Lumps/StaticProps.cs @@ -43,8 +43,8 @@ namespace LibBSP { offset += 4; if (numProps > 0) { structLength = (data.Length - offset) / numProps; - byte[] bytes = new byte[structLength]; for (int i = 0; i < numProps; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, offset, bytes, 0, structLength); Add(new StaticProp(bytes, type, version)); offset += structLength; diff --git a/LibBSP/Source/Structs/BSP/Lumps/Textures.cs b/LibBSP/Source/Structs/BSP/Lumps/Textures.cs index a7e1e95..982566d 100644 --- a/LibBSP/Source/Structs/BSP/Lumps/Textures.cs +++ b/LibBSP/Source/Structs/BSP/Lumps/Textures.cs @@ -65,11 +65,10 @@ namespace LibBSP { case MapType.Vindictus: case MapType.DMoMaM: { int offset = 0; - byte[] myBytes; for (int i = 0; i < data.Length; ++i) { if (data[i] == (byte)0x00) { // 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); Add(new Texture(myBytes, type, version)); offset = i + 1; @@ -80,8 +79,8 @@ namespace LibBSP { case MapType.Quake: { int numElements = BitConverter.ToInt32(data, 0); structLength = 40; - byte[] myBytes = new byte[structLength]; for (int i = 0; i < numElements; ++i) { + byte[] myBytes = new byte[structLength]; Array.Copy(data, BitConverter.ToInt32(data, (i + 1) * 4), myBytes, 0, structLength); Add(new Texture(myBytes, type, version)); } @@ -93,8 +92,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); Add(new Texture(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/Model.cs b/LibBSP/Source/Structs/BSP/Model.cs index 5eb0655..a35f288 100644 --- a/LibBSP/Source/Structs/BSP/Model.cs +++ b/LibBSP/Source/Structs/BSP/Model.cs @@ -16,15 +16,431 @@ namespace LibBSP { /// public struct Model { - public int headNode { get; private set; } // Quake, Half-life, Quake 2, SiN - [Index("leaves")] public int firstLeaf { get; private set; } // 007 nightfire - [Count("leaves")] public int numLeaves { get; private set; } - [Index("brushes")] public int firstBrush { get; private set; } // Quake 3 and derivatives - [Count("brushes")] public int numBrushes { get; private set; } - [Index("faces")] public int firstFace { get; private set; } // Quake/GoldSrc - [Count("faces")] public int numFaces { get; private set; } - [Index("markSurfaces")] public int firstLeafPatch { get; private set; } // CoD - [Count("markSurfaces")] public int numLeafPatches { get; private set; } + public byte[] data; + public MapType type; + public int version; + + [Index("nodes")] public int headNode { + get { + 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: { + 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; + } + } + } + } /// /// Creates a new object from a byte array. @@ -33,89 +449,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public Model(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - headNode = -1; - firstLeaf = -1; - numLeaves = -1; - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -181,8 +521,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new Model(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/Node.cs b/LibBSP/Source/Structs/BSP/Node.cs index a8419d7..d1cc6f3 100644 --- a/LibBSP/Source/Structs/BSP/Node.cs +++ b/LibBSP/Source/Structs/BSP/Node.cs @@ -8,9 +8,174 @@ namespace LibBSP { /// public struct Node { - public int plane { get; private set; } - public int child1 { get; private set; } // Negative values are valid here. However, the child can never be zero, - public int child2 { get; private set; } // since that would reference the head node causing an infinite loop. + public byte[] data; + public MapType type; + 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; + } + } + } + } /// /// Creates a new object from a byte array. @@ -19,51 +184,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public Node(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - plane = BitConverter.ToInt32(data, 0); // All formats I've seen use the first 4 bytes as an int, plane index - switch (type) { - case MapType.Quake: { - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -113,6 +240,7 @@ namespace LibBSP { case MapType.Quake3: case MapType.FAKK: case MapType.CoD: + case MapType.CoD2: case MapType.STEF2: case MapType.STEF2Demo: case MapType.MOHAA: @@ -127,8 +255,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new Node(bytes, type, version)); } @@ -181,6 +309,9 @@ namespace LibBSP { case MapType.CoD: { return 20; } + case MapType.CoD2: { + return 25; + } default: { return -1; } diff --git a/LibBSP/Source/Structs/BSP/Patch.cs b/LibBSP/Source/Structs/BSP/Patch.cs index 8797672..7184b44 100644 --- a/LibBSP/Source/Structs/BSP/Patch.cs +++ b/LibBSP/Source/Structs/BSP/Patch.cs @@ -18,14 +18,229 @@ namespace LibBSP { /// public struct Patch { - public short shader { get; private set; } - public short type { get; private set; } - public Vector2d dimensions { get; private set; } - public int flags { get; private set; } - [Index("patchVerts")] public int firstVertex { get; private set; } - [Count("patchVerts")] public int numVertices { get; private set; } - [Count("patchIndices")] public int numIndices { get; private set; } - [Index("patchIndices")] public int firstIndex { get; private set; } + public byte[] data; + public MapType type; + public int version; + + public short shader { + get { + switch (type) { + 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; + } + } + } + } /// /// Creates a new object from a byte array. @@ -34,34 +249,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public Patch(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - switch (type) { - case MapType.CoD: { - shader = BitConverter.ToInt16(data, 0); - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -89,8 +283,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new Patch(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/StaticModel.cs b/LibBSP/Source/Structs/BSP/StaticModel.cs index a81baab..8707d6c 100644 --- a/LibBSP/Source/Structs/BSP/StaticModel.cs +++ b/LibBSP/Source/Structs/BSP/StaticModel.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Text; #if UNITY using UnityEngine; #endif @@ -18,12 +19,142 @@ namespace LibBSP { /// public struct StaticModel { - public string name { get; private set; } - public Vector3d origin { get; private set; } - public Vector3d angles { get; private set; } - public float scale { get; private set; } - [Index("vertices")] public int firstVertex { get; private set; } - [Count("vertices")] public short numVertices { get; private set; } + public byte[] data; + public MapType type; + public int version; + + public string name { + 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; + } + } + } + } /// /// Creates a new object from a byte array. @@ -32,31 +163,13 @@ namespace LibBSP { /// The map type. /// The version of static prop lump this object is a member of. /// was null. - /// This structure is not implemented for the given maptype. public StaticModel(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - name = ""; - origin = Vector3d.zero; - angles = Vector3d.zero; - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// @@ -84,8 +197,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new StaticModel(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/BSP/StaticProp.cs b/LibBSP/Source/Structs/BSP/StaticProp.cs index 74e423a..401bfc8 100644 --- a/LibBSP/Source/Structs/BSP/StaticProp.cs +++ b/LibBSP/Source/Structs/BSP/StaticProp.cs @@ -3,13 +3,17 @@ #endif using System; +using System.Text; #if UNITY using UnityEngine; #endif namespace LibBSP { #if UNITY + using Color = Color32; using Vector3d = Vector3; +#else + using Color = System.Drawing.Color; #endif /// @@ -17,16 +21,1488 @@ namespace LibBSP { /// public struct StaticProp { - public Vector3d origin { get; private set; } - public Vector3d angles { get; private set; } - public short dictionaryEntry { get; private set; } - public byte solidity { get; private set; } - public byte flags { get; private set; } - public int skin { get; private set; } - public float minFadeDist { get; private set; } - public float maxFadeDist { get; private set; } - public float forcedFadeScale { get; private set; } - public string targetname { get; private set; } + public byte[] data; + 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.L4D2: + case MapType.TacticalInterventionEncrypted: + case MapType.Vindictus: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + return new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)); + } + default: { + return new Vector3d(float.NaN, float.NaN, float.NaN); + } + } + } + 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.L4D2: + case MapType.TacticalInterventionEncrypted: + case MapType.Vindictus: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + value.GetBytes().CopyTo(data, 0); + break; + } + } + break; + } + } + } + } + + public Vector3d angles { + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + return new Vector3d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 16), BitConverter.ToSingle(data, 20)); + } + default: { + return new Vector3d(float.NaN, float.NaN, float.NaN); + } + } + } + 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.L4D2: + case MapType.TacticalInterventionEncrypted: + case MapType.Vindictus: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + value.GetBytes().CopyTo(data, 12); + break; + } + } + break; + } + } + } + } + + public short dictionaryEntry { + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + return BitConverter.ToInt16(data, 24); + } + default: { + return -1; + } + } + } + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + bytes.CopyTo(data, 24); + break; + } + } + break; + } + } + } + } + + public short firstLeaf { + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + return BitConverter.ToInt16(data, 26); + } + default: { + return -1; + } + } + } + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + bytes.CopyTo(data, 26); + break; + } + } + break; + } + } + } + } + + public short numLeafs { + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + return BitConverter.ToInt16(data, 28); + } + default: { + return -1; + } + } + } + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + bytes.CopyTo(data, 28); + break; + } + } + break; + } + } + } + } + + public byte solidity { + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + return data[30]; + } + default: { + return 0; + } + } + } + default: { + return 0; + } + } + } + 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.Vindictus: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + data[30] = value; + break; + } + } + break; + } + } + } + } + + public byte 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + return data[31]; + } + default: { + return 0; + } + } + } + default: { + return 0; + } + } + } + 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.Vindictus: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: { + data[31] = value; + break; + } + } + break; + } + } + } + } + + public int skin { + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 11: { + return BitConverter.ToInt32(data, 32); + } + case 9: { + if (data.Length == 76) { + return BitConverter.ToInt32(data, 36); + } else { + return BitConverter.ToInt32(data, 32); + } + } + default: { + return -1; + } + } + } + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 11: { + bytes.CopyTo(data, 32); + break; + } + case 9: { + if (data.Length == 76) { + bytes.CopyTo(data, 36); + } else { + bytes.CopyTo(data, 32); + } + break; + } + } + break; + } + } + } + } + + public float minFadeDist { + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 11: { + return BitConverter.ToSingle(data, 36); + } + case 9: { + if (data.Length == 76) { + return BitConverter.ToSingle(data, 40); + } else { + return BitConverter.ToSingle(data, 36); + } + } + default: { + return float.NaN; + } + } + } + default: { + return float.NaN; + } + } + } + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 11: { + bytes.CopyTo(data, 36); + break; + } + case 9: { + if (data.Length == 76) { + bytes.CopyTo(data, 40); + } else { + bytes.CopyTo(data, 36); + } + break; + } + } + break; + } + } + } + } + + public float maxFadeDist { + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 11: { + return BitConverter.ToSingle(data, 40); + } + case 9: { + if (data.Length == 76) { + return BitConverter.ToInt32(data, 44); + } else { + return BitConverter.ToInt32(data, 40); + } + } + default: { + return float.NaN; + } + } + } + default: { + return float.NaN; + } + } + } + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 11: { + bytes.CopyTo(data, 40); + break; + } + case 9: { + if (data.Length == 76) { + bytes.CopyTo(data, 36); + } else { + bytes.CopyTo(data, 32); + } + break; + } + } + break; + } + } + } + } + + public Vector3d lightingOrigin { + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 11: { + return new Vector3d(BitConverter.ToSingle(data, 44), BitConverter.ToSingle(data, 48), BitConverter.ToSingle(data, 52)); + } + case 9: { + if (data.Length == 76) { + return new Vector3d(BitConverter.ToSingle(data, 48), BitConverter.ToSingle(data, 52), BitConverter.ToSingle(data, 56)); + } else { + return new Vector3d(BitConverter.ToSingle(data, 44), BitConverter.ToSingle(data, 48), BitConverter.ToSingle(data, 52)); + } + } + default: { + return new Vector3d(float.NaN, float.NaN, float.NaN); + } + } + } + default: { + return new Vector3d(float.NaN, float.NaN, float.NaN); + } + } + } + set { + byte[] bytes = value.GetBytes(); + 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: + case MapType.DMoMaM: { + switch (version) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 11: { + bytes.CopyTo(data, 44); + break; + } + case 9: { + if (data.Length == 76) { + value.GetBytes().CopyTo(data, 48); + } else { + value.GetBytes().CopyTo(data, 44); + } + break; + } + } + break; + } + } + } + } + + public float forcedFadeScale { + 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: + case MapType.DMoMaM: { + switch (version) { + case 5: + case 6: + case 7: + case 8: + case 10: + case 11: { + return BitConverter.ToSingle(data, 56); + } + case 9: { + if (data.Length == 76) { + return BitConverter.ToSingle(data, 60); + } else { + return BitConverter.ToSingle(data, 56); + } + } + default: { + return float.NaN; + } + } + } + default: { + return float.NaN; + } + } + } + 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: + case MapType.DMoMaM: { + switch (version) { + case 5: + case 6: + case 7: + case 8: + case 10: + case 11: { + bytes.CopyTo(data, 56); + break; + } + case 9: { + if (data.Length == 76) { + bytes.CopyTo(data, 60); + } else { + bytes.CopyTo(data, 56); + } + break; + } + } + break; + } + } + } + } + + public short minDXLevel { + 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: { + switch (version) { + case 6: + case 7: { + return BitConverter.ToInt16(data, 60); + } + default: { + return -1; + } + } + } + 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: { + switch (version) { + case 6: + case 7: { + bytes.CopyTo(data, 60); + break; + } + } + break; + } + } + } + } + + public short maxDXLevel { + 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: { + switch (version) { + case 6: + case 7: { + return BitConverter.ToInt16(data, 62); + } + default: { + return -1; + } + } + } + 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: { + switch (version) { + case 6: + case 7: { + bytes.CopyTo(data, 62); + break; + } + } + break; + } + } + } + } + + public byte minCPULevel { + 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: + case MapType.DMoMaM: { + switch (version) { + case 8: + case 10: + case 11: { + return data[60]; + } + case 9: { + if (data.Length == 76) { + return data[64]; + } else { + return data[60]; + } + } + default: { + return 0; + } + } + } + default: { + return 0; + } + } + } + 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.Vindictus: + case MapType.DMoMaM: { + switch (version) { + case 8: + case 10: + case 11: { + data[60] = value; + break; + } + case 9: { + if (data.Length == 76) { + data[64] = value; + } else { + data[60] = value; + } + break; + } + } + break; + } + } + } + } + + public byte maxCPULevel { + 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: + case MapType.DMoMaM: { + switch (version) { + case 8: + case 10: + case 11: { + return data[61]; + } + case 9: { + if (data.Length == 76) { + return data[65]; + } else { + return data[61]; + } + } + default: { + return 0; + } + } + } + default: { + return 0; + } + } + } + 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.Vindictus: + case MapType.DMoMaM: { + switch (version) { + case 8: + case 10: + case 11: { + data[61] = value; + break; + } + case 9: { + if (data.Length == 76) { + data[65] = value; + } else { + data[61] = value; + } + break; + } + } + break; + } + } + } + } + + public byte minGPULevel { + 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: + case MapType.DMoMaM: { + switch (version) { + case 8: + case 10: + case 11: { + return data[62]; + } + case 9: { + if (data.Length == 76) { + return data[66]; + } else { + return data[62]; + } + } + default: { + return 0; + } + } + } + default: { + return 0; + } + } + } + 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.Vindictus: + case MapType.DMoMaM: { + switch (version) { + case 8: + case 10: + case 11: { + data[62] = value; + break; + } + case 9: { + if (data.Length == 76) { + data[66] = value; + } else { + data[62] = value; + } + break; + } + } + break; + } + } + } + } + + public byte maxGPULevel { + 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: + case MapType.DMoMaM: { + switch (version) { + case 8: + case 10: + case 11: { + return data[63]; + } + case 9: { + if (data.Length == 76) { + return data[67]; + } else { + return data[63]; + } + } + default: { + return 0; + } + } + } + default: { + return 0; + } + } + } + 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.Vindictus: + case MapType.DMoMaM: { + switch (version) { + case 8: + case 10: + case 11: { + data[63] = value; + break; + } + case 9: { + if (data.Length == 76) { + data[67] = value; + } else { + data[63] = value; + } + break; + } + } + break; + } + } + } + } + + public Color diffuseModulaton { + 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: + case MapType.DMoMaM: { + switch (version) { + case 7: + case 8: + case 10: + case 11: { + return ColorExtensions.FromArgb(data[67], data[64], data[65], data[66]); + } + case 9: { + if (data.Length == 76) { + return ColorExtensions.FromArgb(data[72], data[69], data[70], data[71]); + } else { + return ColorExtensions.FromArgb(data[67], data[64], data[65], data[66]); + } + } + default: { + return ColorExtensions.FromArgb(255, 255, 255, 255); + } + } + } + default: { + return ColorExtensions.FromArgb(255, 255, 255, 255); + } + } + } + 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.Vindictus: + case MapType.DMoMaM: { + switch (version) { + case 7: + case 8: + case 10: + case 11: { + value.GetBytes().CopyTo(data, 64); + break; + } + case 9: { + if (data.Length == 76) { + value.GetBytes().CopyTo(data, 69); + } else { + value.GetBytes().CopyTo(data, 64); + } + break; + } + } + break; + } + } + } + } + + public float scale { + 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: + case MapType.DMoMaM: { + switch (version) { + case 11: { + return BitConverter.ToSingle(data, 76); + } + default: { + return float.NaN; + } + } + } + default: { + return float.NaN; + } + } + } + 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: + case MapType.DMoMaM: { + switch (version) { + case 11: { + bytes.CopyTo(data, 76); + break; + } + } + break; + } + } + } + } + + public string targetname { + get { + switch (type) { + case MapType.Source20: { + switch (version) { + case 5: + case 6: { + if (data.Length > 128) { + return data.ToNullTerminatedString(data.Length - 128, 128); + } + return null; + } + default: { + return null; + } + } + } + default: { + return null; + } + } + } + set { + switch (type) { + case MapType.Source20: { + switch (version) { + case 5: + case 6: { + if (data.Length > 128) { + for (int i = 0; i < 128; ++i) { + data[data.Length - i - 1] = 0; + } + byte[] strBytes = Encoding.ASCII.GetBytes(value); + Array.Copy(strBytes, 0, data, data.Length - 128, Math.Min(strBytes.Length, 127)); + } + break; + } + } + break; + } + } + } + } /// /// Creates a new object from a byte array. @@ -35,73 +1511,13 @@ namespace LibBSP { /// The map type. /// The version of static prop lump this object is a member of. /// was null. - /// This structure is not implemented for the given maptype. public StaticProp(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - origin = Vector3d.zero; - angles = Vector3d.zero; - dictionaryEntry = 0; - solidity = 0; - flags = 0; - skin = 0; - minFadeDist = 0; - maxFadeDist = 0; - forcedFadeScale = 1; - targetname = null; - 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: - case MapType.DMoMaM: { - switch (version) { - case 5: { - if (data.Length == 188) { - // This is only for The Ship or Bloody Good Time. - byte[] targetnameBytes = new byte[128]; - Array.Copy(data, 60, targetnameBytes, 0, 128); - targetname = targetnameBytes.ToNullTerminatedString(); - if (targetname.Length == 0) { - targetname = null; - } - } - goto case 6; - } - case 6: - case 7: - case 8: - case 9: - case 10: { - forcedFadeScale = BitConverter.ToSingle(data, 56); - goto case 4; - } - case 4: { - origin = new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)); - angles = new Vector3d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 16), BitConverter.ToSingle(data, 20)); - dictionaryEntry = BitConverter.ToInt16(data, 24); - solidity = data[30]; - flags = data[31]; - skin = BitConverter.ToInt32(data, 32); - minFadeDist = BitConverter.ToSingle(data, 36); - maxFadeDist = BitConverter.ToSingle(data, 40); - break; - } - } - break; - } - default: { - throw new ArgumentException("Map type " + type + " isn't supported by the SourceStaticProp class."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// diff --git a/LibBSP/Source/Structs/BSP/Texture.cs b/LibBSP/Source/Structs/BSP/Texture.cs index 66f5019..b95ff88 100644 --- a/LibBSP/Source/Structs/BSP/Texture.cs +++ b/LibBSP/Source/Structs/BSP/Texture.cs @@ -3,6 +3,7 @@ #endif using System; +using System.Text; #if UNITY using UnityEngine; #endif @@ -25,11 +26,265 @@ namespace LibBSP { /// public struct Texture { - public string name { get; private set; } - public string mask { get; private set; } // Only used by MoHAA, "ignore" means it's unused - public int flags { get; private set; } - public int contents { get; private set; } - public TextureInfo texAxes { get; private set; } + public byte[] data; + public MapType type; + public int version; + + 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; + } + } + } + } /// /// Creates a new object from a byte array. @@ -38,79 +293,13 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public Texture(byte[] data, MapType type, int version = 0) : this() { if (data == null) { throw new ArgumentNullException(); } - name = ""; - mask = "ignore"; - flags = 0; - 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."); - } - } + this.data = data; + this.type = type; + this.version = version; } /// diff --git a/LibBSP/Source/Structs/BSP/TextureData.cs b/LibBSP/Source/Structs/BSP/TextureData.cs index 5f933d2..5eb9bc7 100644 --- a/LibBSP/Source/Structs/BSP/TextureData.cs +++ b/LibBSP/Source/Structs/BSP/TextureData.cs @@ -18,12 +18,63 @@ namespace LibBSP { /// public struct TextureData { - public Vector3d reflectivity { get; private set; } - public int stringTableIndex { get; private set; } - public int width { get; private set; } - public int height { get; private set; } - public int view_width { get; private set; } - public int view_height { get; private set; } + public byte[] data; + public MapType type; + public int version; + + public Vector3d reflectivity { + 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); + } + } /// /// Creates a new object from a byte array. @@ -36,12 +87,9 @@ namespace LibBSP { if (data == null) { throw new ArgumentNullException(); } - reflectivity = new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)); - stringTableIndex = BitConverter.ToInt32(data, 12); - width = BitConverter.ToInt32(data, 16); - height = BitConverter.ToInt32(data, 20); - view_width = BitConverter.ToInt32(data, 24); - view_height = BitConverter.ToInt32(data, 28); + this.data = data; + this.type = type; + this.version = version; } /// @@ -59,8 +107,8 @@ namespace LibBSP { int structLength = 32; int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; i++) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new TextureData(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/Common/Entity.cs b/LibBSP/Source/Structs/Common/Entity.cs index e435990..0d7091d 100644 --- a/LibBSP/Source/Structs/Common/Entity.cs +++ b/LibBSP/Source/Structs/Common/Entity.cs @@ -420,7 +420,7 @@ namespace LibBSP { /// /// Clears the bits in "spawnflags" which are set in . - /// Equivalent to spawnflags = ( ^ 0xFFFFFFFF) & spawnflags. + /// Equivalent to spawnflags = ( ^ 0xFFFFFFFF) & spawnflags. /// /// Bitmask of bits to clear. public void ClearSpawnflags(uint bits) { @@ -524,9 +524,9 @@ namespace LibBSP { #region ISerializable /// /// Implements the ISerializable interface and returns the data needed - /// to serialize the instance. + /// to serialize the instance. /// - /// A SerializationInfo object that contains the information required to serialize the instance. + /// A SerializationInfo object that contains the information required to serialize the instance. /// A StreamingContext structure that contains the source and destination of the serialized stream associated with the instance. public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); diff --git a/LibBSP/Source/Structs/Common/Lumps/NumList.cs b/LibBSP/Source/Structs/Common/Lumps/NumList.cs index 04676e5..47bc5b9 100644 --- a/LibBSP/Source/Structs/Common/Lumps/NumList.cs +++ b/LibBSP/Source/Structs/Common/Lumps/NumList.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; namespace LibBSP { @@ -6,8 +7,11 @@ namespace LibBSP { /// /// List class for numbers. Can handle any integer data type except ulong. /// - public class NumList : List { + public class NumList : IList, ICollection, IEnumerable, IList, ICollection, IEnumerable { + /// + /// Enum of the types that may be used in this class. + /// public enum DataType : int { Invalid = 0, SByte = 1, @@ -19,6 +23,7 @@ namespace LibBSP { Int64 = 7, } + public byte[] data; public DataType type { get; private set; } /// @@ -27,61 +32,21 @@ namespace LibBSP { /// byte array to parse. /// The type of number to store. /// was null. - /// was not a member of the DataType enum. public NumList(byte[] data, DataType type) { if (data == null) { throw new ArgumentNullException(); } + this.data = data; + this.type = type; + } + + /// + /// Creates an empty object. + /// + /// The type of number to store. + public NumList(DataType type) { + this.data = new byte[0]; 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."); - } - } } /// @@ -94,6 +59,34 @@ namespace LibBSP { return new NumList(data, type); } + /// + /// Gets the length, in bytes, of the numerical primitive used by this instance of this class. + /// + 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 /// /// 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; return 22; } + case MapType.CoD2: { + dataType = DataType.UInt32; + return 27; + } } dataType = DataType.Invalid; return -1; @@ -279,6 +276,10 @@ namespace LibBSP { dataType = DataType.UInt16; return 8; } + case MapType.CoD2: { + dataType = DataType.UInt16; + return 9; + } case MapType.Raven: case MapType.Quake3: { dataType = DataType.UInt32; @@ -362,5 +363,228 @@ namespace LibBSP { return -1; } #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 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 } } diff --git a/LibBSP/Source/Structs/Common/TextureInfo.cs b/LibBSP/Source/Structs/Common/TextureInfo.cs index dcc062b..5fce0a2 100644 --- a/LibBSP/Source/Structs/Common/TextureInfo.cs +++ b/LibBSP/Source/Structs/Common/TextureInfo.cs @@ -21,36 +21,169 @@ namespace LibBSP { /// [Serializable] public class TextureInfo { - public const int S = 0; - public const int T = 1; - /// - /// Array of base texture axes. When referenced properly, provides a good default texture axis for any given plane. - /// - 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 byte[] data; + public MapType type; + public int version; - public Vector3d[] axes; - public Vector2d translation; - public Vector2d scale; - public int flags; - public int texture; - public double rotation; + // No BSP format uses these so they are fields. + public Vector2d scale = Vector2d.one; + public double rotation = 0; + + public Vector3d uAxis { + 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; + } + } + } + } /// /// Creates a new object with sensible defaults. /// public TextureInfo() { - axes = new Vector3d[2]; + data = new byte[40]; + type = MapType.Quake; + version = 0; + + uAxis = Vector3d.zero; + vAxis = Vector3d.zero; translation = Vector2d.zero; - scale = Vector2d.one; flags = 0; - texture = 0; + texture = -1; + + scale = Vector2d.one; rotation = 0; } @@ -61,70 +194,35 @@ namespace LibBSP { /// The map type. /// The version of this lump. /// was null. - /// This structure is not implemented for the given maptype. public TextureInfo(byte[] data, MapType type, int version = 0) { if (data == null) { throw new ArgumentNullException(); } - texture = -1; - flags = -1; - axes = new Vector3d[2]; - 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. + this.data = data; + this.type = type; + this.version = version; + scale = Vector2d.one; 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."); - } - } } /// /// Creates a new object using the passed data. /// - /// The S texture axis. - /// The T texture axis. + /// The U texture axis. + /// The V texture axis. /// Texture translation along both axes (in pixels). /// Texture scale along both axes. /// The flags for this . /// Index into the texture list for the texture this uses. /// Rotation of the texutre axes. - public TextureInfo(Vector3d s, Vector3d t, Vector2d translation, Vector2d scale, int flags, int texture, double rotation) { - axes = new Vector3d[2]; - axes[S] = s; - axes[T] = t; + public TextureInfo(Vector3d u, Vector3d v, Vector2d translation, Vector2d scale, int flags, int texture, double rotation) { + data = new byte[40]; + type = MapType.Quake; + version = 0; + + uAxis = u; + vAxis = v; this.translation = translation; this.scale = scale; this.flags = flags; @@ -138,19 +236,10 @@ namespace LibBSP { /// of the surface. /// The best matching texture axes for the given . public static Vector3d[] TextureAxisFromPlane(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, new Vector3d(baseAxes[i * 3][0], baseAxes[i * 3][1], baseAxes[i * 3][2])); - if (dot > best) { - best = dot; - bestaxis = i; - } - } + int bestaxis = p.BestAxis(); 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[1] = new Vector3d(baseAxes[bestaxis * 3 + 2][0], baseAxes[bestaxis * 3 + 2][1], baseAxes[bestaxis * 3 + 2][2]); + newAxes[0] = PlaneExtensions.baseAxes[bestaxis * 3 + 1]; + newAxes[1] = PlaneExtensions.baseAxes[bestaxis * 3 + 2]; return newAxes; } @@ -201,8 +290,8 @@ namespace LibBSP { } int numObjects = data.Length / structLength; List lump = new List(numObjects); - byte[] bytes = new byte[structLength]; for (int i = 0; i < numObjects; ++i) { + byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new TextureInfo(bytes, type, version)); } diff --git a/LibBSP/Source/Structs/MAP/MAPBrushSide.cs b/LibBSP/Source/Structs/MAP/MAPBrushSide.cs index ee1e894..e57dd4d 100644 --- a/LibBSP/Source/Structs/MAP/MAPBrushSide.cs +++ b/LibBSP/Source/Structs/MAP/MAPBrushSide.cs @@ -124,7 +124,7 @@ namespace LibBSP { string[] split = tokens[1].SplitUnlessBetweenDelimiters(' ', '[', ']', StringSplitOptions.RemoveEmptyEntries); textureInfo.scale = new Vector2d(float.Parse(split[1], _format), textureInfo.scale.y); 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); break; } @@ -132,7 +132,7 @@ namespace LibBSP { string[] split = tokens[1].SplitUnlessBetweenDelimiters(' ', '[', ']', StringSplitOptions.RemoveEmptyEntries); textureInfo.scale = new Vector2d(textureInfo.scale.x, float.Parse(split[1], _format)); 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)); break; }