vhlt/common/bspfile.cpp

2033 lines
60 KiB
C++

#include "cmdlib.h"
#include "filelib.h"
#include "messages.h"
#include "hlassert.h"
#include "log.h"
#include "mathlib.h"
#include "bspfile.h"
#include "scriplib.h"
#include "blockmem.h"
//=============================================================================
int g_max_map_miptex = DEFAULT_MAX_MAP_MIPTEX;
int g_max_map_lightdata = DEFAULT_MAX_MAP_LIGHTDATA;
int g_nummodels;
dmodel_t g_dmodels[MAX_MAP_MODELS];
int g_dmodels_checksum;
int g_visdatasize;
byte g_dvisdata[MAX_MAP_VISIBILITY];
int g_dvisdata_checksum;
int g_lightdatasize;
byte* g_dlightdata;
int g_dlightdata_checksum;
int g_texdatasize;
byte* g_dtexdata; // (dmiptexlump_t)
int g_dtexdata_checksum;
int g_entdatasize;
char g_dentdata[MAX_MAP_ENTSTRING];
int g_dentdata_checksum;
int g_numleafs;
dleaf_t g_dleafs[MAX_MAP_LEAFS];
int g_dleafs_checksum;
int g_numplanes;
dplane_t g_dplanes[MAX_INTERNAL_MAP_PLANES];
int g_dplanes_checksum;
int g_numvertexes;
dvertex_t g_dvertexes[MAX_MAP_VERTS];
int g_dvertexes_checksum;
int g_numnodes;
dnode_t g_dnodes[MAX_MAP_NODES];
int g_dnodes_checksum;
int g_numtexinfo;
#ifdef HLCSG_HLBSP_REDUCETEXTURE
texinfo_t g_texinfo[MAX_INTERNAL_MAP_TEXINFO];
#else
texinfo_t g_texinfo[MAX_MAP_TEXINFO];
#endif
int g_texinfo_checksum;
int g_numfaces;
dface_t g_dfaces[MAX_MAP_FACES];
int g_dfaces_checksum;
#ifdef ZHLT_XASH2
int g_numclipnodes[MAX_MAP_HULLS - 1];
dclipnode_t g_dclipnodes[MAX_MAP_HULLS - 1][MAX_MAP_CLIPNODES];
int g_dclipnodes_checksum[MAX_MAP_HULLS - 1];
#else
int g_numclipnodes;
dclipnode_t g_dclipnodes[MAX_MAP_CLIPNODES];
int g_dclipnodes_checksum;
#endif
int g_numedges;
dedge_t g_dedges[MAX_MAP_EDGES];
int g_dedges_checksum;
int g_nummarksurfaces;
unsigned short g_dmarksurfaces[MAX_MAP_MARKSURFACES];
int g_dmarksurfaces_checksum;
int g_numsurfedges;
int g_dsurfedges[MAX_MAP_SURFEDGES];
int g_dsurfedges_checksum;
int g_numentities;
entity_t g_entities[MAX_MAP_ENTITIES];
/*
* ===============
* FastChecksum
* ===============
*/
static int FastChecksum(const void* const buffer, int bytes)
{
int checksum = 0;
char* buf = (char*)buffer;
while (bytes--)
{
checksum = rotl(checksum, 4) ^ (*buf);
buf++;
}
return checksum;
}
/*
* ===============
* CompressVis
* ===============
*/
int CompressVis(const byte* const src, const unsigned int src_length, byte* dest, unsigned int dest_length)
{
unsigned int j;
byte* dest_p = dest;
unsigned int current_length = 0;
for (j = 0; j < src_length; j++)
{
current_length++;
hlassume(current_length <= dest_length, assume_COMPRESSVIS_OVERFLOW);
*dest_p = src[j];
dest_p++;
if (src[j])
{
continue;
}
unsigned char rep = 1;
for (j++; j < src_length; j++)
{
if (src[j] || rep == 255)
{
break;
}
else
{
rep++;
}
}
current_length++;
hlassume(current_length <= dest_length, assume_COMPRESSVIS_OVERFLOW);
*dest_p = rep;
dest_p++;
j--;
}
return dest_p - dest;
}
// =====================================================================================
// DecompressVis
//
// =====================================================================================
void DecompressVis(const byte* src, byte* const dest, const unsigned int dest_length)
{
unsigned int current_length = 0;
int c;
byte* out;
int row;
#ifdef ZHLT_DecompressVis_FIX
row = (g_dmodels[0].visleafs + 7) >> 3; // same as the length used by VIS program in CompressVis
// The wrong size will cause DecompressVis to spend extremely long time once the source pointer runs into the invalid area in g_dvisdata (for example, in BuildFaceLights, some faces could hang for a few seconds), and sometimes to crash.
#else
row = (g_numleafs + 7) >> 3;
#endif
out = dest;
do
{
#ifdef ZHLT_DecompressVis_FIX
hlassume (src - g_dvisdata < g_visdatasize, assume_DECOMPRESSVIS_OVERFLOW);
#endif
if (*src)
{
current_length++;
hlassume(current_length <= dest_length, assume_DECOMPRESSVIS_OVERFLOW);
*out = *src;
out++;
src++;
continue;
}
#ifdef ZHLT_DecompressVis_FIX
hlassume (&src[1] - g_dvisdata < g_visdatasize, assume_DECOMPRESSVIS_OVERFLOW);
#endif
c = src[1];
src += 2;
while (c)
{
current_length++;
hlassume(current_length <= dest_length, assume_DECOMPRESSVIS_OVERFLOW);
*out = 0;
out++;
c--;
if (out - dest >= row)
{
return;
}
}
}
while (out - dest < row);
}
//
// =====================================================================================
//
// =====================================================================================
// SwapBSPFile
// byte swaps all data in a bsp file
// =====================================================================================
static void SwapBSPFile(const bool todisk)
{
int i, j, c;
dmodel_t* d;
dmiptexlump_t* mtl;
// models
for (i = 0; i < g_nummodels; i++)
{
d = &g_dmodels[i];
for (j = 0; j < MAX_MAP_HULLS; j++)
{
d->headnode[j] = LittleLong(d->headnode[j]);
}
d->visleafs = LittleLong(d->visleafs);
d->firstface = LittleLong(d->firstface);
d->numfaces = LittleLong(d->numfaces);
for (j = 0; j < 3; j++)
{
d->mins[j] = LittleFloat(d->mins[j]);
d->maxs[j] = LittleFloat(d->maxs[j]);
d->origin[j] = LittleFloat(d->origin[j]);
}
}
//
// vertexes
//
for (i = 0; i < g_numvertexes; i++)
{
for (j = 0; j < 3; j++)
{
g_dvertexes[i].point[j] = LittleFloat(g_dvertexes[i].point[j]);
}
}
//
// planes
//
for (i = 0; i < g_numplanes; i++)
{
for (j = 0; j < 3; j++)
{
g_dplanes[i].normal[j] = LittleFloat(g_dplanes[i].normal[j]);
}
g_dplanes[i].dist = LittleFloat(g_dplanes[i].dist);
g_dplanes[i].type = (planetypes)LittleLong(g_dplanes[i].type);
}
//
// texinfos
//
for (i = 0; i < g_numtexinfo; i++)
{
for (j = 0; j < 8; j++)
{
g_texinfo[i].vecs[0][j] = LittleFloat(g_texinfo[i].vecs[0][j]);
}
g_texinfo[i].miptex = LittleLong(g_texinfo[i].miptex);
g_texinfo[i].flags = LittleLong(g_texinfo[i].flags);
}
//
// faces
//
for (i = 0; i < g_numfaces; i++)
{
g_dfaces[i].texinfo = LittleShort(g_dfaces[i].texinfo);
g_dfaces[i].planenum = LittleShort(g_dfaces[i].planenum);
g_dfaces[i].side = LittleShort(g_dfaces[i].side);
g_dfaces[i].lightofs = LittleLong(g_dfaces[i].lightofs);
g_dfaces[i].firstedge = LittleLong(g_dfaces[i].firstedge);
g_dfaces[i].numedges = LittleShort(g_dfaces[i].numedges);
}
//
// nodes
//
for (i = 0; i < g_numnodes; i++)
{
g_dnodes[i].planenum = LittleLong(g_dnodes[i].planenum);
for (j = 0; j < 3; j++)
{
g_dnodes[i].mins[j] = LittleShort(g_dnodes[i].mins[j]);
g_dnodes[i].maxs[j] = LittleShort(g_dnodes[i].maxs[j]);
}
g_dnodes[i].children[0] = LittleShort(g_dnodes[i].children[0]);
g_dnodes[i].children[1] = LittleShort(g_dnodes[i].children[1]);
g_dnodes[i].firstface = LittleShort(g_dnodes[i].firstface);
g_dnodes[i].numfaces = LittleShort(g_dnodes[i].numfaces);
}
//
// leafs
//
for (i = 0; i < g_numleafs; i++)
{
g_dleafs[i].contents = LittleLong(g_dleafs[i].contents);
for (j = 0; j < 3; j++)
{
g_dleafs[i].mins[j] = LittleShort(g_dleafs[i].mins[j]);
g_dleafs[i].maxs[j] = LittleShort(g_dleafs[i].maxs[j]);
}
g_dleafs[i].firstmarksurface = LittleShort(g_dleafs[i].firstmarksurface);
g_dleafs[i].nummarksurfaces = LittleShort(g_dleafs[i].nummarksurfaces);
g_dleafs[i].visofs = LittleLong(g_dleafs[i].visofs);
}
//
// clipnodes
//
#ifdef ZHLT_XASH2
for (int hull = 1; hull < MAX_MAP_HULLS; hull++)
{
for (i = 0; i < g_numclipnodes[hull - 1]; i++)
{
g_dclipnodes[hull - 1][i].planenum = LittleLong(g_dclipnodes[hull - 1][i].planenum);
g_dclipnodes[hull - 1][i].children[0] = LittleShort(g_dclipnodes[hull - 1][i].children[0]);
g_dclipnodes[hull - 1][i].children[1] = LittleShort(g_dclipnodes[hull - 1][i].children[1]);
}
}
#else
for (i = 0; i < g_numclipnodes; i++)
{
g_dclipnodes[i].planenum = LittleLong(g_dclipnodes[i].planenum);
g_dclipnodes[i].children[0] = LittleShort(g_dclipnodes[i].children[0]);
g_dclipnodes[i].children[1] = LittleShort(g_dclipnodes[i].children[1]);
}
#endif
//
// miptex
//
if (g_texdatasize)
{
mtl = (dmiptexlump_t*)g_dtexdata;
if (todisk)
{
c = mtl->nummiptex;
}
else
{
c = LittleLong(mtl->nummiptex);
}
mtl->nummiptex = LittleLong(mtl->nummiptex);
for (i = 0; i < c; i++)
{
mtl->dataofs[i] = LittleLong(mtl->dataofs[i]);
}
}
//
// marksurfaces
//
for (i = 0; i < g_nummarksurfaces; i++)
{
g_dmarksurfaces[i] = LittleShort(g_dmarksurfaces[i]);
}
//
// surfedges
//
for (i = 0; i < g_numsurfedges; i++)
{
g_dsurfedges[i] = LittleLong(g_dsurfedges[i]);
}
//
// edges
//
for (i = 0; i < g_numedges; i++)
{
g_dedges[i].v[0] = LittleShort(g_dedges[i].v[0]);
g_dedges[i].v[1] = LittleShort(g_dedges[i].v[1]);
}
}
// =====================================================================================
// CopyLump
// balh
// =====================================================================================
static int CopyLump(int lump, void* dest, int size, const dheader_t* const header)
{
int length, ofs;
length = header->lumps[lump].filelen;
ofs = header->lumps[lump].fileofs;
if (length % size)
{
Error("LoadBSPFile: odd lump size");
}
//special handling for tex and lightdata to keep things from exploding - KGP
if(lump == LUMP_TEXTURES && dest == (void*)g_dtexdata)
{ hlassume(g_max_map_miptex > length,assume_MAX_MAP_MIPTEX); }
else if(lump == LUMP_LIGHTING && dest == (void*)g_dlightdata)
{ hlassume(g_max_map_lightdata > length,assume_MAX_MAP_LIGHTING); }
memcpy(dest, (byte*) header + ofs, length);
return length / size;
}
// =====================================================================================
// LoadBSPFile
// balh
// =====================================================================================
void LoadBSPFile(const char* const filename)
{
dheader_t* header;
LoadFile(filename, (char**)&header);
LoadBSPImage(header);
}
// =====================================================================================
// LoadBSPImage
// balh
// =====================================================================================
void LoadBSPImage(dheader_t* const header)
{
unsigned int i;
// swap the header
for (i = 0; i < sizeof(dheader_t) / 4; i++)
{
((int*)header)[i] = LittleLong(((int*)header)[i]);
}
if (header->version != BSPVERSION)
{
Error("BSP is version %i, not %i", header->version, BSPVERSION);
}
g_nummodels = CopyLump(LUMP_MODELS, g_dmodels, sizeof(dmodel_t), header);
g_numvertexes = CopyLump(LUMP_VERTEXES, g_dvertexes, sizeof(dvertex_t), header);
g_numplanes = CopyLump(LUMP_PLANES, g_dplanes, sizeof(dplane_t), header);
g_numleafs = CopyLump(LUMP_LEAFS, g_dleafs, sizeof(dleaf_t), header);
g_numnodes = CopyLump(LUMP_NODES, g_dnodes, sizeof(dnode_t), header);
g_numtexinfo = CopyLump(LUMP_TEXINFO, g_texinfo, sizeof(texinfo_t), header);
#ifdef ZHLT_XASH2
for (int hull = 1; hull < MAX_MAP_HULLS; hull++)
{
int lump;
switch (hull)
{
case 1: lump = LUMP_CLIPNODES; break;
case 2: lump = LUMP_CLIPNODES2; break;
case 3: lump = LUMP_CLIPNODES3; break;
default:
Error ("bad hull number %d", hull);
break;
}
g_numclipnodes[hull - 1] = CopyLump(lump, g_dclipnodes[hull - 1], sizeof(dclipnode_t), header);
}
#else
g_numclipnodes = CopyLump(LUMP_CLIPNODES, g_dclipnodes, sizeof(dclipnode_t), header);
#endif
g_numfaces = CopyLump(LUMP_FACES, g_dfaces, sizeof(dface_t), header);
g_nummarksurfaces = CopyLump(LUMP_MARKSURFACES, g_dmarksurfaces, sizeof(g_dmarksurfaces[0]), header);
g_numsurfedges = CopyLump(LUMP_SURFEDGES, g_dsurfedges, sizeof(g_dsurfedges[0]), header);
g_numedges = CopyLump(LUMP_EDGES, g_dedges, sizeof(dedge_t), header);
g_texdatasize = CopyLump(LUMP_TEXTURES, g_dtexdata, 1, header);
g_visdatasize = CopyLump(LUMP_VISIBILITY, g_dvisdata, 1, header);
g_lightdatasize = CopyLump(LUMP_LIGHTING, g_dlightdata, 1, header);
g_entdatasize = CopyLump(LUMP_ENTITIES, g_dentdata, 1, header);
Free(header); // everything has been copied out
//
// swap everything
//
SwapBSPFile(false);
g_dmodels_checksum = FastChecksum(g_dmodels, g_nummodels * sizeof(g_dmodels[0]));
g_dvertexes_checksum = FastChecksum(g_dvertexes, g_numvertexes * sizeof(g_dvertexes[0]));
g_dplanes_checksum = FastChecksum(g_dplanes, g_numplanes * sizeof(g_dplanes[0]));
g_dleafs_checksum = FastChecksum(g_dleafs, g_numleafs * sizeof(g_dleafs[0]));
g_dnodes_checksum = FastChecksum(g_dnodes, g_numnodes * sizeof(g_dnodes[0]));
g_texinfo_checksum = FastChecksum(g_texinfo, g_numtexinfo * sizeof(g_texinfo[0]));
#ifdef ZHLT_XASH2
for (int hull = 1; hull < MAX_MAP_HULLS; hull++)
{
g_dclipnodes_checksum[hull - 1] = FastChecksum(g_dclipnodes[hull - 1], g_numclipnodes[hull - 1] * sizeof(g_dclipnodes[hull - 1][0]));
}
#else
g_dclipnodes_checksum = FastChecksum(g_dclipnodes, g_numclipnodes * sizeof(g_dclipnodes[0]));
#endif
g_dfaces_checksum = FastChecksum(g_dfaces, g_numfaces * sizeof(g_dfaces[0]));
g_dmarksurfaces_checksum = FastChecksum(g_dmarksurfaces, g_nummarksurfaces * sizeof(g_dmarksurfaces[0]));
g_dsurfedges_checksum = FastChecksum(g_dsurfedges, g_numsurfedges * sizeof(g_dsurfedges[0]));
g_dedges_checksum = FastChecksum(g_dedges, g_numedges * sizeof(g_dedges[0]));
g_dtexdata_checksum = FastChecksum(g_dtexdata, g_numedges * sizeof(g_dtexdata[0]));
g_dvisdata_checksum = FastChecksum(g_dvisdata, g_visdatasize * sizeof(g_dvisdata[0]));
g_dlightdata_checksum = FastChecksum(g_dlightdata, g_lightdatasize * sizeof(g_dlightdata[0]));
g_dentdata_checksum = FastChecksum(g_dentdata, g_entdatasize * sizeof(g_dentdata[0]));
}
//
// =====================================================================================
//
// =====================================================================================
// AddLump
// balh
// =====================================================================================
static void AddLump(int lumpnum, void* data, int len, dheader_t* header, FILE* bspfile)
{
lump_t* lump =&header->lumps[lumpnum];
lump->fileofs = LittleLong(ftell(bspfile));
lump->filelen = LittleLong(len);
SafeWrite(bspfile, data, (len + 3) & ~3);
}
// =====================================================================================
// WriteBSPFile
// Swaps the bsp file in place, so it should not be referenced again
// =====================================================================================
void WriteBSPFile(const char* const filename)
{
dheader_t outheader;
dheader_t* header;
FILE* bspfile;
header = &outheader;
memset(header, 0, sizeof(dheader_t));
SwapBSPFile(true);
header->version = LittleLong(BSPVERSION);
bspfile = SafeOpenWrite(filename);
SafeWrite(bspfile, header, sizeof(dheader_t)); // overwritten later
// LUMP TYPE DATA LENGTH HEADER BSPFILE
AddLump(LUMP_PLANES, g_dplanes, g_numplanes * sizeof(dplane_t), header, bspfile);
AddLump(LUMP_LEAFS, g_dleafs, g_numleafs * sizeof(dleaf_t), header, bspfile);
AddLump(LUMP_VERTEXES, g_dvertexes, g_numvertexes * sizeof(dvertex_t), header, bspfile);
AddLump(LUMP_NODES, g_dnodes, g_numnodes * sizeof(dnode_t), header, bspfile);
AddLump(LUMP_TEXINFO, g_texinfo, g_numtexinfo * sizeof(texinfo_t), header, bspfile);
AddLump(LUMP_FACES, g_dfaces, g_numfaces * sizeof(dface_t), header, bspfile);
#ifdef ZHLT_XASH2
for (int hull = 1; hull < MAX_MAP_HULLS; hull++)
{
int lump;
switch (hull)
{
case 1: lump = LUMP_CLIPNODES; break;
case 2: lump = LUMP_CLIPNODES2; break;
case 3: lump = LUMP_CLIPNODES3; break;
default:
Error ("bad hull number %d", hull);
break;
}
AddLump(lump, g_dclipnodes[hull - 1], g_numclipnodes[hull - 1] * sizeof(dclipnode_t), header, bspfile);
}
#else
AddLump(LUMP_CLIPNODES, g_dclipnodes, g_numclipnodes * sizeof(dclipnode_t), header, bspfile);
#endif
AddLump(LUMP_MARKSURFACES, g_dmarksurfaces, g_nummarksurfaces * sizeof(g_dmarksurfaces[0]), header, bspfile);
AddLump(LUMP_SURFEDGES, g_dsurfedges, g_numsurfedges * sizeof(g_dsurfedges[0]), header, bspfile);
AddLump(LUMP_EDGES, g_dedges, g_numedges * sizeof(dedge_t), header, bspfile);
AddLump(LUMP_MODELS, g_dmodels, g_nummodels * sizeof(dmodel_t), header, bspfile);
AddLump(LUMP_LIGHTING, g_dlightdata, g_lightdatasize, header, bspfile);
AddLump(LUMP_VISIBILITY,g_dvisdata, g_visdatasize, header, bspfile);
AddLump(LUMP_ENTITIES, g_dentdata, g_entdatasize, header, bspfile);
AddLump(LUMP_TEXTURES, g_dtexdata, g_texdatasize, header, bspfile);
fseek(bspfile, 0, SEEK_SET);
SafeWrite(bspfile, header, sizeof(dheader_t));
fclose(bspfile);
}
#ifdef ZHLT_64BIT_FIX
#ifdef PLATFORM_CAN_CALC_EXTENT
// =====================================================================================
// GetFaceExtents (with PLATFORM_CAN_CALC_EXTENT on)
// =====================================================================================
#ifdef SYSTEM_WIN32
#ifdef VERSION_32BIT
static void CorrectFPUPrecision ()
{
unsigned int currentcontrol;
if (_controlfp_s (&currentcontrol, 0, 0))
{
Warning ("Couldn't get FPU precision");
}
else
{
unsigned int val = (currentcontrol & _MCW_PC);
if (val != _PC_53)
{
Warning ("FPU precision is %s. Setting to %s.", (val == _PC_24? "24": val == _PC_64? "64": "invalid"), "53");
if (_controlfp_s (&currentcontrol, _PC_53, _MCW_PC)
|| (currentcontrol & _MCW_PC) != _PC_53)
{
Warning ("Couldn't set FPU precision");
}
}
}
}
#endif
#ifdef VERSION_64BIT
static void CorrectFPUPrecision ()
{
// do nothing, because we use SSE registers
}
#endif
#endif
#ifdef SYSTEM_POSIX
static void CorrectFPUPrecision ()
{
// just leave it to default and see if CalcFaceExtents_test gives us any error
}
#endif
float CalculatePointVecsProduct (const volatile float *point, const volatile float *vecs)
{
volatile double val;
volatile double tmp;
val = (double)point[0] * (double)vecs[0]; // always do one operation at a time and save to memory
tmp = (double)point[1] * (double)vecs[1];
val = val + tmp;
tmp = (double)point[2] * (double)vecs[2];
val = val + tmp;
val = val + (double)vecs[3];
return (float)val;
}
bool CalcFaceExtents_test ()
{
const int numtestcases = 6;
volatile float testcases[numtestcases][8] = {
{1, 1, 1, 1, 0.375 * DBL_EPSILON, 0.375 * DBL_EPSILON, -1, 0},
{1, 1, 1, 0.375 * DBL_EPSILON, 0.375 * DBL_EPSILON, 1, -1, DBL_EPSILON},
{DBL_EPSILON, DBL_EPSILON, 1, 0.375, 0.375, 1, -1, DBL_EPSILON},
{1, 1, 1, 1, 1, 0.375 * FLT_EPSILON, -2, 0.375 * FLT_EPSILON},
{1, 1, 1, 1, 0.375 * FLT_EPSILON, 1, -2, 0.375 * FLT_EPSILON},
{1, 1, 1, 0.375 * FLT_EPSILON, 1, 1, -2, 0.375 * FLT_EPSILON}};
bool ok;
// If the test failed, please check:
// 1. whether the calculation is performed on FPU
// 2. whether the register precision is too low
CorrectFPUPrecision ();
ok = true;
for (int i = 0; i < 6; i++)
{
float val = CalculatePointVecsProduct (&testcases[i][0], &testcases[i][3]);
if (val != testcases[i][7])
{
Warning ("internal error: CalcFaceExtents_test failed on case %d (%.20f != %.20f).", i, val, testcases[i][7]);
ok = false;
}
}
return ok;
}
void GetFaceExtents (int facenum, int mins_out[2], int maxs_out[2])
{
CorrectFPUPrecision ();
dface_t *f;
float mins[2], maxs[2], val;
int i, j, e;
dvertex_t *v;
texinfo_t *tex;
int bmins[2], bmaxs[2];
f = &g_dfaces[facenum];
mins[0] = mins[1] = 999999;
maxs[0] = maxs[1] = -99999;
#ifdef ZHLT_EMBEDLIGHTMAP
tex = &g_texinfo[ParseTexinfoForFace (f)];
#else
tex = &g_texinfo[f->texinfo];
#endif
for (i = 0; i < f->numedges; i++)
{
e = g_dsurfedges[f->firstedge + i];
if (e >= 0)
{
v = &g_dvertexes[g_dedges[e].v[0]];
}
else
{
v = &g_dvertexes[g_dedges[-e].v[1]];
}
for (j = 0; j < 2; j++)
{
// The old code: val = v->point[0] * tex->vecs[j][0] + v->point[1] * tex->vecs[j][1] + v->point[2] * tex->vecs[j][2] + tex->vecs[j][3];
// was meant to be compiled for x86 under MSVC (prior to VS 11), so the intermediate values were stored as 64-bit double by default.
// The new code will produce the same result as the old code, but it's portable for different platforms.
// See this article for details: Intermediate Floating-Point Precision by Bruce-Dawson http://www.altdevblogaday.com/2012/03/22/intermediate-floating-point-precision/
// The essential reason for having this ugly code is to get exactly the same value as the counterpart of game engine.
// The counterpart of game engine is the function CalcFaceExtents in HLSDK.
// So we must also know how Valve compiles HLSDK. I think Valve compiles HLSDK with VC6.0 in the past.
val = CalculatePointVecsProduct (v->point, tex->vecs[j]);
if (val < mins[j])
{
mins[j] = val;
}
if (val > maxs[j])
{
maxs[j] = val;
}
}
}
for (i = 0; i < 2; i++)
{
bmins[i] = (int)floor (mins[i] / TEXTURE_STEP);
bmaxs[i] = (int)ceil (maxs[i] / TEXTURE_STEP);
}
for (i = 0; i < 2; i++)
{
mins_out[i] = bmins[i];
maxs_out[i] = bmaxs[i];
}
}
// =====================================================================================
// WriteExtentFile
// =====================================================================================
void WriteExtentFile (const char *const filename)
{
FILE *f;
f = fopen (filename, "w");
if (!f)
{
Error ("Error opening %s: %s", filename, strerror(errno));
}
fprintf (f, "%i\n", g_numfaces);
for (int i = 0; i < g_numfaces; i++)
{
int mins[2];
int maxs[2];
GetFaceExtents (i, mins, maxs);
fprintf (f, "%i %i %i %i\n", mins[0], mins[1], maxs[0], maxs[1]);
}
fclose (f);
}
#else
typedef struct
{
int mins[2];
int maxs[2];
}
faceextent_t;
bool g_faceextents_loaded = false;
faceextent_t g_faceextents[MAX_MAP_FACES]; //[g_numfaces]
// =====================================================================================
// LoadExtentFile
// =====================================================================================
void LoadExtentFile (const char *const filename)
{
FILE *f;
f = fopen (filename, "r");
if (!f)
{
Error ("Error opening %s: %s", filename, strerror(errno));
}
int count;
int numfaces;
count = fscanf (f, "%i\n", (int *)&numfaces);
if (count != 1)
{
Error ("LoadExtentFile (line %i): scanf failure", 1);
}
if (numfaces != g_numfaces)
{
Error ("LoadExtentFile: numfaces(%i) doesn't match g_numfaces(%i)", numfaces, g_numfaces);
}
for (int i = 0; i < g_numfaces; i++)
{
faceextent_t *e = &g_faceextents[i];
count = fscanf (f, "%i %i %i %i\n", (int *)&e->mins[0], (int *)&e->mins[1], (int *)&e->maxs[0], (int *)&e->maxs[1]);
if (count != 4)
{
Error ("LoadExtentFile (line %i): scanf failure", i + 2);
}
}
fclose (f);
g_faceextents_loaded = true;
}
// =====================================================================================
// GetFaceExtents (with PLATFORM_CAN_CALC_EXTENT off)
// =====================================================================================
// ZHLT_EMBEDLIGHTMAP: the result of "GetFaceExtents" and the values stored in ".ext" file should always be the original extents;
// the new extents of the "?_rad" textures should never appear ("?_rad" textures should be transparent to the tools).
// As a consequance, the reported AllocBlock might be inaccurate (usually falsely larger), but it accurately predicts the amount of AllocBlock after the embedded lightmaps are deleted.
void GetFaceExtents (int facenum, int mins_out[2], int maxs_out[2])
{
if (!g_faceextents_loaded)
{
Error ("GetFaceExtents: internal error: extent file has not been loaded.");
}
faceextent_t *e = &g_faceextents[facenum];
int i;
for (i = 0; i < 2; i++)
{
mins_out[i] = e->mins[i];
maxs_out[i] = e->maxs[i];
}
}
#endif
#endif
//
// =====================================================================================
//
#ifdef ZHLT_CHART_AllocBlock
const int BLOCK_WIDTH = 128;
const int BLOCK_HEIGHT = 128;
typedef struct lightmapblock_s
{
lightmapblock_s *next;
bool used;
int allocated[BLOCK_WIDTH];
}
lightmapblock_t;
void DoAllocBlock (lightmapblock_t *blocks, int w, int h)
{
lightmapblock_t *block;
// code from Quake
int i, j;
int best, best2;
int x, y;
if (w < 1 || h < 1)
{
Error ("DoAllocBlock: internal error.");
}
for (block = blocks; block; block = block->next)
{
best = BLOCK_HEIGHT;
for (i = 0; i < BLOCK_WIDTH - w; i++)
{
best2 = 0;
for (j = 0; j < w; j++)
{
if (block->allocated[i + j] >= best)
break;
if (block->allocated[i + j] > best2)
best2 = block->allocated[i + j];
}
if (j == w)
{
x = i;
y = best = best2;
}
}
if (best + h <= BLOCK_HEIGHT)
{
block->used = true;
for (i = 0; i < w; i++)
{
block->allocated[x + i] = best + h;
}
return;
}
if (!block->next)
{ // need to allocate a new block
if (!block->used)
{
Warning ("CountBlocks: invalid extents %dx%d", w, h);
return;
}
block->next = (lightmapblock_t *)malloc (sizeof (lightmapblock_t));
hlassume (block->next != NULL, assume_NoMemory);
memset (block->next, 0, sizeof (lightmapblock_t));
}
}
}
int CountBlocks ()
{
#ifdef ZHLT_64BIT_FIX
#if !defined (PLATFORM_CAN_CALC_EXTENT) && !defined (HLRAD)
return -1; // otherwise GetFaceExtents will error
#endif
#endif
lightmapblock_t *blocks;
blocks = (lightmapblock_t *)malloc (sizeof (lightmapblock_t));
hlassume (blocks != NULL, assume_NoMemory);
memset (blocks, 0, sizeof (lightmapblock_t));
int k;
for (k = 0; k < g_numfaces; k++)
{
dface_t *f = &g_dfaces[k];
#ifdef ZHLT_EMBEDLIGHTMAP
const char *texname = GetTextureByNumber (ParseTexinfoForFace (f));
#else
const char *texname = GetTextureByNumber (f->texinfo);
#endif
if (!strncmp (texname, "sky", 3) //sky, no lightmap allocation.
|| !strncmp (texname, "!", 1) || !strncasecmp (texname, "water", 5) || !strncasecmp (texname, "laser", 5) //water, no lightmap allocation.
#ifdef ZHLT_EMBEDLIGHTMAP
|| (g_texinfo[ParseTexinfoForFace (f)].flags & TEX_SPECIAL) //aaatrigger, I don't know.
#else
|| (g_texinfo[f->texinfo].flags & TEX_SPECIAL) //aaatrigger, I don't know.
#endif
)
{
continue;
}
int extents[2];
vec3_t point;
{
#ifdef ZHLT_64BIT_FIX
int bmins[2];
int bmaxs[2];
int i;
GetFaceExtents (k, bmins, bmaxs);
for (i = 0; i < 2; i++)
{
extents[i] = (bmaxs[i] - bmins[i]) * TEXTURE_STEP;
}
VectorClear (point);
if (f->numedges > 0)
{
int e = g_dsurfedges[f->firstedge];
dvertex_t *v = &g_dvertexes[g_dedges[abs (e)].v[e >= 0? 0: 1]];
VectorCopy (v->point, point);
}
#else
float mins[2], maxs[2];
int bmins[2], bmaxs[2];
texinfo_t *tex;
tex = &g_texinfo[f->texinfo];
mins[0] = mins[1] = 999999;
maxs[0] = maxs[1] = -99999;
VectorClear (point);
int i;
for (i = 0; i < f->numedges; i++)
{
int e;
dvertex_t *v;
int j;
e = g_dsurfedges[f->firstedge + i];
if (e >= 0)
{
v = &g_dvertexes[g_dedges[e].v[0]];
}
else
{
v = &g_dvertexes[g_dedges[-e].v[1]];
}
if (i == 0)
{
VectorCopy (v->point, point);
}
for (j = 0; j < 2; j++)
{
float val = v->point[0] * tex->vecs[j][0] + v->point[1] * tex->vecs[j][1]
+ v->point[2] * tex->vecs[j][2] + tex->vecs[j][3];
if (val < mins[j])
{
mins[j] = val;
}
if (val > maxs[j])
{
maxs[j] = val;
}
}
}
for (i = 0; i < 2; i++)
{
bmins[i] = floor (mins[i] / TEXTURE_STEP);
bmaxs[i] = ceil (maxs[i] / TEXTURE_STEP);
extents[i] = (bmaxs[i] - bmins[i]) * TEXTURE_STEP;
}
#endif
}
if (extents[0] < 0 || extents[1] < 0 || extents[0] > qmax (512, MAX_SURFACE_EXTENT * TEXTURE_STEP) || extents[1] > qmax (512, MAX_SURFACE_EXTENT * TEXTURE_STEP))
// the default restriction from the engine is 512, but place 'max (512, MAX_SURFACE_EXTENT * TEXTURE_STEP)' here in case someone raise the limit
{
Warning ("Bad surface extents %d/%d at position (%.0f,%.0f,%.0f)", extents[0], extents[1], point[0], point[1], point[2]);
continue;
}
DoAllocBlock (blocks, (extents[0] / TEXTURE_STEP) + 1, (extents[1] / TEXTURE_STEP) + 1);
}
int count = 0;
lightmapblock_t *next;
for (; blocks; blocks = next)
{
if (blocks->used)
{
count++;
}
next = blocks->next;
free (blocks);
}
return count;
}
#endif
#ifdef ZHLT_CHART_WADFILES
bool NoWadTextures ()
{
// copied from loadtextures.cpp
int numtextures = g_texdatasize? ((dmiptexlump_t *)g_dtexdata)->nummiptex: 0;
for (int i = 0; i < numtextures; i++)
{
int offset = ((dmiptexlump_t *)g_dtexdata)->dataofs[i];
int size = g_texdatasize - offset;
if (offset < 0 || size < (int)sizeof (miptex_t))
{
// missing textures have ofs -1
continue;
}
miptex_t *mt = (miptex_t *)&g_dtexdata[offset];
if (!mt->offsets[0])
{
return false;
}
}
return true;
}
char *FindWadValue ()
// return NULL for syntax error
// this function needs to be as stable as possible because it might be called from ripent
{
int linestart, lineend;
bool inentity = false;
for (linestart = 0; linestart < g_entdatasize; )
{
for (lineend = linestart; lineend < g_entdatasize; lineend++)
if (g_dentdata[lineend] == '\r' || g_dentdata[lineend] == '\n')
break;
if (lineend == linestart + 1)
{
if (g_dentdata[linestart] == '{')
{
if (inentity)
return NULL;
inentity = true;
}
else if (g_dentdata[linestart] == '}')
{
if (!inentity)
return NULL;
inentity = false;
return _strdup (""); // only parse the first entity
}
else
return NULL;
}
else
{
if (!inentity)
return NULL;
int quotes[4];
int i, j;
for (i = 0, j = linestart; i < 4; i++, j++)
{
for (; j < lineend; j++)
if (g_dentdata[j] == '\"')
break;
if (j >= lineend)
break;
quotes[i] = j;
}
if (i != 4 || quotes[0] != linestart || quotes[3] != lineend - 1)
{
return NULL;
}
if (quotes[1] - (quotes[0] + 1) == (int)strlen ("wad") && !strncmp (&g_dentdata[quotes[0] + 1], "wad", strlen ("wad")))
{
int len = quotes[3] - (quotes[2] + 1);
char *value = (char *)malloc (len + 1);
hlassume (value != NULL, assume_NoMemory);
memcpy (value, &g_dentdata[quotes[2] + 1], len);
value[len] = '\0';
return value;
}
}
for (linestart = lineend; linestart < g_entdatasize; linestart++)
if (g_dentdata[linestart] != '\r' && g_dentdata[linestart] != '\n')
break;
}
return NULL;
}
#endif
#define ENTRIES(a) (sizeof(a)/sizeof(*(a)))
#define ENTRYSIZE(a) (sizeof(*(a)))
// =====================================================================================
// ArrayUsage
// blah
// =====================================================================================
static int ArrayUsage(const char* const szItem, const int items, const int maxitems, const int itemsize)
{
float percentage = maxitems ? items * 100.0 / maxitems : 0.0;
#ifdef ZHLT_MAX_MAP_LEAFS
Log("%-13s %7i/%-7i %8i/%-8i (%4.1f%%)\n", szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage);
#else
Log("%-12s %7i/%-7i %7i/%-7i (%4.1f%%)\n", szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage);
#endif
return items * itemsize;
}
// =====================================================================================
// GlobUsage
// pritn out global ussage line in chart
// =====================================================================================
static int GlobUsage(const char* const szItem, const int itemstorage, const int maxstorage)
{
float percentage = maxstorage ? itemstorage * 100.0 / maxstorage : 0.0;
#ifdef ZHLT_MAX_MAP_LEAFS
Log("%-13s [variable] %8i/%-8i (%4.1f%%)\n", szItem, itemstorage, maxstorage, percentage);
#else
Log("%-12s [variable] %7i/%-7i (%4.1f%%)\n", szItem, itemstorage, maxstorage, percentage);
#endif
return itemstorage;
}
// =====================================================================================
// PrintBSPFileSizes
// Dumps info about current file
// =====================================================================================
void PrintBSPFileSizes()
{
int numtextures = g_texdatasize ? ((dmiptexlump_t*)g_dtexdata)->nummiptex : 0;
int totalmemory = 0;
#ifdef ZHLT_CHART_AllocBlock
int numallocblocks = CountBlocks ();
int maxallocblocks = 64;
#endif
#ifdef ZHLT_CHART_WADFILES
bool nowadtextures = NoWadTextures (); // We don't have this check at hlcsg, because only legacy compile tools don't empty "wad" value in "-nowadtextures" compiles.
char *wadvalue = FindWadValue ();
#endif
Log("\n");
Log("Object names Objects/Maxobjs Memory / Maxmem Fullness\n");
Log("------------ --------------- --------------- --------\n");
totalmemory += ArrayUsage("models", g_nummodels, ENTRIES(g_dmodels), ENTRYSIZE(g_dmodels));
totalmemory += ArrayUsage("planes", g_numplanes, MAX_MAP_PLANES, ENTRYSIZE(g_dplanes));
totalmemory += ArrayUsage("vertexes", g_numvertexes, ENTRIES(g_dvertexes), ENTRYSIZE(g_dvertexes));
totalmemory += ArrayUsage("nodes", g_numnodes, ENTRIES(g_dnodes), ENTRYSIZE(g_dnodes));
#ifdef HLCSG_HLBSP_REDUCETEXTURE
totalmemory += ArrayUsage("texinfos", g_numtexinfo, MAX_MAP_TEXINFO, ENTRYSIZE(g_texinfo));
#else
totalmemory += ArrayUsage("texinfos", g_numtexinfo, ENTRIES(g_texinfo), ENTRYSIZE(g_texinfo));
#endif
totalmemory += ArrayUsage("faces", g_numfaces, ENTRIES(g_dfaces), ENTRYSIZE(g_dfaces));
#ifdef ZHLT_WARNWORLDFACES
totalmemory += ArrayUsage("* worldfaces", (g_nummodels > 0? g_dmodels[0].numfaces: 0), MAX_MAP_WORLDFACES, 0);
#endif
#ifdef ZHLT_XASH2
for (int hull = 1; hull < MAX_MAP_HULLS; hull++)
{
char buffer[32];
sprintf (buffer, "clipnodes%d", hull);
totalmemory += ArrayUsage(buffer, g_numclipnodes[hull - 1], ENTRIES(g_dclipnodes[hull - 1]), ENTRYSIZE(g_dclipnodes[hull - 1]));
}
#else
totalmemory += ArrayUsage("clipnodes", g_numclipnodes, ENTRIES(g_dclipnodes), ENTRYSIZE(g_dclipnodes));
#endif
#ifdef ZHLT_MAX_MAP_LEAFS
totalmemory += ArrayUsage("leaves", g_numleafs, MAX_MAP_LEAFS, ENTRYSIZE(g_dleafs));
totalmemory += ArrayUsage("* worldleaves", (g_nummodels > 0? g_dmodels[0].visleafs: 0), MAX_MAP_LEAFS_ENGINE, 0);
#else
totalmemory += ArrayUsage("leaves", g_numleafs, ENTRIES(g_dleafs), ENTRYSIZE(g_dleafs));
#endif
totalmemory += ArrayUsage("marksurfaces", g_nummarksurfaces, ENTRIES(g_dmarksurfaces), ENTRYSIZE(g_dmarksurfaces));
totalmemory += ArrayUsage("surfedges", g_numsurfedges, ENTRIES(g_dsurfedges), ENTRYSIZE(g_dsurfedges));
totalmemory += ArrayUsage("edges", g_numedges, ENTRIES(g_dedges), ENTRYSIZE(g_dedges));
totalmemory += GlobUsage("texdata", g_texdatasize, g_max_map_miptex);
totalmemory += GlobUsage("lightdata", g_lightdatasize, g_max_map_lightdata);
totalmemory += GlobUsage("visdata", g_visdatasize, sizeof(g_dvisdata));
totalmemory += GlobUsage("entdata", g_entdatasize, sizeof(g_dentdata));
#ifdef ZHLT_CHART_AllocBlock
#ifdef ZHLT_64BIT_FIX
if (numallocblocks == -1)
{
Log ("* AllocBlock [ not available to the " PLATFORM_VERSIONSTRING " version ]\n");
}
else
{
#endif
totalmemory += ArrayUsage ("* AllocBlock", numallocblocks, maxallocblocks, 0);
#ifdef ZHLT_64BIT_FIX
}
#endif
#endif
Log("%i textures referenced\n", numtextures);
Log("=== Total BSP file data space used: %d bytes ===\n", totalmemory);
#ifdef ZHLT_CHART_WADFILES
if (nowadtextures)
{
Log ("Wad files required to run the map: (None)\n");
}
else if (wadvalue == NULL)
{
Log ("Wad files required to run the map: (Couldn't parse wad keyvalue from entity data)\n");
}
else
{
Log ("Wad files required to run the map: \"%s\"\n", wadvalue);
}
if (wadvalue)
{
free (wadvalue);
}
#endif
}
#ifdef ZHLT_EMBEDLIGHTMAP
// =====================================================================================
// ParseImplicitTexinfoFromTexture
// purpose: get the actual texinfo for a face. the tools shouldn't directly use f->texinfo after embedlightmap is done
// =====================================================================================
int ParseImplicitTexinfoFromTexture (int miptex)
{
int texinfo;
int numtextures = g_texdatasize? ((dmiptexlump_t *)g_dtexdata)->nummiptex: 0;
int offset;
int size;
miptex_t *mt;
char name[16];
if (miptex < 0 || miptex >= numtextures)
{
Warning ("ParseImplicitTexinfoFromTexture: internal error: invalid texture number %d.", miptex);
return -1;
}
offset = ((dmiptexlump_t *)g_dtexdata)->dataofs[miptex];
size = g_texdatasize - offset;
if (offset < 0 || g_dtexdata + offset < (byte *)&((dmiptexlump_t *)g_dtexdata)->dataofs[numtextures] ||
size < (int)sizeof (miptex_t))
{
return -1;
}
mt = (miptex_t *)&g_dtexdata[offset];
safe_strncpy (name, mt->name, 16);
if (!(strlen (name) >= 6 && !strncasecmp (&name[1], "_rad", 4) && '0' <= name[5] && name[5] <= '9'))
{
return -1;
}
texinfo = atoi (&name[5]);
if (texinfo < 0 || texinfo >= g_numtexinfo)
{
Warning ("Invalid index of original texinfo: %d parsed from texture name '%s'.", texinfo, name);
return -1;
}
return texinfo;
}
int ParseTexinfoForFace (const dface_t *f)
{
int texinfo;
int miptex;
int texinfo2;
texinfo = f->texinfo;
miptex = g_texinfo[texinfo].miptex;
if (miptex != -1)
{
texinfo2 = ParseImplicitTexinfoFromTexture (miptex);
if (texinfo2 != -1)
{
texinfo = texinfo2;
}
}
return texinfo;
}
// =====================================================================================
// DeleteEmbeddedLightmaps
// removes all "?_rad*" textures that are created by hlrad
// this function does nothing if the map has no textures with name "?_rad*"
// =====================================================================================
void DeleteEmbeddedLightmaps ()
{
int countrestoredfaces = 0;
int countremovedtexinfos = 0;
int countremovedtextures = 0;
int i;
int numtextures = g_texdatasize? ((dmiptexlump_t *)g_dtexdata)->nummiptex: 0;
// Step 1: parse the original texinfo index stored in each "?_rad*" texture
// and restore the texinfo for the faces that have had their lightmap embedded
for (i = 0; i < g_numfaces; i++)
{
dface_t *f = &g_dfaces[i];
int texinfo;
texinfo = ParseTexinfoForFace (f);
if (texinfo != f->texinfo)
{
f->texinfo = texinfo;
countrestoredfaces++;
}
}
// Step 2: remove redundant texinfo
{
bool *texinfoused = (bool *)malloc (g_numtexinfo * sizeof (bool));
hlassume (texinfoused != NULL, assume_NoMemory);
for (i = 0; i < g_numtexinfo; i++)
{
texinfoused[i] = false;
}
for (i = 0; i < g_numfaces; i++)
{
dface_t *f = &g_dfaces[i];
if (f->texinfo < 0 || f->texinfo >= g_numtexinfo)
{
continue;
}
texinfoused[f->texinfo] = true;
}
for (i = g_numtexinfo - 1; i > -1; i--)
{
texinfo_t *info = &g_texinfo[i];
if (texinfoused[i])
{
break; // still used by a face; should not remove this texinfo
}
if (info->miptex < 0 || info->miptex >= numtextures)
{
break; // invalid; should not remove this texinfo
}
if (ParseImplicitTexinfoFromTexture (info->miptex) == -1)
{
break; // not added by hlrad; should not remove this texinfo
}
countremovedtexinfos++;
}
g_numtexinfo = i + 1; // shrink g_texinfo
free (texinfoused);
}
// Step 3: remove redundant textures
{
int numremaining; // number of remaining textures
bool *textureused = (bool *)malloc (numtextures * sizeof (bool));
hlassume (textureused != NULL, assume_NoMemory);
for (i = 0; i < numtextures; i++)
{
textureused[i] = false;
}
for (i = 0; i < g_numtexinfo; i++)
{
texinfo_t *info = &g_texinfo[i];
if (info->miptex < 0 || info->miptex >= numtextures)
{
continue;
}
textureused[info->miptex] = true;
}
for (i = numtextures - 1; i > -1; i--)
{
if (textureused[i] || ParseImplicitTexinfoFromTexture (i) == -1)
{
break; // should not remove this texture
}
countremovedtextures++;
}
numremaining = i + 1;
free (textureused);
if (numremaining < numtextures)
{
dmiptexlump_t *texdata = (dmiptexlump_t *)g_dtexdata;
byte *dataaddr = (byte *)&texdata->dataofs[texdata->nummiptex];
int datasize = (g_dtexdata + texdata->dataofs[numremaining]) - dataaddr;
byte *newdataaddr = (byte *)&texdata->dataofs[numremaining];
memmove (newdataaddr, dataaddr, datasize);
g_texdatasize = (newdataaddr + datasize) - g_dtexdata;
texdata->nummiptex = numremaining;
for (i = 0; i < numremaining; i++)
{
if (texdata->dataofs[i] < 0) // bad texture
{
continue;
}
texdata->dataofs[i] += newdataaddr - dataaddr;
}
numtextures = texdata->nummiptex;
}
}
if (countrestoredfaces > 0 || countremovedtexinfos > 0 || countremovedtextures > 0)
{
Log ("DeleteEmbeddedLightmaps: restored %d faces, removed %d texinfos and %d textures.\n",
countrestoredfaces, countremovedtexinfos, countremovedtextures);
}
}
#endif
// =====================================================================================
// ParseEpair
// entity key/value pairs
// =====================================================================================
epair_t* ParseEpair()
{
epair_t* e;
e = (epair_t*)Alloc(sizeof(epair_t));
if (strlen(g_token) >= MAX_KEY - 1)
Error("ParseEpair: Key token too long (%i > MAX_KEY)", (int)strlen(g_token));
e->key = _strdup(g_token);
GetToken(false);
if (strlen(g_token) >= MAX_VAL - 1) //MAX_VALUE //vluzacn
Error("ParseEpar: Value token too long (%i > MAX_VALUE)", (int)strlen(g_token));
e->value = _strdup(g_token);
return e;
}
/*
* ================
* ParseEntity
* ================
*/
#ifdef ZHLT_INFO_COMPILE_PARAMETERS
// AJM: each tool should have its own version of GetParamsFromEnt which parseentity calls
extern void GetParamsFromEnt(entity_t* mapent);
#endif
bool ParseEntity()
{
epair_t* e;
entity_t* mapent;
if (!GetToken(true))
{
return false;
}
if (strcmp(g_token, "{"))
{
Error("ParseEntity: { not found");
}
if (g_numentities == MAX_MAP_ENTITIES)
{
Error("g_numentities == MAX_MAP_ENTITIES");
}
mapent = &g_entities[g_numentities];
g_numentities++;
while (1)
{
if (!GetToken(true))
{
Error("ParseEntity: EOF without closing brace");
}
if (!strcmp(g_token, "}"))
{
break;
}
e = ParseEpair();
e->next = mapent->epairs;
mapent->epairs = e;
}
#ifdef ZHLT_INFO_COMPILE_PARAMETERS // AJM
if (!strcmp(ValueForKey(mapent, "classname"), "info_compile_parameters"))
{
Log("Map entity info_compile_parameters detected, using compile settings\n");
GetParamsFromEnt(mapent);
}
#endif
#ifdef ZHLT_ENTITY_LIGHTSURFACE
// ugly code
if (!strncmp(ValueForKey (mapent, "classname"), "light", 5) && *ValueForKey (mapent, "_tex"))
{
SetKeyValue (mapent, "convertto", ValueForKey (mapent, "classname"));
SetKeyValue (mapent, "classname", "light_surface");
}
#endif
#ifdef ZHLT_ENTITY_LIGHTSHADOW
if (!strcmp (ValueForKey (mapent, "convertfrom"), "light_shadow")
#ifdef ZHLT_ENTITY_LIGHTBOUNCE
|| !strcmp (ValueForKey (mapent, "convertfrom"), "light_bounce")
#endif
)
{
SetKeyValue (mapent, "convertto", ValueForKey (mapent, "classname"));
SetKeyValue (mapent, "classname", ValueForKey (mapent, "convertfrom"));
SetKeyValue (mapent, "convertfrom", "");
}
#endif
#ifdef ZHLT_ENTITY_INFOSUNLIGHT
if (!strcmp (ValueForKey (mapent, "classname"), "light_environment") &&
!strcmp (ValueForKey (mapent, "convertfrom"), "info_sunlight"))
{
while (mapent->epairs)
{
DeleteKey (mapent, mapent->epairs->key);
}
memset (mapent, 0, sizeof(entity_t));
g_numentities--;
return true;
}
if (!strcmp (ValueForKey (mapent, "classname"), "light_environment") &&
IntForKey (mapent, "_fake"))
{
SetKeyValue (mapent, "classname", "info_sunlight");
}
#endif
return true;
}
// =====================================================================================
// ParseEntities
// Parses the dentdata string into entities
// =====================================================================================
void ParseEntities()
{
g_numentities = 0;
ParseFromMemory(g_dentdata, g_entdatasize);
while (ParseEntity())
{
}
}
// =====================================================================================
// UnparseEntities
// Generates the dentdata string from all the entities
// =====================================================================================
#ifdef ZHLT_ENTITY_INFOSUNLIGHT
int anglesforvector (float angles[3], const float vector[3])
{
float z = vector[2], r = sqrt (vector[0] * vector[0] + vector[1] * vector[1]);
float tmp;
if (sqrt (z*z + r*r) < NORMAL_EPSILON)
{
return -1;
}
else
{
tmp = sqrt (z*z + r*r);
z /= tmp, r /= tmp;
if (r < NORMAL_EPSILON)
{
if (z < 0)
{
angles[0] = -90, angles[1] = 0;
}
else
{
angles[0] = 90, angles[1] = 0;
}
}
else
{
angles[0] = atan (z / r) / Q_PI * 180;
float x = vector[0], y = vector[1];
tmp = sqrt (x*x + y*y);
x /= tmp, y /= tmp;
if (x < -1 + NORMAL_EPSILON)
{
angles[1] = -180;
}
else
{
if (y >= 0)
{
angles[1] = 2 * atan (y / (1+x)) / Q_PI * 180;
}
else
{
angles[1] = 2 * atan (y / (1+x)) / Q_PI * 180 + 360;
}
}
}
}
angles[2] = 0;
return 0;
}
#endif
void UnparseEntities()
{
char* buf;
char* end;
epair_t* ep;
char line[MAXTOKEN];
int i;
buf = g_dentdata;
end = buf;
*end = 0;
#ifdef ZHLT_ENTITY_INFOSUNLIGHT
for (i = 0; i < g_numentities; i++)
{
entity_t *mapent = &g_entities[i];
if (!strcmp (ValueForKey (mapent, "classname"), "info_sunlight") ||
!strcmp (ValueForKey (mapent, "classname"), "light_environment") )
{
float vec[3] = {0,0,0};
{
sscanf (ValueForKey (mapent, "angles"), "%f %f %f", &vec[0], &vec[1], &vec[2]);
float pitch = FloatForKey(mapent, "pitch");
if (pitch)
vec[0] = pitch;
const char *target = ValueForKey (mapent, "target");
if (target[0])
{
entity_t *targetent = FindTargetEntity (target);
if (targetent)
{
float origin1[3] = {0,0,0}, origin2[3] = {0,0,0}, normal[3];
sscanf (ValueForKey (mapent, "origin"), "%f %f %f", &origin1[0], &origin1[1], &origin1[2]);
sscanf (ValueForKey (targetent, "origin"), "%f %f %f", &origin2[0], &origin2[1], &origin2[2]);
VectorSubtract (origin2, origin1, normal);
anglesforvector (vec, normal);
}
}
}
char stmp[1024];
safe_snprintf (stmp, 1024, "%g %g %g", vec[0], vec[1], vec[2]);
SetKeyValue (mapent, "angles", stmp);
DeleteKey (mapent, "pitch");
if (!strcmp (ValueForKey (mapent, "classname"), "info_sunlight"))
{
if (g_numentities == MAX_MAP_ENTITIES)
{
Error("g_numentities == MAX_MAP_ENTITIES");
}
entity_t *newent = &g_entities[g_numentities++];
newent->epairs = mapent->epairs;
SetKeyValue (newent, "classname", "light_environment");
SetKeyValue (newent, "_fake", "1");
mapent->epairs = NULL;
}
}
}
#endif
#ifdef ZHLT_ENTITY_LIGHTSHADOW
for (i = 0; i < g_numentities; i++)
{
entity_t *mapent = &g_entities[i];
if (!strcmp (ValueForKey (mapent, "classname"), "light_shadow")
#ifdef ZHLT_ENTITY_LIGHTBOUNCE
|| !strcmp (ValueForKey (mapent, "classname"), "light_bounce")
#endif
)
{
SetKeyValue (mapent, "convertfrom", ValueForKey (mapent, "classname"));
SetKeyValue (mapent, "classname", (*ValueForKey (mapent, "convertto")? ValueForKey (mapent, "convertto"): "light"));
SetKeyValue (mapent, "convertto", "");
}
}
#endif
#ifdef ZHLT_ENTITY_LIGHTSURFACE
// ugly code
for (i = 0; i < g_numentities; i++)
{
entity_t *mapent = &g_entities[i];
if (!strcmp (ValueForKey (mapent, "classname"), "light_surface"))
{
if (!*ValueForKey (mapent, "_tex"))
{
SetKeyValue (mapent, "_tex", " ");
}
const char *newclassname = ValueForKey (mapent, "convertto");
if (!*newclassname)
{
SetKeyValue (mapent, "classname", "light");
}
else if (strncmp (newclassname, "light", 5))
{
Error ("New classname for 'light_surface' should begin with 'light' not '%s'.\n", newclassname);
}
else
{
SetKeyValue (mapent, "classname", newclassname);
}
SetKeyValue (mapent, "convertto", "");
}
}
#endif
#ifdef HLCSG_OPTIMIZELIGHTENTITY
#ifdef HLCSG
extern bool g_nolightopt;
if (!g_nolightopt)
{
int i, j;
int count = 0;
bool *lightneedcompare = (bool *)malloc (g_numentities * sizeof (bool));
hlassume (lightneedcompare != NULL, assume_NoMemory);
memset (lightneedcompare, 0, g_numentities * sizeof(bool));
for (i = g_numentities - 1; i > -1; i--)
{
entity_t *ent = &g_entities[i];
const char *classname = ValueForKey (ent, "classname");
const char *targetname = ValueForKey (ent, "targetname");
int style = IntForKey (ent, "style");
if (!targetname[0] || strcmp (classname, "light") && strcmp (classname, "light_spot") && strcmp (classname, "light_environment"))
continue;
for (j = i + 1; j < g_numentities; j++)
{
if (!lightneedcompare[j])
continue;
entity_t *ent2 = &g_entities[j];
const char *targetname2 = ValueForKey (ent2, "targetname");
int style2 = IntForKey (ent2, "style");
if (style == style2 && !strcmp (targetname, targetname2))
break;
}
if (j < g_numentities)
{
DeleteKey (ent, "targetname");
count++;
}
else
{
lightneedcompare[i] = true;
}
}
if (count > 0)
{
Log ("%d redundant named lights optimized.\n", count);
}
free (lightneedcompare);
}
#endif
#endif
for (i = 0; i < g_numentities; i++)
{
ep = g_entities[i].epairs;
if (!ep)
{
continue; // ent got removed
}
strcat(end, "{\n");
end += 2;
for (ep = g_entities[i].epairs; ep; ep = ep->next)
{
sprintf(line, "\"%s\" \"%s\"\n", ep->key, ep->value);
strcat(end, line);
end += strlen(line);
}
strcat(end, "}\n");
end += 2;
if (end > buf + MAX_MAP_ENTSTRING)
{
Error("Entity text too long");
}
}
g_entdatasize = end - buf + 1;
}
// =====================================================================================
// SetKeyValue
// makes a keyvalue
// =====================================================================================
#ifdef ZHLT_DELETEKEY
void DeleteKey(entity_t* ent, const char* const key)
{
epair_t **pep;
for (pep = &ent->epairs; *pep; pep = &(*pep)->next)
{
if (!strcmp ((*pep)->key, key))
{
epair_t *ep = *pep;
*pep = ep->next;
Free(ep->key);
Free(ep->value);
Free(ep);
return;
}
}
}
#endif
void SetKeyValue(entity_t* ent, const char* const key, const char* const value)
{
epair_t* ep;
#ifdef ZHLT_DELETEKEY
if (!value[0])
{
DeleteKey (ent, key);
return;
}
#endif
for (ep = ent->epairs; ep; ep = ep->next)
{
if (!strcmp(ep->key, key))
{
#ifdef ZHLT_DELETEKEY
char *value2 = strdup (value);
Free (ep->value);
ep->value = value2;
#else
Free(ep->value);
ep->value = strdup(value);
#endif
return;
}
}
ep = (epair_t*)Alloc(sizeof(*ep));
ep->next = ent->epairs;
ent->epairs = ep;
ep->key = strdup(key);
ep->value = strdup(value);
}
// =====================================================================================
// ValueForKey
// returns the value for a passed entity and key
// =====================================================================================
const char* ValueForKey(const entity_t* const ent, const char* const key)
{
epair_t* ep;
for (ep = ent->epairs; ep; ep = ep->next)
{
if (!strcmp(ep->key, key))
{
return ep->value;
}
}
return "";
}
// =====================================================================================
// IntForKey
// =====================================================================================
int IntForKey(const entity_t* const ent, const char* const key)
{
return atoi(ValueForKey(ent, key));
}
// =====================================================================================
// FloatForKey
// =====================================================================================
vec_t FloatForKey(const entity_t* const ent, const char* const key)
{
return atof(ValueForKey(ent, key));
}
// =====================================================================================
// GetVectorForKey
// returns value for key in vec[0-2]
// =====================================================================================
void GetVectorForKey(const entity_t* const ent, const char* const key, vec3_t vec)
{
const char* k;
double v1, v2, v3;
k = ValueForKey(ent, key);
// scanf into doubles, then assign, so it is vec_t size independent
v1 = v2 = v3 = 0;
sscanf(k, "%lf %lf %lf", &v1, &v2, &v3);
vec[0] = v1;
vec[1] = v2;
vec[2] = v3;
}
// =====================================================================================
// FindTargetEntity
//
// =====================================================================================
entity_t *FindTargetEntity(const char* const target)
{
int i;
const char* n;
for (i = 0; i < g_numentities; i++)
{
n = ValueForKey(&g_entities[i], "targetname");
if (!strcmp(n, target))
{
return &g_entities[i];
}
}
return NULL;
}
void dtexdata_init()
{
g_dtexdata = (byte*)AllocBlock(g_max_map_miptex);
hlassume(g_dtexdata != NULL, assume_NoMemory);
g_dlightdata = (byte*)AllocBlock(g_max_map_lightdata);
hlassume(g_dlightdata != NULL, assume_NoMemory);
}
void CDECL dtexdata_free()
{
FreeBlock(g_dtexdata);
g_dtexdata = NULL;
FreeBlock(g_dlightdata);
g_dlightdata = NULL;
}
// =====================================================================================
// GetTextureByNumber
// Touchy function, can fail with a page fault if all the data isnt kosher
// (i.e. map was compiled with missing textures)
// =====================================================================================
#ifdef HLCSG_HLBSP_VOIDTEXINFO
static char emptystring[1] = {'\0'};
#endif
char* GetTextureByNumber(int texturenumber)
{
#ifdef HLCSG_HLBSP_VOIDTEXINFO
if (texturenumber == -1)
return emptystring;
#endif
texinfo_t* info;
miptex_t* miptex;
int ofs;
info = &g_texinfo[texturenumber];
ofs = ((dmiptexlump_t*)g_dtexdata)->dataofs[info->miptex];
miptex = (miptex_t*)(&g_dtexdata[ofs]);
#ifdef ZHLT_TRENCHBROOMNULL_FIX
// cypress -- hacked-in support for __TB_empty
// just sorta replacing __TB_empty with null here, because
// ericw-tools does the same thing, and this is the best
// place to do it with the least amount of work required.
if (!strncasecmp(miptex->name, "__TB_empty", 10))
return "null";
#endif
return miptex->name;
}
// =====================================================================================
// EntityForModel
// returns entity addy for given modelnum
// =====================================================================================
entity_t* EntityForModel(const int modnum)
{
int i;
const char* s;
char name[16];
sprintf(name, "*%i", modnum);
// search the entities for one using modnum
for (i = 0; i < g_numentities; i++)
{
s = ValueForKey(&g_entities[i], "model");
if (!strcmp(s, name))
{
return &g_entities[i];
}
}
return &g_entities[0];
}