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; }