2818 lines
111 KiB
C#
2818 lines
111 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using static SourceFormatParser.BSP.SourceBSPStructs;
|
|
using static SourceFormatParser.Common.ParsingUtils;
|
|
using SourceFormatParser.BigEndian;
|
|
using SourceFormatParser.Common;
|
|
|
|
namespace SourceFormatParser.BSP
|
|
{
|
|
/// <summary>
|
|
/// Supported version: 17+ /
|
|
/// Tested on versions: 19-21
|
|
/// </summary>
|
|
public class SourceBSP : IDisposable
|
|
{
|
|
#region Public variables
|
|
public dheader_t Header;
|
|
|
|
public SourceGame Game;
|
|
|
|
//both can be true, both can be false.
|
|
public bool isHDR => (MapFlags.m_LevelFlags & (uint)MAP_FLAGS.LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR) != 0;
|
|
public bool isLDR => (MapFlags.m_LevelFlags & (uint)MAP_FLAGS.LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR) != 0;
|
|
#endregion
|
|
#region Private variables
|
|
BinaryReader BR;
|
|
Stream stream;
|
|
#endregion
|
|
|
|
#region enums
|
|
/// <summary>
|
|
/// this enum for games with modified structs, to parse them properly later
|
|
/// </summary>
|
|
public enum SourceGame
|
|
{
|
|
Unknown,
|
|
//==valve games
|
|
//TeamFortress2,
|
|
//Left4Dead,
|
|
Left4Dead2,
|
|
//Portal2,
|
|
//CounterStrikeGlobalOffensive,
|
|
CSGO_PS3,
|
|
//Dota2,
|
|
|
|
//==idk
|
|
//ZenoClash,
|
|
//DarkMessiah,
|
|
//Vindictus,
|
|
//TheShip,
|
|
//BloodyGoodTime,
|
|
//BlackMesa,
|
|
//AlienSwarm,
|
|
//DearEsther,
|
|
//Titanfall
|
|
}
|
|
#endregion
|
|
|
|
#region Init / Dispose
|
|
public SourceBSP(string path, SourceGame game = SourceGame.Unknown)
|
|
{
|
|
stream = File.OpenRead(path);
|
|
Init(stream, game);
|
|
}
|
|
public SourceBSP(Stream stream, SourceGame game = SourceGame.Unknown) => Init(stream, game);
|
|
|
|
void Init(Stream stream, SourceGame game)
|
|
{
|
|
Game = game;
|
|
if (game == SourceGame.CSGO_PS3)
|
|
BR = new BigBinaryReader(stream, Encoding.ASCII);
|
|
else
|
|
BR = new BinaryReader(stream, Encoding.ASCII);
|
|
ParseHeader();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
BR.Dispose();
|
|
if (stream != null)
|
|
stream.Dispose();
|
|
}
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
void ParseHeader()
|
|
{
|
|
BR.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
int id = BR.ReadInt32();
|
|
if (id != IDBSPHEADER)
|
|
throw new Exception("Not a BSP file! (wrong id)");
|
|
|
|
Header = new dheader_t
|
|
{
|
|
ident = id,
|
|
version = BR.ReadInt32(),
|
|
lumps = ParseLumps(),
|
|
mapRevision = BR.ReadInt32()
|
|
};
|
|
}
|
|
|
|
lump_t[] ParseLumps()
|
|
{
|
|
lump_t[] lumps = new lump_t[HEADER_LUMPS];
|
|
for (int l = 0; l < HEADER_LUMPS; l++)
|
|
{
|
|
if (Game == SourceGame.Left4Dead2)
|
|
lumps[l] = new lump_t
|
|
{
|
|
version = BR.ReadInt32(),
|
|
|
|
fileofs = BR.ReadInt32(),
|
|
filelen = BR.ReadInt32(),
|
|
uncompressedSize = BR.ReadInt32(),
|
|
|
|
lumpNum = l
|
|
};
|
|
else
|
|
lumps[l] = new lump_t
|
|
{
|
|
fileofs = BR.ReadInt32(),
|
|
filelen = BR.ReadInt32(),
|
|
version = BR.ReadInt32(),
|
|
uncompressedSize = BR.ReadInt32(),
|
|
|
|
lumpNum = l
|
|
};
|
|
}
|
|
return lumps;
|
|
}
|
|
#endregion
|
|
|
|
#region Methods for lumps
|
|
private lump_t SetLump(int lumpNum)
|
|
{
|
|
lump_t lump = Header.lumps[lumpNum];
|
|
BR.BaseStream.Seek(lump.fileofs, SeekOrigin.Begin);
|
|
return lump;
|
|
}
|
|
private byte[] ReadLump(int lumpNum) => BR.ReadBytes(SetLump(lumpNum).filelen);
|
|
|
|
private int GetLumpCount(int lumpNum, int byteSize) => GetLumpCount(Header.lumps[lumpNum], byteSize);
|
|
private int GetLumpCount(lump_t lump, int byteSize)
|
|
{
|
|
//check if byteSize is correct
|
|
int c = lump.filelen / byteSize;
|
|
if (lump.filelen % byteSize != 0)
|
|
{
|
|
//trying to find nearest byteSizes
|
|
int nearMin=-1, nearMax=-1;
|
|
for(int s = 1; s < 100; s++)
|
|
{
|
|
if (byteSize - s <= 0) break;
|
|
if (lump.filelen % (byteSize - s) == 0)
|
|
{
|
|
nearMin = byteSize - s;
|
|
break;
|
|
}
|
|
}
|
|
for (int s = 1; s < 100; s++)
|
|
{
|
|
if (lump.filelen % (byteSize + s) == 0)
|
|
{
|
|
nearMax = byteSize + s;
|
|
break;
|
|
}
|
|
}
|
|
string debug_text = $"[Lump #{lump.lumpNum}:v{lump.version}] Wrong count" + (nearMin != -1 || nearMax != -1 ? $" ({nearMin}; {nearMax})" : "");
|
|
DebugLog.Write(debug_text);
|
|
}
|
|
return c;
|
|
}
|
|
#endregion
|
|
|
|
#region Lumps
|
|
|
|
#region Lump #0: Entities
|
|
private List<SourceKV> _entities;
|
|
/// <summary>
|
|
/// Lump #0.
|
|
/// </summary>
|
|
public List<SourceKV> Entities
|
|
{
|
|
get
|
|
{
|
|
if (_entities == null)
|
|
{
|
|
string[] _ents = Encoding.ASCII.GetString(ReadLump(0)).Split('{', '}');
|
|
|
|
List<SourceKV> ents = new List<SourceKV>();
|
|
foreach(string e in _ents)
|
|
{
|
|
if (e.Length > 2)
|
|
ents.Add(new SourceKV(e, true));
|
|
}
|
|
|
|
_entities = ents;
|
|
}
|
|
return _entities;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #1: Planes
|
|
private dplane_t[] _planes;
|
|
/// <summary>
|
|
/// Lump #1.
|
|
/// Purpose: [not sure] Calculations for movement (if player can collide with walls?)
|
|
/// </summary>
|
|
public dplane_t[] Planes
|
|
{
|
|
get
|
|
{
|
|
if (_planes == null)
|
|
{
|
|
lump_t lump = SetLump(1);
|
|
int count = GetLumpCount(lump, 20);
|
|
_planes = new dplane_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_planes[p] = new dplane_t
|
|
{
|
|
normal = BR.ReadVector(),
|
|
dist = BR.ReadSingle(),
|
|
type = BR.ReadInt32()
|
|
};
|
|
}
|
|
}
|
|
return _planes;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #2: Texture data (TEXDATA)
|
|
private dtexdata_t[] _texdata;
|
|
/// <summary>
|
|
/// Lump #2.
|
|
/// Purpose: texture referencing, texture preloading, lighting calculation (reflectivity)
|
|
/// </summary>
|
|
public dtexdata_t[] Texdata
|
|
{
|
|
get
|
|
{
|
|
if (_texdata == null)
|
|
{
|
|
lump_t lump = SetLump(2);
|
|
int count = GetLumpCount(lump, 32);
|
|
_texdata = new dtexdata_t[count];
|
|
for (int t = 0; t < count; t++)
|
|
{
|
|
_texdata[t] = new dtexdata_t
|
|
{
|
|
reflectivity = BR.ReadVector(),
|
|
nameStringTableID = BR.ReadInt32(),
|
|
width = BR.ReadInt32(),
|
|
height = BR.ReadInt32(),
|
|
view_width = BR.ReadInt32(),
|
|
view_height = BR.ReadInt32()
|
|
};
|
|
}
|
|
}
|
|
return _texdata;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #3: Vertexes (vertices)
|
|
private SourceVector[] _vertexes;
|
|
/// <summary>
|
|
/// Lump #3.
|
|
/// Purpose: brush/displacement rendering
|
|
/// </summary>
|
|
public SourceVector[] Vertexes
|
|
{
|
|
get
|
|
{
|
|
if (_vertexes == null)
|
|
{
|
|
lump_t lump = SetLump(3);
|
|
int count = GetLumpCount(lump, 12);
|
|
_vertexes = new SourceVector[count];
|
|
for (int v = 0; v < count; v++)
|
|
_vertexes[v] = BR.ReadVector();
|
|
}
|
|
return _vertexes;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #4: Visibility
|
|
private dvis_t _visibility;
|
|
private bool __visibility;
|
|
/// <summary>
|
|
/// Lump #4.
|
|
/// Purpose: visibility optimizing for brushes/displacements/props/entities.
|
|
/// WARNING: may use many of RAM
|
|
/// </summary>
|
|
public dvis_t Visibility
|
|
{
|
|
get
|
|
{
|
|
if (!__visibility)
|
|
{
|
|
lump_t lump = SetLump(4);
|
|
if(lump.filelen == 0)
|
|
{
|
|
__visibility = true;
|
|
return _visibility;
|
|
}
|
|
|
|
int numclusters = BR.ReadInt32();
|
|
int[][] bitofs = new int[numclusters][];
|
|
for(int b = 0; b < numclusters; b++)
|
|
bitofs[b] = BR.ReadInt32Array(2);
|
|
|
|
_visibility = new dvis_t
|
|
{
|
|
numclusters = numclusters,
|
|
bitofs = bitofs
|
|
};
|
|
|
|
//reading pvs
|
|
_visibility.pvs = new HashSet<byte>[numclusters];
|
|
for (int i = 0; i < numclusters; i++)
|
|
{
|
|
var ofs = bitofs[i][0];
|
|
BR.BaseStream.Seek(lump.fileofs + ofs, SeekOrigin.Begin);
|
|
_visibility.pvs[i] = new HashSet<byte>();
|
|
int offset = 0;
|
|
while (offset < numclusters)
|
|
{
|
|
byte bits = BR.ReadByte();
|
|
if (bits == 0)
|
|
{
|
|
offset += BR.ReadByte() * 8;
|
|
continue;
|
|
}
|
|
|
|
for (var ii = 0; ii < 8 && offset + ii < numclusters; ++ii)
|
|
{
|
|
if ((bits & (1 << ii)) != 0)
|
|
_visibility.pvs[i].Add((byte)(offset + ii));
|
|
}
|
|
|
|
offset += 8;
|
|
}
|
|
}
|
|
|
|
//reading pas
|
|
// === UNCOMMENT THIS IF ITS USEFUL FOR YOU ===
|
|
// == i commented it because it uses additional RAM.
|
|
// == don't think it can be useful for someone
|
|
//_visibility.pas = new HashSet<byte>[numclusters];
|
|
//for (int i = 0; i < numclusters; i++)
|
|
//{
|
|
// var ofs = bitofs[i][1];
|
|
// BR.BaseStream.Seek(lump.fileofs + ofs, SeekOrigin.Begin);
|
|
// _visibility.pas[i] = new HashSet<byte>();
|
|
// int offset = 0;
|
|
// while (offset < numclusters)
|
|
// {
|
|
// byte bits = BR.ReadByte();
|
|
// if (bits == 0)
|
|
// {
|
|
// offset += BR.ReadByte() * 8;
|
|
// continue;
|
|
// }
|
|
|
|
// for (var ii = 0; ii < 8 && offset + ii < numclusters; ++ii)
|
|
// {
|
|
// if ((bits & (1 << ii)) != 0)
|
|
// _visibility.pas[i].Add((byte)(offset + ii));
|
|
// }
|
|
|
|
// offset += 8;
|
|
// }
|
|
//}
|
|
|
|
__visibility = true;
|
|
}
|
|
return _visibility;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #5: Nodes
|
|
private dnode_t[] _nodes;
|
|
/// <summary>
|
|
/// Lump #5.
|
|
/// Purpose: [not sure] to determine player's position for visibility optimisation?
|
|
/// </summary>
|
|
public dnode_t[] Nodes
|
|
{
|
|
get
|
|
{
|
|
if (_nodes == null)
|
|
{
|
|
lump_t lump = SetLump(5);
|
|
int count = GetLumpCount(lump, 32);
|
|
_nodes = new dnode_t[count];
|
|
for (int n = 0; n < count; n++)
|
|
{
|
|
_nodes[n] = new dnode_t
|
|
{
|
|
planenum = BR.ReadInt32(),
|
|
children = BR.ReadInt32Array(2),
|
|
mins = BR.ReadVectorShort(),
|
|
maxs = BR.ReadVectorShort(),
|
|
firstface = BR.ReadUInt16(),
|
|
numfaces = BR.ReadUInt16(),
|
|
area = BR.ReadInt16(),
|
|
//unused = BR.ReadInt16()
|
|
};
|
|
BR.BaseStream.Seek(2, SeekOrigin.Current);
|
|
}
|
|
}
|
|
return _nodes;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #6: Texture info (TEXINFO)
|
|
private texinfo_t[] _texinfo;
|
|
/// <summary>
|
|
/// Lump #6.
|
|
/// Purpose: texture info. Flags, texture offset, lightmap [offset?], index for texdata.
|
|
/// </summary>
|
|
public texinfo_t[] Texinfo
|
|
{
|
|
get
|
|
{
|
|
if (_texinfo == null)
|
|
{
|
|
lump_t lump = SetLump(6);
|
|
int count = GetLumpCount(lump, 72);
|
|
_texinfo = new texinfo_t[count];
|
|
for (int t = 0; t < count; t++)
|
|
{
|
|
_texinfo[t] = new texinfo_t
|
|
{
|
|
textureVecsTexelsPerWorldUnits = new SourceVector4[2] { BR.ReadVector4(), BR.ReadVector4() },
|
|
lightmapVecsLuxelsPerWorldUnits = new SourceVector4[2] { BR.ReadVector4(), BR.ReadVector4() },
|
|
flags = BR.ReadInt32(),
|
|
texdata = BR.ReadInt32()
|
|
};
|
|
}
|
|
}
|
|
return _texinfo;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #7: Faces (LDR)
|
|
private dface_t[] _faces;
|
|
/// <summary>
|
|
/// Lump #7. Version 1.
|
|
/// Purpose: datas for rendering.
|
|
/// Also: Check lump #58 for HDR faces.
|
|
/// </summary>
|
|
public dface_t[] Faces
|
|
{
|
|
get
|
|
{
|
|
if (_faces == null)
|
|
{
|
|
lump_t lump = SetLump(7);
|
|
int count = GetLumpCount(lump, 56);
|
|
_faces = new dface_t[count];
|
|
for (int f = 0; f < count; f++)
|
|
{
|
|
_faces[f] = new dface_t
|
|
{
|
|
planenum = BR.ReadUInt16(),
|
|
side = BR.ReadByte(),
|
|
onNode = BR.ReadByte(),
|
|
firstedge = BR.ReadInt32(),
|
|
numedges = BR.ReadInt16(),
|
|
texinfo = BR.ReadInt16(),
|
|
dispinfo = BR.ReadInt16(),
|
|
surfaceFogVolumeID = BR.ReadInt16(),
|
|
styles = BR.ReadBytes(4),
|
|
lightofs = BR.ReadInt32(),
|
|
area = BR.ReadSingle(),
|
|
m_LightmapTextureMinsInLuxels = BR.ReadVector2Int(),
|
|
m_LightmapTextureSizeInLuxels = BR.ReadVector2Int(),
|
|
origFace = BR.ReadInt32(),
|
|
m_NumPrims = BR.ReadUInt16(),
|
|
firstPrimID = BR.ReadUInt16(),
|
|
smoothingGroups = BR.ReadUInt32()
|
|
};
|
|
}
|
|
}
|
|
return _faces;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #8: Lighting (LDR)
|
|
private ColorRGBExp32[] _lighting;
|
|
/// <summary>
|
|
/// Lump #8. Version 1.
|
|
/// Purpose: lightmap colors / shadows.
|
|
/// Also: Check lump #53 for HDR lighting.
|
|
/// </summary>
|
|
public ColorRGBExp32[] Lighting
|
|
{
|
|
get
|
|
{
|
|
if (_lighting == null)
|
|
{
|
|
lump_t lump = SetLump(8);
|
|
int count = GetLumpCount(lump, 4);
|
|
_lighting = new ColorRGBExp32[count];
|
|
for (int f = 0; f < count; f++)
|
|
{
|
|
_lighting[f] = BR.ReadColorRGBExp32();
|
|
}
|
|
}
|
|
return _lighting;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #9: Occlusion
|
|
private doccluder_t _occlusion;
|
|
private bool __occlusion;
|
|
/// <summary>
|
|
/// Lump #9. Version 1/2.
|
|
/// Purpose: [not sure] func_occluder entity info for compilation.
|
|
/// Usage for data (depending on version): (doccluderdataV?_t[])bsp.Occlusion.data
|
|
/// </summary>
|
|
public doccluder_t Occlusion
|
|
{
|
|
get
|
|
{
|
|
if (!__occlusion)
|
|
{
|
|
lump_t lump = SetLump(9);
|
|
if(lump.filelen == 0)
|
|
{
|
|
__occlusion = true;
|
|
return _occlusion;
|
|
}
|
|
|
|
_occlusion = new doccluder_t();
|
|
_occlusion.count = BR.ReadInt32();
|
|
switch (lump.version)
|
|
{
|
|
case 1:
|
|
doccluderdataV1_t[] _o_v1 = new doccluderdataV1_t[_occlusion.count];
|
|
for (int o = 0; o < _occlusion.count; o++)
|
|
_o_v1[o] = new doccluderdataV1_t
|
|
{
|
|
flags = BR.ReadInt32(),
|
|
firstpoly = BR.ReadInt32(),
|
|
polycount = BR.ReadInt32(),
|
|
mins = BR.ReadVector(),
|
|
maxs = BR.ReadVector()
|
|
};
|
|
_occlusion.data = _o_v1;
|
|
break;
|
|
case 2:
|
|
doccluderdataV2_t[] _o_v2 = new doccluderdataV2_t[_occlusion.count];
|
|
for (int o = 0; o < _occlusion.count; o++)
|
|
_o_v2[o] = new doccluderdataV2_t
|
|
{
|
|
flags = BR.ReadInt32(),
|
|
firstpoly = BR.ReadInt32(),
|
|
polycount = BR.ReadInt32(),
|
|
mins = BR.ReadVector(),
|
|
maxs = BR.ReadVector(),
|
|
area = BR.ReadInt32()
|
|
};
|
|
_occlusion.data = _o_v2;
|
|
break;
|
|
default:
|
|
__occlusion = true;
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
_occlusion.polyDataCount = BR.ReadInt32();
|
|
_occlusion.polyData = new doccluderpolydata_t[_occlusion.polyDataCount];
|
|
for (int p = 0; p < _occlusion.polyDataCount; p++)
|
|
{
|
|
_occlusion.polyData[p] = new doccluderpolydata_t
|
|
{
|
|
firstvertexindex = BR.ReadInt32(),
|
|
vertexcount = BR.ReadInt32(),
|
|
planenum = BR.ReadInt32()
|
|
};
|
|
}
|
|
|
|
_occlusion.vertexIndexCount = BR.ReadInt32();
|
|
_occlusion.vertexIndices = BR.ReadInt32Array(_occlusion.vertexIndexCount);
|
|
|
|
__occlusion = true;
|
|
}
|
|
return _occlusion;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #10: Leafs
|
|
private object _leafs;
|
|
private bool __leafs;
|
|
/// <summary>
|
|
/// Lump #10. Version 0/1.
|
|
/// Purpose: [not sure] to determine player's position for visibility optimisation?
|
|
/// Usage (depending on version): (dleafV?_t[])bsp.Leafs
|
|
/// </summary>
|
|
public object Leafs
|
|
{
|
|
get
|
|
{
|
|
if (!__leafs)
|
|
{
|
|
lump_t lump = SetLump(10);
|
|
if (lump.filelen == 0)
|
|
{
|
|
__leafs = true;
|
|
return _leafs;
|
|
}
|
|
int count;
|
|
int contents;
|
|
short cluster;
|
|
uint packed;
|
|
|
|
switch (lump.version)
|
|
{
|
|
case 0:
|
|
count = GetLumpCount(lump, 56);
|
|
dleafV0_t[] _l_0 = new dleafV0_t[count];
|
|
for (int l = 0; l < count; l++)
|
|
{
|
|
contents = BR.ReadInt32();
|
|
cluster = BR.ReadInt16();
|
|
packed = BR.ReadUInt32(); //area:9 and flags:7
|
|
_l_0[l] = new dleafV0_t
|
|
{
|
|
contents = contents,
|
|
cluster = cluster,
|
|
area = (short)((ushort)(packed << 7) >> 7),
|
|
flags = (short)(packed >> 9),
|
|
mins = BR.ReadVectorShort(),
|
|
maxs = BR.ReadVectorShort(),
|
|
firstleafface = BR.ReadUInt16(),
|
|
numleaffaces = BR.ReadUInt16(),
|
|
firstleafbrush = BR.ReadUInt16(),
|
|
numleafbrushes = BR.ReadUInt16(),
|
|
leafWaterDataID = BR.ReadInt16(),
|
|
m_AmbientLighting = BR.ReadCompressedLightCube()
|
|
};
|
|
}
|
|
_leafs = _l_0;
|
|
break;
|
|
case 1:
|
|
count = GetLumpCount(lump, 32);
|
|
dleafV1_t[] _l_1 = new dleafV1_t[count];
|
|
for (int l = 0; l < count; l++)
|
|
{
|
|
contents = BR.ReadInt32();
|
|
cluster = BR.ReadInt16();
|
|
packed = BR.ReadUInt32(); //area:9 and flags:7
|
|
_l_1[l] = new dleafV1_t
|
|
{
|
|
contents = contents,
|
|
cluster = cluster,
|
|
area = (short)((ushort)(packed << 7) >> 7),
|
|
flags = (short)(packed >> 9),
|
|
mins = BR.ReadVectorShort(),
|
|
maxs = BR.ReadVectorShort(),
|
|
firstleafface = BR.ReadUInt16(),
|
|
numleaffaces = BR.ReadUInt16(),
|
|
firstleafbrush = BR.ReadUInt16(),
|
|
numleafbrushes = BR.ReadUInt16(),
|
|
leafWaterDataID = BR.ReadInt16()
|
|
};
|
|
}
|
|
_leafs = _l_1;
|
|
break;
|
|
default:
|
|
__leafs = true;
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
|
|
__leafs = true;
|
|
}
|
|
return _leafs;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #11: Face IDs
|
|
private dfaceid_t[] _faceids;
|
|
/// <summary>
|
|
/// Lump #11.
|
|
/// Purpose: Face IDs from Valve Hammer Editor. Should equal Faces lump count.
|
|
/// </summary>
|
|
public dfaceid_t[] FaceIDs
|
|
{
|
|
get
|
|
{
|
|
if (_faceids == null)
|
|
{
|
|
lump_t lump = SetLump(11);
|
|
int count = GetLumpCount(lump, 2);
|
|
_faceids = new dfaceid_t[count];
|
|
for (int f = 0; f < count; f++)
|
|
{
|
|
_faceids[f] = new dfaceid_t { hammerfaceid = BR.ReadUInt16() };
|
|
}
|
|
}
|
|
return _faceids;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #12: Edges
|
|
private dedge_t[] _edges;
|
|
/// <summary>
|
|
/// Lump #12.
|
|
/// Purpose: vertex indices for faces
|
|
/// </summary>
|
|
public dedge_t[] Edges
|
|
{
|
|
get
|
|
{
|
|
if (_edges == null)
|
|
{
|
|
lump_t lump = SetLump(12);
|
|
int count = GetLumpCount(lump, 4);
|
|
_edges = new dedge_t[count];
|
|
for (int e = 0; e < count;e++)
|
|
{
|
|
_edges[e] = new dedge_t { v = BR.ReadVector2Short() };
|
|
}
|
|
}
|
|
return _edges;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #13: Surface edges (SURFEDGE)
|
|
private int[] _surfedge;
|
|
/// <summary>
|
|
/// Lump #13.
|
|
/// Purpose: Faces.firstedge/numedges (e) ->| Surfedge[e] (s) ->| Edges[s].
|
|
/// </summary>
|
|
public int[] SurfEdge
|
|
{
|
|
get
|
|
{
|
|
if (_surfedge == null)
|
|
{
|
|
lump_t lump = SetLump(13);
|
|
int count = GetLumpCount(lump, 4);
|
|
_surfedge = BR.ReadInt32Array(count);
|
|
}
|
|
return _surfedge;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #14: Models
|
|
private dmodel_t[] _models;
|
|
/// <summary>
|
|
/// Lump #14.
|
|
/// Purpose: Brushes for rendering (mostly). [0] is global, [1 and more] are entities (func_brush/func_detail)
|
|
/// </summary>
|
|
public dmodel_t[] Models
|
|
{
|
|
get
|
|
{
|
|
if (_models == null)
|
|
{
|
|
lump_t lump = SetLump(14);
|
|
int count = GetLumpCount(lump, 48);
|
|
_models = new dmodel_t[count];
|
|
for (int m = 0; m < count; m++)
|
|
{
|
|
_models[m] = new dmodel_t
|
|
{
|
|
mins = BR.ReadVector(),
|
|
maxs = BR.ReadVector(),
|
|
origin = BR.ReadVector(),
|
|
headnode = BR.ReadInt32(),
|
|
firstface = BR.ReadInt32(),
|
|
numfaces = BR.ReadInt32()
|
|
};
|
|
}
|
|
}
|
|
return _models;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #15: World lights (LDR)
|
|
private object _worldlights;
|
|
/// <summary>
|
|
/// Lump #15. Version 0/1.
|
|
/// Purpose: Contains info about static lights (NOT LIGHTMAPS).
|
|
/// Usage (depending on version): (dworldlightV?_t[])bsp.WorldLights.
|
|
/// Also: Check lump #54 for HDR world lights.
|
|
/// </summary>
|
|
public object WorldLights
|
|
{
|
|
get
|
|
{
|
|
if (_worldlights == null)
|
|
{
|
|
lump_t lump = SetLump(15);
|
|
int count;
|
|
switch (lump.version)
|
|
{
|
|
case 0:
|
|
count = GetLumpCount(lump, 88);
|
|
dworldlightV0_t[] _wl_0 = new dworldlightV0_t[count];
|
|
for (int wl = 0; wl < count; wl++)
|
|
{
|
|
_wl_0[wl] = new dworldlightV0_t
|
|
{
|
|
origin = BR.ReadVector(),
|
|
intensity = BR.ReadVector(),
|
|
normal = BR.ReadVector(),
|
|
cluster = BR.ReadInt32(),
|
|
type = (emittype_t)BR.ReadUInt32(),
|
|
style = BR.ReadInt32(),
|
|
stopdot = BR.ReadSingle(),
|
|
stopdot2 = BR.ReadSingle(),
|
|
exponent = BR.ReadSingle(),
|
|
radius = BR.ReadSingle(),
|
|
constant_attn = BR.ReadSingle(),
|
|
linear_attn = BR.ReadSingle(),
|
|
quadratic_attn = BR.ReadSingle(),
|
|
flags = BR.ReadInt32(),
|
|
texinfo = BR.ReadInt32(),
|
|
owner = BR.ReadInt32()
|
|
};
|
|
}
|
|
_worldlights = _wl_0;
|
|
break;
|
|
case 1:
|
|
count = GetLumpCount(lump, 100);
|
|
dworldlightV1_t[] _wl_1 = new dworldlightV1_t[count];
|
|
for (int wl = 0; wl < count; wl++)
|
|
{
|
|
_wl_1[wl] = new dworldlightV1_t
|
|
{
|
|
origin = BR.ReadVector(),
|
|
intensity = BR.ReadVector(),
|
|
normal = BR.ReadVector(),
|
|
shadow_cast_offset = BR.ReadVector(),
|
|
cluster = BR.ReadInt32(),
|
|
type = (emittype_t)BR.ReadUInt32(),
|
|
style = BR.ReadInt32(),
|
|
stopdot = BR.ReadSingle(),
|
|
stopdot2 = BR.ReadSingle(),
|
|
exponent = BR.ReadSingle(),
|
|
radius = BR.ReadSingle(),
|
|
constant_attn = BR.ReadSingle(),
|
|
linear_attn = BR.ReadSingle(),
|
|
quadratic_attn = BR.ReadSingle(),
|
|
flags = BR.ReadInt32(),
|
|
texinfo = BR.ReadInt32(),
|
|
owner = BR.ReadInt32()
|
|
};
|
|
}
|
|
_worldlights = _wl_1;
|
|
break;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
}
|
|
return _worldlights;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #16: Leaf faces
|
|
private ushort[] _leaffaces;
|
|
/// <summary>
|
|
/// Lump #16.
|
|
/// Purpose: "... which are used to map from faces referenced in the leaf structure to indices in the face array", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public ushort[] LeafFaces
|
|
{
|
|
get
|
|
{
|
|
if (_leaffaces == null)
|
|
{
|
|
lump_t lump = SetLump(16);
|
|
int count = GetLumpCount(lump, 2);
|
|
_leaffaces = BR.ReadUInt16Array(count);
|
|
}
|
|
return _leaffaces;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #17: Leaf brushes
|
|
private ushort[] _leafbrushes;
|
|
/// <summary>
|
|
/// Lump #17.
|
|
/// Purpose: "does the same thing [as lump 16 - leaf faces] for brushes referenced in leaves", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public ushort[] LeafBrushes
|
|
{
|
|
get
|
|
{
|
|
if (_leafbrushes == null)
|
|
{
|
|
lump_t lump = SetLump(17);
|
|
int count = GetLumpCount(lump, 2);
|
|
_leafbrushes = BR.ReadUInt16Array(count);
|
|
}
|
|
return _leafbrushes;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #18: Brushes
|
|
private dbrush_t[] _brushes;
|
|
/// <summary>
|
|
/// Lump #18.
|
|
/// Purpose: Brush info.
|
|
/// </summary>
|
|
public dbrush_t[] Brushes
|
|
{
|
|
get
|
|
{
|
|
if (_brushes == null)
|
|
{
|
|
lump_t lump = SetLump(18);
|
|
int count = GetLumpCount(lump, 12);
|
|
_brushes = new dbrush_t[count];
|
|
for (int b = 0; b < count; b++)
|
|
{
|
|
_brushes[b] = new dbrush_t
|
|
{
|
|
firstside = BR.ReadInt32(),
|
|
numsides = BR.ReadInt32(),
|
|
contents = BR.ReadInt32()
|
|
};
|
|
}
|
|
}
|
|
return _brushes;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #19: Brush Sides
|
|
private dbrushside_t[] _brushsides;
|
|
/// <summary>
|
|
/// Lump #19.
|
|
/// Purpose: Brush side info.
|
|
/// </summary>
|
|
public dbrushside_t[] BrushSides
|
|
{
|
|
get
|
|
{
|
|
if (_brushsides == null)
|
|
{
|
|
lump_t lump = SetLump(19);
|
|
int count = GetLumpCount(lump, 8);
|
|
_brushsides = new dbrushside_t[count];
|
|
for (int bs = 0; bs < count; bs++)
|
|
{
|
|
_brushsides[bs] = new dbrushside_t
|
|
{
|
|
planenum = BR.ReadUInt16(),
|
|
texinfo = BR.ReadInt16(),
|
|
dispinfo = BR.ReadInt16(),
|
|
bevel = BR.ReadInt16()
|
|
};
|
|
}
|
|
}
|
|
return _brushsides;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #20: Areas
|
|
private darea_t[] _areas;
|
|
/// <summary>
|
|
/// Lump #20.
|
|
/// Purpose: Areas that are used for switching of areaportals; visibility things.
|
|
/// </summary>
|
|
public darea_t[] Areas
|
|
{
|
|
get
|
|
{
|
|
if (_areas == null)
|
|
{
|
|
lump_t lump = SetLump(20);
|
|
int count = GetLumpCount(lump, 8);
|
|
_areas = new darea_t[count];
|
|
for (int a = 0; a < count; a++)
|
|
{
|
|
_areas[a] = new darea_t
|
|
{
|
|
numareaportals = BR.ReadInt32(),
|
|
firstareaportal = BR.ReadInt32()
|
|
};
|
|
}
|
|
}
|
|
return _areas;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #21: Areaportals
|
|
private dareaportal_t[] _areaportals;
|
|
/// <summary>
|
|
/// Lump #21.
|
|
/// Purpose: "used with func_areaportal and func_areaportalwindow entities to define sections of the map that can be switched to render or not render", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public dareaportal_t[] Areaportals
|
|
{
|
|
get
|
|
{
|
|
if (_areaportals == null)
|
|
{
|
|
lump_t lump = SetLump(21);
|
|
int count = GetLumpCount(lump, 12);
|
|
_areaportals = new dareaportal_t[count];
|
|
for (int ap = 0; ap < count; ap++)
|
|
{
|
|
_areaportals[ap] = new dareaportal_t
|
|
{
|
|
m_PortalKey = BR.ReadUInt16(),
|
|
otherarea = BR.ReadUInt16(),
|
|
m_FirstClipPortalVert = BR.ReadUInt16(),
|
|
m_nClipPortalVerts = BR.ReadUInt16(),
|
|
planenum = BR.ReadInt32()
|
|
};
|
|
}
|
|
}
|
|
return _areaportals;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #22 : 2004/2009
|
|
#region Lump #22/1: Portals [Source 2004]
|
|
private dportal_t[] _portals;
|
|
/// <summary>
|
|
/// Lump #22.
|
|
/// Purpose: [not sure] Used by VVIS and VRAD only.
|
|
/// </summary>
|
|
public dportal_t[] Portals
|
|
{
|
|
get
|
|
{
|
|
if (_portals == null)
|
|
{
|
|
lump_t lump = SetLump(22);
|
|
if(lump.filelen % 16 != 0) //This lump is not for portals
|
|
return null;
|
|
int count = GetLumpCount(lump, 16);
|
|
_portals = new dportal_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_portals[p] = new dportal_t
|
|
{
|
|
firstportalvert = BR.ReadInt32(),
|
|
numportalverts = BR.ReadInt32(),
|
|
planenum = BR.ReadInt32(),
|
|
cluster = BR.ReadUInt16Array(2)
|
|
};
|
|
}
|
|
}
|
|
return _portals;
|
|
}
|
|
}
|
|
#endregion
|
|
#region Lump #22/2: Prop collisions (PROPCOLLISION) [Source 2009]
|
|
private dpropcollision_t[] _propcollision;
|
|
/// <summary>
|
|
/// Lump #22.
|
|
/// Purpose: "Static props convex hull lists", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public dpropcollision_t[] PropCollision
|
|
{
|
|
get
|
|
{
|
|
if (_propcollision == null)
|
|
{
|
|
lump_t lump = SetLump(22);
|
|
if (lump.filelen % 8 != 0) //This lump is not for prop collisions
|
|
return null;
|
|
|
|
int count = GetLumpCount(lump, 8);
|
|
_propcollision = new dpropcollision_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_propcollision[p] = new dpropcollision_t
|
|
{
|
|
m_nHullCount = BR.ReadInt32(),
|
|
m_nHullStart = BR.ReadInt32()
|
|
};
|
|
}
|
|
}
|
|
return _propcollision;
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Lump #23 : 2004/2009
|
|
#region Lump #23/1: Clusters [Source 2004]
|
|
private dcluster_t[] _clusters;
|
|
/// <summary>
|
|
/// Lump #23.
|
|
/// Purpose: [not sure] Used by VVIS and VRAD only.
|
|
/// </summary>
|
|
public dcluster_t[] Clusters
|
|
{
|
|
get
|
|
{
|
|
if (_clusters == null)
|
|
{
|
|
lump_t lump = SetLump(23);
|
|
if (lump.filelen % 8 != 0) //This lump is not for clusters
|
|
return null;
|
|
|
|
int count = GetLumpCount(lump, 8);
|
|
_clusters = new dcluster_t[count];
|
|
for (int c = 0; c < count; c++)
|
|
{
|
|
_clusters[c] = new dcluster_t
|
|
{
|
|
firstportal = BR.ReadInt32(),
|
|
numportals = BR.ReadInt32()
|
|
};
|
|
}
|
|
}
|
|
return _clusters;
|
|
}
|
|
}
|
|
#endregion
|
|
#region Lump #23/2: Prop hulls (PROPHULLS) [Source 2009]
|
|
private dprophull_t[] _prophulls;
|
|
/// <summary>
|
|
/// Lump #23.
|
|
/// Purpose: "Static prop convex hulls", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public dprophull_t[] PropHulls
|
|
{
|
|
get
|
|
{
|
|
if (_prophulls == null)
|
|
{
|
|
lump_t lump = SetLump(23);
|
|
if (lump.filelen % 16 != 0) //This lump is not for prophulls
|
|
return null;
|
|
|
|
int count = GetLumpCount(lump, 16);
|
|
_prophulls = new dprophull_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_prophulls[p] = new dprophull_t
|
|
{
|
|
m_nVertCount = BR.ReadInt32(),
|
|
m_nVertStart = BR.ReadInt32(),
|
|
m_nSurfaceProp = BR.ReadInt32(),
|
|
m_nContents = BR.ReadUInt32()
|
|
};
|
|
}
|
|
}
|
|
return _prophulls;
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Lump #24 : 2004/2009 - not tested: can't find map with this lump
|
|
#region Lump #24/1: Portal Vertices (PORTALVERTS) [Source 2004]
|
|
private ushort[] _portalverts;
|
|
/// <summary>
|
|
/// Lump #24.
|
|
/// Purpose: [not sure] Used by VVIS and VRAD only.
|
|
/// </summary>
|
|
public ushort[] PortalVerts
|
|
{
|
|
get
|
|
{
|
|
if (_portalverts == null)
|
|
{
|
|
lump_t lump = SetLump(24);
|
|
int count = GetLumpCount(lump, 2);
|
|
_portalverts = BR.ReadUInt16Array(count);
|
|
}
|
|
return _portalverts;
|
|
}
|
|
}
|
|
#endregion
|
|
#region Lump #24/2: Prop hull vertices (PROPHULLVERTS) [Source 2009]
|
|
private SourceVector[] _prophullverts;
|
|
/// <summary>
|
|
/// Lump #24.
|
|
/// Purpose: "Static prop collision vertices", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public SourceVector[] PropHullVerts
|
|
{
|
|
get
|
|
{
|
|
if (_prophullverts == null)
|
|
{
|
|
lump_t lump = SetLump(24);
|
|
int count = GetLumpCount(lump, 12);
|
|
_prophullverts = new SourceVector[count];
|
|
for (int v = 0; v < count; v++)
|
|
{
|
|
_prophullverts[v] = BR.ReadVector();
|
|
}
|
|
}
|
|
return _prophullverts;
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Lump #25 : 2004/2009 - not tested: can't find map with this lump
|
|
#region Lump #25/1: Cluster Portals [Source 2004]
|
|
private ushort[] _clusterportals;
|
|
/// <summary>
|
|
/// Lump #25.
|
|
/// Purpose: [not sure] Used by VVIS and VRAD only.
|
|
/// </summary>
|
|
public ushort[] ClusterPortals
|
|
{
|
|
get
|
|
{
|
|
if (_clusterportals == null)
|
|
{
|
|
lump_t lump = SetLump(25);
|
|
int count = GetLumpCount(lump, 2);
|
|
_clusterportals = BR.ReadUInt16Array(count);
|
|
}
|
|
return _clusterportals;
|
|
}
|
|
}
|
|
#endregion
|
|
#region Lump #25/2: Prop Triangles (PROPTRIS) [Source 2009]
|
|
private dprophulltris_t[] _proptris;
|
|
/// <summary>
|
|
/// Lump #25.
|
|
/// Purpose: "Static prop per hull triangle index start/count", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public dprophulltris_t[] PropTris
|
|
{
|
|
get
|
|
{
|
|
if (_proptris == null)
|
|
{
|
|
lump_t lump = SetLump(25);
|
|
int count = GetLumpCount(lump, 8);
|
|
_proptris = new dprophulltris_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_proptris[p] = new dprophulltris_t
|
|
{
|
|
m_nIndexStart = BR.ReadInt32(),
|
|
m_nIndexCount = BR.ReadInt32()
|
|
};
|
|
}
|
|
}
|
|
return _proptris;
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Lump #26: Displacement infos (DISPINFO) - SKIPPED: m_EdgeNeighbors/m_CornerNeighbors/m_AllowedVerts/unknown
|
|
private ddispinfo_t[] _dispinfo;
|
|
/// <summary>
|
|
/// Lump #26.
|
|
/// Purpose: [not sure] Displacement infos that is used by map compilators?
|
|
/// </summary>
|
|
public ddispinfo_t[] DispInfo
|
|
{
|
|
get
|
|
{
|
|
if (_dispinfo == null)
|
|
{
|
|
lump_t lump = SetLump(26);
|
|
int count = GetLumpCount(lump, 176);
|
|
_dispinfo = new ddispinfo_t[count];
|
|
for (int d = 0; d < count; d++)
|
|
{
|
|
_dispinfo[d] = new ddispinfo_t
|
|
{
|
|
startPosition=BR.ReadVector(),
|
|
m_iDispVertStart=BR.ReadInt32(),
|
|
m_iDispTriStart=BR.ReadInt32(),
|
|
power=BR.ReadInt32(),
|
|
minTess=BR.ReadInt32(),
|
|
smoothingAngle=BR.ReadSingle(),
|
|
contents=BR.ReadInt32(),
|
|
m_iMapFace=BR.ReadUInt16(),
|
|
m_iLightmapAlphaStart=BR.ReadInt32(),
|
|
m_iLightmapSamplePositionStart=BR.ReadInt32()
|
|
};
|
|
|
|
BR.BaseStream.Seek(130, SeekOrigin.Current);
|
|
}
|
|
}
|
|
return _dispinfo;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #27: Original faces
|
|
private dface_t[] _originalfaces;
|
|
/// <summary>
|
|
/// Lump #27. Version 1.
|
|
/// Purpose: "Brush faces array before splitting", VDW. Probably, used by map compilators.
|
|
/// </summary>
|
|
public dface_t[] OriginalFaces
|
|
{
|
|
get
|
|
{
|
|
if (_originalfaces == null)
|
|
{
|
|
lump_t lump = SetLump(27);
|
|
int count = GetLumpCount(lump, 56);
|
|
_originalfaces = new dface_t[count];
|
|
for (int f = 0; f < count; f++)
|
|
{
|
|
_originalfaces[f] = new dface_t
|
|
{
|
|
planenum = BR.ReadUInt16(),
|
|
side = BR.ReadByte(),
|
|
onNode = BR.ReadByte(),
|
|
firstedge = BR.ReadInt32(),
|
|
numedges = BR.ReadInt16(),
|
|
texinfo = BR.ReadInt16(),
|
|
dispinfo = BR.ReadInt16(),
|
|
surfaceFogVolumeID = BR.ReadInt16(),
|
|
styles = BR.ReadBytes(4),
|
|
lightofs = BR.ReadInt32(),
|
|
area = BR.ReadSingle(),
|
|
m_LightmapTextureMinsInLuxels = BR.ReadVector2Int(),
|
|
m_LightmapTextureSizeInLuxels = BR.ReadVector2Int(),
|
|
origFace = BR.ReadInt32(),
|
|
m_NumPrims = BR.ReadUInt16(),
|
|
firstPrimID = BR.ReadUInt16(),
|
|
smoothingGroups = BR.ReadUInt32()
|
|
};
|
|
}
|
|
}
|
|
return _originalfaces;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #28: Displacement Physics (PHYSDISP) - not full: SourcePHY parser needed (i guess)
|
|
private dphysdisp_t[] _physdisp;
|
|
/// <summary>
|
|
/// Lump #28.
|
|
/// Purpose: Displacement physics collision data.
|
|
/// </summary>
|
|
public dphysdisp_t[] PhysDisp
|
|
{
|
|
get
|
|
{
|
|
if (_physdisp == null)
|
|
{
|
|
lump_t lump = SetLump(28);
|
|
//int count = GetLumpCount(lump, 2);
|
|
List<dphysdisp_t> _physdisp_list = new List<dphysdisp_t>();
|
|
while(BR.BaseStream.Position < lump.fileofs + lump.filelen - 1)
|
|
{
|
|
ushort numDisplacements = BR.ReadUInt16();
|
|
ushort[] dataSize = BR.ReadUInt16Array(numDisplacements);
|
|
if (dataSize.Length >= 1 && dataSize[0] == 0)
|
|
break;
|
|
|
|
long totalsize = 0;
|
|
foreach (ushort s in dataSize) totalsize += s;
|
|
|
|
_physdisp_list.Add(new dphysdisp_t
|
|
{
|
|
numDisplacements = numDisplacements,
|
|
dataSize = dataSize
|
|
});
|
|
}
|
|
|
|
//if (lump.filelen > 0 && lump.filelen - (BR.BaseStream.Position - lump.fileofs) > 0)
|
|
// DebugLog.Write($"readen={BR.BaseStream.Position - lump.fileofs}; len={lump.filelen}; left={lump.filelen - (BR.BaseStream.Position - lump.fileofs)}");
|
|
|
|
// == There i should parse these displacements, but i dont know how (idk what is that format, mb PHY?).
|
|
// === g_pPhysDisp, g_PhysDispSize
|
|
|
|
_physdisp = _physdisp_list.ToArray();
|
|
}
|
|
return _physdisp;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #29: Physics collision infos (PHYSCOLLIDE) - not full: SourcePHY parser needed
|
|
private dphysmodel_t[] _physcollide;
|
|
/// <summary>
|
|
/// Lump #29.
|
|
/// Purpose: Physics collision data. Used to calculate VPhysics (for prop_physics, i guess).
|
|
/// </summary>
|
|
public dphysmodel_t[] PhysCollide
|
|
{
|
|
get
|
|
{
|
|
if (_physcollide == null)
|
|
{
|
|
lump_t lump = SetLump(29);
|
|
//int count = GetLumpCount(lump, 2);
|
|
List<dphysmodel_t> _physcollide_list = new List<dphysmodel_t>();
|
|
while (BR.BaseStream.Position < lump.fileofs + lump.filelen - 1)
|
|
{
|
|
var physmodel = new dphysmodel_t
|
|
{
|
|
modelIndex = BR.ReadInt32(),
|
|
dataSize = BR.ReadInt32(),
|
|
keydataSize = BR.ReadInt32(),
|
|
solidCount = BR.ReadInt32()
|
|
};
|
|
|
|
if (physmodel.modelIndex == -1)
|
|
break;
|
|
|
|
int size;
|
|
for (int k = 0; k < physmodel.solidCount; k++)
|
|
{
|
|
size = BR.ReadInt32();
|
|
byte[] collisionData = BR.ReadBytes(size);
|
|
//sourcephy solid = sourcephy.parsebody?(collisiondata);
|
|
// = or
|
|
//sourcephy solid = sourcephy.parsebody?(BR); // - to read bytes dynamically?
|
|
}
|
|
|
|
physmodel.keydata = Encoding.ASCII.GetString(BR.ReadBytes(physmodel.keydataSize - 1)); //-1 because last is always \0
|
|
BR.BaseStream.Seek(1, SeekOrigin.Current); //skip 1 byte because of prev line
|
|
|
|
_physcollide_list.Add(physmodel);
|
|
}
|
|
|
|
// == "The last two parts appear to be identical to the PHY file format, which means their exact contents are unknown."
|
|
// === - from Valve Dev Wiki.
|
|
// === Anchored link: https://developer.valvesoftware.com/wiki/Source_BSP_File_Format#Physics
|
|
|
|
_physcollide = _physcollide_list.ToArray();
|
|
}
|
|
return _physcollide;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #30: Vertex Normals (VERTNORMALS)
|
|
private SourceVector[] _vertnormals;
|
|
/// <summary>
|
|
/// Lump #30.
|
|
/// Purpose: Face plane normals. "may be related to smoothing of lightmaps on faces", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public SourceVector[] VertNormals
|
|
{
|
|
get
|
|
{
|
|
if (_vertnormals == null)
|
|
{
|
|
lump_t lump = SetLump(30);
|
|
int count = GetLumpCount(lump, 12);
|
|
|
|
_vertnormals = new SourceVector[count];
|
|
for (int i = 0; i < count; i++)
|
|
_vertnormals[i] = BR.ReadVector();
|
|
}
|
|
return _vertnormals;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #31: Vertex Normal Indices (VERTNORMALINDICES)
|
|
private ushort[] _vertnormalindices;
|
|
/// <summary>
|
|
/// Lump #31.
|
|
/// Purpose: Face plane normal index array. "may be related to smoothing of lightmaps on faces", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public ushort[] VertNormalIndices
|
|
{
|
|
get
|
|
{
|
|
if (_vertnormalindices == null)
|
|
{
|
|
lump_t lump = SetLump(31);
|
|
int count = GetLumpCount(lump, 2);
|
|
_vertnormalindices = BR.ReadUInt16Array(count);
|
|
}
|
|
return _vertnormalindices;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #32: Lightmap alphas for displacements (DISP_LIGHTMAP_ALPHAS)
|
|
private byte[] _disp_lightmap_alphas;
|
|
/// <summary>
|
|
/// Lump #32.
|
|
/// Purpose: "Displacement lightmap alphas (unused/empty since Source 2006)", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public byte[] DispLightmapAlphas
|
|
{
|
|
get
|
|
{
|
|
if (_disp_lightmap_alphas == null)
|
|
{
|
|
lump_t lump = SetLump(32);
|
|
_disp_lightmap_alphas = BR.ReadBytes(lump.filelen);
|
|
}
|
|
return _disp_lightmap_alphas;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #33: Displacement vertices (DISP_VERTS)
|
|
private CDispVert[] _dispverts;
|
|
/// <summary>
|
|
/// Lump #33.
|
|
/// Purpose: Info about vertices for displacements.
|
|
/// </summary>
|
|
public CDispVert[] DispVerts
|
|
{
|
|
get
|
|
{
|
|
if (_dispverts == null)
|
|
{
|
|
lump_t lump = SetLump(33);
|
|
int count = GetLumpCount(lump, 20);
|
|
_dispverts = new CDispVert[count];
|
|
for (int v = 0; v < count; v++)
|
|
{
|
|
_dispverts[v] = new CDispVert
|
|
{
|
|
m_vVector = BR.ReadVector(),
|
|
m_flDist = BR.ReadSingle(),
|
|
m_flAlpha = BR.ReadSingle()
|
|
};
|
|
}
|
|
}
|
|
return _dispverts;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #34: Displacement Lightmap Sample Positions (DISP_LIGHTMAP_SAMPLE_POSITIONS)
|
|
private byte[] _displmsamplepos;
|
|
/// <summary>
|
|
/// Lump #34.
|
|
/// Purpose: supposed to be ColorRGBExp32, but its byte array in source code.
|
|
/// i dunno that's literally boxes on white background (as i can remember). it's useless because lightmaps for disps are in LUMP_LIGHTING.
|
|
/// </summary>
|
|
public byte[] DispLightmapSamplePositions
|
|
{
|
|
get
|
|
{
|
|
if (_displmsamplepos == null)
|
|
{
|
|
lump_t lump = SetLump(34);
|
|
_displmsamplepos = BR.ReadBytes(lump.filelen);
|
|
}
|
|
return _displmsamplepos;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #35: Game lump
|
|
private dgamelumpheader_t _gamelump;
|
|
/// <summary>
|
|
/// Lump #35.
|
|
/// Purpose: Game-specific data lump. Contains info about static props and detail prop.
|
|
/// </summary>
|
|
public dgamelumpheader_t GameLump
|
|
{
|
|
get
|
|
{
|
|
if (_gamelump.lumps == null)
|
|
{
|
|
lump_t lump = SetLump(35);
|
|
|
|
if (lump.filelen == 0)
|
|
{
|
|
_gamelump.lumps = new dgamelump_t[0];
|
|
return _gamelump;
|
|
}
|
|
|
|
// Read dictionary...
|
|
_gamelump = new dgamelumpheader_t { lumpCount = BR.ReadInt32() };
|
|
_gamelump.lumps = new dgamelump_t[_gamelump.lumpCount];
|
|
for (int l = 0; l < _gamelump.lumpCount; l++)
|
|
{
|
|
_gamelump.lumps[l] = new dgamelump_t
|
|
{
|
|
id = BR.ReadInt32(),
|
|
flags = BR.ReadUInt16(),
|
|
version = BR.ReadUInt16(),
|
|
fileofs = BR.ReadInt32(),
|
|
filelen = BR.ReadInt32()
|
|
};
|
|
}
|
|
for (int l = 0; l < _gamelump.lumpCount; ++l)
|
|
{
|
|
dgamelump_t gamelump = _gamelump.lumps[l];
|
|
long startofs = BR.BaseStream.Position;
|
|
long endofs = startofs + gamelump.filelen;
|
|
int count = 0;
|
|
switch (gamelump.id_code)
|
|
{
|
|
case GAMELUMP_CODES.GAMELUMP_STATIC_PROPS:
|
|
//read names
|
|
count = BR.ReadInt32();
|
|
_gamelump.staticPropNames = new StaticPropDictLump_t[count];
|
|
//Elapsed {00:00:00.0005491} {00:00:00.0005547}
|
|
for (int n = 0; n < count; n++)
|
|
{
|
|
string name = string.Empty;
|
|
|
|
for (int b = 0; b < 128; b++)
|
|
{
|
|
byte _c = BR.ReadByte();
|
|
if (_c == (byte)'\0')
|
|
{
|
|
BR.BaseStream.Seek(128 - b - 1, SeekOrigin.Current);
|
|
break;
|
|
}
|
|
name += Encoding.ASCII.GetString(new byte[1] { _c });
|
|
}
|
|
|
|
_gamelump.staticPropNames[n].m_Name = name;
|
|
}
|
|
|
|
//read leafs
|
|
count = BR.ReadInt32();
|
|
_gamelump.staticPropLeafs = new StaticPropLeafLump_t[count];
|
|
for (int lf = 0; lf < count; lf++)
|
|
_gamelump.staticPropLeafs[lf].m_Leaf = BR.ReadUInt16();
|
|
|
|
|
|
//read the models
|
|
count = BR.ReadInt32();
|
|
switch (gamelump.version)
|
|
{
|
|
case 4:
|
|
StaticPropLumpV4_t[] sp_v4 = new StaticPropLumpV4_t[count];
|
|
for (int sp = 0; sp < count; sp++)
|
|
{
|
|
sp_v4[sp] = new StaticPropLumpV4_t
|
|
{
|
|
m_Origin = BR.ReadVector(),
|
|
m_Angles = BR.ReadQAngle(),
|
|
m_PropType = BR.ReadUInt16(),
|
|
m_FirstLeaf = BR.ReadUInt16(),
|
|
m_LeafCount = BR.ReadUInt16(),
|
|
m_Solid = BR.ReadByte(),
|
|
m_Flags = BR.ReadByte(),
|
|
m_Skin = BR.ReadInt32(),
|
|
m_FadeMinDist = BR.ReadSingle(),
|
|
m_FadeMaxDist = BR.ReadSingle(),
|
|
m_LightingOrigin = BR.ReadVector()
|
|
};
|
|
}
|
|
_gamelump.staticProps = sp_v4;
|
|
break;
|
|
case 5:
|
|
StaticPropLumpV5_t[] sp_v5 = new StaticPropLumpV5_t[count];
|
|
for (int sp = 0; sp < count; sp++)
|
|
{
|
|
sp_v5[sp] = new StaticPropLumpV5_t
|
|
{
|
|
m_Origin = BR.ReadVector(),
|
|
m_Angles = BR.ReadQAngle(),
|
|
m_PropType = BR.ReadUInt16(),
|
|
m_FirstLeaf = BR.ReadUInt16(),
|
|
m_LeafCount = BR.ReadUInt16(),
|
|
m_Solid = BR.ReadByte(),
|
|
m_Flags = BR.ReadByte(),
|
|
m_Skin = BR.ReadInt32(),
|
|
m_FadeMinDist = BR.ReadSingle(),
|
|
m_FadeMaxDist = BR.ReadSingle(),
|
|
m_LightingOrigin = BR.ReadVector(),
|
|
m_flForcedFadeScale = BR.ReadSingle()
|
|
};
|
|
}
|
|
_gamelump.staticProps = sp_v5;
|
|
break;
|
|
case 6:
|
|
StaticPropLumpV6_t[] sp_v6 = new StaticPropLumpV6_t[count];
|
|
for (int sp = 0; sp < count; sp++)
|
|
{
|
|
sp_v6[sp] = new StaticPropLumpV6_t
|
|
{
|
|
m_Origin = BR.ReadVector(),
|
|
m_Angles = BR.ReadQAngle(),
|
|
m_PropType = BR.ReadUInt16(),
|
|
m_FirstLeaf = BR.ReadUInt16(),
|
|
m_LeafCount = BR.ReadUInt16(),
|
|
m_Solid = BR.ReadByte(),
|
|
m_Flags = BR.ReadByte(),
|
|
m_Skin = BR.ReadInt32(),
|
|
m_FadeMinDist = BR.ReadSingle(),
|
|
m_FadeMaxDist = BR.ReadSingle(),
|
|
m_LightingOrigin = BR.ReadVector(),
|
|
m_flForcedFadeScale = BR.ReadSingle(),
|
|
m_nMinDXLevel = BR.ReadUInt16(),
|
|
m_nMaxDXLevel = BR.ReadUInt16()
|
|
};
|
|
}
|
|
_gamelump.staticProps = sp_v6;
|
|
break;
|
|
case 7: //not tested
|
|
StaticPropLumpV7_t[] sp_v7 = new StaticPropLumpV7_t[count];
|
|
for (int sp = 0; sp < count; sp++)
|
|
{
|
|
sp_v7[sp] = new StaticPropLumpV7_t
|
|
{
|
|
m_Origin = BR.ReadVector(),
|
|
m_Angles = BR.ReadQAngle(),
|
|
m_PropType = BR.ReadUInt16(),
|
|
m_FirstLeaf = BR.ReadUInt16(),
|
|
m_LeafCount = BR.ReadUInt16(),
|
|
m_Solid = BR.ReadByte(),
|
|
m_Flags = BR.ReadByte(),
|
|
m_Skin = BR.ReadInt32(),
|
|
m_FadeMinDist = BR.ReadSingle(),
|
|
m_FadeMaxDist = BR.ReadSingle(),
|
|
m_LightingOrigin = BR.ReadVector(),
|
|
m_flForcedFadeScale = BR.ReadSingle(),
|
|
m_nMinDXLevel = BR.ReadUInt16(),
|
|
m_nMaxDXLevel = BR.ReadUInt16(),
|
|
m_DiffuseModulation = BR.ReadColor32()
|
|
};
|
|
}
|
|
_gamelump.staticProps = sp_v7;
|
|
break;
|
|
case 8:
|
|
StaticPropLumpV8_t[] sp_v8 = new StaticPropLumpV8_t[count];
|
|
for (int sp = 0; sp < count; sp++)
|
|
{
|
|
sp_v8[sp] = new StaticPropLumpV8_t
|
|
{
|
|
m_Origin = BR.ReadVector(),
|
|
m_Angles = BR.ReadQAngle(),
|
|
m_PropType = BR.ReadUInt16(),
|
|
m_FirstLeaf = BR.ReadUInt16(),
|
|
m_LeafCount = BR.ReadUInt16(),
|
|
m_Solid = BR.ReadByte(),
|
|
m_Flags = BR.ReadByte(),
|
|
m_Skin = BR.ReadInt32(),
|
|
m_FadeMinDist = BR.ReadSingle(),
|
|
m_FadeMaxDist = BR.ReadSingle(),
|
|
m_LightingOrigin = BR.ReadVector(),
|
|
m_flForcedFadeScale = BR.ReadSingle(),
|
|
m_nMinCPULevel = BR.ReadByte(),
|
|
m_nMaxCPULevel = BR.ReadByte(),
|
|
m_nMinGPULevel = BR.ReadByte(),
|
|
m_nMaxGPULevel = BR.ReadByte(),
|
|
m_DiffuseModulation = BR.ReadColor32()
|
|
};
|
|
}
|
|
_gamelump.staticProps = sp_v8;
|
|
break;
|
|
case 9:
|
|
StaticPropLumpV9_t[] sp_v9 = new StaticPropLumpV9_t[count];
|
|
for (int sp = 0; sp < count; sp++)
|
|
{
|
|
sp_v9[sp] = new StaticPropLumpV9_t
|
|
{
|
|
m_Origin = BR.ReadVector(),
|
|
m_Angles = BR.ReadQAngle(),
|
|
m_PropType = BR.ReadUInt16(),
|
|
m_FirstLeaf = BR.ReadUInt16(),
|
|
m_LeafCount = BR.ReadUInt16(),
|
|
m_Solid = BR.ReadByte(),
|
|
m_Flags = BR.ReadByte(),
|
|
m_Skin = BR.ReadInt32(),
|
|
m_FadeMinDist = BR.ReadSingle(),
|
|
m_FadeMaxDist = BR.ReadSingle(),
|
|
m_LightingOrigin = BR.ReadVector(),
|
|
m_flForcedFadeScale = BR.ReadSingle(),
|
|
m_nMinCPULevel = BR.ReadByte(),
|
|
m_nMaxCPULevel = BR.ReadByte(),
|
|
m_nMinGPULevel = BR.ReadByte(),
|
|
m_nMaxGPULevel = BR.ReadByte(),
|
|
m_DiffuseModulation = BR.ReadColor32(),
|
|
m_bDisableX360 = BR.ReadInt32()
|
|
};
|
|
}
|
|
_gamelump.staticProps = sp_v9;
|
|
break;
|
|
case 10:
|
|
StaticPropLumpV10_t[] sp_v10 = new StaticPropLumpV10_t[count];
|
|
for (int sp = 0; sp < count; sp++)
|
|
{
|
|
sp_v10[sp] = new StaticPropLumpV10_t
|
|
{
|
|
m_Origin = BR.ReadVector(),
|
|
m_Angles = BR.ReadQAngle(),
|
|
m_PropType = BR.ReadUInt16(),
|
|
m_FirstLeaf = BR.ReadUInt16(),
|
|
m_LeafCount = BR.ReadUInt16(),
|
|
m_Solid = BR.ReadByte(),
|
|
m_Flags = BR.ReadByte(),
|
|
m_Skin = BR.ReadInt32(),
|
|
m_FadeMinDist = BR.ReadSingle(),
|
|
m_FadeMaxDist = BR.ReadSingle(),
|
|
m_LightingOrigin = BR.ReadVector(),
|
|
m_flForcedFadeScale = BR.ReadSingle(),
|
|
m_nMinCPULevel = BR.ReadByte(),
|
|
m_nMaxCPULevel = BR.ReadByte(),
|
|
m_nMinGPULevel = BR.ReadByte(),
|
|
m_nMaxGPULevel = BR.ReadByte(),
|
|
m_DiffuseModulation = BR.ReadColor32(),
|
|
m_bDisableX360 = BR.ReadInt32(),
|
|
m_FlagsEx = BR.ReadUInt32()
|
|
};
|
|
}
|
|
_gamelump.staticProps = sp_v10;
|
|
break;
|
|
case 11:
|
|
StaticPropLumpV11_t[] sp_v11 = new StaticPropLumpV11_t[count];
|
|
for (int sp = 0; sp < count; sp++)
|
|
{
|
|
sp_v11[sp] = new StaticPropLumpV11_t
|
|
{
|
|
m_Origin = BR.ReadVector(),
|
|
m_Angles = BR.ReadQAngle(),
|
|
m_PropType = BR.ReadUInt16(),
|
|
m_FirstLeaf = BR.ReadUInt16(),
|
|
m_LeafCount = BR.ReadUInt16(),
|
|
m_Solid = BR.ReadByte(),
|
|
m_Flags = BR.ReadByte(),
|
|
m_Skin = BR.ReadInt32(),
|
|
m_FadeMinDist = BR.ReadSingle(),
|
|
m_FadeMaxDist = BR.ReadSingle(),
|
|
m_LightingOrigin = BR.ReadVector(),
|
|
m_flForcedFadeScale = BR.ReadSingle(),
|
|
m_nMinCPULevel = BR.ReadByte(),
|
|
m_nMaxCPULevel = BR.ReadByte(),
|
|
m_nMinGPULevel = BR.ReadByte(),
|
|
m_nMaxGPULevel = BR.ReadByte(),
|
|
m_DiffuseModulation = BR.ReadColor32(),
|
|
m_bDisableX360 = BR.ReadInt32(),
|
|
m_FlagsEx = BR.ReadUInt32(),
|
|
m_UniformScale = BR.ReadSingle()
|
|
};
|
|
}
|
|
_gamelump.staticProps = sp_v11;
|
|
break;
|
|
default:
|
|
DebugLog.Write("Unsupported static prop version: " + gamelump.version);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case GAMELUMP_CODES.GAMELUMP_DETAIL_PROPS:
|
|
//read names
|
|
count = BR.ReadInt32();
|
|
_gamelump.detailPropNames = new DetailObjectDictLump_t[count];
|
|
for (int n = 0; n < count; n++)
|
|
{
|
|
string name = string.Empty;
|
|
|
|
for (int b = 0; b < 128; b++)
|
|
{
|
|
byte _c = BR.ReadByte();
|
|
if (_c == (byte)'\0')
|
|
{
|
|
BR.BaseStream.Seek(128 - b - 1, SeekOrigin.Current);
|
|
break;
|
|
}
|
|
name += Encoding.ASCII.GetString(new byte[1] { _c });
|
|
}
|
|
|
|
_gamelump.detailPropNames[n].m_Name = name;
|
|
}
|
|
|
|
if (gamelump.version == 4)
|
|
{
|
|
if (count > 0)
|
|
{
|
|
//read detail prop sprites
|
|
count = BR.ReadInt32();
|
|
_gamelump.detailPropSprites = new DetailSpriteDictLump_t[count];
|
|
for (int s = 0; s < count; s++)
|
|
{
|
|
_gamelump.detailPropSprites[s] = new DetailSpriteDictLump_t
|
|
{
|
|
m_UL = BR.ReadVector2(),
|
|
m_LR = BR.ReadVector2(),
|
|
m_TexUL = BR.ReadVector2(),
|
|
m_TexLR = BR.ReadVector2()
|
|
};
|
|
}
|
|
//read detail props
|
|
count = BR.ReadInt32();
|
|
|
|
_gamelump.detailProps = new DetailObjectLump_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_gamelump.detailProps[p] = new DetailObjectLump_t
|
|
{
|
|
m_Origin = BR.ReadVector(),
|
|
m_Angles = BR.ReadQAngle(),
|
|
m_DetailModel = BR.ReadUInt16(),
|
|
m_Leaf = BR.ReadUInt16(),
|
|
m_Lighting = BR.ReadColorRGBExp32(),
|
|
m_LightStyles = BR.ReadUInt32(),
|
|
m_LightStyleCount = BR.ReadByte(),
|
|
m_SwayAmount = BR.ReadByte(),
|
|
m_ShapeAngle = BR.ReadByte(),
|
|
m_ShapeSize = BR.ReadByte(),
|
|
m_Orientation = BR.ReadByte(),
|
|
m_Padding2 = BR.ReadBytes(3),
|
|
m_Type = BR.ReadByte(),
|
|
m_Padding3 = BR.ReadBytes(3),
|
|
m_flScale = BR.ReadSingle()
|
|
};
|
|
}
|
|
}
|
|
}
|
|
else
|
|
DebugLog.Write("Unsupported detail prop version: " + gamelump.version);
|
|
break;
|
|
|
|
case GAMELUMP_CODES.GAMELUMP_DETAIL_PROP_LIGHTING:
|
|
// read prop lightings info
|
|
count = BR.ReadInt32();
|
|
_gamelump.detailPropLighting = new DetailPropLightstylesLump_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_gamelump.detailPropLighting[p] = new DetailPropLightstylesLump_t
|
|
{
|
|
m_Lighting = BR.ReadColorRGBExp32(),
|
|
m_Style = BR.ReadByte()
|
|
};
|
|
}
|
|
break;
|
|
|
|
case GAMELUMP_CODES.GAMELUMP_DETAIL_PROP_LIGHTING_HDR:
|
|
// read prop lightings info
|
|
count = BR.ReadInt32();
|
|
_gamelump.detailPropLightingHDR = new DetailPropLightstylesLump_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_gamelump.detailPropLightingHDR[p] = new DetailPropLightstylesLump_t
|
|
{
|
|
m_Lighting = BR.ReadColorRGBExp32(),
|
|
m_Style = BR.ReadByte()
|
|
};
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DebugLog.Write($"Unknown game lump '{gamelump.id}' ({gamelump.id_code}) didn't get swapped!");
|
|
break;
|
|
}
|
|
BR.BaseStream.Seek(endofs, SeekOrigin.Begin); //for some [unknown for me] reasons we should place reader to the end of lump manually
|
|
}
|
|
}
|
|
return _gamelump;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #36: Water leaf data (LEAFWATERDATA)
|
|
private dleafwaterdata_t[] _leafwaterdata;
|
|
/// <summary>
|
|
/// Lump #36.
|
|
/// Purpose: Data for leaf nodes that are inside water.
|
|
/// </summary>
|
|
public dleafwaterdata_t[] LeafWaterData
|
|
{
|
|
get
|
|
{
|
|
if (_leafwaterdata == null)
|
|
{
|
|
lump_t lump = SetLump(36);
|
|
int count = GetLumpCount(lump, 12);
|
|
|
|
_leafwaterdata = new dleafwaterdata_t[count];
|
|
for (int lw = 0; lw < count; lw++)
|
|
{
|
|
_leafwaterdata[lw] = new dleafwaterdata_t
|
|
{
|
|
surfaceZ = BR.ReadSingle(),
|
|
minZ = BR.ReadSingle(),
|
|
surfaceTexInfoID = BR.ReadInt16(),
|
|
unknown = BR.ReadInt16()
|
|
};
|
|
}
|
|
}
|
|
return _leafwaterdata;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #37: Primitives
|
|
private dprimitive_t[] _primitives;
|
|
/// <summary>
|
|
/// Lump #37.
|
|
/// Purpose: "are used in reference to /non-polygonal primitives/", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public dprimitive_t[] Primitives
|
|
{
|
|
get
|
|
{
|
|
if (_primitives == null)
|
|
{
|
|
lump_t lump = SetLump(37);
|
|
int count = GetLumpCount(lump, 9);
|
|
|
|
_primitives = new dprimitive_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_primitives[p] = new dprimitive_t
|
|
{
|
|
type = BR.ReadByte(),
|
|
firstIndex = BR.ReadUInt16(),
|
|
indexCount = BR.ReadUInt16(),
|
|
firstVert = BR.ReadUInt16(),
|
|
vertCount = BR.ReadUInt16()
|
|
};
|
|
}
|
|
}
|
|
return _primitives;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #38: Primitive vertices - not tested: can't find bsp with this lump
|
|
private SourceVector[] _primverts;
|
|
/// <summary>
|
|
/// Lump #38.
|
|
/// Purpose: "are used in reference to /non-polygonal primitives/", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public SourceVector[] PrimVerts
|
|
{
|
|
get
|
|
{
|
|
if (_primverts == null)
|
|
{
|
|
lump_t lump = SetLump(38);
|
|
int count = GetLumpCount(lump, 12);
|
|
|
|
_primverts = new SourceVector[count];
|
|
for (int p = 0; p < count; p++)
|
|
_primverts[p] = BR.ReadVector();
|
|
}
|
|
return _primverts;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #39: Primitive indices
|
|
private ushort[] _primindices;
|
|
/// <summary>
|
|
/// Lump #39.
|
|
/// Purpose: "are used in reference to /non-polygonal primitives/", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public ushort[] Primindices
|
|
{
|
|
get
|
|
{
|
|
if (_primindices == null)
|
|
{
|
|
lump_t lump = SetLump(39);
|
|
int count = GetLumpCount(lump, 2);
|
|
_primindices = BR.ReadUInt16Array(count);
|
|
}
|
|
return _primindices;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #40: PAK file
|
|
private byte[] _pak;
|
|
/// <summary>
|
|
/// Lump #40.
|
|
/// Purpose: Contains game files for map.
|
|
/// Note: not really sure if i should use something else (not byte[]) there.
|
|
/// </summary>
|
|
public byte[] PakFile
|
|
{
|
|
get
|
|
{
|
|
if (_pak == null)
|
|
{
|
|
lump_t lump = SetLump(40);
|
|
_pak = ReadLump(lump.lumpNum);
|
|
}
|
|
return _pak;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #41: Clipping portal vertices
|
|
private SourceVector[] _clipportalverts;
|
|
/// <summary>
|
|
/// Lump #41.
|
|
/// Purpose: [not sure] Used by VVIS and VRAD only.
|
|
/// </summary>
|
|
public SourceVector[] ClipPortalVerts
|
|
{
|
|
get
|
|
{
|
|
if (_clipportalverts == null)
|
|
{
|
|
lump_t lump = SetLump(41);
|
|
int count = GetLumpCount(lump, 12);
|
|
|
|
_clipportalverts = new SourceVector[count];
|
|
for (int p = 0; p < count; p++)
|
|
_clipportalverts[p] = BR.ReadVector();
|
|
}
|
|
return _clipportalverts;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #42: Cubemaps
|
|
private dcubemapsample_t[] _cubemaps;
|
|
/// <summary>
|
|
/// Lump #42.
|
|
/// Purpose: env_cubemap entities info.
|
|
/// </summary>
|
|
public dcubemapsample_t[] Cubemaps
|
|
{
|
|
get
|
|
{
|
|
if (_cubemaps == null)
|
|
{
|
|
lump_t lump = SetLump(42);
|
|
int count = GetLumpCount(lump, 16);
|
|
|
|
_cubemaps = new dcubemapsample_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_cubemaps[p] = new dcubemapsample_t
|
|
{
|
|
origin = BR.ReadInt32Array(3),
|
|
size = BR.ReadByte(),
|
|
//unused = BR.ReadBytes(3)
|
|
};
|
|
BR.BaseStream.Seek(3, SeekOrigin.Current);
|
|
}
|
|
}
|
|
return _cubemaps;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #43/44: Texture data strings (TEXDATA_STRING_DATA / TEXDATA_STRING_TABLE)
|
|
private string[] _texdatastring;
|
|
/// <summary>
|
|
/// Lump #43/44.
|
|
/// Purpose: Paths to VMTs of textures.
|
|
/// </summary>
|
|
public string[] TexdataString
|
|
{
|
|
get
|
|
{
|
|
if (_texdatastring == null)
|
|
{
|
|
lump_t lump = SetLump(44);
|
|
int count = GetLumpCount(lump, 4);
|
|
int[] ofss = BR.ReadInt32Array(count);
|
|
_texdatastring = new string[count];
|
|
|
|
lump = SetLump(43);
|
|
for (int o = 0; o < count; o++)
|
|
{
|
|
int ofs = ofss[o];
|
|
BR.BaseStream.Seek(lump.fileofs + ofs, SeekOrigin.Begin);
|
|
string tex = string.Empty;
|
|
char c;
|
|
while ((c = (char)BR.ReadByte()) != '\0')
|
|
tex += c;
|
|
_texdatastring[o] = tex;
|
|
}
|
|
}
|
|
return _texdatastring;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #45: Overlays
|
|
private doverlay_t[] _overlays;
|
|
/// <summary>
|
|
/// Lump #45.
|
|
/// Purpose: infodecal entities - overlays.
|
|
/// </summary>
|
|
public doverlay_t[] Overlays
|
|
{
|
|
get
|
|
{
|
|
if (_overlays == null)
|
|
{
|
|
lump_t lump = SetLump(45);
|
|
int count = GetLumpCount(lump, 352);
|
|
|
|
_overlays = new doverlay_t[count];
|
|
for (int p = 0; p < count; p++)
|
|
{
|
|
_overlays[p] = new doverlay_t
|
|
{
|
|
nId = BR.ReadInt32(),
|
|
nTexInfo = BR.ReadInt16(),
|
|
m_nFaceCountAndRenderOrder = BR.ReadUInt16(),
|
|
aFaces = BR.ReadInt32Array(OVERLAY_BSP_FACE_COUNT),
|
|
flU = BR.ReadSingleArray(2),
|
|
flV = BR.ReadSingleArray(2),
|
|
vecUVPoints = new SourceVector[4] { BR.ReadVector(), BR.ReadVector(), BR.ReadVector(), BR.ReadVector() },
|
|
vecOrigin = BR.ReadVector(),
|
|
vecBasisNormal = BR.ReadVector()
|
|
};
|
|
}
|
|
}
|
|
return _overlays;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #46: Minimal distance to water from leafs (LEAFMINDISTTOWATER)
|
|
private ushort[] _leafmindisttowater;
|
|
/// <summary>
|
|
/// Lump #46.
|
|
/// Purpose: Distance from leaf to water.
|
|
/// </summary>
|
|
public ushort[] LeafMinDistToWater
|
|
{
|
|
get
|
|
{
|
|
if (_leafmindisttowater == null)
|
|
{
|
|
lump_t lump = SetLump(46);
|
|
int count = GetLumpCount(lump, 2);
|
|
_leafmindisttowater = BR.ReadUInt16Array(count);
|
|
}
|
|
return _leafmindisttowater;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #47: Face macro texinfo (FACE_MACRO_TEXTURE_INFO)
|
|
private ushort[] _facemacrotexinfo;
|
|
/// <summary>
|
|
/// Lump #47.
|
|
/// Purpose: Macro texture info for faces.
|
|
/// This looks up into g_TexDataStringTable, which looks up into g_TexDataStringData.
|
|
/// 0xFFFF if the face has no macro texture.
|
|
/// </summary>
|
|
public ushort[] FaceMacroTextureInfo
|
|
{
|
|
get
|
|
{
|
|
if (_facemacrotexinfo == null)
|
|
{
|
|
lump_t lump = SetLump(47);
|
|
int count = GetLumpCount(lump, 2);
|
|
_facemacrotexinfo = BR.ReadUInt16Array(count);
|
|
}
|
|
return _facemacrotexinfo;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #48: Displacement triangles (DISP_TRIS)
|
|
private CDispTri[] _disptris;
|
|
/// <summary>
|
|
/// Lump #48.
|
|
/// Purpose: Displacement triangle tags.
|
|
/// </summary>
|
|
public CDispTri[] DispTris
|
|
{
|
|
get
|
|
{
|
|
if (_disptris == null)
|
|
{
|
|
lump_t lump = SetLump(48);
|
|
int count = GetLumpCount(lump, 2);
|
|
_disptris = new CDispTri[count];
|
|
for (int d = 0; d < count; d++)
|
|
{
|
|
_disptris[d] = new CDispTri
|
|
{
|
|
m_uiTags = BR.ReadUInt16()
|
|
};
|
|
}
|
|
}
|
|
return _disptris;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #49 : 2004/2009 - not implemented: can't find structs for this lump
|
|
#region Lump #49/1: Compressed physics collision data (PHYSCOLLIDESURFACE) [Source 2004]
|
|
private object _physcollidesurface;
|
|
/// <summary>
|
|
/// Lump #49.
|
|
/// Purpose: "deprecated. We no longer use win32-specific havok compression on terrain", from Source 2013 code.
|
|
/// </summary>
|
|
public object PhysCollideSurface
|
|
{
|
|
get
|
|
{
|
|
if (_physcollidesurface == null)
|
|
{
|
|
//lump_t lump = SetLump(49);
|
|
//int count = GetLumpCount(lump, 2);
|
|
//_physcollidesurface = new ...[count];
|
|
throw new NotImplementedException();
|
|
}
|
|
return _physcollidesurface;
|
|
}
|
|
}
|
|
#endregion
|
|
#region Lump #49/2: (PROP_BLOB) [Source 2009]
|
|
private object _propblob;
|
|
/// <summary>
|
|
/// Lump #49.
|
|
/// Purpose: "static prop triangle & string data", from Source 20?? code.
|
|
/// </summary>
|
|
public object PropBlob
|
|
{
|
|
get
|
|
{
|
|
if (_propblob == null)
|
|
{
|
|
//lump_t lump = SetLump(49);
|
|
//int count = GetLumpCount(lump, 2);
|
|
//_propblob = new ...[count];
|
|
throw new NotImplementedException();
|
|
}
|
|
return _propblob;
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Lump #50: Water overlays - not tested: can't find map with this lump
|
|
private dwateroverlay_t[] _wateroverlays;
|
|
/// <summary>
|
|
/// Lump #50.
|
|
/// Purpose: [unknown].
|
|
/// </summary>
|
|
public dwateroverlay_t[] WaterOverlays
|
|
{
|
|
get
|
|
{
|
|
if (_wateroverlays == null)
|
|
{
|
|
lump_t lump = SetLump(50);
|
|
int count = GetLumpCount(lump, 1120);
|
|
_wateroverlays = new dwateroverlay_t[count];
|
|
for (int d = 0; d < count; d++)
|
|
{
|
|
_wateroverlays[d] = new dwateroverlay_t
|
|
{
|
|
nId = BR.ReadInt32(),
|
|
nTexInfo = BR.ReadInt16(),
|
|
m_nFaceCountAndRenderOrder = BR.ReadUInt16(),
|
|
aFaces = BR.ReadInt32Array(WATEROVERLAY_BSP_FACE_COUNT),
|
|
flU = BR.ReadSingleArray(2),
|
|
flV = BR.ReadSingleArray(2),
|
|
vecUVPoints = new SourceVector[4] { BR.ReadVector(), BR.ReadVector(), BR.ReadVector(), BR.ReadVector() },
|
|
vecOrigin = BR.ReadVector(),
|
|
vecBasisNormal = BR.ReadVector()
|
|
};
|
|
}
|
|
}
|
|
return _wateroverlays;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #51 XBOX2006/2007
|
|
#region Lump #51/1: Lightmap pages (LIGHTMAPPAGES) [Source 2006 for XBOX] - not tested: can't find map with this lump
|
|
private dlightmappage_t[] _lightmappages;
|
|
/// <summary>
|
|
/// Lump #51.
|
|
/// Purpose: Lightmaps for XBOX.
|
|
/// </summary>
|
|
public dlightmappage_t[] LightmapPages
|
|
{
|
|
get
|
|
{
|
|
if (_lightmappages == null)
|
|
{
|
|
lump_t lump = SetLump(51);
|
|
if (lump.filelen < 33792) //This lump is not for lightmap pages
|
|
return null;
|
|
int count = GetLumpCount(lump, 33792);
|
|
_lightmappages = new dlightmappage_t[count];
|
|
for (int d = 0; d < count; d++)
|
|
{
|
|
_lightmappages[d] = new dlightmappage_t
|
|
{
|
|
data = BR.ReadBytes(MAX_LIGHTMAPPAGE_WIDTH * MAX_LIGHTMAPPAGE_HEIGHT)
|
|
};
|
|
_lightmappages[d].palette = new ColorRGBExp32[256];
|
|
for (int p = 0; p < 256; p++)
|
|
_lightmappages[d].palette[p] = BR.ReadColorRGBExp32();
|
|
}
|
|
}
|
|
return _lightmappages;
|
|
}
|
|
}
|
|
#endregion
|
|
#region Lump #51/2: (LEAF_AMBIENT_INDEX_HDR) [Source 2007]
|
|
private dleafambientindex_t[] _leafambientindexhdr;
|
|
/// <summary>
|
|
/// Lump #51.
|
|
/// Purpose: "index of LUMP_LEAF_AMBIENT_LIGHTING_HDR", from Source 2013 code.
|
|
/// </summary>
|
|
public dleafambientindex_t[] LeafAmbientIndexHDR
|
|
{
|
|
get
|
|
{
|
|
if (_leafambientindexhdr == null)
|
|
{
|
|
lump_t lump = SetLump(51);
|
|
if (lump.filelen == 0 || lump.filelen % 4 != 0) //This lump is not for ... this
|
|
return null;
|
|
int count = GetLumpCount(lump, 4);
|
|
_leafambientindexhdr = new dleafambientindex_t[count];
|
|
for (int d = 0; d < count; d++)
|
|
{
|
|
_leafambientindexhdr[d] = new dleafambientindex_t
|
|
{
|
|
ambientSampleCount = BR.ReadUInt16(),
|
|
firstAmbientSample = BR.ReadUInt16()
|
|
};
|
|
}
|
|
}
|
|
return _leafambientindexhdr;
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Lump #52 XBOX2006/2007
|
|
#region Lump #52/1: Lightmap page infos (LIGHTMAPPAGEINFOS) [Source 2006 for XBOX] - not tested: can't find map with this lump
|
|
private dlightmappageinfo_t[] _lightmappageinfos;
|
|
/// <summary>
|
|
/// Lump #52.
|
|
/// Purpose: Lightmaps for XBOX.
|
|
/// </summary>
|
|
public dlightmappageinfo_t[] LightmapPageInfos
|
|
{
|
|
get
|
|
{
|
|
if (_lightmappageinfos == null)
|
|
{
|
|
lump_t lump = SetLump(52);
|
|
if (lump.filelen == 0 || lump.filelen % 8 != 0) //This lump is not for lightmap page infos
|
|
return null;
|
|
int count = GetLumpCount(lump, 8);
|
|
_lightmappageinfos = new dlightmappageinfo_t[count];
|
|
for (int d = 0; d < count; d++)
|
|
{
|
|
_lightmappageinfos[d] = new dlightmappageinfo_t
|
|
{
|
|
page = BR.ReadByte(),
|
|
offset = BR.ReadBytes(2),
|
|
pad = BR.ReadByte(),
|
|
avgColor = BR.ReadColorRGBExp32()
|
|
};
|
|
}
|
|
}
|
|
return _lightmappageinfos;
|
|
}
|
|
}
|
|
#endregion
|
|
#region Lump #52/2: (LEAF_AMBIENT_INDEX) [Source 2007]
|
|
private dleafambientindex_t[] _leafambientindex;
|
|
/// <summary>
|
|
/// Lump #52.
|
|
/// Purpose: "index of LUMP_LEAF_AMBIENT_LIGHTING", from Source 2013 code.
|
|
/// </summary>
|
|
public dleafambientindex_t[] LeafAmbientIndex
|
|
{
|
|
get
|
|
{
|
|
if (_leafambientindex == null)
|
|
{
|
|
lump_t lump = SetLump(52);
|
|
if (lump.filelen == 0 || lump.filelen % 4 != 0) //This lump is not for ... this
|
|
return null;
|
|
int count = GetLumpCount(lump, 4);
|
|
_leafambientindex = new dleafambientindex_t[count];
|
|
for (int d = 0; d < count; d++)
|
|
{
|
|
_leafambientindex[d] = new dleafambientindex_t
|
|
{
|
|
ambientSampleCount = BR.ReadUInt16(),
|
|
firstAmbientSample = BR.ReadUInt16()
|
|
};
|
|
}
|
|
}
|
|
return _leafambientindex;
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Lump #53: Lighting (HDR)
|
|
private ColorRGBExp32[] _lightinghdr;
|
|
/// <summary>
|
|
/// Lump #53. Version 1.
|
|
/// Purpose: lightmap colors / shadows.
|
|
/// Also: Check lump #8 for LDR lighting.
|
|
/// </summary>
|
|
public ColorRGBExp32[] LightingHDR
|
|
{
|
|
get
|
|
{
|
|
if (_lightinghdr == null)
|
|
{
|
|
lump_t lump = SetLump(53);
|
|
int count = GetLumpCount(lump, 4);
|
|
_lightinghdr = new ColorRGBExp32[count];
|
|
for (int f = 0; f < count; f++)
|
|
{
|
|
_lightinghdr[f] = BR.ReadColorRGBExp32();
|
|
}
|
|
}
|
|
return _lightinghdr;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #54: World lights (HDR)
|
|
private object _worldlightshdr;
|
|
/// <summary>
|
|
/// Lump #54. Version 0/1.
|
|
/// Purpose: Contains info about static lights (NOT LIGHTMAPS).
|
|
/// Usage (depending on version): (dworldlightV?_t[])bsp.WorldLightsHDR.
|
|
/// Also: Check lump #15 for LDR world lights.
|
|
/// </summary>
|
|
public object WorldLightsHDR
|
|
{
|
|
get
|
|
{
|
|
if (_worldlightshdr == null)
|
|
{
|
|
lump_t lump = SetLump(54);
|
|
int count;
|
|
switch (lump.version)
|
|
{
|
|
case 0:
|
|
count = GetLumpCount(lump, 88);
|
|
dworldlightV0_t[] _wl_0 = new dworldlightV0_t[count];
|
|
for (int wl = 0; wl < count; wl++)
|
|
{
|
|
_wl_0[wl] = new dworldlightV0_t
|
|
{
|
|
origin = BR.ReadVector(),
|
|
intensity = BR.ReadVector(),
|
|
normal = BR.ReadVector(),
|
|
cluster = BR.ReadInt32(),
|
|
type = (emittype_t)BR.ReadUInt32(),
|
|
style = BR.ReadInt32(),
|
|
stopdot = BR.ReadSingle(),
|
|
stopdot2 = BR.ReadSingle(),
|
|
exponent = BR.ReadSingle(),
|
|
radius = BR.ReadSingle(),
|
|
constant_attn = BR.ReadSingle(),
|
|
linear_attn = BR.ReadSingle(),
|
|
quadratic_attn = BR.ReadSingle(),
|
|
flags = BR.ReadInt32(),
|
|
texinfo = BR.ReadInt32(),
|
|
owner = BR.ReadInt32()
|
|
};
|
|
}
|
|
_worldlightshdr = _wl_0;
|
|
break;
|
|
case 1:
|
|
count = GetLumpCount(lump, 100);
|
|
dworldlightV1_t[] _wl_1 = new dworldlightV1_t[count];
|
|
for (int wl = 0; wl < count; wl++)
|
|
{
|
|
_wl_1[wl] = new dworldlightV1_t
|
|
{
|
|
origin = BR.ReadVector(),
|
|
intensity = BR.ReadVector(),
|
|
normal = BR.ReadVector(),
|
|
shadow_cast_offset = BR.ReadVector(),
|
|
cluster = BR.ReadInt32(),
|
|
type = (emittype_t)BR.ReadUInt32(),
|
|
style = BR.ReadInt32(),
|
|
stopdot = BR.ReadSingle(),
|
|
stopdot2 = BR.ReadSingle(),
|
|
exponent = BR.ReadSingle(),
|
|
radius = BR.ReadSingle(),
|
|
constant_attn = BR.ReadSingle(),
|
|
linear_attn = BR.ReadSingle(),
|
|
quadratic_attn = BR.ReadSingle(),
|
|
flags = BR.ReadInt32(),
|
|
texinfo = BR.ReadInt32(),
|
|
owner = BR.ReadInt32()
|
|
};
|
|
}
|
|
_worldlightshdr = _wl_1;
|
|
break;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
}
|
|
return _worldlightshdr;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #55: Per-leaf ambient lighting (HDR) (LEAF_AMBIENT_LIGHTING_HDR)
|
|
private dleafambientlighting_t[] _leafambientlightinghdr;
|
|
/// <summary>
|
|
/// Lump #55.
|
|
/// Purpose: Stores volumetric ambient lighting information for each leaf.
|
|
/// Also: Check lump #56 for LDR ambient lighting.
|
|
/// </summary>
|
|
public dleafambientlighting_t[] LeafAmbientLightingHDR
|
|
{
|
|
get
|
|
{
|
|
if (_leafambientlightinghdr == null)
|
|
{
|
|
lump_t lump = SetLump(55);
|
|
int count = GetLumpCount(lump, 28);
|
|
_leafambientlightinghdr = new dleafambientlighting_t[count];
|
|
for (int l = 0; l < count; l++)
|
|
{
|
|
_leafambientlightinghdr[l] = new dleafambientlighting_t
|
|
{
|
|
cube = BR.ReadCompressedLightCube(),
|
|
x = BR.ReadByte(),
|
|
y = BR.ReadByte(),
|
|
z = BR.ReadByte(),
|
|
pad = BR.ReadByte()
|
|
};
|
|
}
|
|
}
|
|
return _leafambientlightinghdr;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #56: Per-leaf ambient lighting (LDR) (LEAF_AMBIENT_LIGHTING)
|
|
private dleafambientlighting_t[] _leafambientlighting;
|
|
/// <summary>
|
|
/// Lump #56.
|
|
/// Purpose: Stores volumetric ambient lighting information for each leaf.
|
|
/// Also: Check lump #55 for HDR ambient lighting.
|
|
/// </summary>
|
|
public dleafambientlighting_t[] LeafAmbientLighting
|
|
{
|
|
get
|
|
{
|
|
if (_leafambientlighting == null)
|
|
{
|
|
lump_t lump = SetLump(56);
|
|
int count = GetLumpCount(lump, 28);
|
|
_leafambientlighting = new dleafambientlighting_t[count];
|
|
for (int l = 0; l < count; l++)
|
|
{
|
|
_leafambientlighting[l] = new dleafambientlighting_t
|
|
{
|
|
cube = BR.ReadCompressedLightCube(),
|
|
x = BR.ReadByte(),
|
|
y = BR.ReadByte(),
|
|
z = BR.ReadByte(),
|
|
pad = BR.ReadByte()
|
|
};
|
|
}
|
|
}
|
|
return _leafambientlighting;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #57: XZIP PAK file (for XBOX) - not implemented: can't find structs for this lump (mb it's a byte array)
|
|
private object _xzippakfile;
|
|
/// <summary>
|
|
/// Lump #57.
|
|
/// Purpose: "XZip version of pak file for Xbox. Deprecated.", from Valve Dev Wiki.
|
|
/// </summary>
|
|
public object XZIPPAKFile
|
|
{
|
|
get
|
|
{
|
|
if (_xzippakfile == null)
|
|
{
|
|
//lump_t lump = SetLump(57);
|
|
//int count = GetLumpCount(lump, 28);
|
|
//_xzippakfile = new ...[count];
|
|
throw new NotImplementedException();
|
|
}
|
|
return _xzippakfile;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #58: Faces (HDR)
|
|
private dface_t[] _faceshdr;
|
|
/// <summary>
|
|
/// Lump #58. Version 1.
|
|
/// Purpose: datas for rendering.
|
|
/// Also: Check lump #7 for LDR faces.
|
|
/// </summary>
|
|
public dface_t[] FacesHDR
|
|
{
|
|
get
|
|
{
|
|
if (_faceshdr == null)
|
|
{
|
|
lump_t lump = SetLump(58);
|
|
int count = GetLumpCount(lump, 56);
|
|
_faceshdr = new dface_t[count];
|
|
for (int f = 0; f < count; f++)
|
|
{
|
|
_faceshdr[f] = new dface_t
|
|
{
|
|
planenum = BR.ReadUInt16(),
|
|
side = BR.ReadByte(),
|
|
onNode = BR.ReadByte(),
|
|
firstedge = BR.ReadInt32(),
|
|
numedges = BR.ReadInt16(),
|
|
texinfo = BR.ReadInt16(),
|
|
dispinfo = BR.ReadInt16(),
|
|
surfaceFogVolumeID = BR.ReadInt16(),
|
|
styles = BR.ReadBytes(4),
|
|
lightofs = BR.ReadInt32(),
|
|
area = BR.ReadSingle(),
|
|
m_LightmapTextureMinsInLuxels = BR.ReadVector2Int(),
|
|
m_LightmapTextureSizeInLuxels = BR.ReadVector2Int(),
|
|
origFace = BR.ReadInt32(),
|
|
m_NumPrims = BR.ReadUInt16(),
|
|
firstPrimID = BR.ReadUInt16(),
|
|
smoothingGroups = BR.ReadUInt32()
|
|
};
|
|
}
|
|
}
|
|
return _faceshdr;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #59: Map flags
|
|
private dflagslump_t _mapflags;
|
|
private bool __mapflags;
|
|
/// <summary>
|
|
/// Lump #59.
|
|
/// Purpose: Map compilation flags.
|
|
/// </summary>
|
|
public dflagslump_t MapFlags
|
|
{
|
|
get
|
|
{
|
|
if (!__mapflags)
|
|
{
|
|
lump_t lump = SetLump(59);
|
|
_mapflags = new dflagslump_t
|
|
{
|
|
m_LevelFlags = BR.ReadUInt32()
|
|
};
|
|
|
|
__mapflags = true;
|
|
}
|
|
return _mapflags;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Lump #60: Overlay fades
|
|
private doverlayfade_t[] _overlayfades;
|
|
/// <summary>
|
|
/// Lump #60.
|
|
/// Purpose: [not sure] fade distances for overlays?
|
|
/// </summary>
|
|
public doverlayfade_t[] OverlayFades
|
|
{
|
|
get
|
|
{
|
|
if (_overlayfades == null)
|
|
{
|
|
lump_t lump = SetLump(60);
|
|
int count = GetLumpCount(lump, 8);
|
|
_overlayfades = new doverlayfade_t[count];
|
|
for (int of = 0; of < count; of++)
|
|
{
|
|
_overlayfades[of] = new doverlayfade_t
|
|
{
|
|
flFadeDistMinSq = BR.ReadSingle(),
|
|
flFadeDistMaxSq = BR.ReadSingle()
|
|
};
|
|
}
|
|
}
|
|
return _overlayfades;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
//not gonna try to do 61-63 lumps because i can't find any structs in google/github. Valve's secret?
|
|
|
|
#endregion
|
|
}
|
|
}
|