Call of Duty (1): Add support for various map structures based on my most recent reverse engineering.

Other minor cleanup.
This commit is contained in:
wfowler
2018-08-05 16:38:27 -06:00
parent 015829f2e2
commit 1014184f24
10 changed files with 278 additions and 12 deletions

View File

@@ -68,6 +68,7 @@
<Compile Include="Source\Structs\BSP\DisplacementInfo.cs" />
<Compile Include="Source\Structs\BSP\DisplacementVertex.cs" />
<Compile Include="Source\Structs\BSP\Lumps\DisplacementVertices.cs" />
<Compile Include="Source\Structs\BSP\Patch.cs" />
<Compile Include="Source\Structs\BSP\StaticModel.cs" />
<Compile Include="Source\Structs\BSP\StaticProp.cs" />
<Compile Include="Source\Structs\BSP\TextureData.cs" />

View File

@@ -107,9 +107,17 @@ namespace LibBSP {
}
UIVertex result = new UIVertex();
switch (type) {
case MapType.CoD: {
if (version == 0) {
goto case MapType.Quake3;
} else if (version == 1) {
result.color = Color32Extensions.FromArgb(255, 255, 255, 255);
goto case MapType.DMoMaM;
}
break;
}
case MapType.MOHAA:
case MapType.Quake3:
case MapType.CoD:
case MapType.CoD2:
case MapType.CoD4:
case MapType.FAKK: {
@@ -194,9 +202,16 @@ namespace LibBSP {
structLength = 12;
break;
}
case MapType.CoD: {
if (version == 0) {
goto case MapType.Quake3;
} else if (version == 1) {
goto case MapType.DMoMaM;
}
break;
}
case MapType.MOHAA:
case MapType.Quake3:
case MapType.CoD:
case MapType.FAKK: {
structLength = 44;
break;
@@ -224,7 +239,7 @@ namespace LibBSP {
}
/// <summary>
/// Gets the index for this lump in the BSP file for a specific map format.
/// Gets the index for the vertices 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>
@@ -260,6 +275,9 @@ namespace LibBSP {
case MapType.STEF2Demo: {
return 6;
}
case MapType.CoD: {
return 7;
}
case MapType.Raven:
case MapType.Quake3: {
return 10;
@@ -270,5 +288,21 @@ namespace LibBSP {
}
}
/// <summary>
/// Gets the index for the patch vertices 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 GetIndexForPatchVertsLump(MapType type) {
switch (type) {
case MapType.CoD: {
return 25;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -93,6 +93,10 @@ namespace LibBSP {
// MoHAA
private List<LODTerrain> _lodTerrains;
private List<StaticModel> _staticModels;
// CoD
private List<Patch> _patches;
private List<UIVertex> _patchVerts;
private NumList _patchIndices;
// Nightfire
private Textures _materials;
private NumList _indices;
@@ -481,6 +485,52 @@ namespace LibBSP {
}
}
/// <summary>
/// A <c>List</c> of <see cref="Patch"/> objects in the BSP file, if available.
/// </summary>
public List<Patch> patches {
get {
if (_patches == null) {
int index = Patch.GetIndexForLump(version);
if (index >= 0) {
_patches = Patch.LumpFactory(reader.ReadLump(this[index]), version, this[index].version);
}
}
return _patches;
}
}
/// <summary>
/// A <c>List</c> of <see cref="UIVertex"/> objects in the BSP file representing the patch vertices of the BSP, if available.
/// </summary>
public List<UIVertex> patchVerts {
get {
if (_patchVerts == null) {
int index = UIVertexExtensions.GetIndexForPatchVertsLump(version);
if (index >= 0) {
_patchVerts = UIVertexExtensions.LumpFactory(reader.ReadLump(this[index]), version, 1);
}
}
return _patchVerts;
}
}
/// <summary>
/// A <see cref="NumList"/> object containing the Patch Vertex Indices lump, if available.
/// </summary>
public NumList patchIndices {
get {
if (_patchIndices == null) {
NumList.DataType type;
int index = NumList.GetIndexForPatchIndicesLump(version, out type);
if (index >= 0) {
_patchIndices = NumList.LumpFactory(reader.ReadLump(this[index]), type);
}
}
return _patchIndices;
}
}
/// <summary>
/// A <see cref="NumList"/> object containing the Face Vertex Indices lump, if available.
/// </summary>
@@ -772,8 +822,8 @@ namespace LibBSP {
}
// Get the index and length from the object
int index = (int)indexProperty.GetGetMethod().Invoke(o, null);
int count = (int)countProperty.GetGetMethod().Invoke(o, null);
int index = (int)(indexProperty.GetGetMethod().Invoke(o, null));
int count = (int)(countProperty.GetGetMethod().Invoke(o, null));
// Get the lump from this class
List<T> theLump = targetLump.GetGetMethod().Invoke(this, null) as List<T>;

View File

@@ -74,6 +74,14 @@ namespace LibBSP {
lightMaps = -1;
patchSize = new Vector2(Single.NaN, Single.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:
@@ -180,6 +188,10 @@ namespace LibBSP {
}
int structLength = 0;
switch (type) {
case MapType.CoD: {
structLength = 16;
break;
}
case MapType.Quake:
case MapType.Quake2:
case MapType.Daikatana: {
@@ -272,7 +284,8 @@ namespace LibBSP {
case MapType.Quake2:
case MapType.SiN:
case MapType.Daikatana:
case MapType.SoF: {
case MapType.SoF:
case MapType.CoD: {
return 6;
}
case MapType.Vindictus:

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace LibBSP {
/// <summary>
/// Holds the data used by the brush structures of all formats of BSP.
/// Holds the data for a terrain object in MoHAA.
/// </summary>
public struct LODTerrain {

View File

@@ -90,6 +90,13 @@ namespace LibBSP {
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.");
}
@@ -209,6 +216,9 @@ namespace LibBSP {
case MapType.Nightfire: {
return 11;
}
case MapType.CoD: {
return 21;
}
default: {
return -1;
}

View File

@@ -22,6 +22,8 @@ namespace LibBSP {
[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; }
/// <summary>
/// Creates a new <see cref="Model"/> object from a <c>byte</c> array.
@@ -42,6 +44,8 @@ namespace LibBSP {
numBrushes = -1;
firstFace = -1;
numFaces = -1;
firstLeafPatch = -1;
numLeafPatches = -1;
switch (type) {
case MapType.Quake: {
headNode = BitConverter.ToInt32(data, 36);
@@ -94,7 +98,13 @@ namespace LibBSP {
numBrushes = BitConverter.ToInt32(data, 36);
break;
}
case MapType.CoD:
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);

View File

@@ -27,8 +27,8 @@ namespace LibBSP {
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: {
this.child1 = BitConverter.ToInt16(data, 4);
this.child2 = BitConverter.ToInt16(data, 6);
child1 = BitConverter.ToInt16(data, 4);
child2 = BitConverter.ToInt16(data, 6);
break;
}
// These all use the first three ints for planenum and children
@@ -56,8 +56,8 @@ namespace LibBSP {
case MapType.Quake3:
case MapType.FAKK:
case MapType.CoD: {
this.child1 = BitConverter.ToInt32(data, 4);
this.child2 = BitConverter.ToInt32(data, 8);
child1 = BitConverter.ToInt32(data, 4);
child2 = BitConverter.ToInt32(data, 8);
break;
}
default: {
@@ -179,6 +179,9 @@ namespace LibBSP {
case MapType.STEF2Demo: {
return 11;
}
case MapType.CoD: {
return 20;
}
default: {
return -1;
}

View File

@@ -0,0 +1,116 @@
#if UNITY_2_6 || UNITY_2_6_1 || UNITY_3_0 || UNITY_3_0_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || 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 Vector2 = Vector2d;
#endif
/// <summary>
/// Holds the data for a patch in a CoD BSP.
/// </summary>
public struct Patch {
public short shader { get; private set; }
public short type { get; private set; }
public Vector2 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; }
/// <summary>
/// Creates a new <see cref="Patch"/> 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>
/// <param name="version">The version of this lump.</param>
/// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public Patch(byte[] data, MapType type, int version = 0) : this() {
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 Vector2(x, y);
flags = BitConverter.ToInt32(data, 8);
firstVertex = BitConverter.ToInt32(data, 12);
numVertices = x * y;
} else if (this.type == 1) { // Terrain
numVertices = BitConverter.ToInt16(data, 4);
numIndices = BitConverter.ToInt16(data, 6);
firstVertex = BitConverter.ToInt32(data, 8);
firstIndex = BitConverter.ToInt32(data, 12);
}
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Patch class.");
}
}
}
/// <summary>
/// Factory method to parse a <c>byte</c> array into a <c>List</c> of <see cref="Patch"/> objects.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <param name="type">The map type.</param>
/// <param name="version">The version of this lump.</param>
/// <returns>A <c>List</c> of <see cref="Patch"/> objects.</returns>
/// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception>
/// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception>
public static List<Patch> LumpFactory(byte[] data, MapType type, int version = 0) {
if (data == null) {
throw new ArgumentNullException();
}
int structLength = 0;
switch (type) {
case MapType.CoD: {
structLength = 16;
break;
}
default: {
throw new ArgumentException("Map type " + type + " isn't supported by the Patch lump factory.");
}
}
List<Patch> lump = new List<Patch>(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 Patch(bytes, type, version));
}
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 24;
}
default: {
return -1;
}
}
}
}
}

View File

@@ -150,6 +150,10 @@ namespace LibBSP {
dataType = DataType.UInt16;
return 16;
}
case MapType.CoD: {
dataType = DataType.UInt32;
return 23;
}
}
dataType = DataType.Invalid;
return -1;
@@ -239,6 +243,10 @@ namespace LibBSP {
dataType = DataType.UInt16;
return 17;
}
case MapType.CoD: {
dataType = DataType.UInt32;
return 22;
}
}
dataType = DataType.Invalid;
return -1;
@@ -266,6 +274,10 @@ namespace LibBSP {
dataType = DataType.UInt32;
return 7;
}
case MapType.CoD: {
dataType = DataType.UInt16;
return 8;
}
case MapType.Raven:
case MapType.Quake3: {
dataType = DataType.UInt32;
@@ -276,6 +288,23 @@ namespace LibBSP {
return -1;
}
/// <summary>
/// Gets the index for the patch indices lump in the BSP file for a specific map format, and the type of data the format uses.
/// </summary>
/// <param name="version">The map type.</param>
/// <param name="dataType"><c>out</c> parameter that will contain 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 GetIndexForPatchIndicesLump(MapType version, out DataType dataType) {
switch (version) {
case MapType.CoD: {
dataType = DataType.UInt16;
return 26;
}
}
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>