diff --git a/doc/server.md b/doc/server.md index 5bffcc533..f06400077 100644 --- a/doc/server.md +++ b/doc/server.md @@ -537,18 +537,23 @@ if found, is replaced with a single character representing message type ### Miscellaneous #### `map_override_path` -Specifies the prefix used to construct path to the entity string override -file. Override file will be loaded from `$\{map_override_path}$\{mapname}.ent`. -Usually this variable is set to `maps/` (notice the trailing slash), and -`.ent` files are placed together with `.bsp` files. Default value is empty -(don't try to override entity strings). +Specifies the directory from which override files with extensions `.ent` or +`.bsp.override` are loaded. Default value is empty (don't try to override +entity strings). Typical value for this is `maps`, but can be customized +per server port. #### Entity overrides -Override files allow the entity string of a map being loaded to be replaced by -a custom data supplied by server operator. This makes it possible to change the -layout of entities on the map (thus creating a new version of the map) without -requiring clients to download anything. Entity string can be dumped from the current -map using `dumpents` server command and later changed with a text editor. +Override files with `.ent` extension allow the entity string of the map being +loaded to be replaced by a custom data supplied by server operator. This makes +it possible to change the layout of entities on the map (thus creating a new +version of the map) without requiring clients to download anything. Entity +string can be dumped from the current map using `dumpents` server command and +later changed with a text editor. + +Override files with `.bsp.override` extension are more complex: they are binary +files that can replace map entity string or checksum. They can also create an +alias for the map. How to create such files is out of scope of this manual +(search the internet for ‘r1q2 map override file generator’). #### `map_visibility_patch` Attempt to patch miscalculated visibility data for some well-known maps diff --git a/inc/common/bsp.h b/inc/common/bsp.h index 7a6804084..90edda4da 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "format/bsp.h" // maximum size of a PVS row, in bytes -#define VIS_MAX_BYTES (MAX_MAP_LEAFS >> 3) +#define VIS_MAX_BYTES (MAX_MAP_CLUSTERS >> 3) // take advantage of 64-bit systems #define VIS_FAST_LONGS(bsp) \ @@ -75,14 +75,10 @@ typedef struct { #define SURF_TRANS_MASK (SURF_TRANS33 | SURF_TRANS66) #define SURF_COLOR_MASK (SURF_TRANS_MASK | SURF_WARP) -#define SURF_NOLM_MASK (SURF_COLOR_MASK | SURF_FLOWING | SURF_SKY) +#define SURF_NOLM_MASK (SURF_COLOR_MASK | SURF_FLOWING | SURF_SKY | SURF_NODRAW) #define DSURF_PLANEBACK 1 -// for lightmap block calculation -#define S_MAX(surf) (((surf)->extents[0] >> 4) + 1) -#define T_MAX(surf) (((surf)->extents[1] >> 4) + 1) - typedef struct mface_s { msurfedge_t *firstsurfedge; int numsurfedges; @@ -95,8 +91,11 @@ typedef struct mface_s { int numstyles; mtexinfo_t *texinfo; - int texturemins[2]; - int extents[2]; + vec3_t lm_axis[2]; + vec2_t lm_offset; + vec2_t lm_scale; + int lm_width; + int lm_height; #if USE_REF == REF_GL int texnum[2]; @@ -110,11 +109,13 @@ typedef struct mface_s { int firstbasis; - int drawframe; + unsigned drawframe; + + unsigned dlightframe; + unsigned dlightbits; - int dlightframe; - int dlightbits; - struct mface_s *next; + struct entity_s *entity; + struct mface_s *next; } mface_t; #endif @@ -125,7 +126,7 @@ typedef struct mnode_s { vec3_t mins; vec3_t maxs; - int visframe; + unsigned visframe; #endif struct mnode_s *parent; /* <====== */ @@ -147,7 +148,7 @@ typedef struct { int contents; int numsides; mbrushside_t *firstbrushside; - int checkcount; // to avoid repeated testings + unsigned checkcount; // to avoid repeated testings } mbrush_t; typedef struct { @@ -157,7 +158,7 @@ typedef struct { vec3_t mins; vec3_t maxs; - int visframe; + unsigned visframe; #endif struct mnode_s *parent; /* <====== */ @@ -181,7 +182,7 @@ typedef struct { typedef struct { int numareaportals; mareaportal_t *firstareaportal; - int floodvalid; + unsigned floodvalid; } marea_t; typedef struct mmodel_s { @@ -201,7 +202,7 @@ typedef struct mmodel_s { mface_t *firstface; #if USE_REF == REF_GL - int drawframe; + unsigned drawframe; #endif #endif } mmodel_t; @@ -248,7 +249,7 @@ typedef struct bsp_s { int numareas; marea_t *areas; - int lastareaportal; // largest portal number used + int numportals; // largest portal number used plus one int numareaportals; // size of the array below mareaportal_t *areaportals; @@ -276,6 +277,8 @@ typedef struct bsp_s { int numbases; mbasis_t *bases; + + bool lm_decoupled; #endif byte *pvs_matrix; @@ -290,13 +293,13 @@ typedef struct bsp_s { int BSP_Load(const char *name, bsp_t **bsp_p); void BSP_Free(bsp_t *bsp); -const char *BSP_GetError(void); +const char *BSP_ErrorString(int err); #if USE_REF typedef struct { mface_t *surf; cplane_t plane; - int s, t; + float s, t; float fraction; } lightpoint_t; diff --git a/inc/common/cmodel.h b/inc/common/cmodel.h index da5afdda8..c6d974389 100644 --- a/inc/common/cmodel.h +++ b/inc/common/cmodel.h @@ -30,12 +30,16 @@ typedef struct { int *floodnums; // if two areas have equal floodnums, // they are connected bool *portalopen; + int override_bits; + int checksum; + char *entitystring; } cm_t; void CM_Init(void); void CM_FreeMap(cm_t *cm); int CM_LoadMap(cm_t *cm, const char *name); +void CM_LoadOverrides(cm_t *cm, char *server, size_t server_size); int CM_NumClusters(cm_t *cm); int CM_NumInlineModels(cm_t *cm); diff --git a/inc/common/utils.h b/inc/common/utils.h index d744d560f..b93dc2273 100644 --- a/inc/common/utils.h +++ b/inc/common/utils.h @@ -44,6 +44,8 @@ bool Com_ParseTimespec(const char *s, int *frames); void Com_PlayerToEntityState(const player_state_t *ps, entity_state_t *es); +bool Com_ParseMapName(char *out, const char *in, size_t size); + unsigned Com_HashString(const char *s, unsigned size); unsigned Com_HashStringLen(const char *s, size_t len, unsigned size); diff --git a/inc/format/bsp.h b/inc/format/bsp.h index c45b91ce5..6d09e386a 100644 --- a/inc/format/bsp.h +++ b/inc/format/bsp.h @@ -28,64 +28,19 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #define IDBSPHEADER MakeLittleLong('I','B','S','P') +#define IDBSPHEADER_EXT MakeLittleLong('Q','B','S','P') #define BSPVERSION 38 -// upper design bounds -// leaffaces, leafbrushes, planes, and verts are still bounded by -// 16 bit short limits -#define MAX_MAP_MODELS 1024 -#define MAX_MAP_BRUSHES 8192 -#define MAX_MAP_ENTITIES 2048 -#define MAX_MAP_ENTSTRING 0x40000 -#define MAX_MAP_TEXINFO 8192 - +// can't be increased without changing network protocol #define MAX_MAP_AREAS 256 -#define MAX_MAP_AREAPORTALS 1024 -#define MAX_MAP_PLANES 65536 -#define MAX_MAP_NODES 65536 -#define MAX_MAP_BRUSHSIDES 65536 -#define MAX_MAP_LEAFS 65536 -#define MAX_MAP_VERTS 65536 -#define MAX_MAP_VERTEXES MAX_MAP_VERTS -#define MAX_MAP_FACES 65536 -#define MAX_MAP_LEAFFACES 65536 -#define MAX_MAP_LEAFBRUSHES 65536 -#define MAX_MAP_PORTALS 65536 -#define MAX_MAP_EDGES 128000 -#define MAX_MAP_SURFEDGES 256000 -#define MAX_MAP_LIGHTING 0x800000 -#define MAX_MAP_VISIBILITY 0x100000 + +// arbitrary limit +#define MAX_MAP_CLUSTERS 65536 // QBSP stuff #define QBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'Q') -// upper design bounds -// leaffaces, leafbrushes, planes, and verts are still bounded by -// 16 bit short limits -#define MAX_QBSP_MAP_MODELS INT_MAX -#define MAX_QBSP_MAP_BRUSHES INT_MAX -#define MAX_QBSP_MAP_ENTITIES INT_MAX -#define MAX_QBSP_MAP_ENTSTRING INT_MAX -#define MAX_QBSP_MAP_TEXINFO INT_MAX - -#define MAX_QBSP_MAP_AREAS INT_MAX -#define MAX_QBSP_MAP_AREAPORTALS INT_MAX -#define MAX_QBSP_MAP_PLANES INT_MAX -#define MAX_QBSP_MAP_NODES INT_MAX -#define MAX_QBSP_MAP_BRUSHSIDES INT_MAX -#define MAX_QBSP_MAP_LEAFS INT_MAX -#define MAX_QBSP_MAP_VERTS INT_MAX -#define MAX_QBSP_MAP_VERTEXES INT_MAX -#define MAX_QBSP_MAP_FACES INT_MAX -#define MAX_QBSP_MAP_LEAFFACES INT_MAX -#define MAX_QBSP_MAP_LEAFBRUSHES INT_MAX -#define MAX_QBSP_MAP_PORTALS INT_MAX -#define MAX_QBSP_MAP_EDGES INT_MAX -#define MAX_QBSP_MAP_SURFEDGES INT_MAX -#define MAX_QBSP_MAP_LIGHTING INT_MAX -#define MAX_QBSP_MAP_VISIBILITY INT_MAX - // key / value pair sizes #define MAX_KEY 32 @@ -99,26 +54,6 @@ typedef struct { uint32_t fileofs, filelen; } lump_t; -#define LUMP_ENTITIES 0 -#define LUMP_ENTSTRING LUMP_ENTITIES -#define LUMP_PLANES 1 -#define LUMP_VERTEXES 2 -#define LUMP_VISIBILITY 3 -#define LUMP_NODES 4 -#define LUMP_TEXINFO 5 -#define LUMP_FACES 6 -#define LUMP_LIGHTING 7 -#define LUMP_LEAFS 8 -#define LUMP_LEAFFACES 9 -#define LUMP_LEAFBRUSHES 10 -#define LUMP_EDGES 11 -#define LUMP_SURFEDGES 12 -#define LUMP_MODELS 13 -#define LUMP_BRUSHES 14 -#define LUMP_BRUSHSIDES 15 -#define LUMP_POP 16 -#define LUMP_AREAS 17 -#define LUMP_AREAPORTALS 18 #define HEADER_LUMPS 19 typedef struct { @@ -127,93 +62,11 @@ typedef struct { lump_t lumps[HEADER_LUMPS]; } dheader_t; -typedef struct { - float mins[3], maxs[3]; - float origin[3]; // for sounds or lights - uint32_t headnode; - uint32_t firstface, numfaces; // submodels just draw faces - // without walking the bsp tree -} dmodel_t; - -typedef struct { - float point[3]; -} dvertex_t; - -typedef struct { - float normal[3]; - float dist; - uint32_t type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate -} dplane_t; - -typedef struct { - uint32_t planenum; - uint32_t children[2]; // negative numbers are -(leafs+1), not nodes - int16_t mins[3]; // for frustom culling - int16_t maxs[3]; - uint16_t firstface; - uint16_t numfaces; // counting both sides -} dnode_t; - -typedef struct { - float vecs[2][4]; // [s/t][xyz offset] - uint32_t flags; // miptex flags + overrides - int32_t value; // light emission, etc - char texture[MAX_TEXNAME]; // texture name (textures/*.wal) - uint32_t nexttexinfo; // for animations, -1 = end of chain -} dtexinfo_t; - -// note that edge 0 is never used, because negative edge nums are used for -// counterclockwise use of the edge in a face -typedef struct { - uint16_t v[2]; // vertex numbers -} dedge_t; - #define MAX_LIGHTMAPS 4 -typedef struct { - uint16_t planenum; - uint16_t side; - - uint32_t firstedge; // we must support > 64k edges - uint16_t numedges; - uint16_t texinfo; - - // lighting info - uint8_t styles[MAX_LIGHTMAPS]; - uint32_t lightofs; // start of [numstyles*surfsize] samples -} dface_t; - -typedef struct { - uint32_t contents; // OR of all brushes (not needed?) - - uint16_t cluster; - uint16_t area; - - int16_t mins[3]; // for frustum culling - int16_t maxs[3]; - - uint16_t firstleafface; - uint16_t numleaffaces; - - uint16_t firstleafbrush; - uint16_t numleafbrushes; -} dleaf_t; - -typedef struct { - uint16_t planenum; // facing out of the leaf - uint16_t texinfo; -} dbrushside_t; - -typedef struct { - uint32_t firstside; - uint32_t numsides; - uint32_t contents; -} dbrush_t; - #define ANGLE_UP -1 #define ANGLE_DOWN -2 - // the visibility lump consists of a header with a count, then // byte offsets for the PVS and PHS of each cluster, then the raw // compressed bit vectors @@ -226,91 +79,14 @@ typedef struct { uint32_t bitofs[][2]; // bitofs[numclusters][2] } dvis_t; -// each area has a list of portals that lead into other areas -// when portals are closed, other areas may not be visible or -// hearable even if the vis info says that it should be -typedef struct { - uint32_t portalnum; - uint32_t otherarea; -} dareaportal_t; - -typedef struct { - uint32_t numareaportals; - uint32_t firstareaportal; -} darea_t; - -// QBSP versions -typedef struct { - uint32_t planenum; - uint32_t children[2]; // negative numbers are -(leafs+1), not nodes - float mins[3]; // for frustom culling - float maxs[3]; - uint32_t firstface; - uint32_t numfaces; // counting both sides -} dnode_qbsp_t; - -typedef struct { - uint32_t v[2]; // vertex numbers -} dedge_qbsp_t; - -typedef struct { - uint32_t planenum; - uint32_t side; - - uint32_t firstedge; // we must support > 64k edges - uint32_t numedges; - uint32_t texinfo; - - // lighting info - uint8_t styles[MAX_LIGHTMAPS]; - uint32_t lightofs; // start of [numstyles*surfsize] samples -} dface_qbsp_t; - -typedef struct { - uint32_t contents; // OR of all brushes (not needed?) - - uint32_t cluster; - uint32_t area; - - float mins[3]; // for frustum culling - float maxs[3]; - - uint32_t firstleafface; - uint32_t numleaffaces; - - uint32_t firstleafbrush; - uint32_t numleafbrushes; -} dleaf_qbsp_t; - -typedef struct { - uint32_t planenum; // facing out of the leaf - uint32_t texinfo; -} dbrushside_qbsp_t; - -typedef struct { - char id[4]; // 'BSPX' - uint32_t numlumps; -} bspx_header_t; +//============================================================================= -typedef struct { - char lumpname[24]; // up to 23 chars, zero-padded - uint32_t fileofs; // from file start - uint32_t filelen; -} bspx_lump_t; +#define BSPXHEADER MakeLittleLong('B','S','P','X') typedef struct { - uint32_t num_vectors; - /* followed by: - vec3 vectors[num_vectors] - - for each face in bsp { - for each vert in face { - u32 normal_index; - u32 tangent_index; - u32 bitangent_index; - } - } - */ -} bspx_facenormals_header_t; + char name[24]; + uint32_t fileofs; + uint32_t filelen; +} xlump_t; #endif // FORMAT_BSP_H diff --git a/inc/shared/shared.h b/inc/shared/shared.h index 8f5fbcecb..545c04c73 100644 --- a/inc/shared/shared.h +++ b/inc/shared/shared.h @@ -535,6 +535,17 @@ static inline float FloatSwap(float f) return dat2.f; } +static inline float LongToFloat(uint32_t l) +{ + union { + float f; + uint32_t l; + } dat; + + dat.l = l; + return dat.f; +} + #if USE_LITTLE_ENDIAN #define BigShort ShortSwap #define BigLong LongSwap diff --git a/src/client/demo.c b/src/client/demo.c index 3153d6652..84810168b 100644 --- a/src/client/demo.c +++ b/src/client/demo.c @@ -1050,7 +1050,6 @@ static void CL_Seek_f(void) static void parse_info_string(demoInfo_t *info, int clientNum, int index, const char *string) { - size_t len; char *p; if (index >= CS_PLAYERSKINS && index < CS_PLAYERSKINS + MAX_CLIENTS) { @@ -1062,11 +1061,7 @@ static void parse_info_string(demoInfo_t *info, int clientNum, int index, const } } } else if (index == CS_MODELS + 1) { - len = strlen(string); - if (len > 9) { - memcpy(info->map, string + 5, len - 9); // skip "maps/" - info->map[len - 9] = 0; // cut off ".bsp" - } + Com_ParseMapName(info->map, string, sizeof(info->map)); } } diff --git a/src/client/precache.c b/src/client/precache.c index b0d3c0121..d50d4063d 100644 --- a/src/client/precache.c +++ b/src/client/precache.c @@ -231,7 +231,7 @@ void CL_RegisterBspModels(void) ret = BSP_Load(cl.configstrings[CS_MODELS + 1], &cl.bsp); if (cl.bsp == NULL) { Com_Error(ERR_DROP, "Couldn't load %s: %s", - cl.configstrings[CS_MODELS + 1], Q_ErrorString(ret)); + cl.configstrings[CS_MODELS + 1], BSP_ErrorString(ret)); } if (cl.bsp->checksum != atoi(cl.configstrings[CS_MAPCHECKSUM])) { @@ -419,13 +419,8 @@ void CL_UpdateConfigstring(int index) } if (index == CS_MODELS + 1) { - size_t len = strlen(s); - - if (len <= 9) { + if (!Com_ParseMapName(cl.mapname, s, sizeof(cl.mapname))) Com_Error(ERR_DROP, "%s: bad world model: %s", __func__, s); - } - memcpy(cl.mapname, s + 5, len - 9); // skip "maps/" - cl.mapname[len - 9] = 0; // cut off ".bsp" return; } diff --git a/src/common/bsp.c b/src/common/bsp.c index 2f3bf7543..5cfdf1fd8 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -22,14 +22,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "shared/shared.h" #include "shared/list.h" -#include "common/cvar.h" +#include "common/bsp.h" #include "common/cmd.h" #include "common/common.h" +#include "common/cvar.h" #include "common/files.h" -#include "common/bsp.h" +#include "common/intreadwrite.h" #include "common/math.h" -#include "common/utils.h" #include "common/mdfour.h" +#include "common/utils.h" #include "system/hunk.h" extern mtexinfo_t nulltexinfo; @@ -48,18 +49,22 @@ static cvar_t *map_visibility_patch; Hunk_Alloc(&bsp->hunk, size) #define LOAD(func) \ - static int BSP_Load##func(bsp_t *bsp, void *base, size_t count) - -// QBSP -#define LOAD_EXT(func) \ - static int BSP_QBSP_Load##func(bsp_t *bsp, void *base, size_t count) + static int BSP_Load##func(bsp_t *bsp, const byte *in, size_t count) #define DEBUG(msg) \ - Com_DPrintf("%s: %s\n", __func__, msg) + Com_SetLastError(va("%s: %s", __func__, msg)) + +#define BSP_Short() (in += 2, RL16(in - 2)) +#define BSP_Long() (in += 4, RL32(in - 4)) +#define BSP_Float() LongToFloat(BSP_Long()) + +#define BSP_ExtFloat() (bsp->extended ? BSP_Float() : (int16_t)BSP_Short()) +#define BSP_ExtLong() (bsp->extended ? BSP_Long() : BSP_Short()) +#define BSP_ExtNull (bsp->extended ? (uint32_t)-1 : (uint16_t)-1) LOAD(Visibility) { - uint32_t numclusters, bitofs; + uint32_t numclusters, bitofs, hdrsize; int i, j; if (!count) { @@ -68,48 +73,49 @@ LOAD(Visibility) if (count < 4) { DEBUG("too small header"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } - bsp->numvisibility = count; - bsp->vis = ALLOC(count); - memcpy(bsp->vis, base, count); - - numclusters = LittleLong(bsp->vis->numclusters); - if (numclusters > (bsp->extended ? MAX_QBSP_MAP_LEAFS : MAX_MAP_LEAFS)) { - DEBUG("bad numclusters"); - return Q_ERR_TOO_MANY; + numclusters = BSP_Long(); + if (numclusters > MAX_MAP_CLUSTERS) { + DEBUG("too many clusters"); + return Q_ERR_INVALID_FORMAT; } - if (numclusters > (count - 4) / 8) { + hdrsize = 4 + numclusters * 8; + if (count < hdrsize) { DEBUG("too small header"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } + bsp->numvisibility = count; + bsp->vis = ALLOC(count); bsp->vis->numclusters = numclusters; bsp->visrowsize = (numclusters + 7) >> 3; + Q_assert(bsp->visrowsize <= VIS_MAX_BYTES); for (i = 0; i < numclusters; i++) { for (j = 0; j < 2; j++) { - bitofs = LittleLong(bsp->vis->bitofs[i][j]); - if (bitofs >= count) { + bitofs = BSP_Long(); + if (bitofs < hdrsize || bitofs >= count) { DEBUG("bad bitofs"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } bsp->vis->bitofs[i][j] = bitofs; } } + memcpy(bsp->vis->bitofs + numclusters, in, count - hdrsize); + return Q_ERR_SUCCESS; } LOAD(Texinfo) { - dtexinfo_t *in; mtexinfo_t *out; int i; #if USE_REF - int j, k; + int j; int32_t next; mtexinfo_t *step; #endif @@ -117,35 +123,39 @@ LOAD(Texinfo) bsp->numtexinfo = count; bsp->texinfo = ALLOC(sizeof(*out) * count); - in = base; out = bsp->texinfo; - for (i = 0; i < count; i++, in++, out++) { - memcpy(out->c.name, in->texture, sizeof(out->c.name)); - out->c.name[sizeof(out->c.name) - 1] = 0; - memcpy(out->name, in->texture, sizeof(out->name)); - out->name[sizeof(out->name) - 1] = 0; - out->c.flags = LittleLong(in->flags); - out->c.value = LittleLong(in->value); - + for (i = 0; i < count; i++, out++) { #if USE_REF - out->radiance = in->value; for (j = 0; j < 2; j++) { - for (k = 0; k < 3; k++) { - out->axis[j][k] = LittleFloat(in->vecs[j][k]); - } - out->offset[j] = LittleFloat(in->vecs[j][k]); + out->axis[j][0] = BSP_Float(); + out->axis[j][1] = BSP_Float(); + out->axis[j][2] = BSP_Float(); + out->offset[j] = BSP_Float(); } +#else + in += 32; +#endif + out->c.flags = BSP_Long(); + out->c.value = BSP_Long(); - next = (int32_t)LittleLong(in->nexttexinfo); + memcpy(out->c.name, in, sizeof(out->c.name) - 1); + memcpy(out->name, in, sizeof(out->name) - 1); + in += MAX_TEXNAME; + +#if USE_REF + out->radiance = out->c.value; + next = (int32_t)BSP_Long(); if (next > 0) { if (next >= count) { DEBUG("bad anim chain"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->next = bsp->texinfo + next; } else { out->next = NULL; } +#else + in += 4; #endif } @@ -169,20 +179,18 @@ LOAD(Texinfo) LOAD(Planes) { - dplane_t *in; cplane_t *out; - int i, j; + int i; bsp->numplanes = count; bsp->planes = ALLOC(sizeof(*out) * count); - in = base; out = bsp->planes; - for (i = 0; i < count; i++, in++, out++) { - for (j = 0; j < 3; j++) { - out->normal[j] = LittleFloat(in->normal[j]); - } - out->dist = LittleFloat(in->dist); + for (i = 0; i < count; i++, in += 4, out++) { + out->normal[0] = BSP_Float(); + out->normal[1] = BSP_Float(); + out->normal[2] = BSP_Float(); + out->dist = BSP_Float(); SetPlaneType(out); SetPlaneSignbits(out); } @@ -192,41 +200,6 @@ LOAD(Planes) LOAD(BrushSides) { - dbrushside_t *in; - mbrushside_t *out; - int i; - uint16_t planenum, texinfo; - - bsp->numbrushsides = count; - bsp->brushsides = ALLOC(sizeof(*out) * count); - - in = base; - out = bsp->brushsides; - for (i = 0; i < count; i++, in++, out++) { - planenum = LittleShort(in->planenum); - if (planenum >= bsp->numplanes) { - DEBUG("bad planenum"); - return Q_ERR_BAD_INDEX; - } - out->plane = bsp->planes + planenum; - texinfo = LittleShort(in->texinfo); - if (texinfo == (uint16_t)-1) { - out->texinfo = &nulltexinfo; - } else { - if (texinfo >= bsp->numtexinfo) { - DEBUG("bad texinfo"); - return Q_ERR_BAD_INDEX; - } - out->texinfo = bsp->texinfo + texinfo; - } - } - - return Q_ERR_SUCCESS; -} - -LOAD_EXT(BrushSides) -{ - dbrushside_qbsp_t *in; mbrushside_t *out; int i; uint32_t planenum, texinfo; @@ -234,22 +207,21 @@ LOAD_EXT(BrushSides) bsp->numbrushsides = count; bsp->brushsides = ALLOC(sizeof(*out) * count); - in = base; out = bsp->brushsides; - for (i = 0; i < count; i++, in++, out++) { - planenum = LittleLong(in->planenum); + for (i = 0; i < count; i++, out++) { + planenum = BSP_ExtLong(); if (planenum >= bsp->numplanes) { DEBUG("bad planenum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->plane = bsp->planes + planenum; - texinfo = LittleLong(in->texinfo); - if (texinfo == (uint32_t)-1) { + texinfo = BSP_ExtLong(); + if (texinfo == BSP_ExtNull) { out->texinfo = &nulltexinfo; } else { if (texinfo >= bsp->numtexinfo) { DEBUG("bad texinfo"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->texinfo = bsp->texinfo + texinfo; } @@ -260,7 +232,6 @@ LOAD_EXT(BrushSides) LOAD(Brushes) { - dbrush_t *in; mbrush_t *out; int i; uint32_t firstside, numsides, lastside; @@ -268,19 +239,18 @@ LOAD(Brushes) bsp->numbrushes = count; bsp->brushes = ALLOC(sizeof(*out) * count); - in = base; out = bsp->brushes; - for (i = 0; i < count; i++, out++, in++) { - firstside = LittleLong(in->firstside); - numsides = LittleLong(in->numsides); + for (i = 0; i < count; i++, out++) { + firstside = BSP_Long(); + numsides = BSP_Long(); lastside = firstside + numsides; if (lastside < firstside || lastside > bsp->numbrushsides) { DEBUG("bad brushsides"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->firstbrushside = bsp->brushsides + firstside; out->numsides = numsides; - out->contents = LittleLong(in->contents); + out->contents = BSP_Long(); out->checkcount = 0; } @@ -289,31 +259,6 @@ LOAD(Brushes) LOAD(LeafBrushes) { - uint16_t *in; - mbrush_t **out; - int i; - uint16_t brushnum; - - bsp->numleafbrushes = count; - bsp->leafbrushes = ALLOC(sizeof(*out) * count); - - in = base; - out = bsp->leafbrushes; - for (i = 0; i < count; i++, in++, out++) { - brushnum = LittleShort(*in); - if (brushnum >= bsp->numbrushes) { - DEBUG("bad brushnum"); - return Q_ERR_BAD_INDEX; - } - *out = bsp->brushes + brushnum; - } - - return Q_ERR_SUCCESS; -} - -LOAD_EXT(LeafBrushes) -{ - uint32_t *in; mbrush_t **out; int i; uint32_t brushnum; @@ -321,13 +266,12 @@ LOAD_EXT(LeafBrushes) bsp->numleafbrushes = count; bsp->leafbrushes = ALLOC(sizeof(*out) * count); - in = base; out = bsp->leafbrushes; - for (i = 0; i < count; i++, in++, out++) { - brushnum = LittleLong(*in); + for (i = 0; i < count; i++, out++) { + brushnum = BSP_ExtLong(); if (brushnum >= bsp->numbrushes) { DEBUG("bad brushnum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } *out = bsp->brushes + brushnum; } @@ -339,33 +283,28 @@ LOAD_EXT(LeafBrushes) #if USE_REF LOAD(Lightmap) { - if (!count) { - return Q_ERR_SUCCESS; + if (count) { + bsp->numlightmapbytes = count; + bsp->lightmap = ALLOC(count); + memcpy(bsp->lightmap, in, count); } - bsp->numlightmapbytes = count; - bsp->lightmap = ALLOC(count); - - memcpy(bsp->lightmap, base, count); - return Q_ERR_SUCCESS; } LOAD(Vertices) { - dvertex_t *in; mvertex_t *out; - int i, j; + int i; bsp->numvertices = count; bsp->vertices = ALLOC(sizeof(*out) * count); - in = base; out = bsp->vertices; - for (i = 0; i < count; i++, out++, in++) { - for (j = 0; j < 3; j++) { - out->point[j] = LittleFloat(in->point[j]); - } + for (i = 0; i < count; i++, out++) { + out->point[0] = BSP_Float(); + out->point[1] = BSP_Float(); + out->point[2] = BSP_Float(); } return Q_ERR_SUCCESS; @@ -373,33 +312,6 @@ LOAD(Vertices) LOAD(Edges) { - dedge_t *in; - medge_t *out; - int i, j; - uint16_t vertnum; - - bsp->numedges = count; - bsp->edges = ALLOC(sizeof(*out) * count); - - in = base; - out = bsp->edges; - for (i = 0; i < count; i++, out++, in++) { - for (j = 0; j < 2; j++) { - vertnum = LittleShort(in->v[j]); - if (vertnum >= bsp->numvertices) { - DEBUG("bad vertnum"); - return Q_ERR_BAD_INDEX; - } - out->v[j] = bsp->vertices + vertnum; - } - } - - return Q_ERR_SUCCESS; -} - -LOAD_EXT(Edges) -{ - dedge_qbsp_t *in; medge_t *out; int i, j; uint32_t vertnum; @@ -407,14 +319,13 @@ LOAD_EXT(Edges) bsp->numedges = count; bsp->edges = ALLOC(sizeof(*out) * count); - in = base; out = bsp->edges; - for (i = 0; i < count; i++, out++, in++) { + for (i = 0; i < count; i++, out++) { for (j = 0; j < 2; j++) { - vertnum = LittleLong(in->v[j]); + vertnum = BSP_ExtLong(); if (vertnum >= bsp->numvertices) { DEBUG("bad vertnum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->v[j] = bsp->vertices + vertnum; } @@ -425,7 +336,6 @@ LOAD_EXT(Edges) LOAD(SurfEdges) { - int *in; msurfedge_t *out; int i, vert; int32_t index; @@ -433,10 +343,9 @@ LOAD(SurfEdges) bsp->numsurfedges = count; bsp->surfedges = ALLOC(sizeof(*out) * count); - in = base; out = bsp->surfedges; - for (i = 0; i < count; i++, out++, in++) { - index = (int32_t)LittleLong(*in); + for (i = 0; i < count; i++, out++) { + index = (int32_t)BSP_Long(); vert = 0; if (index < 0) { @@ -446,7 +355,7 @@ LOAD(SurfEdges) if (index >= bsp->numedges) { DEBUG("bad edgenum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->edge = bsp->edges + index; @@ -458,145 +367,70 @@ LOAD(SurfEdges) LOAD(Faces) { - dface_t *in; mface_t *out; int i, j; uint32_t firstedge, numedges, lastedge; - uint16_t planenum, texinfo, side; + uint32_t planenum, texinfo, side; uint32_t lightofs; bsp->numfaces = count; bsp->faces = ALLOC(sizeof(*out) * count); - in = base; out = bsp->faces; - for (i = 0; i < count; i++, in++, out++) { - firstedge = LittleLong(in->firstedge); - numedges = LittleShort(in->numedges); - lastedge = firstedge + numedges; - if (numedges < 3) { - DEBUG("bad surfedges"); - return Q_ERR_TOO_FEW; - } - if (numedges > 4096) { - DEBUG("bad surfedges"); - return Q_ERR_TOO_MANY; - } - if (lastedge < firstedge || lastedge > bsp->numsurfedges) { - DEBUG("bad surfedges"); - return Q_ERR_BAD_INDEX; - } - out->firstsurfedge = bsp->surfedges + firstedge; - out->numsurfedges = numedges; - - planenum = LittleShort(in->planenum); + for (i = 0; i < count; i++, out++) { + planenum = BSP_ExtLong(); if (planenum >= bsp->numplanes) { DEBUG("bad planenum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->plane = bsp->planes + planenum; - texinfo = LittleShort(in->texinfo); - if (texinfo >= bsp->numtexinfo) { - DEBUG("bad texinfo"); - return Q_ERR_BAD_INDEX; - } - out->texinfo = bsp->texinfo + texinfo; - - for (j = 0; j < MAX_LIGHTMAPS && in->styles[j] != 255; j++) { - out->styles[j] = in->styles[j]; - } - out->numstyles = j; - for (; j < MAX_LIGHTMAPS; j++) { - out->styles[j] = 255; - } - - lightofs = LittleLong(in->lightofs); - if (lightofs == (uint32_t)-1 || bsp->numlightmapbytes == 0) { - out->lightmap = NULL; - } else { - if (lightofs >= bsp->numlightmapbytes) { - DEBUG("bad lightofs"); - return Q_ERR_BAD_INDEX; - } - out->lightmap = bsp->lightmap + lightofs; - } - - side = LittleShort(in->side); + side = BSP_ExtLong(); out->drawflags = side & DSURF_PLANEBACK; - } - - return Q_ERR_SUCCESS; -} -LOAD_EXT(Faces) -{ - dface_qbsp_t *in; - mface_t *out; - int i, j; - uint32_t firstedge, numedges, lastedge; - uint32_t planenum, texinfo, side; - uint32_t lightofs; - - bsp->numfaces = count; - bsp->faces = ALLOC(sizeof(*out) * count); - - in = base; - out = bsp->faces; - for (i = 0; i < count; i++, in++, out++) { - firstedge = LittleLong(in->firstedge); - numedges = LittleLong(in->numedges); + firstedge = BSP_Long(); + numedges = BSP_ExtLong(); lastedge = firstedge + numedges; if (numedges < 3) { - DEBUG("bad surfedges"); - return Q_ERR_TOO_FEW; + DEBUG("too few surfedges"); + return Q_ERR_INVALID_FORMAT; } if (numedges > 4096) { - DEBUG("bad surfedges"); - return Q_ERR_TOO_MANY; + DEBUG("too many surfedges"); + return Q_ERR_INVALID_FORMAT; } if (lastedge < firstedge || lastedge > bsp->numsurfedges) { DEBUG("bad surfedges"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->firstsurfedge = bsp->surfedges + firstedge; out->numsurfedges = numedges; - planenum = LittleLong(in->planenum); - if (planenum >= bsp->numplanes) { - DEBUG("bad planenum"); - return Q_ERR_BAD_INDEX; - } - out->plane = bsp->planes + planenum; - - texinfo = LittleLong(in->texinfo); + texinfo = BSP_ExtLong(); if (texinfo >= bsp->numtexinfo) { DEBUG("bad texinfo"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->texinfo = bsp->texinfo + texinfo; - for (j = 0; j < MAX_LIGHTMAPS && in->styles[j] != 255; j++) { - out->styles[j] = in->styles[j]; + for (j = 0; j < MAX_LIGHTMAPS && in[j] != 255; j++) { + out->styles[j] = in[j]; } - out->numstyles = j; - for (; j < MAX_LIGHTMAPS; j++) { + for (out->numstyles = j; j < MAX_LIGHTMAPS; j++) { out->styles[j] = 255; } + in += MAX_LIGHTMAPS; - lightofs = LittleLong(in->lightofs); + lightofs = BSP_Long(); if (lightofs == (uint32_t)-1 || bsp->numlightmapbytes == 0) { out->lightmap = NULL; } else { if (lightofs >= bsp->numlightmapbytes) { DEBUG("bad lightofs"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->lightmap = bsp->lightmap + lightofs; } - - side = LittleLong(in->side); - out->drawflags = side & DSURF_PLANEBACK; } return Q_ERR_SUCCESS; @@ -604,31 +438,6 @@ LOAD_EXT(Faces) LOAD(LeafFaces) { - uint16_t *in; - mface_t **out; - int i; - uint16_t facenum; - - bsp->numleaffaces = count; - bsp->leaffaces = ALLOC(sizeof(*out) * count); - - in = base; - out = bsp->leaffaces; - for (i = 0; i < count; i++, in++, out++) { - facenum = LittleShort(*in); - if (facenum >= bsp->numfaces) { - DEBUG("bad facenum"); - return Q_ERR_BAD_INDEX; - } - *out = bsp->faces + facenum; - } - - return Q_ERR_SUCCESS; -} - -LOAD_EXT(LeafFaces) -{ - uint32_t *in; mface_t **out; int i; uint32_t facenum; @@ -636,13 +445,12 @@ LOAD_EXT(LeafFaces) bsp->numleaffaces = count; bsp->leaffaces = ALLOC(sizeof(*out) * count); - in = base; out = bsp->leaffaces; - for (i = 0; i < count; i++, in++, out++) { - facenum = LittleLong(*in); + for (i = 0; i < count; i++, out++) { + facenum = BSP_ExtLong(); if (facenum >= bsp->numfaces) { DEBUG("bad facenum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } *out = bsp->faces + facenum; } @@ -653,31 +461,29 @@ LOAD_EXT(LeafFaces) LOAD(Leafs) { - dleaf_t *in; mleaf_t *out; int i; - uint16_t cluster, area; - uint16_t firstleafbrush, numleafbrushes, lastleafbrush; + uint32_t cluster, area; + uint32_t firstleafbrush, numleafbrushes, lastleafbrush; #if USE_REF int j; - uint16_t firstleafface, numleaffaces, lastleafface; + uint32_t firstleafface, numleaffaces, lastleafface; #endif if (!count) { DEBUG("map with no leafs"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } bsp->numleafs = count; bsp->leafs = ALLOC(sizeof(*out) * count); - in = base; out = bsp->leafs; - for (i = 0; i < count; i++, in++, out++) { + for (i = 0; i < count; i++, out++) { out->plane = NULL; - out->contents = LittleLong(in->contents); - cluster = LittleShort(in->cluster); - if (cluster == (uint16_t)-1) { + out->contents = BSP_Long(); + cluster = BSP_ExtLong(); + if (cluster == BSP_ExtNull) { // solid leafs use special -1 cluster out->cluster = -1; } else if (bsp->vis == NULL) { @@ -687,134 +493,49 @@ LOAD(Leafs) // validate cluster if (cluster >= bsp->vis->numclusters) { DEBUG("bad cluster"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->cluster = cluster; } - area = LittleShort(in->area); + area = BSP_ExtLong(); if (area >= bsp->numareas) { DEBUG("bad area"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->area = area; - firstleafbrush = LittleShort(in->firstleafbrush); - numleafbrushes = LittleShort(in->numleafbrushes); - lastleafbrush = firstleafbrush + numleafbrushes; - if (lastleafbrush < firstleafbrush || lastleafbrush > bsp->numleafbrushes) { - DEBUG("bad leafbrushes"); - return Q_ERR_BAD_INDEX; - } - out->firstleafbrush = bsp->leafbrushes + firstleafbrush; - out->numleafbrushes = numleafbrushes; - #if USE_REF - firstleafface = LittleShort(in->firstleafface); - numleaffaces = LittleShort(in->numleaffaces); + for (j = 0; j < 3; j++) + out->mins[j] = BSP_ExtFloat(); + for (j = 0; j < 3; j++) + out->maxs[j] = BSP_ExtFloat(); + + firstleafface = BSP_ExtLong(); + numleaffaces = BSP_ExtLong(); lastleafface = firstleafface + numleaffaces; if (lastleafface < firstleafface || lastleafface > bsp->numleaffaces) { DEBUG("bad leaffaces"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->firstleafface = bsp->leaffaces + firstleafface; out->numleaffaces = numleaffaces; - for (j = 0; j < 3; j++) { - out->mins[j] = (int16_t)LittleShort(in->mins[j]); - out->maxs[j] = (int16_t)LittleShort(in->maxs[j]); - } - out->parent = NULL; out->visframe = -1; +#else + in += 16 * (bsp->extended + 1); #endif - } - if (bsp->leafs[0].contents != CONTENTS_SOLID) { - DEBUG("map leaf 0 is not CONTENTS_SOLID"); - return Q_ERR_INVALID_FORMAT; - } - - return Q_ERR_SUCCESS; -} - -LOAD_EXT(Leafs) -{ - dleaf_qbsp_t *in; - mleaf_t *out; - int i; - uint32_t cluster, area; - uint32_t firstleafbrush, numleafbrushes, lastleafbrush; -#if USE_REF - int j; - uint32_t firstleafface, numleaffaces, lastleafface; -#endif - - if (!count) { - DEBUG("map with no leafs"); - return Q_ERR_TOO_FEW; - } - - bsp->numleafs = count; - bsp->leafs = ALLOC(sizeof(*out) * count); - - in = base; - out = bsp->leafs; - for (i = 0; i < count; i++, in++, out++) { - out->plane = NULL; - out->contents = LittleLong(in->contents); - cluster = LittleLong(in->cluster); - if (cluster == (uint32_t)-1) { - // solid leafs use special -1 cluster - out->cluster = -1; - } else if (bsp->vis == NULL) { - // map has no vis, use 0 as a default cluster - out->cluster = 0; - } else { - // validate cluster - if (cluster >= bsp->vis->numclusters) { - DEBUG("bad cluster"); - return Q_ERR_BAD_INDEX; - } - out->cluster = cluster; - } - - area = LittleLong(in->area); - if (area >= bsp->numareas) { - DEBUG("bad area"); - return Q_ERR_BAD_INDEX; - } - out->area = area; - - firstleafbrush = LittleLong(in->firstleafbrush); - numleafbrushes = LittleLong(in->numleafbrushes); + firstleafbrush = BSP_ExtLong(); + numleafbrushes = BSP_ExtLong(); lastleafbrush = firstleafbrush + numleafbrushes; if (lastleafbrush < firstleafbrush || lastleafbrush > bsp->numleafbrushes) { DEBUG("bad leafbrushes"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->firstleafbrush = bsp->leafbrushes + firstleafbrush; out->numleafbrushes = numleafbrushes; - -#if USE_REF - firstleafface = LittleLong(in->firstleafface); - numleaffaces = LittleLong(in->numleaffaces); - lastleafface = firstleafface + numleaffaces; - if (lastleafface < firstleafface || lastleafface > bsp->numleaffaces) { - DEBUG("bad leaffaces"); - return Q_ERR_BAD_INDEX; - } - out->firstleafface = bsp->leaffaces + firstleafface; - out->numleaffaces = numleaffaces; - - for (j = 0; j < 3; j++) { - out->mins[j] = LittleFloat(in->mins[j]); - out->maxs[j] = LittleFloat(in->maxs[j]); - } - - out->parent = NULL; - out->visframe = -1; -#endif } if (bsp->leafs[0].contents != CONTENTS_SOLID) { @@ -827,77 +548,6 @@ LOAD_EXT(Leafs) LOAD(Nodes) { - dnode_t *in; - mnode_t *out; - int i, j; - uint32_t planenum, child; -#if USE_REF - uint16_t firstface, numfaces, lastface; -#endif - - if (!count) { - DEBUG("map with no nodes"); - return Q_ERR_TOO_FEW; - } - - bsp->numnodes = count; - bsp->nodes = ALLOC(sizeof(*out) * count); - - in = base; - out = bsp->nodes; - for (i = 0; i < count; i++, out++, in++) { - planenum = LittleLong(in->planenum); - if (planenum >= bsp->numplanes) { - DEBUG("bad planenum"); - return Q_ERR_BAD_INDEX; - } - out->plane = bsp->planes + planenum; - - for (j = 0; j < 2; j++) { - child = LittleLong(in->children[j]); - if (child & 0x80000000) { - child = ~child; - if (child >= bsp->numleafs) { - DEBUG("bad leafnum"); - return Q_ERR_BAD_INDEX; - } - out->children[j] = (mnode_t *)(bsp->leafs + child); - } else { - if (child >= count) { - DEBUG("bad nodenum"); - return Q_ERR_BAD_INDEX; - } - out->children[j] = bsp->nodes + child; - } - } - -#if USE_REF - firstface = LittleShort(in->firstface); - numfaces = LittleShort(in->numfaces); - lastface = firstface + numfaces; - if (lastface < firstface || lastface > bsp->numfaces) { - DEBUG("bad faces"); - return Q_ERR_BAD_INDEX; - } - out->firstface = bsp->faces + firstface; - out->numfaces = numfaces; - - for (j = 0; j < 3; j++) { - out->mins[j] = (int16_t)LittleShort(in->mins[j]); - out->maxs[j] = (int16_t)LittleShort(in->maxs[j]); - } - - out->parent = NULL; - out->visframe = -1; -#endif - } - - return Q_ERR_SUCCESS; -} - -LOAD_EXT(Nodes) -{ - dnode_qbsp_t *in; mnode_t *out; int i, j; uint32_t planenum, child; @@ -907,67 +557,67 @@ LOAD_EXT(Nodes) if (!count) { DEBUG("map with no nodes"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } bsp->numnodes = count; bsp->nodes = ALLOC(sizeof(*out) * count); - in = base; out = bsp->nodes; - for (i = 0; i < count; i++, out++, in++) { - planenum = LittleLong(in->planenum); + for (i = 0; i < count; i++, out++) { + planenum = BSP_Long(); if (planenum >= bsp->numplanes) { DEBUG("bad planenum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->plane = bsp->planes + planenum; for (j = 0; j < 2; j++) { - child = LittleLong(in->children[j]); + child = BSP_Long(); if (child & 0x80000000) { child = ~child; if (child >= bsp->numleafs) { DEBUG("bad leafnum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->children[j] = (mnode_t *)(bsp->leafs + child); } else { if (child >= count) { DEBUG("bad nodenum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->children[j] = bsp->nodes + child; } } #if USE_REF - firstface = LittleLong(in->firstface); - numfaces = LittleLong(in->numfaces); + for (j = 0; j < 3; j++) + out->mins[j] = BSP_ExtFloat(); + for (j = 0; j < 3; j++) + out->maxs[j] = BSP_ExtFloat(); + + firstface = BSP_ExtLong(); + numfaces = BSP_ExtLong(); lastface = firstface + numfaces; if (lastface < firstface || lastface > bsp->numfaces) { DEBUG("bad faces"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->firstface = bsp->faces + firstface; out->numfaces = numfaces; - for (j = 0; j < 3; j++) { - out->mins[j] = LittleFloat(in->mins[j]); - out->maxs[j] = LittleFloat(in->maxs[j]); - } - out->parent = NULL; out->visframe = -1; +#else + in += 16 * (bsp->extended + 1); #endif } return Q_ERR_SUCCESS; } -LOAD(Submodels) +LOAD(SubModels) { - dmodel_t *in; mmodel_t *out; int i, j; uint32_t headnode; @@ -977,52 +627,56 @@ LOAD(Submodels) if (!count) { DEBUG("map with no models"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } - bsp->models = ALLOC(sizeof(*out) * count); bsp->nummodels = count; + bsp->models = ALLOC(sizeof(*out) * count); - in = base; out = bsp->models; - for (i = 0; i < count; i++, in++, out++) { - for (j = 0; j < 3; j++) { - // spread the mins / maxs by a pixel - out->mins[j] = LittleFloat(in->mins[j]) - 1; - out->maxs[j] = LittleFloat(in->maxs[j]) + 1; - out->origin[j] = LittleFloat(in->origin[j]); - } - headnode = LittleLong(in->headnode); + for (i = 0; i < count; i++, out++) { + // spread the mins / maxs by a pixel + for (j = 0; j < 3; j++) + out->mins[j] = BSP_Float() - 1; + for (j = 0; j < 3; j++) + out->maxs[j] = BSP_Float() + 1; + for (j = 0; j < 3; j++) + out->origin[j] = BSP_Float(); + + headnode = BSP_Long(); if (headnode & 0x80000000) { // be careful, some models have no nodes, just a leaf headnode = ~headnode; if (headnode >= bsp->numleafs) { DEBUG("bad headleaf"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->headnode = (mnode_t *)(bsp->leafs + headnode); } else { if (headnode >= bsp->numnodes) { DEBUG("bad headnode"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->headnode = bsp->nodes + headnode; } #if USE_REF if (i == 0) { + in += 8; continue; } - firstface = LittleLong(in->firstface); - numfaces = LittleLong(in->numfaces); + firstface = BSP_Long(); + numfaces = BSP_Long(); lastface = firstface + numfaces; if (lastface < firstface || lastface > bsp->numfaces) { DEBUG("bad faces"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->firstface = bsp->faces + firstface; out->numfaces = numfaces; out->radius = RadiusFromBounds(out->mins, out->maxs); +#else + in += 8; #endif } @@ -1032,18 +686,16 @@ LOAD(Submodels) // These are validated after all the areas are loaded LOAD(AreaPortals) { - dareaportal_t *in; mareaportal_t *out; int i; bsp->numareaportals = count; bsp->areaportals = ALLOC(sizeof(*out) * count); - in = base; out = bsp->areaportals; - for (i = 0; i < count; i++, in++, out++) { - out->portalnum = LittleLong(in->portalnum); - out->otherarea = LittleLong(in->otherarea); + for (i = 0; i < count; i++, out++) { + out->portalnum = BSP_Long(); + out->otherarea = BSP_Long(); } return Q_ERR_SUCCESS; @@ -1051,23 +703,26 @@ LOAD(AreaPortals) LOAD(Areas) { - darea_t *in; marea_t *out; int i; uint32_t numareaportals, firstareaportal, lastareaportal; + if (count > MAX_MAP_AREAS) { + DEBUG("too many areas"); + return Q_ERR_INVALID_FORMAT; + } + bsp->numareas = count; bsp->areas = ALLOC(sizeof(*out) * count); - in = base; out = bsp->areas; - for (i = 0; i < count; i++, in++, out++) { - numareaportals = LittleLong(in->numareaportals); - firstareaportal = LittleLong(in->firstareaportal); + for (i = 0; i < count; i++, out++) { + numareaportals = BSP_Long(); + firstareaportal = BSP_Long(); lastareaportal = firstareaportal + numareaportals; if (lastareaportal < firstareaportal || lastareaportal > bsp->numareaportals) { DEBUG("bad areaportals"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->numareaportals = numareaportals; out->firstareaportal = bsp->areaportals + firstareaportal; @@ -1081,7 +736,7 @@ LOAD(EntString) { bsp->numentitychars = count; bsp->entitystring = ALLOC(count + 1); - memcpy(bsp->entitystring, base, count); + memcpy(bsp->entitystring, in, count); bsp->entitystring[count] = 0; return Q_ERR_SUCCESS; @@ -1096,75 +751,39 @@ LOAD(EntString) */ typedef struct { - int (*load)(bsp_t *, void *, size_t); + int (*load)(bsp_t *, const byte *, size_t); + const char *name; uint8_t lump; - uint8_t disksize; - uint8_t diskalign; + uint8_t disksize[2]; uint32_t memsize; - uint32_t maxcount; } lump_info_t; -#define L(func, lump, disk_t, mem_t) \ - { BSP_Load##func, LUMP_##lump, sizeof(disk_t), q_alignof(disk_t), sizeof(mem_t), MAX_MAP_##lump } +#define L(name, lump, mem_t, disksize1, disksize2) \ + { BSP_Load##name, #name, lump, { disksize1, disksize2 }, sizeof(mem_t) } static const lump_info_t bsp_lumps[] = { - L(Visibility, VISIBILITY, byte, byte), - L(Texinfo, TEXINFO, dtexinfo_t, mtexinfo_t), - L(Planes, PLANES, dplane_t, cplane_t), - L(BrushSides, BRUSHSIDES, dbrushside_t, mbrushside_t), - L(Brushes, BRUSHES, dbrush_t, mbrush_t), - L(LeafBrushes, LEAFBRUSHES, uint16_t, mbrush_t *), - L(AreaPortals, AREAPORTALS, dareaportal_t, mareaportal_t), - L(Areas, AREAS, darea_t, marea_t), -#if USE_REF - L(Lightmap, LIGHTING, byte, byte), - L(Vertices, VERTEXES, dvertex_t, mvertex_t), - L(Edges, EDGES, dedge_t, medge_t), - L(SurfEdges, SURFEDGES, uint32_t, msurfedge_t), - L(Faces, FACES, dface_t, mface_t), - L(LeafFaces, LEAFFACES, uint16_t, mface_t *), -#endif - L(Leafs, LEAFS, dleaf_t, mleaf_t), - L(Nodes, NODES, dnode_t, mnode_t), - L(Submodels, MODELS, dmodel_t, mmodel_t), - L(EntString, ENTSTRING, char, char), - { NULL } -}; - -#undef L - -// QBSP - -#define LS(func, lump, disk_t, mem_t) \ - { BSP_Load##func, LUMP_##lump, sizeof(disk_t), sizeof(mem_t), MAX_QBSP_MAP_##lump } -#define L(func, lump, disk_t, mem_t) \ - { BSP_QBSP_Load##func, LUMP_##lump, sizeof(disk_t), sizeof(mem_t), MAX_QBSP_MAP_##lump } - -static const lump_info_t qbsp_lumps[] = { - LS(Visibility, VISIBILITY, byte, byte), - LS(Texinfo, TEXINFO, dtexinfo_t, mtexinfo_t), - LS(Planes, PLANES, dplane_t, cplane_t), - L(BrushSides, BRUSHSIDES, dbrushside_qbsp_t, mbrushside_t), - LS(Brushes, BRUSHES, dbrush_t, mbrush_t), - L(LeafBrushes, LEAFBRUSHES, uint32_t, mbrush_t *), - LS(AreaPortals, AREAPORTALS, dareaportal_t, mareaportal_t), - LS(Areas, AREAS, darea_t, marea_t), + L(Visibility, 3, byte, 1, 1), + L(Texinfo, 5, mtexinfo_t, 76, 76), + L(Planes, 1, cplane_t, 20, 20), + L(BrushSides, 15, mbrushside_t, 4, 8), + L(Brushes, 14, mbrush_t, 12, 12), + L(LeafBrushes, 10, mbrush_t *, 2, 4), + L(AreaPortals, 18, mareaportal_t, 8, 8), + L(Areas, 17, marea_t, 8, 8), #if USE_REF - LS(Lightmap, LIGHTING, byte, byte), - LS(Vertices, VERTEXES, dvertex_t, mvertex_t), - L(Edges, EDGES, dedge_qbsp_t, medge_t), - LS(SurfEdges, SURFEDGES, uint32_t, msurfedge_t), - L(Faces, FACES, dface_qbsp_t, mface_t), - L(LeafFaces, LEAFFACES, uint32_t, mface_t *), + L(Lightmap, 7, byte, 1, 1), + L(Vertices, 2, mvertex_t, 12, 12), + L(Edges, 11, medge_t, 4, 8), + L(SurfEdges, 12, msurfedge_t, 4, 4), + L(Faces, 6, mface_t, 20, 28), + L(LeafFaces, 9, mface_t *, 2, 4), #endif - L(Leafs, LEAFS, dleaf_qbsp_t, mleaf_t), - L(Nodes, NODES, dnode_qbsp_t, mnode_t), - LS(Submodels, MODELS, dmodel_t, mmodel_t), - LS(EntString, ENTSTRING, char, char), - { NULL } + L(Leafs, 8, mleaf_t, 28, 52), + L(Nodes, 4, mnode_t, 28, 44), + L(SubModels, 13, mmodel_t, 48, 48), + L(EntString, 0, char, 1, 1), }; -#undef LS #undef L static list_t bsp_cache; @@ -1203,7 +822,7 @@ static bsp_t *BSP_Find(const char *name) return NULL; } -static int BSP_SetParent(mnode_t *node, int key) +static int BSP_SetParent(mnode_t *node, unsigned key) { mnode_t *child; #if USE_REF @@ -1287,19 +906,17 @@ static int BSP_ValidateAreaPortals(bsp_t *bsp) mareaportal_t *p; int i; - bsp->lastareaportal = 0; + bsp->numportals = 0; for (i = 0, p = bsp->areaportals; i < bsp->numareaportals; i++, p++) { - if (p->portalnum >= MAX_MAP_AREAPORTALS) { + if (p->portalnum >= bsp->numareaportals) { DEBUG("bad portalnum"); - return Q_ERR_TOO_MANY; - } - if (p->portalnum > bsp->lastareaportal) { - bsp->lastareaportal = p->portalnum; + return Q_ERR_INVALID_FORMAT; } if (p->otherarea >= bsp->numareas) { DEBUG("bad otherarea"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } + bsp->numportals = max(bsp->numportals, p->portalnum + 1); } return Q_ERR_SUCCESS; @@ -1452,56 +1069,39 @@ bool BSP_SavePatchedPVS(bsp_t *bsp) } #if USE_REF -static bool BSP_FindBspxLump(dheader_t* header, size_t file_size, const char* name, const void** pLump, size_t* pLumpSize) +static bool BSP_FindBspxLump(const byte *buf, uint32_t pos, uint32_t filelen, const char* name, const void** pLump, uint32_t* pLumpSize) { - // Find the end of the last BSP lump - size_t max_bsp_lump = 0; - for (uint32_t i = 0; i < HEADER_LUMPS; i++) - { - size_t end_of_lump = header->lumps[i].fileofs + header->lumps[i].filelen; - max_bsp_lump = max(max_bsp_lump, end_of_lump); - } - - // Align to 4 bytes - max_bsp_lump = (max_bsp_lump + 3) & ~3ull; - - // See if the BSPX header still fits in the file after the last BSP lump - if (max_bsp_lump + sizeof(bspx_header_t) > file_size) - return false; - - // Validate the BSPX header - const bspx_header_t* bspx = (bspx_header_t*)((uint8_t*)header + max_bsp_lump); - if (bspx->id[0] != 'B' || bspx->id[1] != 'S' || bspx->id[2] != 'P' || bspx->id[3] != 'X') - return false; - if (max_bsp_lump + sizeof(bspx_header_t) + sizeof(bspx_lump_t) * bspx->numlumps > file_size) - return false; - - // Go over the BSPX lumps and find one with the right name - for (uint32_t i = 0; i < bspx->numlumps; i++) - { - const bspx_lump_t* lump = (const bspx_lump_t*)(bspx + 1) + i; - if (strncmp(name, lump->lumpname, sizeof(lump->lumpname) - 1) == 0) - { - // See if the lump as declared fits in the file - if (lump->fileofs + lump->filelen > file_size) - { - Com_WPrintf("Malformed BSPX file: lump '%s' points at data past the end of file\n", name); - return false; - } - - // Found a valid lump, return it - *pLump = (uint8_t*)header + lump->fileofs; - *pLumpSize = lump->filelen; - return true; - } - } + pos = ALIGN(pos, 4); + if (pos > filelen - 8) + return false; + if (RL32(buf + pos) != BSPXHEADER) + return false; + pos += 8; + + uint32_t numlumps = RL32(buf + pos - 4); + if (numlumps > (filelen - pos) / sizeof(xlump_t)) + return false; + + xlump_t *l = (xlump_t *)(buf + pos); + for (int i = 0; i < numlumps; i++, l++) { + uint32_t ofs = LittleLong(l->fileofs); + uint32_t len = LittleLong(l->filelen); + uint32_t end = ofs + len; + if (end < ofs || end > filelen) + continue; - return false; + if (!strcmp(l->name, name)) { + *pLump = buf + ofs; + *pLumpSize = len; + return true; + } + } + return false; } -static void BSP_LoadBspxNormals(bsp_t* bsp, const void* data, size_t data_size) +static void BSP_LoadBspxNormals(bsp_t* bsp, const byte* in, uint32_t data_size) { - if (data_size < sizeof(bspx_facenormals_header_t)) + if (data_size < sizeof(uint32_t)) return; // Count the total number of face-vertices in the BSP @@ -1513,27 +1113,32 @@ static void BSP_LoadBspxNormals(bsp_t* bsp, const void* data, size_t data_size) } // Validate the header and that all data fits into the lump - const bspx_facenormals_header_t* header = data; + uint32_t num_vectors = BSP_Long(); size_t expected_data_size = - sizeof(bspx_facenormals_header_t) + - sizeof(vec3_t) * header->num_vectors + // vectors + sizeof(uint32_t) + + sizeof(vec3_t) * num_vectors + // vectors sizeof(uint32_t) * 3 * total_vertices; // indices if (data_size < expected_data_size) return; // Allocate the storage arrays - bsp->basisvectors = ALLOC(sizeof(vec3_t) * header->num_vectors); - bsp->numbasisvectors = header->num_vectors; + bsp->basisvectors = ALLOC(sizeof(vec3_t) * num_vectors); + bsp->numbasisvectors = num_vectors; bsp->bases = ALLOC(sizeof(mbasis_t) * total_vertices); bsp->numbases = total_vertices; // Copy the vectors data - const float* vectors = (const float*)((const bspx_facenormals_header_t*)data + 1); - memcpy(bsp->basisvectors, vectors, sizeof(vec3_t) * header->num_vectors); + for (uint32_t i = 0; i < num_vectors; i++) { + for (int j = 0; j < 3; j++) + bsp->basisvectors[i][j] = BSP_Float(); + } // Copy the indices data - const uint32_t* indices = (const uint32_t*)(vectors + header->num_vectors * 3); - memcpy(bsp->bases, indices, sizeof(uint32_t) * 3 * total_vertices); + for (uint32_t i = 0; i < total_vertices; i++) { + bsp->bases[i].normal = BSP_Long(); + bsp->bases[i].tangent = BSP_Long(); + bsp->bases[i].bitangent = BSP_Long(); + } // Add basis indexing int basis_offset = 0; @@ -1544,6 +1149,68 @@ static void BSP_LoadBspxNormals(bsp_t* bsp, const void* data, size_t data_size) basis_offset += face->numsurfedges; } } + +static void BSP_ParseDecoupledLM(bsp_t *bsp, const byte *in, uint32_t filelen) +{ + mface_t *out; + uint32_t offset; + + if (filelen % 40) + return; + if (bsp->numfaces > filelen / 40) + return; + + out = bsp->faces; + for (int i = 0; i < bsp->numfaces; i++, out++) { + out->lm_width = BSP_Short(); + out->lm_height = BSP_Short(); + + offset = BSP_Long(); + if (offset < bsp->numlightmapbytes) + out->lightmap = bsp->lightmap + offset; + + for (int j = 0; j < 2; j++) { + out->lm_axis[j][0] = BSP_Float(); + out->lm_axis[j][1] = BSP_Float(); + out->lm_axis[j][2] = BSP_Float(); + out->lm_offset[j] = BSP_Float(); + } + } + + bsp->lm_decoupled = true; +} + +static void BSP_ParseExtensions(bsp_t *bsp, const byte *buf, uint32_t pos, uint32_t filelen) +{ + pos = ALIGN(pos, 4); + if (pos > filelen - 8) + return; + if (RL32(buf + pos) != BSPXHEADER) + return; + pos += 8; + + uint32_t numlumps = RL32(buf + pos - 4); + if (numlumps > (filelen - pos) / sizeof(xlump_t)) + return; + + xlump_t *l = (xlump_t *)(buf + pos); + for (int i = 0; i < numlumps; i++, l++) { + uint32_t ofs = LittleLong(l->fileofs); + uint32_t len = LittleLong(l->filelen); + uint32_t end = ofs + len; + if (end < ofs || end > filelen) + continue; + + if (!strcmp(l->name, "DECOUPLED_LM")) { + BSP_ParseDecoupledLM(bsp, buf + ofs, len); + continue; + } else if (!strcmp(l->name, "FACENORMALS")) { + BSP_LoadBspxNormals(bsp, buf + ofs, len); + continue; + } + } +} + #endif /* @@ -1559,11 +1226,12 @@ int BSP_Load(const char *name, bsp_t **bsp_p) byte *buf; dheader_t *header; const lump_info_t *info; - size_t filelen, ofs, len, end, count; - int ret; - byte *lumpdata[HEADER_LUMPS]; - size_t lumpcount[HEADER_LUMPS]; + uint32_t filelen, ofs, len, end, count, maxpos; + int i, ret; + uint32_t lump_ofs[q_countof(bsp_lumps)]; + uint32_t lump_count[q_countof(bsp_lumps)]; size_t memsize; + bool extended = false; Q_assert(name); Q_assert(bsp_p); @@ -1588,10 +1256,20 @@ int BSP_Load(const char *name, bsp_t **bsp_p) return filelen; } + if (filelen < sizeof(dheader_t)) { + ret = Q_ERR_FILE_TOO_SMALL; + goto fail2; + } + // byte swap and validate the header header = (dheader_t *)buf; - if (LittleLong(header->ident) != IDBSPHEADER && - LittleLong(header->ident) != QBSPHEADER) { + switch (LittleLong(header->ident)) { + case IDBSPHEADER: + break; + case IDBSPHEADER_EXT: + extended = true; + break; + default: ret = Q_ERR_UNKNOWN_FORMAT; goto fail2; } @@ -1600,42 +1278,38 @@ int BSP_Load(const char *name, bsp_t **bsp_p) goto fail2; } - const lump_info_t *lumps = LittleLong(header->ident) == IDBSPHEADER ? bsp_lumps : qbsp_lumps; - // byte swap and validate all lumps memsize = 0; - for (info = lumps; info->load; info++) { + maxpos = 0; + for (i = 0, info = bsp_lumps; i < q_countof(bsp_lumps); i++, info++) { ofs = LittleLong(header->lumps[info->lump].fileofs); len = LittleLong(header->lumps[info->lump].filelen); end = ofs + len; if (end < ofs || end > filelen) { - ret = Q_ERR_BAD_EXTENT; + Com_SetLastError(va("%s lump out of bounds", info->name)); + ret = Q_ERR_INVALID_FORMAT; goto fail2; } - if (ofs % info->diskalign) { - ret = Q_ERR_BAD_ALIGN; - goto fail2; - } - if (len % info->disksize) { - ret = Q_ERR_ODD_SIZE; - goto fail2; - } - count = len / info->disksize; - if (count > info->maxcount) { - ret = Q_ERR_TOO_MANY; + if (len % info->disksize[extended]) { + Com_SetLastError(va("%s lump has odd size", info->name)); + ret = Q_ERR_INVALID_FORMAT; goto fail2; } + count = len / info->disksize[extended]; + Q_assert(count <= INT_MAX / info->memsize); - lumpdata[info->lump] = buf + ofs; - lumpcount[info->lump] = count; + lump_ofs[i] = ofs; + lump_count[i] = count; - memsize += count * info->memsize; + // round to cacheline + memsize += ALIGN(count * info->memsize, 64); + maxpos = max(maxpos, end); } - + #if USE_REF const void* normal_lump_data = NULL; - size_t normal_lump_size = 0; - if (BSP_FindBspxLump(header, filelen, "FACENORMALS", &normal_lump_data, &normal_lump_size)) + uint32_t normal_lump_size = 0; + if (BSP_FindBspxLump(buf, maxpos, filelen, "FACENORMALS", &normal_lump_data, &normal_lump_size)) { memsize += normal_lump_size; } @@ -1646,17 +1320,16 @@ int BSP_Load(const char *name, bsp_t **bsp_p) bsp = Z_Mallocz(sizeof(*bsp) + len); memcpy(bsp->name, name, len + 1); bsp->refcount = 1; - bsp->extended = (lumps == qbsp_lumps); + bsp->extended = extended; - // add an extra page for cacheline alignment overhead - Hunk_Begin(&bsp->hunk, memsize + 4096); + Hunk_Begin(&bsp->hunk, memsize); // calculate the checksum bsp->checksum = Com_BlockChecksum(buf, filelen); // load all lumps - for (info = lumps; info->load; info++) { - ret = info->load(bsp, lumpdata[info->lump], lumpcount[info->lump]); + for (i = 0; i < q_countof(bsp_lumps); i++) { + ret = bsp_lumps[i].load(bsp, buf + lump_ofs[i], lump_count[i]); if (ret) { goto fail1; } @@ -1682,10 +1355,7 @@ int BSP_Load(const char *name, bsp_t **bsp_p) } #if USE_REF - if (normal_lump_size) - { - BSP_LoadBspxNormals(bsp, normal_lump_data, normal_lump_size); - } + BSP_ParseExtensions(bsp, buf, maxpos, filelen); #endif Hunk_End(&bsp->hunk); @@ -1705,6 +1375,17 @@ int BSP_Load(const char *name, bsp_t **bsp_p) return ret; } +const char *BSP_ErrorString(int err) +{ + switch (err) { + case Q_ERR_INVALID_FORMAT: + case Q_ERR_INFINITE_LOOP: + return Com_GetLastError(); + default: + return Q_ErrorString(err); + } +} + /* =============================================================================== @@ -1719,11 +1400,10 @@ static lightpoint_t *light_point; static bool BSP_RecursiveLightPoint(mnode_t *node, float p1f, float p2f, const vec3_t p1, const vec3_t p2) { - vec_t d1, d2, frac, midf; + vec_t d1, d2, frac, midf, s, t; vec3_t mid; - int i, side, s, t; + int i, side; mface_t *surf; - mtexinfo_t *texinfo; while (node->plane) { // calculate distancies @@ -1749,19 +1429,14 @@ static bool BSP_RecursiveLightPoint(mnode_t *node, float p1f, float p2f, const v for (i = 0, surf = node->firstface; i < node->numfaces; i++, surf++) { if (!surf->lightmap) continue; - - texinfo = surf->texinfo; - if (texinfo->c.flags & SURF_NOLM_MASK) + if (surf->drawflags & SURF_NOLM_MASK) continue; - s = DotProduct(texinfo->axis[0], mid) + texinfo->offset[0]; - t = DotProduct(texinfo->axis[1], mid) + texinfo->offset[1]; - - s -= surf->texturemins[0]; - t -= surf->texturemins[1]; - if (s < 0 || s > surf->extents[0]) + s = DotProduct(surf->lm_axis[0], mid) + surf->lm_offset[0]; + t = DotProduct(surf->lm_axis[1], mid) + surf->lm_offset[1]; + if (s < 0 || s > surf->lm_width - 1) continue; - if (t < 0 || t > surf->extents[1]) + if (t < 0 || t > surf->lm_height - 1) continue; light_point->surf = surf; @@ -1949,12 +1624,10 @@ mmodel_t *BSP_InlineModel(bsp_t *bsp, const char *name) { int num; - if (!bsp || !name) { - Com_Error(ERR_DROP, "%s: NULL", __func__); - } - if (name[0] != '*') { - Com_Error(ERR_DROP, "%s: bad name: %s", __func__, name); - } + Q_assert(bsp); + Q_assert(name); + Q_assert(name[0] == '*'); + num = atoi(name + 1); if (num < 1 || num >= bsp->nummodels) { Com_Error(ERR_DROP, "%s: bad number: %d", __func__, num); diff --git a/src/common/cmodel.c b/src/common/cmodel.c index 756d951b0..2490a4d21 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -23,7 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/cmodel.h" #include "common/common.h" #include "common/cvar.h" +#include "common/files.h" #include "common/math.h" +#include "common/sizebuf.h" #include "common/zone.h" #include "system/hunk.h" @@ -31,14 +33,138 @@ mtexinfo_t nulltexinfo; static mleaf_t nullleaf; -static int floodvalid; -static int checkcount; +static unsigned floodvalid; +static unsigned checkcount; static cvar_t *map_noareas; static cvar_t *map_allsolid_bug; +static cvar_t *map_override_path; static void FloodAreaConnections(cm_t *cm); +//======================================================================= + +enum { + OVERRIDE_NAME = 1, + OVERRIDE_CSUM = 2, + OVERRIDE_ENTS = 4, + OVERRIDE_ALL = 7 +}; + +static void load_entstring_override(cm_t *cm, const char *server) +{ + char buffer[MAX_QPATH], *data = NULL; + int ret; + + if (Q_snprintf(buffer, sizeof(buffer), "%s/%s.ent", map_override_path->string, server) >= sizeof(buffer)) { + ret = Q_ERR(ENAMETOOLONG); + goto fail; + } + + ret = FS_LoadFileEx(buffer, (void **)&data, 0, TAG_CMODEL); + if (!data) { + if (ret == Q_ERR(ENOENT)) + return; + goto fail; + } + + Com_Printf("Loaded entity string from %s\n", buffer); + cm->entitystring = data; + cm->override_bits |= OVERRIDE_ENTS; + return; + +fail: + FS_FreeFile(data); + Com_EPrintf("Couldn't load entity string from %s: %s\n", buffer, Q_ErrorString(ret)); +} + +static void load_binary_override(cm_t *cm, char *server, size_t server_size) +{ + sizebuf_t sz; + char buffer[MAX_QPATH]; + byte *data = NULL; + int ret, bits, len; + char *buf, name_buf[MAX_QPATH]; + + if (Q_snprintf(buffer, sizeof(buffer), "%s/%s.bsp.override", map_override_path->string, server) >= sizeof(buffer)) { + ret = Q_ERR(ENAMETOOLONG); + goto fail; + } + + ret = FS_LoadFile(buffer, (void **)&data); + if (!data) { + if (ret == Q_ERR(ENOENT)) + return; + goto fail; + } + + SZ_Init(&sz, data, ret); + sz.cursize = ret; + + ret = Q_ERR_INVALID_FORMAT; + + bits = SZ_ReadLong(&sz); + if (bits & ~OVERRIDE_ALL) + goto fail; + + if (bits & OVERRIDE_NAME) { + if (!(buf = SZ_ReadData(&sz, MAX_QPATH))) + goto fail; + if (!memchr(buf, 0, MAX_QPATH)) + goto fail; + if (!Com_ParseMapName(name_buf, buf, sizeof(name_buf))) + goto fail; + } + + if (bits & OVERRIDE_CSUM) + cm->checksum = SZ_ReadLong(&sz); + + if (bits & OVERRIDE_ENTS) { + len = SZ_ReadLong(&sz); + if (len <= 0) + goto fail; + if (!(buf = SZ_ReadData(&sz, len))) + goto fail; + cm->entitystring = Z_TagMalloc(len + 1, TAG_CMODEL); + memcpy(cm->entitystring, buf, len); + cm->entitystring[len] = 0; + } + + if (bits & OVERRIDE_NAME) + Q_strlcpy(server, name_buf, server_size); + + Com_Printf("Loaded %s\n", buffer); + FS_FreeFile(data); + cm->override_bits = bits; + return; + +fail: + Com_EPrintf("Couldn't load %s: %s\n", buffer, Q_ErrorString(ret)); + FS_FreeFile(data); +} + +/* +================== +CM_LoadOverrides + +Ugly hack to override entstring and other parameters. + +Must be called before CM_LoadMap. +May modify server buffer if name override is in effect. +May allocate enstring, must be freed with CM_FreeMap(). +================== +*/ +void CM_LoadOverrides(cm_t *cm, char *server, size_t server_size) +{ + if (!*map_override_path->string) + return; + + load_binary_override(cm, server, server_size); + + if (!(cm->override_bits & OVERRIDE_ENTS)) + load_entstring_override(cm, server); +} + /* ================== CM_FreeMap @@ -46,7 +172,12 @@ CM_FreeMap */ void CM_FreeMap(cm_t *cm) { + Z_Free(cm->portalopen); Z_Free(cm->floodnums); + + if (cm->override_bits & OVERRIDE_ENTS) + Z_Free(cm->entitystring); + BSP_Free(cm->cache); memset(cm, 0, sizeof(*cm)); @@ -61,18 +192,20 @@ Loads in the map and all submodels */ int CM_LoadMap(cm_t *cm, const char *name) { - bsp_t *cache; int ret; - ret = BSP_Load(name, &cache); - if (!cache) { + ret = BSP_Load(name, &cm->cache); + if (!cm->cache) return ret; - } - cm->cache = cache; - cm->floodnums = Z_TagMallocz(sizeof(int) * cm->cache->numareas + - sizeof(bool) * (cm->cache->lastareaportal + 1), TAG_CMODEL); - cm->portalopen = (bool *)(cm->floodnums + cm->cache->numareas); + if (!(cm->override_bits & OVERRIDE_CSUM)) + cm->checksum = cm->cache->checksum; + + if (!(cm->override_bits & OVERRIDE_ENTS)) + cm->entitystring = cm->cache->entitystring; + + cm->floodnums = Z_TagMallocz(sizeof(cm->floodnums[0]) * cm->cache->numareas, TAG_CMODEL); + cm->portalopen = Z_TagMallocz(sizeof(cm->portalopen[0]) * cm->cache->numportals, TAG_CMODEL); FloodAreaConnections(cm); return Q_ERR_SUCCESS; @@ -225,9 +358,9 @@ static void CM_BoxLeafs_r(mnode_t *node) while (node->plane) { s = BoxOnPlaneSideFast(leaf_mins, leaf_maxs, node->plane); - if (s == 1) { + if (s == BOX_INFRONT) { node = node->children[0]; - } else if (s == 2) { + } else if (s == BOX_BEHIND) { node = node->children[1]; } else { // go down both @@ -644,7 +777,7 @@ void CM_BoxTrace(trace_t *trace, VectorCopy(end, trace_end); for (i = 0; i < 8; i++) for (j = 0; j < 3; j++) - trace_offsets[i][j] = bounds[i >> j & 1][j]; + trace_offsets[i][j] = bounds[(i >> j) & 1][j]; // // check for position test special case @@ -809,14 +942,8 @@ void CM_SetAreaPortalState(cm_t *cm, int portalnum, bool open) return; } - if (portalnum < 0 || portalnum >= MAX_MAP_AREAPORTALS) { - Com_EPrintf("%s: portalnum %d is out of range\n", __func__, portalnum); - return; - } - - // ignore areaportals not referenced by areas - if (portalnum > cm->cache->lastareaportal) { - Com_DPrintf("%s: portalnum %d is not in use\n", __func__, portalnum); + if (portalnum < 0 || portalnum >= cm->cache->numportals) { + Com_DPrintf("%s: portalnum %d is out of range\n", __func__, portalnum); return; } @@ -870,6 +997,7 @@ int CM_WriteAreaBits(cm_t *cm, byte *buffer, int area) } bytes = (cache->numareas + 7) >> 3; + Q_assert(bytes <= MAX_MAP_AREA_BYTES); if (map_noareas->integer || !area) { // for debugging, send everything @@ -896,7 +1024,7 @@ int CM_WritePortalBits(cm_t *cm, byte *buffer) return 0; } - numportals = min(cm->cache->lastareaportal + 1, MAX_MAP_PORTAL_BYTES << 3); + numportals = min(cm->cache->numportals, MAX_MAP_PORTAL_BYTES << 3); bytes = (numportals + 7) >> 3; memset(buffer, 0, bytes); @@ -917,11 +1045,11 @@ void CM_SetPortalStates(cm_t *cm, byte *buffer, int bytes) return; } - numportals = min(cm->cache->lastareaportal + 1, bytes << 3); + numportals = min(cm->cache->numportals, bytes << 3); for (i = 0; i < numportals; i++) { cm->portalopen[i] = Q_IsBitSet(buffer, i); } - for (; i <= cm->cache->lastareaportal; i++) { + for (; i < cm->cache->numportals; i++) { cm->portalopen[i] = true; } @@ -1029,5 +1157,6 @@ void CM_Init(void) map_noareas = Cvar_Get("map_noareas", "0", 0); map_allsolid_bug = Cvar_Get("map_allsolid_bug", "1", 0); + map_override_path = Cvar_Get("map_override_path", "", 0); } diff --git a/src/common/tests.c b/src/common/tests.c index 2837de920..287219335 100644 --- a/src/common/tests.c +++ b/src/common/tests.c @@ -140,12 +140,11 @@ static void BSP_Test_f(void) name = list[i]; ret = BSP_Load(name, &bsp); if (!bsp) { - Com_EPrintf("%s: %s\n", name, Q_ErrorString(ret)); + Com_EPrintf("Couldn't load %s: %s\n", name, BSP_ErrorString(ret)); errors++; continue; } - Com_DPrintf("%s: success\n", name); BSP_Free(bsp); } diff --git a/src/common/utils.c b/src/common/utils.c index df2d5a7a8..2fc33b46d 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -281,6 +281,24 @@ void Com_PlayerToEntityState(const player_state_t *ps, entity_state_t *es) es->angles[ROLL] = 0; } +/* +================ +Com_ParseMapName +================ +*/ +bool Com_ParseMapName(char *out, const char *in, size_t size) +{ + if (Q_stricmpn(in, "maps/", 5)) + return false; + in += 5; + + char *ext = COM_FileExtension(in); + if (ext == in || Q_stricmp(ext, ".bsp")) + return false; + + return COM_StripExtension(out, in, size) < size; +} + #if USE_CLIENT || USE_MVD_CLIENT /* ================ diff --git a/src/refresh/gl/gl.h b/src/refresh/gl/gl.h index 6c5dbb1ac..e26038409 100644 --- a/src/refresh/gl/gl.h +++ b/src/refresh/gl/gl.h @@ -103,9 +103,9 @@ typedef struct { refdef_t fd; vec3_t viewaxis[3]; GLfloat viewmatrix[16]; - int visframe; - int drawframe; - int dlightframe; + unsigned visframe; + unsigned drawframe; + unsigned dlightframe; int viewcluster1; int viewcluster2; cplane_t frustumPlanes[4]; @@ -214,7 +214,8 @@ bool GL_AllocBlock(int width, int height, int *inuse, int w, int h, int *s, int *t); void GL_MultMatrix(GLfloat *out, const GLfloat *a, const GLfloat *b); -void GL_RotateForEntity(vec3_t origin, float scale); +void GL_SetEntityAxis(void); +void GL_RotateForEntity(void); void QGL_ClearErrors(void); bool GL_ShowErrors(const char *func); @@ -263,8 +264,8 @@ typedef struct maliasmesh_s { &glr.fd.lightstyles[gl_static.lightstylemap[(surf)->styles[i]]] #define LM_MAX_LIGHTMAPS 32 -#define LM_BLOCK_WIDTH 256 -#define LM_BLOCK_HEIGHT 256 +#define LM_BLOCK_WIDTH 512 +#define LM_BLOCK_HEIGHT 512 typedef struct { int inuse[LM_BLOCK_WIDTH]; @@ -532,7 +533,7 @@ void GL_BindArrays(void); void GL_Flush3D(void); void GL_DrawFace(mface_t *surf); -void GL_AddAlphaFace(mface_t *face); +void GL_AddAlphaFace(mface_t *face, entity_t *ent); void GL_AddSolidFace(mface_t *face); void GL_DrawAlphaFaces(void); void GL_DrawSolidFaces(void); diff --git a/src/refresh/gl/main.c b/src/refresh/gl/main.c index d2117dbf4..2658d8bcb 100644 --- a/src/refresh/gl/main.c +++ b/src/refresh/gl/main.c @@ -261,24 +261,41 @@ void GL_MultMatrix(GLfloat *p, const GLfloat *a, const GLfloat *b) } } -void GL_RotateForEntity(vec3_t origin, float scale) +void GL_SetEntityAxis(void) { + if (VectorEmpty(glr.ent->angles)) { + glr.entrotated = false; + VectorSet(glr.entaxis[0], 1, 0, 0); + VectorSet(glr.entaxis[1], 0, 1, 0); + VectorSet(glr.entaxis[2], 0, 0, 1); + } else { + glr.entrotated = true; + AnglesToAxis(glr.ent->angles, glr.entaxis); + } +} + +void GL_RotateForEntity(void) +{ + float scale = 1.f; + if (glr.ent->scale > 0.f) + scale = glr.ent->scale; + GLfloat matrix[16]; matrix[0] = glr.entaxis[0][0] * scale; matrix[4] = glr.entaxis[1][0] * scale; matrix[8] = glr.entaxis[2][0] * scale; - matrix[12] = origin[0]; + matrix[12] = glr.ent->origin[0]; matrix[1] = glr.entaxis[0][1] * scale; matrix[5] = glr.entaxis[1][1] * scale; matrix[9] = glr.entaxis[2][1] * scale; - matrix[13] = origin[1]; + matrix[13] = glr.ent->origin[1]; matrix[2] = glr.entaxis[0][2] * scale; matrix[6] = glr.entaxis[1][2] * scale; matrix[10] = glr.entaxis[2][2] * scale; - matrix[14] = origin[2]; + matrix[14] = glr.ent->origin[2]; matrix[3] = 0; matrix[7] = 0; @@ -394,15 +411,7 @@ static void GL_DrawEntities(int mask) glr.ent = ent; // convert angles to axis - if (VectorEmpty(ent->angles)) { - glr.entrotated = false; - VectorSet(glr.entaxis[0], 1, 0, 0); - VectorSet(glr.entaxis[1], 0, 1, 0); - VectorSet(glr.entaxis[2], 0, 0, 1); - } else { - glr.entrotated = true; - AnglesToAxis(ent->angles, glr.entaxis); - } + GL_SetEntityAxis(); // inline BSP model if (ent->model & 0x80000000) { diff --git a/src/refresh/gl/mesh.c b/src/refresh/gl/mesh.c index f50d5efef..440b62630 100644 --- a/src/refresh/gl/mesh.c +++ b/src/refresh/gl/mesh.c @@ -710,11 +710,7 @@ void GL_DrawAliasModel(const model_t *model) tess_static_plain : tess_lerped_plain; } - float scale = 1.f; - if (ent->scale > 0.f) - scale = ent->scale; - - GL_RotateForEntity(origin, scale); + GL_RotateForEntity(); if (ent->flags & RF_WEAPONMODEL) setup_weaponmodel(); diff --git a/src/refresh/gl/surf.c b/src/refresh/gl/surf.c index 8b95294a2..3a37a8e66 100644 --- a/src/refresh/gl/surf.c +++ b/src/refresh/gl/surf.c @@ -128,16 +128,17 @@ static void put_blocklights(byte *out, int smax, int tmax, int stride) static void add_dynamic_lights(mface_t *surf) { dlight_t *light; - mtexinfo_t *tex; vec3_t point; - int local[2]; + vec2_t local; + vec_t s_scale, t_scale, sd, td; vec_t dist, rad, minlight, scale, frac; float *bl; - int i, smax, tmax, s, t, sd, td; + int i, smax, tmax, s, t; - smax = S_MAX(surf); - tmax = T_MAX(surf); - tex = surf->texinfo; + smax = surf->lm_width; + tmax = surf->lm_height; + s_scale = surf->lm_scale[0]; + t_scale = surf->lm_scale[1]; for (i = 0; i < glr.fd.num_dlights; i++) { if (!(surf->dlightbits & (1U << i))) @@ -159,21 +160,18 @@ static void add_dynamic_lights(mface_t *surf) VectorMA(light->transformed, -dist, surf->plane->normal, point); - local[0] = DotProduct(point, tex->axis[0]) + tex->offset[0]; - local[1] = DotProduct(point, tex->axis[1]) + tex->offset[1]; - - local[0] -= surf->texturemins[0]; - local[1] -= surf->texturemins[1]; + local[0] = DotProduct(point, surf->lm_axis[0]) + surf->lm_offset[0]; + local[1] = DotProduct(point, surf->lm_axis[1]) + surf->lm_offset[1]; bl = blocklights; for (t = 0; t < tmax; t++) { - td = abs(local[1] - (t << 4)); + td = fabsf(local[1] - t) * t_scale; for (s = 0; s < smax; s++) { - sd = abs(local[0] - (s << 4)); + sd = fabsf(local[0] - s) * s_scale; if (sd > td) - dist = sd + (td >> 1); + dist = sd + td * 0.5f; else - dist = td + (sd >> 1); + dist = td + sd * 0.5f; if (dist < minlight) { frac = rad - dist * scale; bl[0] += light->color[0] * frac; @@ -246,8 +244,8 @@ static void update_dynamic_lightmap(mface_t *surf) byte temp[MAX_BLOCKLIGHTS * 4]; int smax, tmax, size; - smax = S_MAX(surf); - tmax = T_MAX(surf); + smax = surf->lm_width; + tmax = surf->lm_height; size = smax * tmax; // add all the lightmaps @@ -394,8 +392,8 @@ static void build_primary_lightmap(mface_t *surf) { int smax, tmax, size; - smax = S_MAX(surf); - tmax = T_MAX(surf); + smax = surf->lm_width; + tmax = surf->lm_height; size = smax * tmax; // add all the lightmaps @@ -412,8 +410,8 @@ static void LM_BuildSurface(mface_t *surf, vec_t *vbo) { int smax, tmax, s, t; - smax = S_MAX(surf); - tmax = T_MAX(surf); + smax = surf->lm_width; + tmax = surf->lm_height; if (!LM_AllocBlock(smax, tmax, &s, &t)) { LM_UploadBlock(); @@ -513,6 +511,7 @@ static uint32_t color_for_surface(mface_t *surf) static void build_surface_poly(mface_t *surf, vec_t *vbo) { + bsp_t *bsp = gl_static.world.cache; msurfedge_t *src_surfedge; mvertex_t *src_vert; medge_t *src_edge; @@ -578,33 +577,53 @@ static void build_surface_poly(mface_t *surf, vec_t *vbo) tc[0] = DotProduct(vbo, texinfo->axis[0]) + texinfo->offset[0]; tc[1] = DotProduct(vbo, texinfo->axis[1]) + texinfo->offset[1]; - if (mins[0] > tc[0]) mins[0] = tc[0]; - if (maxs[0] < tc[0]) maxs[0] = tc[0]; - - if (mins[1] > tc[1]) mins[1] = tc[1]; - if (maxs[1] < tc[1]) maxs[1] = tc[1]; - vbo[4] = tc[0] * scale[0]; vbo[5] = tc[1] * scale[1]; // texture1 coordinates - vbo[6] = tc[0]; - vbo[7] = tc[1]; + if (bsp->lm_decoupled) { + vbo[6] = DotProduct(vbo, surf->lm_axis[0]) + surf->lm_offset[0]; + vbo[7] = DotProduct(vbo, surf->lm_axis[1]) + surf->lm_offset[1]; + } else { + if (mins[0] > tc[0]) mins[0] = tc[0]; + if (maxs[0] < tc[0]) maxs[0] = tc[0]; + + if (mins[1] > tc[1]) mins[1] = tc[1]; + if (maxs[1] < tc[1]) maxs[1] = tc[1]; + + vbo[6] = tc[0] / 16; + vbo[7] = tc[1] / 16; + } vbo += VERTEX_SIZE; } + if (bsp->lm_decoupled) { + surf->lm_scale[0] = 1.0f / VectorLength(surf->lm_axis[0]); + surf->lm_scale[1] = 1.0f / VectorLength(surf->lm_axis[1]); + return; + } + // calculate surface extents bmins[0] = floor(mins[0] / 16); bmins[1] = floor(mins[1] / 16); bmaxs[0] = ceil(maxs[0] / 16); bmaxs[1] = ceil(maxs[1] / 16); - surf->texturemins[0] = bmins[0] * 16; - surf->texturemins[1] = bmins[1] * 16; + VectorScale(texinfo->axis[0], 1.0f / 16, surf->lm_axis[0]); + VectorScale(texinfo->axis[1], 1.0f / 16, surf->lm_axis[1]); + surf->lm_offset[0] = texinfo->offset[0] / 16 - bmins[0]; + surf->lm_offset[1] = texinfo->offset[1] / 16 - bmins[1]; + surf->lm_width = bmaxs[0] - bmins[0] + 1; + surf->lm_height = bmaxs[1] - bmins[1] + 1; + surf->lm_scale[0] = 16; + surf->lm_scale[1] = 16; - surf->extents[0] = (bmaxs[0] - bmins[0]) * 16; - surf->extents[1] = (bmaxs[1] - bmins[1]) * 16; + for (i = 0; i < surf->numsurfedges; i++) { + vbo -= VERTEX_SIZE; + vbo[6] -= bmins[0]; + vbo[7] -= bmins[1]; + } } // vertex lighting approximation @@ -617,8 +636,8 @@ static void sample_surface_verts(mface_t *surf, vec_t *vbo) glr.lightpoint.surf = surf; for (i = 0; i < surf->numsurfedges; i++) { - glr.lightpoint.s = (int)vbo[6] - surf->texturemins[0]; - glr.lightpoint.t = (int)vbo[7] - surf->texturemins[1]; + glr.lightpoint.s = (int)vbo[6]; + glr.lightpoint.t = (int)vbo[7]; GL_SampleLightPoint(color); adjust_color_f(color, color, lm.add, lm.modulate, lm.scale); @@ -651,17 +670,17 @@ static void build_surface_light(mface_t *surf, vec_t *vbo) if (surf->drawflags & SURF_NOLM_MASK) return; + smax = surf->lm_width; + tmax = surf->lm_height; + // validate extents - if (surf->extents[0] < 0 || surf->extents[0] > MAX_SURFACE_EXTENTS || - surf->extents[1] < 0 || surf->extents[1] > MAX_SURFACE_EXTENTS) { + if (smax > LM_BLOCK_HEIGHT || tmax > LM_BLOCK_HEIGHT) { Com_EPrintf("%s: bad surface extents\n", __func__); surf->lightmap = NULL; // don't use this lightmap return; } // validate blocklights size - smax = S_MAX(surf); - tmax = T_MAX(surf); size = smax * tmax; if (size > MAX_BLOCKLIGHTS) { Com_EPrintf("%s: MAX_BLOCKLIGHTS exceeded\n", __func__); @@ -690,14 +709,14 @@ static void normalize_surface_lmtc(mface_t *surf, vec_t *vbo) float s, t; int i; - s = ((surf->light_s << 4) + 8) - surf->texturemins[0]; - t = ((surf->light_t << 4) + 8) - surf->texturemins[1]; + s = surf->light_s + 0.5f; + t = surf->light_t + 0.5f; for (i = 0; i < surf->numsurfedges; i++) { vbo[6] += s; vbo[7] += t; - vbo[6] *= 1.0f / (LM_BLOCK_WIDTH * 16); - vbo[7] *= 1.0f / (LM_BLOCK_HEIGHT * 16); + vbo[6] *= 1.0f / LM_BLOCK_WIDTH; + vbo[7] *= 1.0f / LM_BLOCK_HEIGHT; vbo += VERTEX_SIZE; } @@ -889,7 +908,7 @@ void GL_LoadWorld(const char *name) ret = BSP_Load(name, &bsp); if (!bsp) { Com_Error(ERR_DROP, "%s: couldn't load %s: %s", - __func__, name, Q_ErrorString(ret)); + __func__, name, BSP_ErrorString(ret)); } // check if the required world model was already loaded diff --git a/src/refresh/gl/tess.c b/src/refresh/gl/tess.c index 7bd4cf50d..f039e20cd 100644 --- a/src/refresh/gl/tess.c +++ b/src/refresh/gl/tess.c @@ -404,17 +404,6 @@ void GL_DrawFace(mface_t *surf) c.facesDrawn++; } -static inline void GL_DrawChain(mface_t **head) -{ - mface_t *face; - - for (face = *head; face; face = face->next) { - GL_DrawFace(face); - } - - *head = NULL; -} - void GL_ClearSolidFaces(void) { int i; @@ -426,26 +415,40 @@ void GL_ClearSolidFaces(void) void GL_DrawSolidFaces(void) { + mface_t *face; int i; for (i = 0; i < FACE_HASH_SIZE; i++) { - GL_DrawChain(&faces_head[i]); + for (face = faces_head[i]; face; face = face->next) { + GL_DrawFace(face); + } + faces_head[i] = NULL; } } void GL_DrawAlphaFaces(void) { + mface_t *face; + if (!faces_alpha) { return; } - glr.ent = &gl_world; - - GL_LoadMatrix(glr.viewmatrix); + glr.ent = NULL; GL_BindArrays(); - GL_DrawChain(&faces_alpha); + for (face = faces_alpha; face; face = face->next) { + if (glr.ent != face->entity) { + glr.ent = face->entity; + GL_Flush3D(); + GL_SetEntityAxis(); + GL_RotateForEntity(); + } + GL_DrawFace(face); + } + + faces_alpha = NULL; GL_Flush3D(); } @@ -464,9 +467,10 @@ void GL_AddSolidFace(mface_t *face) faces_next[hash] = &face->next; } -void GL_AddAlphaFace(mface_t *face) +void GL_AddAlphaFace(mface_t *face, entity_t *ent) { // draw back-to-front + face->entity = ent; face->next = faces_alpha; faces_alpha = face; } diff --git a/src/refresh/gl/world.c b/src/refresh/gl/world.c index 06d6b50eb..7a2dde3e7 100644 --- a/src/refresh/gl/world.c +++ b/src/refresh/gl/world.c @@ -20,32 +20,30 @@ with this program; if not, write to the Free Software Foundation, Inc., void GL_SampleLightPoint(vec3_t color) { - mface_t *surf; + mface_t *surf = glr.lightpoint.surf; int s, t, i; byte *lightmap; byte *b1, *b2, *b3, *b4; - int fracu, fracv; - int w1, w2, w3, w4; - byte temp[3]; + float fracu, fracv; + float w1, w2, w3, w4; + vec3_t temp; int smax, tmax, size; lightstyle_t *style; - fracu = glr.lightpoint.s & 15; - fracv = glr.lightpoint.t & 15; + s = glr.lightpoint.s; + t = glr.lightpoint.t; + + fracu = glr.lightpoint.s - s; + fracv = glr.lightpoint.t - t; // compute weights of lightmap blocks - w1 = (16 - fracu) * (16 - fracv); - w2 = fracu * (16 - fracv); + w1 = (1.0f - fracu) * (1.0f - fracv); + w2 = fracu * (1.0f - fracv); w3 = fracu * fracv; - w4 = (16 - fracu) * fracv; - - s = glr.lightpoint.s >> 4; - t = glr.lightpoint.t >> 4; - - surf = glr.lightpoint.surf; + w4 = (1.0f - fracu) * fracv; - smax = S_MAX(surf); - tmax = T_MAX(surf); + smax = surf->lm_width; + tmax = surf->lm_height; size = smax * tmax * 3; VectorClear(color); @@ -58,9 +56,9 @@ void GL_SampleLightPoint(vec3_t color) b3 = &lightmap[3 * ((t + 1) * smax + (s + 1))]; b4 = &lightmap[3 * ((t + 1) * smax + (s + 0))]; - temp[0] = (w1 * b1[0] + w2 * b2[0] + w3 * b3[0] + w4 * b4[0]) >> 8; - temp[1] = (w1 * b1[1] + w2 * b2[1] + w3 * b3[1] + w4 * b4[1]) >> 8; - temp[2] = (w1 * b1[2] + w2 * b2[2] + w3 * b3[2] + w4 * b4[2]) >> 8; + temp[0] = w1 * b1[0] + w2 * b2[0] + w3 * b3[0] + w4 * b4[0]; + temp[1] = w1 * b1[1] + w2 * b2[1] + w3 * b3[1] + w4 * b4[1]; + temp[2] = w1 * b1[2] + w2 * b2[2] + w3 * b3[2] + w4 * b4[2]; style = LIGHT_STYLE(surf, i); @@ -142,7 +140,7 @@ static bool _GL_LightPoint(const vec3_t start, vec3_t color) return true; } -static void GL_MarkLights_r(mnode_t *node, dlight_t *light, int lightbit) +static void GL_MarkLights_r(mnode_t *node, dlight_t *light, unsigned lightbit) { vec_t dot; int count; @@ -399,7 +397,7 @@ void GL_DrawBspModel(mmodel_t *model) GL_TransformLights(model); - GL_RotateForEntity(ent->origin, 1.f); + GL_RotateForEntity(); GL_BindArrays(); @@ -417,12 +415,9 @@ void GL_DrawBspModel(mmodel_t *model) continue; } - // alpha faces on transformed inline models are drawn with world GL - // matrix. this Q2 bug is not fixed intentionally: some maps exploit it - // to hide surfaces that would otherwise be visible. if (face->drawflags & SURF_TRANS_MASK) { if (model->drawframe != glr.drawframe) - GL_AddAlphaFace(face); + GL_AddAlphaFace(face, ent); continue; } @@ -503,8 +498,12 @@ static inline void GL_DrawNode(mnode_t *node) continue; } + if (face->drawflags & SURF_NODRAW) { + continue; + } + if (face->drawflags & SURF_TRANS_MASK) { - GL_AddAlphaFace(face); + GL_AddAlphaFace(face, &gl_world); continue; } diff --git a/src/refresh/vkpt/vertex_buffer.c b/src/refresh/vkpt/vertex_buffer.c index 5b5e309ee..9319e712a 100644 --- a/src/refresh/vkpt/vertex_buffer.c +++ b/src/refresh/vkpt/vertex_buffer.c @@ -460,9 +460,9 @@ vkpt_iqm_matrix_buffer_upload_staging(VkCommandBuffer cmd_buf) return VK_SUCCESS; } -static int local_light_counts[MAX_MAP_LEAFS]; -static int cluster_light_counts[MAX_MAP_LEAFS]; -static int light_list_tails[MAX_MAP_LEAFS]; +static int local_light_counts[MAX_MAP_CLUSTERS]; +static int cluster_light_counts[MAX_MAP_CLUSTERS]; +static int light_list_tails[MAX_MAP_CLUSTERS]; static int max_model_lights; void vkpt_light_buffer_reset_counts() diff --git a/src/server/init.c b/src/server/init.c index db33fdca5..27c98464a 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -88,46 +88,6 @@ static void resolve_masters(void) #endif } -// optionally load the entity string from external source -static void override_entity_string(const char *server) -{ - char *path = map_override_path->string; - char buffer[MAX_QPATH], *str; - int ret; - - if (!*path) { - return; - } - - if (Q_concat(buffer, sizeof(buffer), path, server, ".ent") >= sizeof(buffer)) { - ret = Q_ERR(ENAMETOOLONG); - goto fail1; - } - - ret = SV_LoadFile(buffer, (void **)&str); - if (!str) { - if (ret == Q_ERR(ENOENT)) { - return; - } - goto fail1; - } - - if (ret > MAX_MAP_ENTSTRING) { - ret = Q_ERR(EFBIG); - goto fail2; - } - - Com_Printf("Loaded entity string from %s\n", buffer); - sv.entitystring = str; - return; - -fail2: - SV_FreeFile(str); -fail1: - Com_EPrintf("Couldn't load entity string from %s: %s\n", - buffer, Q_ErrorString(ret)); -} - /* ================ @@ -141,7 +101,6 @@ void SV_SpawnServer(mapcmd_t *cmd) { int i; client_t *client; - char *entitystring; SCR_BeginLoadingPlaque(); // for local system @@ -169,7 +128,6 @@ void SV_SpawnServer(mapcmd_t *cmd) // free current level CM_FreeMap(&sv.cm); - SV_FreeFile(sv.entitystring); // wipe the entire per-level structure memset(&sv, 0, sizeof(sv)); @@ -200,22 +158,18 @@ void SV_SpawnServer(mapcmd_t *cmd) resolve_masters(); if (cmd->state == ss_game) { - override_entity_string(cmd->server); - sv.cm = cmd->cm; - sprintf(sv.configstrings[CS_MAPCHECKSUM], "%d", (int)sv.cm.cache->checksum); + sprintf(sv.configstrings[CS_MAPCHECKSUM], "%d", sv.cm.checksum); // set inline model names Q_concat(sv.configstrings[CS_MODELS + 1], MAX_QPATH, "maps/", cmd->server, ".bsp"); for (i = 1; i < sv.cm.cache->nummodels; i++) { sprintf(sv.configstrings[CS_MODELS + 1 + i], "*%d", i); } - - entitystring = sv.entitystring ? sv.entitystring : sv.cm.cache->entitystring; } else { // no real map strcpy(sv.configstrings[CS_MAPCHECKSUM], "0"); - entitystring = ""; + sv.cm.entitystring = ""; } // @@ -232,7 +186,7 @@ void SV_SpawnServer(mapcmd_t *cmd) sv.state = ss_loading; // load and spawn all other entities - ge->SpawnEntities(sv.name, entitystring, cmd->spawnpoint); + ge->SpawnEntities(sv.name, sv.cm.entitystring, cmd->spawnpoint); // run two frames to allow everything to settle ge->RunFrame(); sv.framenum++; @@ -304,6 +258,7 @@ static bool check_server(mapcmd_t *cmd, const char *server, bool nextserver) cmd->state = ss_cinematic; } else { + CM_LoadOverrides(&cmd->cm, cmd->server, sizeof(cmd->server)); if (Q_concat(expanded, sizeof(expanded), "maps/", s, ".bsp") < sizeof(expanded)) { ret = CM_LoadMap(&cmd->cm, expanded); } @@ -311,7 +266,8 @@ static bool check_server(mapcmd_t *cmd, const char *server, bool nextserver) } if (ret < 0) { - Com_Printf("Couldn't load %s: %s\n", expanded, Q_ErrorString(ret)); + Com_Printf("Couldn't load %s: %s\n", expanded, BSP_ErrorString(ret)); + CM_FreeMap(&cmd->cm); // free entstring if overridden return false; } @@ -403,7 +359,6 @@ void SV_InitGame(unsigned mvd_spawn) SCR_BeginLoadingPlaque(); CM_FreeMap(&sv.cm); - SV_FreeFile(sv.entitystring); memset(&sv, 0, sizeof(sv)); #if USE_FPS diff --git a/src/server/main.c b/src/server/main.c index 10a4a9271..32032bf17 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -110,8 +110,6 @@ cvar_t *sv_lrcon_password; cvar_t *g_features; -cvar_t *map_override_path; - static bool sv_registered; //============================================================================ @@ -2251,8 +2249,6 @@ void SV_Init(void) Cvar_Get("sv_features", va("%d", SV_FEATURES), CVAR_ROM); g_features = Cvar_Get("g_features", "0", CVAR_ROM); - map_override_path = Cvar_Get("map_override_path", "", 0); - init_rate_limits(); #if USE_FPS @@ -2362,7 +2358,6 @@ void SV_Shutdown(const char *finalmsg, error_type_t type) // free current level CM_FreeMap(&sv.cm); - SV_FreeFile(sv.entitystring); memset(&sv, 0, sizeof(sv)); // free server static data diff --git a/src/server/mvd.c b/src/server/mvd.c index eab3adabe..ee27cb7db 100644 --- a/src/server/mvd.c +++ b/src/server/mvd.c @@ -1117,6 +1117,10 @@ void SV_MvdMulticast(int leafnum, multicast_t to) if (!mvd.active) { return; } + if (leafnum >= UINT16_MAX) { + Com_WPrintf("%s: leafnum out of range\n", __func__); + return; + } op = mvd_multicast_all + to; buf = to < MULTICAST_ALL_R ? &mvd.datagram : &mvd.message; diff --git a/src/server/mvd/game.c b/src/server/mvd/game.c index 32a3dd46d..9c0049068 100644 --- a/src/server/mvd/game.c +++ b/src/server/mvd/game.c @@ -1745,7 +1745,7 @@ static void MVD_GameInit(void) ret = BSP_Load(buffer, &bsp); if (!bsp) { Com_EPrintf("Couldn't load %s for the Waiting Room: %s\n", - buffer, Q_ErrorString(ret)); + buffer, BSP_ErrorString(ret)); Cvar_Reset(mvd_default_map); strcpy(buffer, "maps/q2dm1.bsp"); checksum = 80717714; diff --git a/src/server/mvd/parse.c b/src/server/mvd/parse.c index a5d448c44..7799783a0 100644 --- a/src/server/mvd/parse.c +++ b/src/server/mvd/parse.c @@ -920,7 +920,7 @@ static void MVD_ChangeLevel(mvd_t *mvd) static void MVD_ParseServerData(mvd_t *mvd, int extrabits) { int protocol; - size_t len, maxlen; + size_t maxlen; char *string; int index; int ret; @@ -1016,18 +1016,15 @@ static void MVD_ParseServerData(mvd_t *mvd, int extrabits) // parse world model string = mvd->configstrings[CS_MODELS + 1]; - len = strlen(string); - if (len <= 9) { + if (!Com_ParseMapName(mvd->mapname, string, sizeof(mvd->mapname))) { MVD_Destroyf(mvd, "Bad world model: %s", string); } - memcpy(mvd->mapname, string + 5, len - 9); // skip "maps/" - mvd->mapname[len - 9] = 0; // cut off ".bsp" // load the world model (we are only interesed in visibility info) Com_Printf("[%s] -=- Loading %s...\n", mvd->name, string); ret = CM_LoadMap(&mvd->cm, string); if (ret) { - Com_EPrintf("[%s] =!= Couldn't load %s: %s\n", mvd->name, string, Q_ErrorString(ret)); + Com_EPrintf("[%s] =!= Couldn't load %s: %s\n", mvd->name, string, BSP_ErrorString(ret)); // continue with null visibility } else if (mvd->cm.cache->checksum != atoi(mvd->configstrings[CS_MAPCHECKSUM])) { Com_EPrintf("[%s] =!= Local map version differs from server!\n", mvd->name); diff --git a/src/server/server.h b/src/server/server.h index df7616b89..9dafd9768 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -163,7 +163,6 @@ typedef struct { char name[MAX_QPATH]; // map name, or cinematic name cm_t cm; - char *entitystring; char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH]; @@ -556,8 +555,6 @@ extern cvar_t *sv_allow_unconnected_cmds; extern cvar_t *g_features; -extern cvar_t *map_override_path; - extern cvar_t *sv_timeout; extern cvar_t *sv_zombietime; extern cvar_t *sv_ghostime;