From eaad94de95125ddc5534593eec14f1164dffbbba Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 17 Jun 2022 22:39:22 +0300 Subject: [PATCH 01/26] Check for too small file in BSP_Load(). --- src/common/bsp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common/bsp.c b/src/common/bsp.c index 2f3bf7543..358b3617b 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -1588,6 +1588,11 @@ 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 && From db56e0d3ceefb3711e2221904433f06e4368bb94 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 22 Jun 2022 13:20:26 +0300 Subject: [PATCH 02/26] Print detailed BSP loading errors. --- inc/common/bsp.h | 2 +- src/client/precache.c | 2 +- src/common/bsp.c | 17 ++++++++++++++++- src/common/tests.c | 3 +-- src/refresh/gl/surf.c | 2 +- src/server/init.c | 2 +- src/server/mvd/game.c | 2 +- src/server/mvd/parse.c | 2 +- 8 files changed, 23 insertions(+), 9 deletions(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index 7a6804084..37ebce2d1 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -290,7 +290,7 @@ 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 { diff --git a/src/client/precache.c b/src/client/precache.c index b0d3c0121..f16b43098 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])) { diff --git a/src/common/bsp.c b/src/common/bsp.c index 358b3617b..a94611359 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -55,7 +55,7 @@ static cvar_t *map_visibility_patch; static int BSP_QBSP_Load##func(bsp_t *bsp, void *base, size_t count) #define DEBUG(msg) \ - Com_DPrintf("%s: %s\n", __func__, msg) + Com_SetLastError(va("%s: %s", __func__, msg)) LOAD(Visibility) { @@ -1627,6 +1627,7 @@ int BSP_Load(const char *name, bsp_t **bsp_p) } count = len / info->disksize; if (count > info->maxcount) { + Com_SetLastError("Lump too big"); ret = Q_ERR_TOO_MANY; goto fail2; } @@ -1710,6 +1711,20 @@ 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_TOO_MANY: + case Q_ERR_TOO_FEW: + case Q_ERR_BAD_INDEX: + case Q_ERR_INFINITE_LOOP: + return Com_GetLastError(); + default: + return Q_ErrorString(err); + } +} + /* =============================================================================== 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/refresh/gl/surf.c b/src/refresh/gl/surf.c index 8b95294a2..492724d2e 100644 --- a/src/refresh/gl/surf.c +++ b/src/refresh/gl/surf.c @@ -889,7 +889,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/server/init.c b/src/server/init.c index db33fdca5..807ba5cca 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -311,7 +311,7 @@ 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)); return false; } diff --git a/src/server/mvd/game.c b/src/server/mvd/game.c index 72ebdb458..29dd54bbf 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..46995ce6b 100644 --- a/src/server/mvd/parse.c +++ b/src/server/mvd/parse.c @@ -1027,7 +1027,7 @@ static void MVD_ParseServerData(mvd_t *mvd, int extrabits) 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); From 5e2a2d30e8d2101e272a14eff4f93281230aed4f Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 16 Nov 2022 22:32:47 +0300 Subject: [PATCH 03/26] Fix alpha faces on transformed BSP models. Draw with proper entity matrix. Excuse not to fix this due to compatibility no longer holds since other engines seem to fix this unconditionally. --- inc/common/bsp.h | 4 +++- src/refresh/gl/gl.h | 5 +++-- src/refresh/gl/main.c | 35 ++++++++++++++++++++++------------- src/refresh/gl/mesh.c | 6 +----- src/refresh/gl/tess.c | 38 +++++++++++++++++++++----------------- src/refresh/gl/world.c | 9 +++------ 6 files changed, 53 insertions(+), 44 deletions(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index 37ebce2d1..0998892ef 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -114,7 +114,9 @@ typedef struct mface_s { int dlightframe; int dlightbits; - struct mface_s *next; + + struct entity_s *entity; + struct mface_s *next; } mface_t; #endif diff --git a/src/refresh/gl/gl.h b/src/refresh/gl/gl.h index 6c5dbb1ac..d81b1bba5 100644 --- a/src/refresh/gl/gl.h +++ b/src/refresh/gl/gl.h @@ -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); @@ -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/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..ff9732a10 100644 --- a/src/refresh/gl/world.c +++ b/src/refresh/gl/world.c @@ -399,7 +399,7 @@ void GL_DrawBspModel(mmodel_t *model) GL_TransformLights(model); - GL_RotateForEntity(ent->origin, 1.f); + GL_RotateForEntity(); GL_BindArrays(); @@ -417,12 +417,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; } @@ -504,7 +501,7 @@ static inline void GL_DrawNode(mnode_t *node) } if (face->drawflags & SURF_TRANS_MASK) { - GL_AddAlphaFace(face); + GL_AddAlphaFace(face, &gl_world); continue; } From 6e1d5fe88b168b2b6d2af8b669a0f089a42f8cb0 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 17 Nov 2022 15:07:34 +0300 Subject: [PATCH 04/26] Change BSP counters that can overflow to unsigned. --- inc/common/bsp.h | 16 ++++++++-------- src/common/bsp.c | 2 +- src/common/cmodel.c | 4 ++-- src/refresh/gl/gl.h | 6 +++--- src/refresh/gl/world.c | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index 0998892ef..7d42ebed5 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -110,10 +110,10 @@ typedef struct mface_s { int firstbasis; - int drawframe; + unsigned drawframe; - int dlightframe; - int dlightbits; + unsigned dlightframe; + unsigned dlightbits; struct entity_s *entity; struct mface_s *next; @@ -127,7 +127,7 @@ typedef struct mnode_s { vec3_t mins; vec3_t maxs; - int visframe; + unsigned visframe; #endif struct mnode_s *parent; /* <====== */ @@ -149,7 +149,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 { @@ -159,7 +159,7 @@ typedef struct { vec3_t mins; vec3_t maxs; - int visframe; + unsigned visframe; #endif struct mnode_s *parent; /* <====== */ @@ -183,7 +183,7 @@ typedef struct { typedef struct { int numareaportals; mareaportal_t *firstareaportal; - int floodvalid; + unsigned floodvalid; } marea_t; typedef struct mmodel_s { @@ -203,7 +203,7 @@ typedef struct mmodel_s { mface_t *firstface; #if USE_REF == REF_GL - int drawframe; + unsigned drawframe; #endif #endif } mmodel_t; diff --git a/src/common/bsp.c b/src/common/bsp.c index a94611359..a9beb1cf1 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -1203,7 +1203,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 diff --git a/src/common/cmodel.c b/src/common/cmodel.c index 756d951b0..100681682 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -31,8 +31,8 @@ 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; diff --git a/src/refresh/gl/gl.h b/src/refresh/gl/gl.h index d81b1bba5..f6d589112 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]; diff --git a/src/refresh/gl/world.c b/src/refresh/gl/world.c index ff9732a10..fa72889e8 100644 --- a/src/refresh/gl/world.c +++ b/src/refresh/gl/world.c @@ -142,7 +142,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; From 41f471624c8b1a91945b60437b80528d21e41a97 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 18 Nov 2022 15:20:37 +0300 Subject: [PATCH 05/26] Use LittleVector() where appropriate. --- src/common/bsp.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index a9beb1cf1..8a7234134 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -109,7 +109,7 @@ LOAD(Texinfo) mtexinfo_t *out; int i; #if USE_REF - int j, k; + int j; int32_t next; mtexinfo_t *step; #endif @@ -130,10 +130,8 @@ LOAD(Texinfo) #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]); + LittleVector(in->vecs[j], out->axis[j]); + out->offset[j] = LittleFloat(in->vecs[j][3]); } next = (int32_t)LittleLong(in->nexttexinfo); @@ -171,7 +169,7 @@ LOAD(Planes) { dplane_t *in; cplane_t *out; - int i, j; + int i; bsp->numplanes = count; bsp->planes = ALLOC(sizeof(*out) * count); @@ -179,9 +177,7 @@ LOAD(Planes) 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]); - } + LittleVector(in->normal, out->normal); out->dist = LittleFloat(in->dist); SetPlaneType(out); SetPlaneSignbits(out); @@ -355,7 +351,7 @@ LOAD(Vertices) { dvertex_t *in; mvertex_t *out; - int i, j; + int i; bsp->numvertices = count; bsp->vertices = ALLOC(sizeof(*out) * count); @@ -363,9 +359,7 @@ LOAD(Vertices) 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]); - } + LittleVector(in->point, out->point); } return Q_ERR_SUCCESS; From 328c1e85bd0683027bac3fc77d181a7954358026 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 6 Dec 2022 19:18:42 +0300 Subject: [PATCH 06/26] Add more asserts. --- src/common/bsp.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index 8a7234134..4bb1380b6 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -1963,12 +1963,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); From 507dcce31bd456270055f01ef9bec1245a1fce29 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 10 Jul 2023 20:49:16 +0300 Subject: [PATCH 07/26] Use proper type for data read from disk. --- src/common/bsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index 4bb1380b6..d63b04212 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -419,7 +419,7 @@ LOAD_EXT(Edges) LOAD(SurfEdges) { - int *in; + int32_t *in; msurfedge_t *out; int i, vert; int32_t index; From effd4b370b2ed9528d5b1737e31d8e23ada2e97e Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 19 Aug 2022 20:11:45 +0300 Subject: [PATCH 08/26] Add and use Com_ParseMapName(). --- inc/common/utils.h | 2 ++ src/client/demo.c | 7 +------ src/client/precache.c | 7 +------ src/common/utils.c | 18 ++++++++++++++++++ src/server/mvd/parse.c | 7 ++----- 5 files changed, 24 insertions(+), 17 deletions(-) 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/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 f16b43098..d50d4063d 100644 --- a/src/client/precache.c +++ b/src/client/precache.c @@ -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/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/server/mvd/parse.c b/src/server/mvd/parse.c index 46995ce6b..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,12 +1016,9 @@ 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); From 4275b7c5ebfb9020bb3040a06871c3277e6d8bcd Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 19 Aug 2022 20:26:07 +0300 Subject: [PATCH 09/26] =?UTF-8?q?Add=20support=20for=20loading=20binary=20?= =?UTF-8?q?=E2=80=98.override=E2=80=99=20files.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also treat ‘map_override_path’ as directory name rather than arbitary prefix. --- doc/server.md | 25 ++++--- inc/common/cmodel.h | 4 ++ src/common/cmodel.c | 155 +++++++++++++++++++++++++++++++++++++++++--- src/server/init.c | 55 ++-------------- src/server/main.c | 5 -- src/server/server.h | 3 - 6 files changed, 171 insertions(+), 76 deletions(-) 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/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/src/common/cmodel.c b/src/common/cmodel.c index 100681682..b80be82d4 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" @@ -36,9 +38,138 @@ 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; + } + + if (ret >= MAX_MAP_ENTSTRING) { + ret = Q_ERR(EFBIG); + 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 < 1 || len >= MAX_MAP_ENTSTRING) + 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 +177,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 +197,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->lastareaportal + 1), TAG_CMODEL); FloodAreaConnections(cm); return Q_ERR_SUCCESS; @@ -1029,5 +1167,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/server/init.c b/src/server/init.c index 807ba5cca..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); } @@ -312,6 +267,7 @@ static bool check_server(mapcmd_t *cmd, const char *server, bool nextserver) if (ret < 0) { 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 88a9eb0e0..924c339f4 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; //============================================================================ @@ -2256,8 +2254,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 @@ -2367,7 +2363,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/server.h b/src/server/server.h index 1553c0de2..56b770366 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; From af600f3178de7bfab6d925a75cd6ba2de658fc22 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 6 Dec 2022 19:19:36 +0300 Subject: [PATCH 10/26] Add braces for readability. --- src/common/cmodel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/cmodel.c b/src/common/cmodel.c index b80be82d4..f354f7347 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -782,7 +782,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 From 6f733ede830b82ccadff3a86e6656039230fce83 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 6 Dec 2022 19:20:26 +0300 Subject: [PATCH 11/26] Use symbolic names for constants. --- src/common/cmodel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/cmodel.c b/src/common/cmodel.c index f354f7347..f33b517c5 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -363,9 +363,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 From b7c75af3121011b75f41b65fe500629aaa602209 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 12 Aug 2023 10:29:46 +0300 Subject: [PATCH 12/26] Remove BSP limits, refactor loading. Remove hardcoded BSP lump size limits, except for number of leafs and areas, which can't be increased without changing network protocol. Add extended QBSP format support to further remove 16-bit field limits. Parse BSP file using integer r/w macros, rather than casting input data to structures. This unifies regular BSP and QBSP parsing and also allows parsing misaligned data. --- inc/format/bsp.h | 217 +------------ inc/shared/shared.h | 11 + src/common/bsp.c | 742 +++++++++++--------------------------------- src/common/cmodel.c | 8 +- 4 files changed, 212 insertions(+), 766 deletions(-) diff --git a/inc/format/bsp.h b/inc/format/bsp.h index c45b91ce5..53a6470e0 100644 --- a/inc/format/bsp.h +++ b/inc/format/bsp.h @@ -28,63 +28,21 @@ 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_AREAPORTALS 1024 // 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 @@ -127,93 +85,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,67 +102,6 @@ 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; @@ -299,18 +114,18 @@ typedef struct { } bspx_lump_t; typedef struct { - uint32_t num_vectors; - /* followed by: - vec3 vectors[num_vectors] + 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; - } - } - */ + for each face in bsp { + for each vert in face { + u32 normal_index; + u32 tangent_index; + u32 bitangent_index; + } + } + */ } bspx_facenormals_header_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/common/bsp.c b/src/common/bsp.c index d63b04212..652dc24d3 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_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) { @@ -71,28 +76,27 @@ LOAD(Visibility) return Q_ERR_TOO_FEW; } - bsp->numvisibility = count; - bsp->vis = ALLOC(count); - memcpy(bsp->vis, base, count); - - numclusters = LittleLong(bsp->vis->numclusters); + numclusters = BSP_Long(); if (numclusters > (bsp->extended ? MAX_QBSP_MAP_LEAFS : MAX_MAP_LEAFS)) { DEBUG("bad numclusters"); return Q_ERR_TOO_MANY; } - if (numclusters > (count - 4) / 8) { + hdrsize = 4 + numclusters * 8; + if (count < hdrsize) { DEBUG("too small header"); return Q_ERR_TOO_FEW; } + bsp->numvisibility = count; + bsp->vis = ALLOC(count); bsp->vis->numclusters = numclusters; bsp->visrowsize = (numclusters + 7) >> 3; 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; } @@ -100,12 +104,13 @@ LOAD(Visibility) } } + 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 @@ -117,24 +122,28 @@ 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++) { - LittleVector(in->vecs[j], out->axis[j]); - out->offset[j] = LittleFloat(in->vecs[j][3]); + 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(); + + memcpy(out->c.name, in, sizeof(out->c.name) - 1); + memcpy(out->name, in, sizeof(out->name) - 1); + in += MAX_TEXNAME; - next = (int32_t)LittleLong(in->nexttexinfo); +#if USE_REF + out->radiance = out->c.value; + next = (int32_t)BSP_Long(); if (next > 0) { if (next >= count) { DEBUG("bad anim chain"); @@ -144,6 +153,8 @@ LOAD(Texinfo) } else { out->next = NULL; } +#else + in += 4; #endif } @@ -167,18 +178,18 @@ LOAD(Texinfo) LOAD(Planes) { - dplane_t *in; cplane_t *out; int i; bsp->numplanes = count; bsp->planes = ALLOC(sizeof(*out) * count); - in = base; out = bsp->planes; - for (i = 0; i < count; i++, in++, out++) { - LittleVector(in->normal, out->normal); - 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); } @@ -188,41 +199,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; @@ -230,17 +206,16 @@ 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; } 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) { @@ -256,7 +231,6 @@ LOAD_EXT(BrushSides) LOAD(Brushes) { - dbrush_t *in; mbrush_t *out; int i; uint32_t firstside, numsides, lastside; @@ -264,11 +238,10 @@ 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"); @@ -276,7 +249,7 @@ LOAD(Brushes) } out->firstbrushside = bsp->brushsides + firstside; out->numsides = numsides; - out->contents = LittleLong(in->contents); + out->contents = BSP_Long(); out->checkcount = 0; } @@ -285,31 +258,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; @@ -317,10 +265,9 @@ 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; @@ -335,31 +282,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; bsp->numvertices = count; bsp->vertices = ALLOC(sizeof(*out) * count); - in = base; out = bsp->vertices; - for (i = 0; i < count; i++, out++, in++) { - LittleVector(in->point, out->point); + 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; @@ -367,33 +311,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; @@ -401,11 +318,10 @@ 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; @@ -419,7 +335,6 @@ LOAD_EXT(Edges) LOAD(SurfEdges) { - int32_t *in; msurfedge_t *out; int i, vert; int32_t index; @@ -427,10 +342,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) { @@ -452,94 +366,29 @@ 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; } 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"); @@ -556,29 +405,22 @@ LOAD_EXT(Faces) 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; } 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 { @@ -588,9 +430,6 @@ LOAD_EXT(Faces) } out->lightmap = bsp->lightmap + lightofs; } - - side = LittleLong(in->side); - out->drawflags = side & DSURF_PLANEBACK; } return Q_ERR_SUCCESS; @@ -598,31 +437,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; @@ -630,10 +444,9 @@ 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; @@ -647,31 +460,33 @@ 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; } + if (count > MAX_MAP_LEAFS) { + DEBUG("too many leafs"); + return Q_ERR_TOO_MANY; + } 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) { @@ -686,26 +501,21 @@ LOAD(Leafs) out->cluster = cluster; } - area = LittleShort(in->area); + area = BSP_ExtLong(); if (area >= bsp->numareas) { DEBUG("bad area"); return Q_ERR_BAD_INDEX; } 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"); @@ -714,74 +524,14 @@ LOAD(Leafs) 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"); @@ -789,26 +539,6 @@ LOAD_EXT(Leafs) } 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) { @@ -821,77 +551,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,10 +566,9 @@ LOAD_EXT(Nodes) 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; @@ -918,7 +576,7 @@ LOAD_EXT(Nodes) 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) { @@ -936,8 +594,13 @@ LOAD_EXT(Nodes) } #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"); @@ -946,13 +609,10 @@ LOAD_EXT(Nodes) 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 } @@ -961,7 +621,6 @@ LOAD_EXT(Nodes) LOAD(Submodels) { - dmodel_t *in; mmodel_t *out; int i, j; uint32_t headnode; @@ -974,19 +633,20 @@ LOAD(Submodels) return Q_ERR_TOO_FEW; } - 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; @@ -1004,10 +664,11 @@ LOAD(Submodels) } #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"); @@ -1017,6 +678,8 @@ LOAD(Submodels) out->numfaces = numfaces; out->radius = RadiusFromBounds(out->mins, out->maxs); +#else + in += 8; #endif } @@ -1026,18 +689,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; @@ -1045,19 +706,22 @@ 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_TOO_MANY; + } + 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"); @@ -1075,7 +739,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; @@ -1090,77 +754,41 @@ LOAD(EntString) */ typedef struct { - int (*load)(bsp_t *, void *, size_t); + int (*load)(bsp_t *, const byte *, size_t); 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(func, lump, mem_t, disksize1, disksize2) \ + { BSP_Load##func, LUMP_##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), + L(Visibility, VISIBILITY, byte, 1, 1), + L(Texinfo, TEXINFO, mtexinfo_t, 76, 76), + L(Planes, PLANES, cplane_t, 20, 20), + L(BrushSides, BRUSHSIDES, mbrushside_t, 4, 8), + L(Brushes, BRUSHES, mbrush_t, 12, 12), + L(LeafBrushes, LEAFBRUSHES, mbrush_t *, 2, 4), + L(AreaPortals, AREAPORTALS, mareaportal_t, 8, 8), + L(Areas, AREAS, marea_t, 8, 8), #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 *), + L(Lightmap, LIGHTING, byte, 1, 1), + L(Vertices, VERTEXES, mvertex_t, 12, 12), + L(Edges, EDGES, medge_t, 4, 8), + L(SurfEdges, SURFEDGES, msurfedge_t, 4, 4), + L(Faces, FACES, mface_t, 20, 28), + L(LeafFaces, LEAFFACES, mface_t *, 2, 4), #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), + L(Leafs, LEAFS, mleaf_t, 28, 52), + L(Nodes, NODES, mnode_t, 28, 44), + L(Submodels, MODELS, mmodel_t, 48, 48), + L(EntString, ENTSTRING, char, 1, 1), { 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), -#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 *), -#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 } -}; - -#undef LS -#undef L - static list_t bsp_cache; static void BSP_List_f(void) @@ -1553,11 +1181,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; + uint32_t filelen, ofs, len, end, count; int ret; byte *lumpdata[HEADER_LUMPS]; size_t lumpcount[HEADER_LUMPS]; size_t memsize; + bool extended = false; Q_assert(name); Q_assert(bsp_p); @@ -1589,8 +1218,13 @@ int BSP_Load(const char *name, bsp_t **bsp_p) // 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; } @@ -1599,11 +1233,9 @@ 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++) { + for (info = bsp_lumps; info->load; info++) { ofs = LittleLong(header->lumps[info->lump].fileofs); len = LittleLong(header->lumps[info->lump].filelen); end = ofs + len; @@ -1611,20 +1243,12 @@ int BSP_Load(const char *name, bsp_t **bsp_p) ret = Q_ERR_BAD_EXTENT; goto fail2; } - if (ofs % info->diskalign) { - ret = Q_ERR_BAD_ALIGN; - goto fail2; - } - if (len % info->disksize) { + if (len % info->disksize[extended]) { ret = Q_ERR_ODD_SIZE; goto fail2; } - count = len / info->disksize; - if (count > info->maxcount) { - Com_SetLastError("Lump too big"); - ret = Q_ERR_TOO_MANY; - goto fail2; - } + count = len / info->disksize[extended]; + Q_assert(count <= INT_MAX); lumpdata[info->lump] = buf + ofs; lumpcount[info->lump] = count; @@ -1646,7 +1270,7 @@ 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); @@ -1655,7 +1279,7 @@ int BSP_Load(const char *name, bsp_t **bsp_p) bsp->checksum = Com_BlockChecksum(buf, filelen); // load all lumps - for (info = lumps; info->load; info++) { + for (info = bsp_lumps; info->load; info++) { ret = info->load(bsp, lumpdata[info->lump], lumpcount[info->lump]); if (ret) { goto fail1; diff --git a/src/common/cmodel.c b/src/common/cmodel.c index f33b517c5..7490c18d7 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -68,11 +68,6 @@ static void load_entstring_override(cm_t *cm, const char *server) goto fail; } - if (ret >= MAX_MAP_ENTSTRING) { - ret = Q_ERR(EFBIG); - goto fail; - } - Com_Printf("Loaded entity string from %s\n", buffer); cm->entitystring = data; cm->override_bits |= OVERRIDE_ENTS; @@ -126,7 +121,7 @@ static void load_binary_override(cm_t *cm, char *server, size_t server_size) if (bits & OVERRIDE_ENTS) { len = SZ_ReadLong(&sz); - if (len < 1 || len >= MAX_MAP_ENTSTRING) + if (len <= 0) goto fail; if (!(buf = SZ_ReadData(&sz, len))) goto fail; @@ -1008,6 +1003,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 From 2e5a6596c380cdf31f344484b7ed5081162dcba7 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 13 Aug 2023 21:09:00 +0300 Subject: [PATCH 13/26] Better account for alignment overhead. --- src/common/bsp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index 652dc24d3..e707a0184 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -1253,7 +1253,8 @@ int BSP_Load(const char *name, bsp_t **bsp_p) lumpdata[info->lump] = buf + ofs; lumpcount[info->lump] = count; - memsize += count * info->memsize; + // round to cacheline + memsize += ALIGN(count * info->memsize, 64); } #if USE_REF @@ -1272,8 +1273,7 @@ int BSP_Load(const char *name, bsp_t **bsp_p) bsp->refcount = 1; 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); From 979caac16b54a75253074f815d23e0fe532b4ca8 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 13 Aug 2023 21:25:50 +0300 Subject: [PATCH 14/26] Add support for DECOUPLED_LM. --- inc/common/bsp.h | 15 ++-- inc/format/bsp.h | 17 ++--- src/common/bsp.c | 161 ++++++++++++++++++++++++++--------------- src/refresh/gl/gl.h | 4 +- src/refresh/gl/surf.c | 105 ++++++++++++++++----------- src/refresh/gl/world.c | 36 +++++---- 6 files changed, 198 insertions(+), 140 deletions(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index 7d42ebed5..8484664dc 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -79,10 +79,6 @@ typedef struct { #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]; @@ -278,6 +277,8 @@ typedef struct bsp_s { int numbases; mbasis_t *bases; + + bool lm_decoupled; #endif byte *pvs_matrix; @@ -298,7 +299,7 @@ const char *BSP_ErrorString(int err); typedef struct { mface_t *surf; cplane_t plane; - int s, t; + float s, t; float fraction; } lightpoint_t; diff --git a/inc/format/bsp.h b/inc/format/bsp.h index 53a6470e0..7d31f071c 100644 --- a/inc/format/bsp.h +++ b/inc/format/bsp.h @@ -102,16 +102,9 @@ typedef struct { uint32_t bitofs[][2]; // bitofs[numclusters][2] } dvis_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; @@ -128,4 +121,10 @@ typedef struct { */ } bspx_facenormals_header_t; +typedef struct { + char name[24]; + uint32_t fileofs; + uint32_t filelen; +} xlump_t; + #endif // FORMAT_BSP_H diff --git a/src/common/bsp.c b/src/common/bsp.c index e707a0184..daa5e04a1 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -1074,51 +1074,34 @@ 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) @@ -1166,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 /* @@ -1181,7 +1226,7 @@ int BSP_Load(const char *name, bsp_t **bsp_p) byte *buf; dheader_t *header; const lump_info_t *info; - uint32_t filelen, ofs, len, end, count; + uint32_t filelen, ofs, len, end, count, maxpos; int ret; byte *lumpdata[HEADER_LUMPS]; size_t lumpcount[HEADER_LUMPS]; @@ -1235,6 +1280,7 @@ int BSP_Load(const char *name, bsp_t **bsp_p) // byte swap and validate all lumps memsize = 0; + maxpos = 0; for (info = bsp_lumps; info->load; info++) { ofs = LittleLong(header->lumps[info->lump].fileofs); len = LittleLong(header->lumps[info->lump].filelen); @@ -1255,12 +1301,13 @@ int BSP_Load(const char *name, bsp_t **bsp_p) // 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; } @@ -1306,10 +1353,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); @@ -1357,9 +1401,9 @@ 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; @@ -1392,14 +1436,11 @@ static bool BSP_RecursiveLightPoint(mnode_t *node, float p1f, float p2f, const v if (texinfo->c.flags & 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; diff --git a/src/refresh/gl/gl.h b/src/refresh/gl/gl.h index f6d589112..e26038409 100644 --- a/src/refresh/gl/gl.h +++ b/src/refresh/gl/gl.h @@ -264,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]; diff --git a/src/refresh/gl/surf.c b/src/refresh/gl/surf.c index 492724d2e..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; } diff --git a/src/refresh/gl/world.c b/src/refresh/gl/world.c index fa72889e8..c992688cf 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); From 8ab100348c0ab6f981e00f90359459b3fe47d294 Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Fri, 6 Oct 2023 19:58:33 +0200 Subject: [PATCH 15/26] Change style of BSP_LoadBspxNormals() to match other BSP loading functions --- inc/format/bsp.h | 15 --------------- src/common/bsp.c | 27 ++++++++++++++++----------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/inc/format/bsp.h b/inc/format/bsp.h index 7d31f071c..9906d9346 100644 --- a/inc/format/bsp.h +++ b/inc/format/bsp.h @@ -106,21 +106,6 @@ typedef struct { #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; - typedef struct { char name[24]; uint32_t fileofs; diff --git a/src/common/bsp.c b/src/common/bsp.c index daa5e04a1..56ae00d4c 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -1104,9 +1104,9 @@ static bool BSP_FindBspxLump(const byte *buf, uint32_t pos, uint32_t filelen, co 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 @@ -1118,27 +1118,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; From 14816f97f419dcbda9cee938c9991400c81c3a8d Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 15 Aug 2023 10:57:17 +0300 Subject: [PATCH 16/26] Generalize BSP loading error codes. --- src/common/bsp.c | 83 ++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index 56ae00d4c..2f580d42b 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -73,19 +73,19 @@ LOAD(Visibility) if (count < 4) { DEBUG("too small header"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } numclusters = BSP_Long(); if (numclusters > (bsp->extended ? MAX_QBSP_MAP_LEAFS : MAX_MAP_LEAFS)) { DEBUG("bad numclusters"); - return Q_ERR_TOO_MANY; + return Q_ERR_INVALID_FORMAT; } hdrsize = 4 + numclusters * 8; if (count < hdrsize) { DEBUG("too small header"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } bsp->numvisibility = count; @@ -98,7 +98,7 @@ LOAD(Visibility) 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; } @@ -147,7 +147,7 @@ LOAD(Texinfo) 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 { @@ -211,7 +211,7 @@ LOAD(BrushSides) 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 = BSP_ExtLong(); @@ -220,7 +220,7 @@ LOAD(BrushSides) } else { if (texinfo >= bsp->numtexinfo) { DEBUG("bad texinfo"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->texinfo = bsp->texinfo + texinfo; } @@ -245,7 +245,7 @@ LOAD(Brushes) 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; @@ -270,7 +270,7 @@ LOAD(LeafBrushes) brushnum = BSP_ExtLong(); if (brushnum >= bsp->numbrushes) { DEBUG("bad brushnum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } *out = bsp->brushes + brushnum; } @@ -324,7 +324,7 @@ LOAD(Edges) 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; } @@ -354,7 +354,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; @@ -380,7 +380,7 @@ LOAD(Faces) 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; @@ -392,15 +392,15 @@ LOAD(Faces) lastedge = firstedge + numedges; if (numedges < 3) { DEBUG("bad surfedges"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } if (numedges > 4096) { DEBUG("bad surfedges"); - return Q_ERR_TOO_MANY; + 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; @@ -408,7 +408,7 @@ LOAD(Faces) 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; @@ -426,7 +426,7 @@ LOAD(Faces) } else { if (lightofs >= bsp->numlightmapbytes) { DEBUG("bad lightofs"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->lightmap = bsp->lightmap + lightofs; } @@ -449,7 +449,7 @@ LOAD(LeafFaces) facenum = BSP_ExtLong(); if (facenum >= bsp->numfaces) { DEBUG("bad facenum"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } *out = bsp->faces + facenum; } @@ -471,11 +471,11 @@ LOAD(Leafs) if (!count) { DEBUG("map with no leafs"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } if (count > MAX_MAP_LEAFS) { DEBUG("too many leafs"); - return Q_ERR_TOO_MANY; + return Q_ERR_INVALID_FORMAT; } bsp->numleafs = count; @@ -496,7 +496,7 @@ 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; } @@ -504,7 +504,7 @@ LOAD(Leafs) area = BSP_ExtLong(); if (area >= bsp->numareas) { DEBUG("bad area"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } out->area = area; @@ -519,7 +519,7 @@ LOAD(Leafs) 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; @@ -535,7 +535,7 @@ LOAD(Leafs) 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; @@ -560,7 +560,7 @@ LOAD(Nodes) if (!count) { DEBUG("map with no nodes"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } bsp->numnodes = count; @@ -571,7 +571,7 @@ LOAD(Nodes) 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; @@ -581,13 +581,13 @@ LOAD(Nodes) 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; } @@ -604,7 +604,7 @@ LOAD(Nodes) 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; @@ -630,7 +630,7 @@ LOAD(Submodels) if (!count) { DEBUG("map with no models"); - return Q_ERR_TOO_FEW; + return Q_ERR_INVALID_FORMAT; } bsp->nummodels = count; @@ -652,13 +652,13 @@ LOAD(Submodels) 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; } @@ -672,7 +672,7 @@ LOAD(Submodels) 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; @@ -712,7 +712,7 @@ LOAD(Areas) if (count > MAX_MAP_AREAS) { DEBUG("too many areas"); - return Q_ERR_TOO_MANY; + return Q_ERR_INVALID_FORMAT; } bsp->numareas = count; @@ -725,7 +725,7 @@ LOAD(Areas) 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; @@ -913,14 +913,14 @@ static int BSP_ValidateAreaPortals(bsp_t *bsp) for (i = 0, p = bsp->areaportals; i < bsp->numareaportals; i++, p++) { if (p->portalnum >= MAX_MAP_AREAPORTALS) { DEBUG("bad portalnum"); - return Q_ERR_TOO_MANY; + return Q_ERR_INVALID_FORMAT; } if (p->portalnum > bsp->lastareaportal) { bsp->lastareaportal = p->portalnum; } if (p->otherarea >= bsp->numareas) { DEBUG("bad otherarea"); - return Q_ERR_BAD_INDEX; + return Q_ERR_INVALID_FORMAT; } } @@ -1291,11 +1291,13 @@ int BSP_Load(const char *name, bsp_t **bsp_p) len = LittleLong(header->lumps[info->lump].filelen); end = ofs + len; if (end < ofs || end > filelen) { - ret = Q_ERR_BAD_EXTENT; + Com_SetLastError(va("Lump %d out of bounds", info->lump)); + ret = Q_ERR_INVALID_FORMAT; goto fail2; } if (len % info->disksize[extended]) { - ret = Q_ERR_ODD_SIZE; + Com_SetLastError(va("Lump %d has odd size", info->lump)); + ret = Q_ERR_INVALID_FORMAT; goto fail2; } count = len / info->disksize[extended]; @@ -1382,9 +1384,6 @@ const char *BSP_ErrorString(int err) { switch (err) { case Q_ERR_INVALID_FORMAT: - case Q_ERR_TOO_MANY: - case Q_ERR_TOO_FEW: - case Q_ERR_BAD_INDEX: case Q_ERR_INFINITE_LOOP: return Com_GetLastError(); default: From e7f039a0e0bbdfbb11149064464d02dbb0746e6f Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 15 Aug 2023 18:46:32 +0300 Subject: [PATCH 17/26] Simplify checking surface flags. --- src/common/bsp.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index 2f580d42b..a6910533a 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -1409,7 +1409,6 @@ static bool BSP_RecursiveLightPoint(mnode_t *node, float p1f, float p2f, const v vec3_t mid; int i, side; mface_t *surf; - mtexinfo_t *texinfo; while (node->plane) { // calculate distancies @@ -1435,9 +1434,7 @@ 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(surf->lm_axis[0], mid) + surf->lm_offset[0]; From 9cb649d0b6ef772d74eb08c5a586fe7a5e0d4c3f Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 15 Aug 2023 18:48:36 +0300 Subject: [PATCH 18/26] Make surfedge error messages distinct. --- src/common/bsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index a6910533a..ca4935f07 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -391,11 +391,11 @@ LOAD(Faces) numedges = BSP_ExtLong(); lastedge = firstedge + numedges; if (numedges < 3) { - DEBUG("bad surfedges"); + DEBUG("too few surfedges"); return Q_ERR_INVALID_FORMAT; } if (numedges > 4096) { - DEBUG("bad surfedges"); + DEBUG("too many surfedges"); return Q_ERR_INVALID_FORMAT; } if (lastedge < firstedge || lastedge > bsp->numsurfedges) { From b02372d9fe4615e14ee2c93bb1a5a35be7886118 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 28 Aug 2023 15:28:07 +0300 Subject: [PATCH 19/26] Clean up BSP_Load() a bit. --- src/common/bsp.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index ca4935f07..69c75ad60 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -784,7 +784,6 @@ static const lump_info_t bsp_lumps[] = { L(Nodes, NODES, mnode_t, 28, 44), L(Submodels, MODELS, mmodel_t, 48, 48), L(EntString, ENTSTRING, char, 1, 1), - { NULL } }; #undef L @@ -1232,9 +1231,9 @@ int BSP_Load(const char *name, bsp_t **bsp_p) dheader_t *header; const lump_info_t *info; uint32_t filelen, ofs, len, end, count, maxpos; - int ret; - byte *lumpdata[HEADER_LUMPS]; - size_t lumpcount[HEADER_LUMPS]; + 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; @@ -1286,7 +1285,7 @@ int BSP_Load(const char *name, bsp_t **bsp_p) // byte swap and validate all lumps memsize = 0; maxpos = 0; - for (info = bsp_lumps; info->load; info++) { + 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; @@ -1301,10 +1300,10 @@ int BSP_Load(const char *name, bsp_t **bsp_p) goto fail2; } count = len / info->disksize[extended]; - Q_assert(count <= INT_MAX); + Q_assert(count <= INT_MAX / info->memsize); - lumpdata[info->lump] = buf + ofs; - lumpcount[info->lump] = count; + lump_ofs[i] = ofs; + lump_count[i] = count; // round to cacheline memsize += ALIGN(count * info->memsize, 64); @@ -1333,8 +1332,8 @@ int BSP_Load(const char *name, bsp_t **bsp_p) bsp->checksum = Com_BlockChecksum(buf, filelen); // load all lumps - for (info = bsp_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; } From e468fe5645e9b9170dea793fe64993771c2e3037 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 28 Aug 2023 15:55:18 +0300 Subject: [PATCH 20/26] Get rid of LUMP_ defines. --- inc/format/bsp.h | 20 -------------------- src/common/bsp.c | 38 +++++++++++++++++++------------------- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/inc/format/bsp.h b/inc/format/bsp.h index 9906d9346..e3982e9bb 100644 --- a/inc/format/bsp.h +++ b/inc/format/bsp.h @@ -57,26 +57,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 { diff --git a/src/common/bsp.c b/src/common/bsp.c index 69c75ad60..8766ca3c6 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -761,29 +761,29 @@ typedef struct { } lump_info_t; #define L(func, lump, mem_t, disksize1, disksize2) \ - { BSP_Load##func, LUMP_##lump, { disksize1, disksize2 }, sizeof(mem_t) } + { BSP_Load##func, lump, { disksize1, disksize2 }, sizeof(mem_t) } static const lump_info_t bsp_lumps[] = { - L(Visibility, VISIBILITY, byte, 1, 1), - L(Texinfo, TEXINFO, mtexinfo_t, 76, 76), - L(Planes, PLANES, cplane_t, 20, 20), - L(BrushSides, BRUSHSIDES, mbrushside_t, 4, 8), - L(Brushes, BRUSHES, mbrush_t, 12, 12), - L(LeafBrushes, LEAFBRUSHES, mbrush_t *, 2, 4), - L(AreaPortals, AREAPORTALS, mareaportal_t, 8, 8), - L(Areas, AREAS, marea_t, 8, 8), + 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 - L(Lightmap, LIGHTING, byte, 1, 1), - L(Vertices, VERTEXES, mvertex_t, 12, 12), - L(Edges, EDGES, medge_t, 4, 8), - L(SurfEdges, SURFEDGES, msurfedge_t, 4, 4), - L(Faces, FACES, mface_t, 20, 28), - L(LeafFaces, LEAFFACES, mface_t *, 2, 4), + 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, mleaf_t, 28, 52), - L(Nodes, NODES, mnode_t, 28, 44), - L(Submodels, MODELS, mmodel_t, 48, 48), - L(EntString, ENTSTRING, char, 1, 1), + 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 L From 256bb3a4977c689c0728c02bad7466db713f1634 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 28 Aug 2023 18:49:35 +0300 Subject: [PATCH 21/26] Print lump name in case of error. --- src/common/bsp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index 8766ca3c6..56df22526 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -755,13 +755,14 @@ LOAD(EntString) typedef struct { int (*load)(bsp_t *, const byte *, size_t); + const char *name; uint8_t lump; uint8_t disksize[2]; uint32_t memsize; } lump_info_t; -#define L(func, lump, mem_t, disksize1, disksize2) \ - { BSP_Load##func, lump, { disksize1, disksize2 }, sizeof(mem_t) } +#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, 3, byte, 1, 1), @@ -1290,12 +1291,12 @@ int BSP_Load(const char *name, bsp_t **bsp_p) len = LittleLong(header->lumps[info->lump].filelen); end = ofs + len; if (end < ofs || end > filelen) { - Com_SetLastError(va("Lump %d out of bounds", info->lump)); + Com_SetLastError(va("%s lump out of bounds", info->name)); ret = Q_ERR_INVALID_FORMAT; goto fail2; } if (len % info->disksize[extended]) { - Com_SetLastError(va("Lump %d has odd size", info->lump)); + Com_SetLastError(va("%s lump has odd size", info->name)); ret = Q_ERR_INVALID_FORMAT; goto fail2; } From 01c577b4269910288d5d742deba0f2bf3cdd75f1 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 28 Aug 2023 21:13:57 +0300 Subject: [PATCH 22/26] Rename lump for consistency. --- src/common/bsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index 56df22526..e5deaea85 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -619,7 +619,7 @@ LOAD(Nodes) return Q_ERR_SUCCESS; } -LOAD(Submodels) +LOAD(SubModels) { mmodel_t *out; int i, j; @@ -783,7 +783,7 @@ static const lump_info_t bsp_lumps[] = { #endif L(Leafs, 8, mleaf_t, 28, 52), L(Nodes, 4, mnode_t, 28, 44), - L(Submodels, 13, mmodel_t, 48, 48), + L(SubModels, 13, mmodel_t, 48, 48), L(EntString, 0, char, 1, 1), }; From f033b9304d29a6fa87a8985cfee9782cdfdb7b34 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 28 Aug 2023 21:53:44 +0300 Subject: [PATCH 23/26] Allow more than 65536 leafs. Limit PVS row size to 65536 clusters instead. Max 65536 leafs is MVD protocol limitation, and it shouldn't prevent the map from loading. --- inc/common/bsp.h | 2 +- inc/format/bsp.h | 4 +--- src/common/bsp.c | 9 +++------ src/refresh/vkpt/vertex_buffer.c | 6 +++--- src/server/mvd.c | 4 ++++ 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index 8484664dc..c34485e3d 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) \ diff --git a/inc/format/bsp.h b/inc/format/bsp.h index e3982e9bb..6ef4977dc 100644 --- a/inc/format/bsp.h +++ b/inc/format/bsp.h @@ -34,16 +34,14 @@ with this program; if not, write to the Free Software Foundation, Inc., // can't be increased without changing network protocol #define MAX_MAP_AREAS 256 -#define MAX_MAP_LEAFS 65536 // arbitrary limit #define MAX_MAP_AREAPORTALS 1024 +#define MAX_MAP_CLUSTERS 65536 // QBSP stuff #define QBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'Q') -#define MAX_QBSP_MAP_LEAFS INT_MAX - // key / value pair sizes #define MAX_KEY 32 diff --git a/src/common/bsp.c b/src/common/bsp.c index e5deaea85..664015197 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -77,8 +77,8 @@ LOAD(Visibility) } numclusters = BSP_Long(); - if (numclusters > (bsp->extended ? MAX_QBSP_MAP_LEAFS : MAX_MAP_LEAFS)) { - DEBUG("bad numclusters"); + if (numclusters > MAX_MAP_CLUSTERS) { + DEBUG("too many clusters"); return Q_ERR_INVALID_FORMAT; } @@ -92,6 +92,7 @@ LOAD(Visibility) 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++) { @@ -473,10 +474,6 @@ LOAD(Leafs) DEBUG("map with no leafs"); return Q_ERR_INVALID_FORMAT; } - if (count > MAX_MAP_LEAFS) { - DEBUG("too many leafs"); - return Q_ERR_INVALID_FORMAT; - } bsp->numleafs = count; bsp->leafs = ALLOC(sizeof(*out) * count); 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/mvd.c b/src/server/mvd.c index 794f5fc12..e4ecbf600 100644 --- a/src/server/mvd.c +++ b/src/server/mvd.c @@ -1119,6 +1119,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; From e7657d34b6c48cd8a0db49197d9b5904d5bc78e7 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 29 Aug 2023 01:09:32 +0300 Subject: [PATCH 24/26] Remove MAX_MAP_AREAPORTALS. --- inc/common/bsp.h | 2 +- inc/format/bsp.h | 1 - src/common/bsp.c | 8 +++----- src/common/cmodel.c | 16 +++++----------- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index c34485e3d..3e944af1c 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -249,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; diff --git a/inc/format/bsp.h b/inc/format/bsp.h index 6ef4977dc..6d09e386a 100644 --- a/inc/format/bsp.h +++ b/inc/format/bsp.h @@ -36,7 +36,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MAX_MAP_AREAS 256 // arbitrary limit -#define MAX_MAP_AREAPORTALS 1024 #define MAX_MAP_CLUSTERS 65536 // QBSP stuff diff --git a/src/common/bsp.c b/src/common/bsp.c index 664015197..5cfdf1fd8 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -906,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_INVALID_FORMAT; } - if (p->portalnum > bsp->lastareaportal) { - bsp->lastareaportal = p->portalnum; - } if (p->otherarea >= bsp->numareas) { DEBUG("bad otherarea"); return Q_ERR_INVALID_FORMAT; } + bsp->numportals = max(bsp->numportals, p->portalnum + 1); } return Q_ERR_SUCCESS; diff --git a/src/common/cmodel.c b/src/common/cmodel.c index 7490c18d7..37900892b 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -205,7 +205,7 @@ int CM_LoadMap(cm_t *cm, const char *name) 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->lastareaportal + 1), TAG_CMODEL); + cm->portalopen = Z_TagMallocz(sizeof(cm->portalopen[0]) * cm->cache->numportals, TAG_CMODEL); FloodAreaConnections(cm); return Q_ERR_SUCCESS; @@ -942,17 +942,11 @@ void CM_SetAreaPortalState(cm_t *cm, int portalnum, bool open) return; } - if (portalnum < 0 || portalnum >= MAX_MAP_AREAPORTALS) { + if (portalnum < 0 || portalnum >= cm->cache->numportals) { 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); - return; - } - cm->portalopen[portalnum] = open; FloodAreaConnections(cm); } @@ -1030,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); @@ -1051,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; } From 9be311695b43fbd5a63dfdf681d5130b4d9de4e3 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 6 Sep 2023 19:52:40 +0300 Subject: [PATCH 25/26] Fix rendering issues with some maps. Commit 0dbe1bba attempted to disable rendering of SURF_NODRAW surfaces, but only disabled polygon generation for such surfaces, not actual rendering. This created visual glitches in some (non-standard) maps. Actually disable rendering and lighmap generation for such surfaces. --- inc/common/bsp.h | 2 +- src/refresh/gl/world.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index 3e944af1c..90edda4da 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -75,7 +75,7 @@ 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 diff --git a/src/refresh/gl/world.c b/src/refresh/gl/world.c index c992688cf..7a2dde3e7 100644 --- a/src/refresh/gl/world.c +++ b/src/refresh/gl/world.c @@ -498,6 +498,10 @@ 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_world); continue; From 9c038938e47c38dc2532c10556b6ecc3f51380de Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 10 Sep 2023 15:53:58 +0300 Subject: [PATCH 26/26] =?UTF-8?q?Silence=20=E2=80=98portalnum=20out=20of?= =?UTF-8?q?=20range=E2=80=99=20errors.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/cmodel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/cmodel.c b/src/common/cmodel.c index 37900892b..2490a4d21 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -943,7 +943,7 @@ void CM_SetAreaPortalState(cm_t *cm, int portalnum, bool open) } if (portalnum < 0 || portalnum >= cm->cache->numportals) { - Com_EPrintf("%s: portalnum %d is out of range\n", __func__, portalnum); + Com_DPrintf("%s: portalnum %d is out of range\n", __func__, portalnum); return; }