From d58aa37228b23b04ae8237b680d946cc40db6c13 Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Fri, 2 May 2025 15:44:31 +0200 Subject: [PATCH] Loading map patches --- src/p_setup.c | 137 +++++++++++++++++++++++++++++++++++++++++++------- src/r_state.h | 3 +- 2 files changed, 122 insertions(+), 18 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 6ed8862d6..3e5292273 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -125,7 +125,7 @@ UINT64 maphash; // boolean udmf; -INT32 udmf_version; +INT32 udmf_version, patch_version; mapnamespace_t mapnamespace = MNS_UNKNOWN; size_t numvertexes, numsegs, numsectors, numsubsectors, numnodes, numlines, numsides, nummapthings; size_t num_orig_vertexes; @@ -1461,30 +1461,82 @@ static void TextmapStorePos(textmap_block_t *blocks, size_t *count) } // Determine total amount of map data in TEXTMAP. -static boolean TextmapCount(size_t size) +static boolean TextmapCount(size_t size, boolean patch) { TracyCZone(__zone, true); const char *tkn = M_TokenizerRead(0); UINT8 brackets = 0; - nummapthings = 0; - numlines = 0; - numsides = 0; - numvertexes = 0; - numsectors = 0; + // check namespace for patches + if (patch) + { + // Look for namespace at the beginning. + if (!fastcmp(tkn, "namespace")) + { + CONS_Alert(CONS_ERROR, "No namespace at beginning of lump!\n"); + return false; + } - // skip namespace, it's checked by P_SetMapNamespace - M_TokenizerRead(0); + // Check if namespace is valid. + tkn = M_TokenizerRead(0); + if (!fastcmp(tkn, "blankart-patch")) + { + CONS_Alert(CONS_ERROR, "Invalid namespace '%s'. Only 'blankart-patch' is supported.\n", tkn); + return false; + } + } + else + M_TokenizerRead(0); // Check for version tkn = M_TokenizerRead(0); if (fastcmp(tkn, "version")) { tkn = M_TokenizerRead(0); - udmf_version = atoi(tkn); - if (udmf_version > UDMF_CURRENT_VERSION) - CONS_Alert(CONS_WARNING, "Map is intended for future UDMF version '%d', current supported version is '%d'. This map may have issues loading.\n", udmf_version, UDMF_CURRENT_VERSION); + if (patch) + { + patch_version = atoi(tkn); + if (patch_version > PATCH_CURRENT_VERSION) + CONS_Alert(CONS_WARNING, "Patch is intended for future patch version '%d', current supported version is '%d'. This patch may have issues loading.\n", patch_version, PATCH_CURRENT_VERSION); + } + else + { + udmf_version = atoi(tkn); + if (udmf_version > UDMF_CURRENT_VERSION) + CONS_Alert(CONS_WARNING, "Map is intended for future UDMF version '%d', current supported version is '%d'. This map may have issues loading.\n", udmf_version, UDMF_CURRENT_VERSION); + } + } + + // check hash for patches + if (patch) + { + tkn = M_TokenizerRead(0); + if (!fastcmp(tkn, "checksum")) + { + CONS_Alert(CONS_ERROR, "No checksum in lump!\n"); + return false; + } + + tkn = M_TokenizerRead(0); + char *end = NULL; + UINT64 hash = strtoull(tkn, &end, 16); + if (!end || *end) + { + CONS_Alert(CONS_ERROR, "Invalid checksum value '%s'\n", tkn); + return false; + } + if (hash != maphash) + CONS_Alert(CONS_WARNING, "Patch checksum " HASHFMT " does not match map checksum " HASHFMT ". This patch may not work.\n", hash, maphash); + } + + nummapthings = 0; + if (!patch) + { + numlines = 0; + numsides = 0; + numvertexes = 0; + numsectors = 0; } while ((tkn = M_TokenizerRead(0)) && M_TokenizerGetEndPos() < size) @@ -1500,6 +1552,8 @@ static boolean TextmapCount(size_t size) // Check for valid fields. else if (fastcmp(tkn, "thing")) TextmapStorePos(&mapthingBlocks, &nummapthings); + else if (patch) + continue; else if (fastcmp(tkn, "linedef")) TextmapStorePos(&linedefBlocks, &numlines); else if (fastcmp(tkn, "sidedef")) @@ -3213,7 +3267,7 @@ static void P_WriteTextmapWaypoints(void) /** Loads the textmap data, after obtaining the elements count and allocating their respective space. */ -static void P_LoadTextmap(void) +static void P_LoadTextmap(boolean patch) { TracyCZone(__zone, true); @@ -3232,6 +3286,7 @@ static void P_LoadTextmap(void) /// from the textmap, and therefore we have to account for it by /// preemptively setting that value beforehand. + if (!patch) for (i = 0, vt = vertexes; i < numvertexes; i++, vt++) { // Defaults. @@ -3247,6 +3302,7 @@ static void P_LoadTextmap(void) I_Error("P_LoadTextmap: vertex %s has no y value set!\n", sizeu1(i)); } + if (!patch) for (i = 0, sc = sectors; i < numsectors; i++, sc++) { // Defaults. @@ -3332,6 +3388,7 @@ static void P_LoadTextmap(void) TextmapFixFlatOffsets(sc); } + if (!patch) for (i = 0, ld = lines; i < numlines; i++, ld++) { // Defaults. @@ -3362,6 +3419,7 @@ static void P_LoadTextmap(void) P_InitializeLinedef(ld); } + if (!patch) for (i = 0, sd = sides; i < numsides; i++, sd++) { // Defaults. @@ -3576,7 +3634,7 @@ static boolean P_LoadMapData(const virtres_t *virt) { virtlump_t *textmap = vres_Find(virt, "TEXTMAP"); M_TokenizerOpen((char *)textmap->data, textmap->size); - if (!TextmapCount(textmap->size)) + if (!TextmapCount(textmap->size, false)) { M_TokenizerClose(); TracyCZoneEnd(__zone); @@ -3640,7 +3698,7 @@ static boolean P_LoadMapData(const virtres_t *virt) // Load map data. if (udmf) { - P_LoadTextmap(); + P_LoadTextmap(false); M_TokenizerClose(); } else @@ -3670,6 +3728,44 @@ static boolean P_LoadMapData(const virtres_t *virt) return true; } +static boolean P_LoadMapPatchData(const lumpnum_t lumpnum) +{ + TracyCZone(__zone, true); + + size_t realnummapthings = nummapthings; + + // Count map data. + size_t patchlen = W_LumpLength(lumpnum); + char *patchdata = malloc(patchlen); + W_ReadLump(lumpnum, patchdata); + M_TokenizerOpen(patchdata, patchlen); + if (!TextmapCount(patchlen, true)) + { + M_TokenizerClose(); + free(patchdata); + TracyCZoneEnd(__zone); + return false; + } + + mapthing_t *realmapthings = mapthings; + mapthings = Z_Calloc(nummapthings * sizeof (*mapthings), PU_LEVEL, NULL); + + // Load map data. + P_LoadTextmap(true); + M_TokenizerClose(); + free(patchdata); + + // concatenate the two mapthing arrays + realmapthings = Z_Realloc(realmapthings, (nummapthings + realnummapthings) * sizeof (*mapthings), PU_LEVEL, NULL); + memcpy(realmapthings + realnummapthings, mapthings, nummapthings * sizeof (*mapthings)); + Z_Free(mapthings); + mapthings = realmapthings; + nummapthings += realnummapthings; + + TracyCZoneEnd(__zone); + return true; +} + static void P_InitializeSubsector(subsector_t *ss) { ss->sector = NULL; @@ -7728,7 +7824,7 @@ static boolean P_LoadMapFromFile(void) virtlump_t *textmap = vres_Find(curmapvirt, "TEXTMAP"); size_t i; udmf = textmap != NULL; - udmf_version = 0; + udmf_version = patch_version = 0; if (!P_LoadMapData(curmapvirt)) { @@ -7748,6 +7844,14 @@ static boolean P_LoadMapFromFile(void) if (!udmf) P_ConvertBinaryMap(); + P_MakeMapHash(curmapvirt, &maphash); + + // load map patch? + lumpnum_t patchlump = W_CheckNumForNameInFolder(G_BuildMapName(gamemap), "MapPatch/"); + if (patchlump != LUMPERROR) + if (!P_LoadMapPatchData(patchlump)) + CONS_Alert(CONS_ERROR, "Failed to load map patch for %s\n", G_BuildMapName(gamemap)); + // Copy relevant map data for NetArchive purposes. spawnsectors = Z_Calloc(numsectors * sizeof(*sectors), PU_LEVEL, NULL); spawnlines = Z_Calloc(numlines * sizeof(*lines), PU_LEVEL, NULL); @@ -7761,7 +7865,6 @@ static boolean P_LoadMapFromFile(void) if (sectors[i].tags.count) spawnsectors[i].tags.tags = memcpy(Z_Malloc(sectors[i].tags.count*sizeof(mtag_t), PU_LEVEL, NULL), sectors[i].tags.tags, sectors[i].tags.count*sizeof(mtag_t)); - P_MakeMapHash(curmapvirt, &maphash); TracyCZoneEnd(__zone); return true; } diff --git a/src/r_state.h b/src/r_state.h index bb5aea6b3..2bbcab956 100644 --- a/src/r_state.h +++ b/src/r_state.h @@ -64,8 +64,9 @@ extern size_t numspritelumps, max_spritelumps; // Lookup tables for map data. // #define UDMF_CURRENT_VERSION (3) +#define PATCH_CURRENT_VERSION (1) extern boolean udmf; -extern INT32 udmf_version; +extern INT32 udmf_version, patch_version; extern size_t numsprites; extern spritedef_t *sprites;