mirror of
https://github.com/nzp-team/vhlt.git
synced 2024-11-10 06:31:38 +00:00
2033 lines
No EOL
60 KiB
C++
2033 lines
No EOL
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 (¤tcontrol, 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 (¤tcontrol, _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];
|
|
} |