Add a code to allow reading lumps on-the-fly, and only as needed.

This will allow the library to be used in many different projects with minimal effort messing with file reading, and should improve performance and memory usage in those projects.
This commit is contained in:
Will
2015-10-11 11:07:37 -06:00
parent facb96e307
commit c8ad5a842a
27 changed files with 2050 additions and 216 deletions

View File

@@ -52,6 +52,7 @@
<Compile Include="src\Structs\BSP\Edge.cs" />
<Compile Include="src\Structs\BSP\Face.cs" />
<Compile Include="src\Structs\BSP\Leaf.cs" />
<Compile Include="src\Structs\BSP\Lumps\GameLump.cs" />
<Compile Include="src\Structs\BSP\Lumps\SourceStaticProps.cs" />
<Compile Include="src\Structs\BSP\Lumps\Textures.cs" />
<Compile Include="src\Structs\BSP\Model.cs" />
@@ -85,6 +86,7 @@
<Compile Include="src\Structs\Common\Vector2d.cs" />
<Compile Include="src\Structs\Common\Vector4d.cs" />
<Compile Include="src\Structs\Common\Vector3d.cs" />
<Compile Include="src\Util\BSPReader.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@@ -2,6 +2,8 @@
#define UNITY
#endif
using System;
using System.Collections.Generic;
#if UNITY
using UnityEngine;
#endif
@@ -154,5 +156,112 @@ namespace LibBSP {
public static Ray Intersect(this Plane p1, Plane p2) {
return Intersection(p1, p2);
}
/// <summary>
/// Factory method to parse a <c>byte</c> array into a <c>List</c> of <c>Plane</c> objects.
/// </summary>
/// <param name="data">The data to parse</param>
/// <param name="type">The map type</param>
/// <returns>A <c>List</c> of <c>Plane</c> objects</returns>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was null</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype</exception>
/// <remarks>This function goes here since it can't go into Unity's Plane class, and so can't depend
/// on having a constructor taking a byte array.</remarks>
public static List<Plane> LumpFactory(byte[] data, MapType type) {
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.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;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Plane class.");
}
}
List<Plane> lump = new List<Plane>(data.Length / structLength);
byte[] bytes = new byte[structLength];
for (int i = 0; i < data.Length / structLength; ++i) {
Vector3 normal = new Vector3(BitConverter.ToSingle(data, data.Length * i), BitConverter.ToSingle(data, (data.Length * i) + 4), BitConverter.ToSingle(data, (data.Length * i) + 8));
float distance = BitConverter.ToSingle(data, (data.Length * i) + 12);
lump.Add(new Plane(normal, distance));
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump or it's not implemented</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.FAKK:
case MapType.MOHAA:
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake:
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF:
case MapType.Nightfire:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 1;
}
case MapType.CoD:
case MapType.Raven:
case MapType.Quake3: {
return 2;
}
case MapType.CoD4:
case MapType.CoD2: {
return 4;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -6,10 +6,14 @@ using System;
using System.Collections.Generic;
#if UNITY
using UnityEngine;
#else
using System.Drawing;
#endif
namespace LibBSP {
#if !UNITY
using Color32 = Color;
using Vector2 = Vector2d;
using Vector3 = Vector3d;
#endif
/// <summary>
@@ -62,5 +66,187 @@ namespace LibBSP {
};
}
/// <summary>
/// Creates a new <c>UIVertex</c> object from a <c>byte</c> array.
/// </summary>
/// <param name="data"><c>byte</c> array to parse</param>
/// <param name="type">The map type</param>
/// <returns>The resulting <c>UIVertex</c> object</returns>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was null</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype</exception>
/// <remarks><c>UIVertex</c> has no constructor, so the object must be initialized field-by-field. Even if it
/// did have a constructor, the way data needs to be read wouldn't allow use of it.</remarks>
public static UIVertex CreateVertex(byte[] data, MapType type) {
if (data == null) {
throw new ArgumentNullException();
}
UIVertex result = new UIVertex();
switch (type) {
case MapType.Doom:
case MapType.Hexen: {
result.position = new Vector3(BitConverter.ToInt16(data, 0), BitConverter.ToInt16(data, 2));
break;
}
case MapType.MOHAA:
case MapType.Quake3:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.FAKK: {
result.uv0 = new Vector2(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 16));
result.color = Color32Extensions.FromArgb(data[43], data[40], data[41], data[42]);
goto case MapType.DMoMaM;
}
case MapType.Raven: {
result.uv0 = new Vector2(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 16));
result.color = Color32Extensions.FromArgb(data[67], data[64], data[65], data[66]);
goto case MapType.DMoMaM;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
result.uv0 = new Vector2(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 16));
result.color = Color32Extensions.FromArgb(data[35], data[32], data[33], data[34]);
goto case MapType.DMoMaM;
}
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.TacticalInterventionEncrypted:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.Vindictus:
case MapType.DMoMaM: {
result.position = new Vector3(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8));
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the UIVertex class factory.");
}
}
return result;
}
/// <summary>
/// Factory method to parse a <c>byte</c> array into a <c>List</c> of <c>UIVertex</c> objects.
/// </summary>
/// <param name="data">The data to parse</param>
/// <param name="type">The map type</param>
/// <returns>A <c>List</c> of <c>UIVertex</c> objects</returns>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was null</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype</exception>
/// <remarks>This function goes here since I can't put it into Unity's <c>UIVertex</c> class, and so I can't
/// depend on having a constructor taking a byte array.</remarks>
public static List<UIVertex> LumpFactory(byte[] data, MapType type) {
if (data == null) {
throw new ArgumentNullException();
}
int structLength = 0;
switch (type) {
case MapType.Doom:
case MapType.Hexen: {
structLength = 4;
break;
}
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.TacticalInterventionEncrypted:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.Vindictus:
case MapType.DMoMaM: {
structLength = 12;
break;
}
case MapType.STEF2:
case MapType.MOHAA:
case MapType.STEF2Demo:
case MapType.Quake3:
case MapType.CoD:
case MapType.FAKK: {
structLength = 44;
break;
}
case MapType.Raven: {
structLength = 80;
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Vertex lump factory.");
}
}
List<UIVertex> lump = new List<UIVertex>(data.Length / structLength);
byte[] bytes = new byte[structLength];
for (int i = 0; i < data.Length / structLength; ++i) {
Array.Copy(data, i * structLength, bytes, 0, structLength);
lump.Add(CreateVertex(bytes, type));
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
return 2;
}
case MapType.Quake:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 3;
}
case MapType.MOHAA:
case MapType.FAKK:
case MapType.Nightfire: {
return 4;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
return 6;
}
case MapType.Raven:
case MapType.Quake3: {
return 10;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -3,6 +3,7 @@
#endif
using System;
using System.IO;
using System.Collections.Generic;
#if UNITY
using UnityEngine;
@@ -25,7 +26,7 @@ namespace LibBSP {
Hexen = 1145132872, // "HWAD"
STEF2Demo = 1263223129,
FAKK = 1263223152,
TacticalIntervention = 1268885814,
TacticalInterventionEncrypted = 1268885814,
CoD2 = 1347633741, // Uses same algorithm and structures as COD1. Read differently.
SiN = 1347633747, // The headers for SiN and Jedi Outcast are exactly the same
Raven = 1347633748,
@@ -37,6 +38,7 @@ namespace LibBSP {
Source21 = 1347633771,
Source22 = 1347633772,
Source23 = 1347633773,
L4D2 = 1347633774,
Quake2 = 1347633775,
Source27 = 1347633777,
Daikatana = 1347633778,
@@ -54,44 +56,313 @@ namespace LibBSP {
/// </summary>
public class BSP {
public MapType version { get; set; }
private MapType _version;
public string filePath { get; private set; }
private BSPReader reader;
// Map structures
// Quake 1/GoldSrc
public Entities entities;
public List<Plane> planes;
public Textures textures;
public List<UIVertex> vertices;
public List<Node> nodes;
public List<TexInfo> texInfo;
public List<Face> faces;
public List<Leaf> leaves;
public NumList markSurfaces;
public List<Edge> edges;
public NumList surfEdges;
public List<Model> models;
public byte[] pvs;
private Entities _entities;
private List<Plane> _planes;
private Textures _textures;
private List<UIVertex> _vertices;
private List<Node> _nodes;
private List<TexInfo> _texInfo;
private List<Face> _faces;
private List<Leaf> _leaves;
private NumList _markSurfaces;
private List<Edge> _edges;
private NumList _surfEdges;
private List<Model> _models;
// public byte[] pvs;
// Quake 2
public List<Brush> brushes;
public List<BrushSide> brushSides;
public NumList markBrushes;
// MOHAA
// public MoHAAStaticProps staticProps;
private List<Brush> _brushes;
private List<BrushSide> _brushSides;
private NumList _markBrushes;
// Nightfire
public Textures materials;
public NumList indices;
private Textures _materials;
private NumList _indices;
// Source
public List<Face> originalFaces;
public NumList texTable;
public List<SourceTexData> texDatas;
public List<SourceDispInfo> dispInfos;
public SourceDispVertices dispVerts;
public NumList displacementTriangles;
public SourceStaticProps staticProps;
public List<SourceCubemap> cubemaps;
private List<Face> _originalFaces;
private NumList _texTable;
private List<SourceTexData> _texDatas;
private List<SourceDispInfo> _dispInfos;
private SourceDispVertices _dispVerts;
private NumList _displacementTriangles;
// public SourceOverlays overlays;
private List<SourceCubemap> _cubemaps;
private GameLump _gameLump;
private SourceStaticProps _staticProps;
/// <summary>
/// An XOr encryption key for encrypted map formats. Must be read and set.
/// </summary>
public byte[] key = new byte[0];
/// <summary>
/// The version of this BSP. DO NOT CHANGE THIS unless you want to force reading a BSP as a certain format.
/// </summary>
public MapType version {
get {
if (_version == MapType.Undefined) {
_version = reader.GetVersion();
}
return _version;
}
set {
_version = value;
}
}
public bool bigEndian { get { return reader.bigEndian; } }
public Entities entities {
get {
if (_entities == null) {
_entities = Entity.LumpFactory(reader.ReadLumpNum(Entity.GetIndexForLump(version), version), version);
}
return _entities;
}
}
public List<Plane> planes {
get {
if (_planes == null) {
_planes = PlaneExtensions.LumpFactory(reader.ReadLumpNum(PlaneExtensions.GetIndexForLump(version), version), version);
}
return _planes;
}
}
public Textures textures {
get {
if (_textures == null) {
_textures = Texture.LumpFactory(reader.ReadLumpNum(Texture.GetIndexForLump(version), version), version);
}
return _textures;
}
}
public List<UIVertex> vertices {
get {
if (_vertices == null) {
_vertices = UIVertexExtensions.LumpFactory(reader.ReadLumpNum(UIVertexExtensions.GetIndexForLump(version), version), version);
}
return _vertices;
}
}
public List<Node> nodes {
get {
if (_nodes == null) {
_nodes = Node.LumpFactory(reader.ReadLumpNum(Node.GetIndexForLump(version), version), version);
}
return _nodes;
}
}
public List<TexInfo> texInfo {
get {
if (_texInfo == null) {
_texInfo = TexInfo.LumpFactory(reader.ReadLumpNum(TexInfo.GetIndexForLump(version), version), version);
}
return _texInfo;
}
}
public List<Face> faces {
get {
if (_faces == null) {
_faces = Face.LumpFactory(reader.ReadLumpNum(Face.GetIndexForLump(version), version), version);
}
return _faces;
}
}
public List<Leaf> leaves {
get {
if (_leaves == null) {
_leaves = Leaf.LumpFactory(reader.ReadLumpNum(Leaf.GetIndexForLump(version), version), version);
}
return _leaves;
}
}
public List<Edge> edges {
get {
if (_edges == null) {
_edges = Edge.LumpFactory(reader.ReadLumpNum(Edge.GetIndexForLump(version), version), version);
}
return _edges;
}
}
public List<Model> models {
get {
if (_models == null) {
_models = Model.LumpFactory(reader.ReadLumpNum(Model.GetIndexForLump(version), version), version);
}
return _models;
}
}
public List<Brush> brushes {
get {
if (_brushes == null) {
_brushes = Brush.LumpFactory(reader.ReadLumpNum(Brush.GetIndexForLump(version), version), version);
}
return _brushes;
}
}
public List<BrushSide> brushSides {
get {
if (_brushSides == null) {
_brushSides = BrushSide.LumpFactory(reader.ReadLumpNum(BrushSide.GetIndexForLump(version), version), version);
}
return _brushSides;
}
}
public Textures materials {
get {
if (_materials == null) {
_materials = Texture.LumpFactory(reader.ReadLumpNum(Texture.GetIndexForMaterialLump(version), version), version);
}
return _materials;
}
}
public List<Face> originalFaces {
get {
if (_originalFaces == null) {
_originalFaces = Face.LumpFactory(reader.ReadLumpNum(Face.GetIndexForOriginalFacesLump(version), version), version);
}
return _originalFaces;
}
}
public List<SourceTexData> texDatas {
get {
if (_texDatas == null) {
_texDatas = SourceTexData.LumpFactory(reader.ReadLumpNum(SourceTexData.GetIndexForLump(version), version), version);
}
return _texDatas;
}
}
public List<SourceDispInfo> dispInfos {
get {
if (_dispInfos == null) {
_dispInfos = SourceDispInfo.LumpFactory(reader.ReadLumpNum(SourceDispInfo.GetIndexForLump(version), version), version);
}
return _dispInfos;
}
}
public SourceDispVertices dispVerts {
get {
if (_dispVerts == null) {
_dispVerts = SourceDispVertex.LumpFactory(reader.ReadLumpNum(SourceDispVertex.GetIndexForLump(version), version), version);
}
return _dispVerts;
}
}
public List<SourceCubemap> cubemaps {
get {
if (_cubemaps == null) {
_cubemaps = SourceCubemap.LumpFactory(reader.ReadLumpNum(SourceCubemap.GetIndexForLump(version), version), version);
}
return _cubemaps;
}
}
public NumList markSurfaces {
get {
if (_markSurfaces == null) {
NumList.DataType type;
_markSurfaces = NumList.LumpFactory(reader.ReadLumpNum(NumList.GetIndexForMarkSurfacesLump(version, out type), version), type);
}
return _markSurfaces;
}
}
public NumList surfEdges {
get {
if (_surfEdges == null) {
NumList.DataType type;
_surfEdges = NumList.LumpFactory(reader.ReadLumpNum(NumList.GetIndexForSurfEdgesLump(version, out type), version), type);
}
return _surfEdges;
}
}
public NumList markBrushes {
get {
if (_markBrushes == null) {
NumList.DataType type;
_markBrushes = NumList.LumpFactory(reader.ReadLumpNum(NumList.GetIndexForMarkBrushesLump(version, out type), version), type);
}
return _markBrushes;
}
}
public NumList indices {
get {
if (_indices == null) {
NumList.DataType type;
_indices = NumList.LumpFactory(reader.ReadLumpNum(NumList.GetIndexForIndicesLump(version, out type), version), type);
}
return _indices;
}
}
public NumList texTable {
get {
if (_texTable == null) {
NumList.DataType type;
_texTable = NumList.LumpFactory(reader.ReadLumpNum(NumList.GetIndexForTexTableLump(version, out type), version), type);
}
return _texTable;
}
}
public NumList displacementTriangles {
get {
if (_displacementTriangles == null) {
NumList.DataType type;
_displacementTriangles = NumList.LumpFactory(reader.ReadLumpNum(NumList.GetIndexForDisplacementTrianglesLump(version, out type), version), type);
}
return _displacementTriangles;
}
}
public GameLump gameLump {
get {
if (_gameLump == null) {
_gameLump = GameLump.LumpFactory(reader.ReadLumpNum(GameLump.GetIndexForLump(version), version), version);
}
return _gameLump;
}
}
public SourceStaticProps staticProps {
get {
if (_staticProps == null) {
GameLump.GameLumpInfo info = gameLump[GameLumpType.sprp];
byte[] thisLump = new byte[info.length];
Array.Copy(gameLump.rawData, info.offset - gameLump.gameLumpOffset, thisLump, 0, info.length);
_staticProps = SourceStaticProp.LumpFactory(thisLump, version, info.version);
}
return _staticProps;
}
}
/// <summary>
/// Gets the path to this BSP file.
/// </summary>
public string filePath { get; private set; }
/// <summary>
/// Gets the file name of this map.
@@ -146,13 +417,23 @@ namespace LibBSP {
}
/// <summary>
/// Creates a new <c>BSP</c> instance with no initial data. The <c>List</c>s in this class must be populated elsewhere.
/// Creates a new <c>BSP</c> instance pointing to the file at <paramref name="filePath"/>. The
/// <c>List</c>s in this class will be read and populated when accessed through their properties.
/// </summary>
/// <param name="filePath">The path to the .BSP file</param>
/// <param name="version">The version of the map</param>
public BSP(string filePath, MapType version) {
public BSP(string filePath) {
reader = new BSPReader(new FileInfo(filePath));
this.filePath = filePath;
this.version = version;
}
/// <summary>
/// Creates a new <c>BSP</c> instance using the file referenced by <paramref name="file"/>. The
/// <c>List</c>s in this class will be read and populated when accessed through their properties.
/// </summary>
/// <param name="file">A reference to the .BSP file</param>
public BSP(FileInfo file) {
reader = new BSPReader(file);
this.filePath = file.FullName;
}
}
}

View File

@@ -41,7 +41,7 @@ namespace LibBSP {
case MapType.Source23:
case MapType.Source27:
case MapType.Vindictus:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
firstSide = BitConverter.ToInt32(data, 0);
numSides = BitConverter.ToInt32(data, 4);
@@ -109,7 +109,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM:
case MapType.Nightfire:
@@ -141,5 +141,61 @@ namespace LibBSP {
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump or it's not implemented</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.CoD: {
return 4;
}
case MapType.CoD2: {
return 6;
}
case MapType.CoD4:
case MapType.Raven:
case MapType.Quake3: {
return 8;
}
case MapType.FAKK: {
return 11;
}
case MapType.MOHAA: {
return 12;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
return 13;
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
return 14;
}
case MapType.Nightfire: {
return 15;
}
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 18;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -68,7 +68,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
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
@@ -138,7 +138,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.FAKK: {
structLength = 8;
@@ -165,5 +165,61 @@ namespace LibBSP {
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.CoD: {
return 3;
}
case MapType.CoD4:
case MapType.CoD2: {
return 5;
}
case MapType.Raven:
case MapType.Quake3: {
return 9;
}
case MapType.FAKK: {
return 10;
}
case MapType.MOHAA: {
return 11;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
return 12;
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
return 15;
}
case MapType.Nightfire: {
return 16;
}
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 19;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -35,7 +35,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Quake2:
case MapType.SoF: {
@@ -85,7 +85,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Quake2:
case MapType.SoF: {
@@ -113,5 +113,38 @@ namespace LibBSP {
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
return 11;
}
case MapType.Quake:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 12;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -117,7 +117,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
plane = BitConverter.ToUInt16(data, 0);
side = (int)data[2];
@@ -202,7 +202,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
structLength = 56;
break;
@@ -240,5 +240,79 @@ namespace LibBSP {
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.FAKK:
case MapType.MOHAA: {
return 3;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
return 5;
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
return 6;
}
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM:
case MapType.Quake: {
return 7;
}
case MapType.Nightfire: {
return 9;
}
case MapType.Raven:
case MapType.Quake3: {
return 13;
}
default: {
return -1;
}
}
}
/// <summary>
/// Gets the index for the original faces lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForOriginalFacesLump(MapType type) {
switch (type) {
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 27;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -50,7 +50,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM:
case MapType.Daikatana: {
contents = BitConverter.ToInt32(data, 0);
@@ -120,7 +120,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.SoF:
case MapType.Daikatana:
case MapType.DMoMaM: {
@@ -165,5 +165,49 @@ namespace LibBSP {
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Raven:
case MapType.Quake3: {
return 4;
}
case MapType.MOHAA:
case MapType.FAKK:
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
return 8;
}
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.Quake:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 10;
}
case MapType.Nightfire: {
return 11;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
namespace LibBSP {
public enum GameLumpType : int {
sprp = 1936749168,
}
/// <summary>
/// Class containing the identification and information for the various Game Lumps in Source
/// engine BSPs. The only one we're really concerned with is the Static Props.
/// </summary>
public class GameLump : Dictionary<GameLumpType, GameLump.GameLumpInfo> {
public struct GameLumpInfo {
public ushort flags;
public ushort version;
public int offset;
public int length;
}
private byte[] _rawData;
private int _gameLumpOffset;
/// <summary>
/// Byte array representing the raw data read from the BSP for the game lump
/// </summary>
public byte[] rawData {
get {
return _rawData;
}
}
/// <summary>
/// The amount to subtract from all the <c>GameLumpInfo.offset</c> values to find the offset relative to the start of the Game Lump data. May be 0.
/// </summary>
public int gameLumpOffset {
get {
return _gameLumpOffset;
}
}
/// <summary>
/// Creates a new <c>GameLump</c> object by parsing a <c>byte</c> array into a <c>Dictionary</c> of <c>GameLumpInfo</c> objects.
/// These objects contain offsets, lengths and versions of the GameLump lumps.
/// </summary>
/// <param name="data">The data to parse</param>
/// <param name="type">The map type</param>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was null</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype</exception>
public GameLump(byte[] data, MapType type) {
if (data == null) {
throw new ArgumentNullException();
}
_rawData = data;
int structLength = 0;
switch (type) {
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27: {
structLength = 16;
break;
}
case MapType.DMoMaM: {
structLength = 20;
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the GameLump class.");
}
}
int numGameLumps = BitConverter.ToInt32(data, 0);
if (numGameLumps > 0) {
int headerLength = 4 + (numGameLumps * structLength);
int lowestLumpOffset = Int32.MaxValue;
for (int i = 0; i < numGameLumps; ++i) {
GameLumpInfo info = new GameLumpInfo {
flags = BitConverter.ToUInt16(data, (i * structLength) + 8),
version = BitConverter.ToUInt16(data, (i * structLength) + 10),
offset = BitConverter.ToInt32(data, (i * structLength) + 14),
length = BitConverter.ToInt32(data, (i * structLength) + 18),
};
this[(GameLumpType)BitConverter.ToInt32(data, (i * structLength) + 4)] = info;
if (info.offset < lowestLumpOffset) {
lowestLumpOffset = info.offset;
}
}
// If the offset values are given relative to the beginning of the lump, this should give 0.
_gameLumpOffset = lowestLumpOffset - headerLength;
}
}
/// <summary>
/// Passes a <c>byte</c> array into the constructor for <c>GameLump</c>.
/// </summary>
/// <param name="data">The data to parse</param>
/// <param name="type">The map type</param>
/// <returns>A new <c>GameLump</c> object</returns>
/// <remarks>This is only here for consistency with the other lump structures.</remarks>
public static GameLump LumpFactory(byte[] data, MapType type) {
return new GameLump(data, type);
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 35;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -58,7 +58,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM: {
int offset = 0;

View File

@@ -61,7 +61,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
headNode = BitConverter.ToInt32(data, 36);
firstFace = BitConverter.ToInt32(data, 40);
@@ -144,7 +144,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
structLength = 48;
break;
@@ -175,5 +175,58 @@ namespace LibBSP {
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Raven:
case MapType.Quake3: {
return 7;
}
case MapType.MOHAA:
case MapType.FAKK:
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
return 13;
}
case MapType.Quake:
case MapType.Nightfire:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 14;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
return 15;
}
case MapType.CoD: {
return 27;
}
case MapType.CoD2: {
return 35;
}
case MapType.CoD4: {
return 37;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -43,7 +43,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM:
case MapType.STEF2:
@@ -96,7 +96,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
structLength = 32;
break;
@@ -130,5 +130,53 @@ namespace LibBSP {
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Raven:
case MapType.Quake3: {
return 3;
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
return 4;
}
case MapType.Quake:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 5;
}
case MapType.Nightfire: {
return 8;
}
case MapType.FAKK:
case MapType.MOHAA: {
return 9;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
return 11;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -40,7 +40,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM: {
origin = new Vector3(BitConverter.ToInt32(data, 0), BitConverter.ToInt32(data, 4), BitConverter.ToInt32(data, 8));
@@ -75,7 +75,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM: {
structLength = 16;
@@ -96,5 +96,31 @@ namespace LibBSP {
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 42;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -47,7 +47,7 @@ namespace LibBSP {
case MapType.Source20:
case MapType.Source21:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
offset = 136;
break;
@@ -93,7 +93,7 @@ namespace LibBSP {
case MapType.Source20:
case MapType.Source21:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.DMoMaM: {
structLength = 176;
break;
@@ -124,5 +124,31 @@ namespace LibBSP {
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 26;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -49,5 +49,31 @@ namespace LibBSP {
}
return new SourceDispVertices(data, type);
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 33;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -58,7 +58,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM: {
switch (version) {

View File

@@ -63,5 +63,31 @@ namespace LibBSP {
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 2;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -61,7 +61,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
texture = BitConverter.ToInt32(data, 68);
flags = BitConverter.ToInt32(data, 64);
@@ -161,7 +161,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus: {
structLength = 72;
break;
@@ -182,5 +182,35 @@ namespace LibBSP {
}
return lump;
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Quake:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 6;
}
case MapType.Nightfire: {
return 17;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -86,7 +86,7 @@ namespace LibBSP {
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.TacticalIntervention:
case MapType.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM: {
name = data.ToRawString();
@@ -99,7 +99,7 @@ namespace LibBSP {
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Node class.");
throw new ArgumentException("Map type " + type + " isn't supported by the Texture class.");
}
}
}
@@ -113,5 +113,70 @@ namespace LibBSP {
public static Textures LumpFactory(byte[] data, MapType type) {
return new Textures(data, type);
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.FAKK:
case MapType.MOHAA:
case MapType.STEF2:
case MapType.STEF2Demo: {
return 0;
}
case MapType.Raven:
case MapType.Quake3: {
return 1;
}
case MapType.Quake:
case MapType.Nightfire: {
return 2;
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
return 5;
}
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 43;
}
default: {
return -1;
}
}
}
/// <summary>
/// Gets the index for the materials lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump</returns>
public static int GetIndexForMaterialLump(MapType type) {
switch (type) {
case MapType.Nightfire: {
return 3;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -106,9 +106,10 @@ namespace LibBSP {
if (ContainsKey("model")) {
string st = this["model"];
if (st[0] == '*') {
try {
return System.Int32.Parse(st.Substring(1));
} catch (System.FormatException) {
int ret = -1;
if (Int32.TryParse(st.Substring(1), out ret)) {
return ret;
} else {
return -1;
}
} else {
@@ -697,6 +698,57 @@ namespace LibBSP {
return new Entities(data, type);
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// </summary>
/// <param name="type">The map type</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump or it's not implemented</returns>
public static int GetIndexForLump(MapType type) {
switch (type) {
case MapType.Raven:
case MapType.Quake3:
case MapType.Quake:
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF:
case MapType.Nightfire:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
return 0;
}
case MapType.FAKK:
case MapType.MOHAA: {
return 14;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
return 16;
}
case MapType.CoD: {
return 29;
}
case MapType.CoD2: {
return 37;
}
case MapType.CoD4: {
return 39;
}
default: {
return -1;
}
}
}
/// <summary>
/// Struct containing the fields necessary for Source entity I/O
/// </summary>

View File

@@ -8,13 +8,14 @@ namespace LibBSP {
public class NumList : List<long> {
public enum DataType : int {
SByte = 0,
Byte = 1,
Int16 = 2,
UInt16 = 3,
Int32 = 4,
UInt32 = 5,
Int64 = 6,
Invalid = 0,
SByte = 1,
Byte = 2,
Int16 = 3,
UInt16 = 4,
Int32 = 5,
UInt32 = 6,
Int64 = 7,
}
public DataType type { get; private set; }
@@ -91,5 +92,229 @@ namespace LibBSP {
public static NumList LumpFactory(byte[] data, DataType type) {
return new NumList(data, type);
}
#region IndicesForLumps
/// <summary>
/// Gets the index for the Mark Surfaces lump in the BSP file for a specific map format, and the type of data the format uses.
/// </summary>
/// <param name="type">The map type</param>
/// <param name="dataType"><c>out</c> parameter for the data type this version uses</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump or it's not implemented</returns>
public static int GetIndexForMarkSurfacesLump(MapType version, out DataType dataType) {
switch (version) {
case MapType.Raven:
case MapType.Quake3: {
dataType = DataType.Int32;
return 5;
}
case MapType.FAKK:
case MapType.MOHAA: {
dataType = DataType.Int32;
return 7;
}
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
dataType = DataType.UInt16;
return 9;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
dataType = DataType.UInt32;
return 9;
}
case MapType.Quake: {
dataType = DataType.UInt16;
return 11;
}
case MapType.Nightfire: {
dataType = DataType.UInt32;
return 12;
}
case MapType.Vindictus: {
dataType = DataType.UInt32;
return 16;
}
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
dataType = DataType.UInt16;
return 16;
}
}
dataType = DataType.Invalid;
return -1;
}
/// <summary>
/// Gets the index for the Surface Edges lump in the BSP file for a specific map format, and the type of data the format uses.
/// </summary>
/// <param name="type">The map type</param>
/// <param name="dataType"><c>out</c> parameter for the data type this version uses</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump or it's not implemented</returns>
public static int GetIndexForSurfEdgesLump(MapType version, out DataType dataType) {
switch (version) {
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
dataType = DataType.Int32;
return 12;
}
case MapType.Quake:
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
dataType = DataType.Int32;
return 13;
}
}
dataType = DataType.Invalid;
return -1;
}
/// <summary>
/// Gets the index for the Mark Brushes lump in the BSP file for a specific map format, and the type of data the format uses.
/// </summary>
/// <param name="type">The map type</param>
/// <param name="dataType"><c>out</c> parameter for the data type this version uses</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump or it's not implemented</returns>
public static int GetIndexForMarkBrushesLump(MapType version, out DataType dataType) {
switch (version) {
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
dataType = DataType.UInt16;
return 10;
}
case MapType.Nightfire: {
dataType = DataType.UInt32;
return 13;
}
case MapType.Vindictus: {
dataType = DataType.UInt32;
return 17;
}
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
dataType = DataType.UInt16;
return 17;
}
}
dataType = DataType.Invalid;
return -1;
}
/// <summary>
/// Gets the index for the indices lump in the BSP file for a specific map format, and the type of data the format uses.
/// </summary>
/// <param name="type">The map type</param>
/// <param name="dataType"><c>out</c> parameter for the data type this version uses</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump or it's not implemented</returns>
public static int GetIndexForIndicesLump(MapType version, out DataType dataType) {
switch (version) {
case MapType.FAKK:
case MapType.MOHAA: {
dataType = DataType.UInt32;
return 5;
}
case MapType.Nightfire: {
dataType = DataType.UInt32;
return 6;
}
case MapType.STEF2:
case MapType.STEF2Demo: {
dataType = DataType.UInt32;
return 7;
}
case MapType.Raven:
case MapType.Quake3: {
dataType = DataType.UInt32;
return 11;
}
}
dataType = DataType.Invalid;
return -1;
}
/// <summary>
/// Gets the index for the texture table lump in the BSP file for a specific map format, and the type of data the format uses.
/// </summary>
/// <param name="type">The map type</param>
/// <param name="dataType"><c>out</c> parameter for the data type this version uses</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump or it's not implemented</returns>
public static int GetIndexForTexTableLump(MapType version, out DataType dataType) {
switch (version) {
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
dataType = DataType.Int32;
return 44;
}
}
dataType = DataType.Invalid;
return -1;
}
/// <summary>
/// Gets the index for the displacement triangles lump in the BSP file for a specific map format, and the type of data the format uses.
/// </summary>
/// <param name="type">The map type</param>
/// <param name="dataType"><c>out</c> parameter for the data type this version uses</param>
/// <returns>Index for this lump, or -1 if the format doesn't have this lump or it's not implemented</returns>
public static int GetIndexForDisplacementTrianglesLump(MapType version, out DataType dataType) {
switch (version) {
case MapType.Vindictus:
case MapType.TacticalInterventionEncrypted:
case MapType.Source17:
case MapType.Source18:
case MapType.Source19:
case MapType.Source20:
case MapType.Source21:
case MapType.Source22:
case MapType.Source23:
case MapType.Source27:
case MapType.DMoMaM: {
dataType = DataType.UInt16;
return 48;
}
}
dataType = DataType.Invalid;
return -1;
}
#endregion
}
}

View File

@@ -19,7 +19,7 @@ namespace LibBSP {
}
/// <summary>
/// The a component of this <c>Plane</c>
/// The <c>a</c> component of this <c>Plane</c>
/// </summary>
public double a {
get {
@@ -28,7 +28,7 @@ namespace LibBSP {
}
/// <summary>
/// The b component of this <c>Plane</c>
/// The <c>b</c> component of this <c>Plane</c>
/// </summary>
public double b {
get {
@@ -37,7 +37,7 @@ namespace LibBSP {
}
/// <summary>
/// The c component of this <c>Plane</c>
/// The <c>c</c> component of this <c>Plane</c>
/// </summary>
public double c {
get {
@@ -140,28 +140,6 @@ namespace LibBSP {
distance = point0 * _normal;
}
/// <summary>
/// Creates a new <c>Plane</c> object from a <c>byte</c> array.
/// </summary>
/// <param name="data"><c>byte</c> array to parse</param>
/// <param name="type">The map type</param>
/// <remarks>
/// This constructor is intended for use with BSP reading. The <c>byte</c> array must have a length of at least 16, all components are read as <c>float</c>s.
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was null</exception>
/// <exception cref="ArgumentException">There was not enough bytes in the <paramref name="data" /> array</exception>
public Plane(byte[] data, MapType type) {
if (data == null) {
throw new ArgumentNullException();
}
if (data.Length < 16) {
throw new ArgumentException("Not enough data in provided array");
}
_normal = new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8));
_normal.Normalize();
distance = Convert.ToDouble(BitConverter.ToSingle(data, 12));
}
/// <summary>
/// Determines whether this <c>Plane</c> is equal to another <c>object</c>.
/// </summary>
@@ -362,62 +340,6 @@ namespace LibBSP {
}
return plane;
}
/// <summary>
/// Factory method to parse a <c>byte</c> array into a <c>List</c> of <c>Plane</c> objects.
/// </summary>
/// <param name="data">The data to parse</param>
/// <param name="type">The map type</param>
/// <returns>A <c>List</c> of <c>Plane</c> objects</returns>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was null</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype</exception>
public static List<Plane> LumpFactory(byte[] data, MapType type) {
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.DMoMaM:
case MapType.Vindictus:
case MapType.Quake2:
case MapType.Daikatana:
case MapType.TacticalIntervention:
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;
default:
throw new ArgumentException("Map type " + type + " isn't supported by the Plane class.");
}
List<Plane> lump = new List<Plane>(data.Length / structLength);
byte[] bytes = new byte[structLength];
for (int i = 0; i < data.Length / structLength; ++i) {
Array.Copy(data, i * structLength, bytes, 0, structLength);
lump.Add(new Plane(bytes, type));
}
return lump;
}
}
}
#endif

View File

@@ -148,26 +148,6 @@ namespace LibBSP {
y = vector.y;
}
/// <summary>
/// Creates a new <c>Vector2d</c> instance by parsing a <c>byte</c> array representing either <c>float</c>s or <c>double</c>s.
/// </summary>
/// <param name="data"><c>Byte</c> array to parse</param>
/// <exception cref="ArgumentException">The passed byte array was not 8 or 16 elements long</exception>
public Vector2d(params byte[] data) {
if (data == null) {
throw new ArgumentNullException();
}
if (data.Length == 8) {
x = Convert.ToDouble(BitConverter.ToSingle(data, 0));
y = Convert.ToDouble(BitConverter.ToSingle(data, 4));
} else if (data.Length == 24) {
x = BitConverter.ToDouble(data, 0);
y = BitConverter.ToDouble(data, 8);
} else {
throw new System.ArgumentException("Byte array passed to Vector3d Ctor must have 8 or 16 items", "data");
}
}
/// <summary>
/// Adds two vectors together componentwise. This operation is commutative.
/// </summary>

View File

@@ -178,28 +178,6 @@ namespace LibBSP {
z = vector.z;
}
/// <summary>
/// Creates a new <c>Vector3d</c> instance by parsing a <c>byte</c> array representing either <c>float</c>s or <c>double</c>s.
/// </summary>
/// <param name="data"><c>Byte</c> array to parse</param>
/// <exception cref="ArgumentException">The passed byte array was not 12 or 24 elements long</exception>
public Vector3d(params byte[] data) {
if (data == null) {
throw new ArgumentNullException();
}
if (data.Length == 12) {
x = Convert.ToDouble(BitConverter.ToSingle(data, 0));
y = Convert.ToDouble(BitConverter.ToSingle(data, 4));
z = Convert.ToDouble(BitConverter.ToSingle(data, 8));
} else if (data.Length == 24) {
x = BitConverter.ToDouble(data, 0);
y = BitConverter.ToDouble(data, 8);
z = BitConverter.ToDouble(data, 16);
} else {
throw new System.ArgumentException("Byte array passed to Vector3d Ctor must have 12 or 24 items", "data");
}
}
/// <summary>
/// Adds two vectors together componentwise. This operation is commutative.
/// </summary>

View File

@@ -193,30 +193,6 @@ namespace LibBSP {
w = vector.w;
}
/// <summary>
/// Creates a new <c>Vector4d</c> instance by parsing a <c>byte</c> array representing either <c>float</c>s or <c>double</c>s.
/// </summary>
/// <param name="data"><c>Byte</c> array to parse</param>
/// <exception cref="ArgumentException">The passed byte array was not 16 or 32 elements long</exception>
public Vector4d(params byte[] data) {
if (data == null) {
throw new ArgumentNullException();
}
if (data.Length == 16) {
x = Convert.ToDouble(BitConverter.ToSingle(data, 0));
y = Convert.ToDouble(BitConverter.ToSingle(data, 4));
z = Convert.ToDouble(BitConverter.ToSingle(data, 8));
w = Convert.ToDouble(BitConverter.ToSingle(data, 12));
} else if (data.Length == 32) {
x = BitConverter.ToDouble(data, 0);
y = BitConverter.ToDouble(data, 8);
z = BitConverter.ToDouble(data, 16);
w = BitConverter.ToDouble(data, 24);
} else {
throw new System.ArgumentException("Byte array passed to Vector3d Ctor must have 16 or 32 items", "data");
}
}
/// <summary>
/// Adds two vectors together componentwise. This operation is commutative.
/// </summary>

View File

@@ -0,0 +1,418 @@
using System;
using System.IO;
using System.Collections.Generic;
namespace LibBSP {
/// <summary>
/// Handles reading of a BSP file on-demand.
/// </summary>
public class BSPReader {
private FileInfo bspFile;
private FileStream stream;
private BinaryReader binaryReader;
private bool _bigEndian = false;
// Decryption key for Tactical Intervention; should be 32 bytes
private byte[] key = new byte[0];
public bool bigEndian { get { return _bigEndian; } }
/// <summary>
/// Creates a new instance of a <c>BSPReader</c> class to read the specified file.
/// </summary>
/// <param name="file"></param>
public BSPReader(FileInfo file) {
if (!File.Exists(file.FullName)) {
throw new FileNotFoundException("Unable to open BSP file; file " + file.FullName + " not found.");
} else {
this.bspFile = file;
this.stream = new FileStream(file.FullName, FileMode.Open);
this.binaryReader = new BinaryReader(this.stream);
}
}
/// <summary>
/// Reads this lump in the map.
/// </summary>
/// <param name="index">The index of the lump to get</param>
/// <param name="version">The version of BSP this is</param>
/// <returns>Array of bytes read from the BSP file</returns>
public byte[] ReadLumpNum(int index, MapType version) {
switch (version) {
case MapType.Quake:
case MapType.Nightfire: {
return ReadLumpFromOffsetLengthPairAtOffset(4 + (8 * index), version);
}
case MapType.Quake2:
case MapType.Daikatana:
case MapType.SiN:
case MapType.SoF:
case MapType.Quake3:
case MapType.Raven:
case MapType.CoD: {
return ReadLumpFromOffsetLengthPairAtOffset(8 + (8 * index), version);
}
case MapType.CoD2: {
stream.Seek(8 + (8 * index), SeekOrigin.Begin);
int temp = binaryReader.ReadInt32();
return ReadLump(binaryReader.ReadInt32(), temp, version);
}
case MapType.STEF2:
case MapType.STEF2Demo:
case MapType.MOHAA:
case MapType.FAKK: {
return ReadLumpFromOffsetLengthPairAtOffset(12 + (8 * index), version);
}
case MapType.CoD4: {
stream.Seek(8, SeekOrigin.Begin);
int numlumps = binaryReader.ReadInt32();
int offset = (numlumps * 8) + 12;
for (int i = 0; i < numlumps; i++) {
int id = binaryReader.ReadInt32();
int length = binaryReader.ReadInt32();
if (id == index) {
return ReadLump(offset, length, version);
} else {
offset += length;
while (offset % 4 != 0) { offset++; }
}
}
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.TacticalInterventionEncrypted:
case MapType.Vindictus:
case MapType.DMoMaM: {
return ReadLumpFromOffsetLengthPairAtOffset(8 + (16 * index), version);
}
/*
case MapType.Doom:
case MapType.Hexen: {
int[] ol = getLumpInfo(index);
return readLump(ol[0], ol[1]);
}*/
}
return new byte[0];
}
/// <summary>
/// Returns the lump referenced by the offset/length pair at the specified <paramref name="offset"/>,
/// read as two Int32.
/// </summary>
/// <param name="offset">The byte offset for the offset/length pair</param>
/// <param name="version">The version of BSP this is</param>
/// <returns>Array of bytes read from the BSP file</returns>
private byte[] ReadLumpFromOffsetLengthPairAtOffset(int offset, MapType version) {
stream.Seek(offset, SeekOrigin.Begin);
byte[] input = binaryReader.ReadBytes(12);
if (version == MapType.TacticalInterventionEncrypted) {
input = XorWithKeyStartingAtIndex(input, offset);
}
int lumpOffset;
int lumpLength;
if (version == MapType.L4D2) {
lumpOffset = BitConverter.ToInt32(input, 4);
lumpLength = BitConverter.ToInt32(input, 8);
} else {
lumpOffset = BitConverter.ToInt32(input, 0);
lumpLength = BitConverter.ToInt32(input, 4);
}
/*if (bigEndian) {
byte[] bytes = BitConverter.GetBytes(lumpLength);
Array.Reverse(bytes);
lumpLength = BitConverter.ToInt32(bytes, 0);
bytes = BitConverter.GetBytes(lumpOffset);
Array.Reverse(bytes);
lumpOffset = BitConverter.ToInt32(bytes, 0);
}*/
return ReadLump(lumpOffset, lumpLength, version);
}
/// <summary>
/// Reads the lump <paramref name="length"/> bytes long at <paramref name="offset"/> in the file
/// </summary>
/// <param name="offset">Offset to start reading from</param>
/// <param name="length">Length of the lump to read</param>
/// <param name="version">The version of BSP this is</param>
/// <returns>Array of bytes read from the BSP file</returns>
public byte[] ReadLump(int offset, int length, MapType version) {
stream.Seek(offset, SeekOrigin.Begin);
byte[] input = binaryReader.ReadBytes(length);
if (version == MapType.TacticalInterventionEncrypted) {
input = XorWithKeyStartingAtIndex(input, offset);
}
return input;
}
/// <summary>
/// Xors the <paramref name="data"/> <c>byte</c> array with the localls stored key <c>byte</c> array, starting at a certain <paramref name="index"/> in the key.
/// </summary>
/// <param name="data">The byte array to Xor</param>
/// <param name="index">The index in the key byte array to start reading from</param>
/// <returns>The input <c>byte</c> array Xored with the key <c>byte</c> array</returns>
/// <exception cref="ArgumentNullException">The passed <paramref name="data"/> parameter was null</exception>
private byte[] XorWithKeyStartingAtIndex(byte[] data, int index = 0) {
if (data == null) {
throw new ArgumentNullException();
}
if (key.Length == 0 || data.Length == 0) {
return data;
}
byte[] output = new byte[data.Length];
for (int i = 0; i < data.Length; i++) {
output[i] = (byte)(data[i] ^ key[(i + index) % key.Length]);
}
return output;
}
/// <summary>
/// Tries to get the <c>MapType</c> member most closely represented by the referenced file. If the file is
/// found to be big-endian, this will set <c>bigEndian</c> to <c>true</c>.
/// </summary>
/// <returns>The <c>MapType</c> of this BSP, <c>MapType.Undefined</c> if it could not be determined</returns>
public MapType GetVersion() {
MapType ret = GetVersion(false);
if (ret == MapType.Undefined) {
ret = GetVersion(true);
if (ret != MapType.Undefined) {
_bigEndian = true;
}
}
return ret;
}
/// <summary>
/// Tries to get the <c>MapType</c> member most closely represented by the referenced file.
/// </summary>
/// <param name="bigEndian">Set to <c>true</c> to attempt reading the data in big-endian byte order</param>
/// <returns>The <c>MapType</c> of this BSP, <c>MapType.Undefined</c> if it could not be determined</returns>
private MapType GetVersion(bool bigEndian) {
MapType current = MapType.Undefined;
stream.Seek(0, SeekOrigin.Begin);
int data = binaryReader.ReadInt32();
if (bigEndian) {
byte[] bytes = BitConverter.GetBytes(data);
Array.Reverse(bytes);
data = BitConverter.ToInt32(bytes, 0);
}
if (data == 1347633737) {
// 1347633737 reads in ASCII as "IBSP"
// Versions: CoD, CoD2, CoD4, Quake 2, Daikatana, Quake 3 (RtCW), Soldier of Fortune
data = binaryReader.ReadInt32();
if (bigEndian) {
byte[] bytes = BitConverter.GetBytes(data);
Array.Reverse(bytes);
data = BitConverter.ToInt32(bytes, 0);
}
switch (data) {
case 4: {
current = MapType.CoD2;
break;
}
case 22: {
current = MapType.CoD4;
break;
}
case 38: {
current = MapType.Quake2;
break;
}
case 41: {
current = MapType.Daikatana;
break;
}
case 46: {
current = MapType.Quake3;
// This version number is both Quake 3 and Soldier of Fortune. Find out the length of the
// header, based on offsets.
for (int i = 0; i < 17; i++) {
stream.Seek((i + 1) * 8, SeekOrigin.Begin);
int temp = binaryReader.ReadInt32();
if (bigEndian) {
byte[] bytes = BitConverter.GetBytes(temp);
Array.Reverse(bytes);
temp = BitConverter.ToInt32(bytes, 0);
}
if (temp == 184) {
current = MapType.SoF;
break;
} else {
if (temp == 144) {
break;
}
}
}
break;
}
case 47: {
current = MapType.Quake3;
break;
}
case 59: {
current = MapType.CoD;
break;
}
}
} else {
if (data == 892416050) {
// 892416050 reads in ASCII as "2015," the game studio which developed MoHAA
current = MapType.MOHAA;
} else {
if (data == 1095516485) {
// 1095516485 reads in ASCII as "EALA," the ones who developed MoHAA Spearhead and Breakthrough
current = MapType.MOHAA;
} else {
if (data == 1347633750) {
// 1347633750 reads in ASCII as "VBSP." Indicates Source engine.
// Some source games handle this as 2 shorts.
// TODO: Big endian?
// Formats: Source 17-23 and 27, DMoMaM, Vindictus
data = (int)binaryReader.ReadUInt16();
switch (data) {
case 17: {
current = MapType.Source17;
break;
}
case 18: {
current = MapType.Source18;
break;
}
case 19: {
current = MapType.Source19;
break;
}
case 20: {
int version2 = (int)binaryReader.ReadUInt16();
if (version2 == 4) {
// TODO: This doesn't necessarily mean the whole map should be read as DMoMaM.
current = MapType.DMoMaM;
} else {
// TODO: Vindictus? Before I was determining these by looking at the Game Lump data, is there a better way?
current = MapType.Source20;
}
break;
}
case 21: {
current = MapType.Source21;
// Hack to determine if this is a L4D2 map. Read what would normally be the offset of
// a lump. If it is less than the header length it's probably not an offset, indicating L4D2.
stream.Seek(8, SeekOrigin.Begin);
int test = binaryReader.ReadInt32();
if (bigEndian) {
byte[] bytes = BitConverter.GetBytes(test);
Array.Reverse(bytes);
test = BitConverter.ToInt32(bytes, 0);
}
if (test < 1032) {
current = MapType.L4D2;
}
break;
}
case 22: {
current = MapType.Source22;
break;
}
case 23: {
current = MapType.Source23;
break;
}
case 27: {
current = MapType.Source27;
break;
}
}
} else {
if (data == 1347633746) {
// Reads in ASCII as "RBSP". Raven software's modification of Q3BSP, or Ritual's modification of Q2.
// Formats: Raven, SiN
current = MapType.Raven;
for (int i = 0; i < 17; i++) {
// Find out where the first lump starts, based on offsets.
stream.Seek((i + 1) * 8, SeekOrigin.Begin);
int temp = binaryReader.ReadInt32();
if (bigEndian) {
byte[] bytes = BitConverter.GetBytes(temp);
Array.Reverse(bytes);
temp = BitConverter.ToInt32(bytes, 0);
}
if (temp == 168) {
current = MapType.SiN;
break;
} else {
if (temp == 152) {
break;
}
}
}
} else {
if (data == 556942917) {
// "EF2!"
current = MapType.STEF2;
} else {
if (data == 1263223110) {
// "FAKK"
// Formats: STEF2 demo, Heavy Metal FAKK2 (American McGee's Alice)
data = binaryReader.ReadInt32();
if (bigEndian) {
byte[] bytes = BitConverter.GetBytes(data);
Array.Reverse(bytes);
data = BitConverter.ToInt32(bytes, 0);
}
switch (data) {
case 19: {
current = MapType.STEF2Demo;
break;
}
case 12:
case 42: {// American McGee's Alice
current = MapType.FAKK;
break;
}
}
} else {
switch (data) {
// Various numbers not representing a string
// Formats: HL1, Quake, Nightfire, or perhaps Tactical Intervention's encrypted format
case 29:
case 30: {
current = MapType.Quake;
break;
}
case 42: {
current = MapType.Nightfire;
break;
}
default: {
// Hack to get Tactical Intervention's encryption key. At offset 384, there are two unused lumps whose
// values in the header are always 0s. Grab these 32 bytes (256 bits) and see if they match an expected value.
stream.Seek(384, SeekOrigin.Begin);
key = binaryReader.ReadBytes(32);
stream.Seek(0, SeekOrigin.Begin);
data = BitConverter.ToInt32(XorWithKeyStartingAtIndex(binaryReader.ReadBytes(4)), 0);
if (data == 1347633750) {
current = MapType.TacticalInterventionEncrypted;
} else {
current = MapType.Undefined;
}
break;
}
}
}
}
}
}
}
}
}
return current;
}
}
}