Ridiculously massive refactor:

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

View File

@@ -1,23 +1,374 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace LibBSP {
/// <summary>
/// Holds the data used by the brush side structures of all formats of BSP.
/// </summary>
[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;
}
}
}
}
/// <summary>
/// Creates a new <see cref="BrushSide"/> object from a <c>byte</c> array.
@@ -26,80 +377,13 @@ namespace LibBSP {
/// <param name="type">The map type.</param>
/// <param name="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 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;
}
/// <summary>
@@ -161,8 +445,8 @@ namespace LibBSP {
}
int numObjects = data.Length / structLength;
List<BrushSide> lump = new List<BrushSide>(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));
}