From 3996abef3638dfb09c86021254a876bd7133b32e Mon Sep 17 00:00:00 2001 From: Alug Date: Thu, 4 Dec 2025 18:55:05 +0100 Subject: [PATCH 01/35] convert w_wad to ceepeepee prep for smth i might try --- src/Sourcefile | 2 +- src/console.h | 8 ++ src/dehacked.h | 9 ++ src/lzf.h | 8 ++ src/md5.h | 8 ++ src/{w_wad.c => w_wad.cpp} | 190 +++++++++++++++++++------------------ src/w_wad.h | 2 +- 7 files changed, 134 insertions(+), 93 deletions(-) rename src/{w_wad.c => w_wad.cpp} (90%) diff --git a/src/Sourcefile b/src/Sourcefile index 761806e6b..cd5a44c86 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -45,7 +45,7 @@ st_stuff.c string.c tables.c v_video.c -w_wad.c +w_wad.cpp y_inter.c z_zone.cpp r_bsp.cpp diff --git a/src/console.h b/src/console.h index a16ef47b6..3292bc6e3 100644 --- a/src/console.h +++ b/src/console.h @@ -13,6 +13,10 @@ #ifndef __CONSOLE__ #define __CONSOLE__ +#ifdef __cplusplus +extern "C" { +#endif + #include "d_event.h" #include "command.h" #include "i_threads.h" @@ -63,4 +67,8 @@ boolean CON_Ready(void); void CON_LogMessage(const char *msg); +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/src/dehacked.h b/src/dehacked.h index eb22c5f6a..0db20ee56 100644 --- a/src/dehacked.h +++ b/src/dehacked.h @@ -13,6 +13,10 @@ #ifndef __DEHACKED_H__ #define __DEHACKED_H__ +#ifdef __cplusplus +extern "C" { +#endif + #include "m_fixed.h" // for get_number #include "info.h" @@ -71,4 +75,9 @@ typedef struct } MYFILE; #define myfeof(a) (a->data + a->size <= a->curpos) char *myfgets(char *buf, size_t bufsize, MYFILE *f); + +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/src/lzf.h b/src/lzf.h index 85daf255e..995e4ebd6 100644 --- a/src/lzf.h +++ b/src/lzf.h @@ -13,6 +13,10 @@ #define LZF_H #include +#ifdef __cplusplus +extern "C" { +#endif + /*********************************************************************** ** ** lzf -- an extremely fast/free compression/decompression-method @@ -71,5 +75,9 @@ size_t lzf_decompress (const void *const in_data, size_t in_len, void *out_data, size_t out_len); +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/src/md5.h b/src/md5.h index f09d865ab..85384c479 100644 --- a/src/md5.h +++ b/src/md5.h @@ -16,6 +16,10 @@ #ifndef _MD5_H #define _MD5_H 1 +#ifdef __cplusplus +extern "C" { +#endif + #include #if defined (HAVE_LIMITS_H) || (defined (_LIBC) && _LIBC) || defined (_WIN32) @@ -52,4 +56,8 @@ int md5_stream(FILE *stream, void *resblock) THROWFUNC; digest. */ extern void *md5_buffer(const char *buffer, size_t len, void *resblock) THROWFUNC; +#ifdef __cplusplus +} // extern "C" +#endif + #endif /* md5.h */ diff --git a/src/w_wad.c b/src/w_wad.cpp similarity index 90% rename from src/w_wad.c rename to src/w_wad.cpp index 640ee0367..114e5c77c 100644 --- a/src/w_wad.c +++ b/src/w_wad.cpp @@ -257,7 +257,7 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum) lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[posStart]; size_t length = len + 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name - char *name = malloc(length + 1); + char *name = static_cast(malloc(length + 1)); if (!name) I_Error("W_LoadDehackedLumpsPK3: Out of memory!\n"); @@ -293,7 +293,7 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum) if (memcmp(lump_p->name, "SOC_", 4) == 0) // Check for generic SOC lump { // shameless copy+paste of code from LUA_LoadLump size_t length = len + 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name - char *name = malloc(length + 1); + char *name = static_cast(malloc(length + 1)); if (!name) I_Error("W_LoadDehackedLumps: Out of memory!\n"); @@ -389,24 +389,24 @@ static restype_t ResourceFileDetect(const char* filename) */ static lumpinfo_t* ResGetLumpsStandalone(FILE* handle, UINT16* numlumps, const char* lumpname) { - lumpinfo_t* lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL); + lumpinfo_t* lumpinfo = static_cast(Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL)); lumpinfo->position = 0; fseek(handle, 0, SEEK_END); - lumpinfo->size = ftell(handle); + lumpinfo->size = static_cast(ftell(handle)); fseek(handle, 0, SEEK_SET); strlcpy(lumpinfo->name, lumpname, sizeof(lumpinfo->name)); lumpinfo->namelength = strlen(lumpinfo->name); lumpinfo->hash.name = W_HashLumpName(lumpname); // Allocate the lump's long name. - lumpinfo->longname = Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL); + lumpinfo->longname = static_cast(Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL)); strcpy(lumpinfo->longname, lumpname); lumpinfo->longname[lumpinfo->namelength] = '\0'; lumpinfo->longnamelength = lumpinfo->namelength; lumpinfo->hash.longname = lumpinfo->hash.name; // Allocate the lump's full name. - lumpinfo->fullname = Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL); + lumpinfo->fullname = static_cast(Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL)); strcpy(lumpinfo->fullname, lumpname); lumpinfo->fullname[lumpinfo->namelength] = '\0'; lumpinfo->fullnamelength = lumpinfo->namelength; @@ -454,7 +454,7 @@ static lumpinfo_t* ResGetLumpsWad(FILE* handle, UINT16* nlmp, const char* filena // read wad file directory i = header.numlumps * sizeof (*fileinfo); - fileinfov = fileinfo = malloc(i); + fileinfov = fileinfo = static_cast(malloc(i)); if (fseek(handle, header.infotableofs, SEEK_SET) == -1 || fread(fileinfo, 1, i, handle) < i) @@ -466,7 +466,7 @@ static lumpinfo_t* ResGetLumpsWad(FILE* handle, UINT16* nlmp, const char* filena numlumps = header.numlumps; // fill in lumpinfo for this wad - lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL); + lump_p = lumpinfo = static_cast(Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL)); for (i = 0; i < numlumps; i++, lump_p++, fileinfo++) { @@ -510,14 +510,14 @@ static lumpinfo_t* ResGetLumpsWad(FILE* handle, UINT16* nlmp, const char* filena lump_p->hash.name = W_HashLumpName(lump_p->name); // Allocate the lump's long name. - lump_p->longname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); + lump_p->longname = static_cast(Z_Malloc(9 * sizeof(char), PU_STATIC, NULL)); strncpy(lump_p->longname, fileinfo->name, 8); lump_p->longname[8] = '\0'; lump_p->longnamelength = lump_p->namelength; lump_p->hash.longname = lump_p->hash.name; // Allocate the lump's full name. - lump_p->fullname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); + lump_p->fullname = static_cast(Z_Malloc(9 * sizeof(char), PU_STATIC, NULL)); strncpy(lump_p->fullname, fileinfo->name, 8); lump_p->fullname[8] = '\0'; lump_p->fullnamelength = lump_p->namelength; @@ -538,24 +538,24 @@ static boolean ResFindSignature(FILE* handle, const char endPat[], UINT32 startp int c; fseek(handle, 0, SEEK_END); - size_t len = ftell(handle); + size_t len = static_cast(ftell(handle)); fseek(handle, startpos, SEEK_SET); - size_t remaining = len - startpos; - size_t chunkpos = startpos; + size_t remaining = static_cast(len - startpos); + size_t chunkpos = static_cast(startpos); s = endPat; //128k buffers - size_t buffer_size = min(128 * 1024 * sizeof(char), remaining); - CLEANUP(pfree) char* buffer = (char*)(malloc(buffer_size)); + size_t buffer_size = std::min(128 * 1024 * sizeof(char), remaining); + CLEANUP(pfree) char* buffer = static_cast(malloc(buffer_size)); size_t bytes_read = 0; while ((bytes_read = fread(buffer, 1, buffer_size, handle)) > 0) { for (size_t i = 0; i < bytes_read; i++) { - c = (int)buffer[i]; + c = static_cast(buffer[i]); if (*s != c && s > endPat) // No match? s = endPat; // We "reset" the counter by sending the s pointer back to the start of the array. @@ -567,7 +567,7 @@ static boolean ResFindSignature(FILE* handle, const char endPat[], UINT32 startp if (*s == 0x00) // The array pointer has reached the key char which marks the end. It means we have matched the signature. { //the original function would leave the FILE* seeked to the end of the match - size_t foundpos = chunkpos + i + 1; + size_t foundpos = static_cast(chunkpos + i + 1); fseek(handle, foundpos, SEEK_SET); return true; } @@ -644,8 +644,8 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) // Contains entry number (number of lumps), and central directory start offset. fseek(handle, 0, SEEK_END); - const long startpos = (ftell(handle) - (22 + 65536)); - if (!ResFindSignature(handle, pat_end, max(0l, startpos))) + const long startpos = ftell(handle) - (22 + 65536); + if (!ResFindSignature(handle, pat_end, std::max(0l, startpos))) { CONS_Alert(CONS_ERROR, "Missing central directory\n"); return NULL; @@ -660,11 +660,11 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) numlumps = SHORT(zend.entries); - lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL); + lump_p = lumpinfo = static_cast(Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL)); fseek(handle, LONG(zend.cdiroffset), SEEK_SET); - CLEANUP(Z_Pfree) char *cdir = Z_Malloc(LONG(zend.cdirsize), PU_STATIC, &cdir); + CLEANUP(Z_Pfree) char *cdir = static_cast(Z_Malloc(LONG(zend.cdirsize), PU_STATIC, &cdir)); if (fread(cdir, 1, LONG(zend.cdirsize), handle) < (UINT32)(LONG(zend.cdirsize))) { @@ -677,7 +677,7 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) for (i = 0; i < numlumps; i++, lump_p++) { - zentry_t *zentry = (zentry_t*)(cdir + offset); + zentry_t *zentry = reinterpret_cast(cdir + offset); char* fullname; char* trimname; char* dotpos; @@ -693,8 +693,8 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) lump_p->disksize = LONG(zentry->compsize); lump_p->size = LONG(zentry->size); - fullname = (char*)(malloc(SHORT(zentry->namelen) + 1)); - strlcpy(fullname, (char*)(zentry + 1), SHORT(zentry->namelen) + 1); + fullname = static_cast(malloc(SHORT(zentry->namelen) + 1)); + strlcpy(fullname, reinterpret_cast(zentry + 1), SHORT(zentry->namelen) + 1); // Strip away file address and extension for the 8char name. if ((trimname = strrchr(fullname, '/')) != 0) @@ -706,16 +706,16 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) dotpos = fullname + strlen(fullname); // Watch for files without extension. memset(lump_p->name, '\0', 9); // Making sure they're initialized to 0. Is it necessary? - strncpy(lump_p->name, trimname, min(8, dotpos - trimname)); + strncpy(lump_p->name, trimname, std::min(8, dotpos - trimname)); lump_p->namelength = strlen(lump_p->name); lump_p->hash.name = W_HashLumpName(lump_p->name); - lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL); + lump_p->longname = static_cast(Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL)); strlcpy(lump_p->longname, trimname, dotpos - trimname + 1); lump_p->longnamelength = strlen(lump_p->longname); lump_p->hash.longname = W_HashLumpName(lump_p->longname); - lump_p->fullname = (char*)(Z_Calloc(SHORT(zentry->namelen) + 1, PU_STATIC, NULL)); + lump_p->fullname = static_cast(Z_Calloc(SHORT(zentry->namelen) + 1, PU_STATIC, NULL)); strncpy(lump_p->fullname, fullname, SHORT(zentry->namelen)); lump_p->fullnamelength = zentry->namelen; lump_p->hash.fullname = W_HashLumpName(lump_p->fullname); @@ -893,14 +893,14 @@ UINT16 W_InitFile(const char *filename, boolean local, boolean startup) // // link wad file to search files // - wadfile = Z_Calloc(sizeof (*wadfile), PU_STATIC, NULL); + wadfile = static_cast(Z_Calloc(sizeof (*wadfile), PU_STATIC, NULL)); wadfile->filename = Z_StrDup(filename); wadfile->handle = handle; - wadfile->numlumps = (UINT16)numlumps; + wadfile->numlumps = static_cast(numlumps); wadfile->lumpinfo = lumpinfo; wadfile->important = important; fseek(handle, 0, SEEK_END); - wadfile->filesize = (unsigned)ftell(handle); + wadfile->filesize = static_cast(ftell(handle)); wadfile->type = type; wadfile->startfolders = M_AATreeAlloc(AATREE_STRING); wadfile->endfolders = M_AATreeAlloc(AATREE_STRING); @@ -928,7 +928,7 @@ UINT16 W_InitFile(const char *filename, boolean local, boolean startup) #ifdef HWRENDER // Read shaders from file if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED)) - HWR_LoadCustomShadersFromFile(numwadfiles - 1, (type == RET_PK3)); + HWR_LoadCustomShadersFromFile(numwadfiles - 1, static_cast(type == RET_PK3)); #endif // TODO: HACK ALERT - Load Lua & SOC stuff right here. I feel like this should be out of this place, but... Let's stick with this for now. @@ -1028,7 +1028,7 @@ INT32 W_AddAutoloadedLocalFiles(char **filenames) /** Make sure a lump number is valid. * Compiles away to nothing if PARANOIA is not defined. */ -static boolean TestValidLump(UINT16 wad, UINT16 lump) +static inline boolean TestValidLump(UINT16 wad, UINT16 lump) { I_Assert(wad < MAX_WADFILES); if (!wadfiles[wad]) // make sure the wad file exists @@ -1051,7 +1051,7 @@ const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump) const char *W_CheckNameForNum(lumpnum_t lumpnum) { - return W_CheckNameForNumPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum)); + return W_CheckNameForNumPwad(WADFILENUM(lumpnum), LUMPNUM(lumpnum)); } // @@ -1142,9 +1142,11 @@ UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump) UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlump) { UINT16 marker; + marker = W_CheckNumForNamePwad(name, wad, startlump); if (marker != INT16_MAX) marker++; // Do not count the first marker + return marker; } @@ -1152,13 +1154,13 @@ UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlu UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump) { size_t name_length; - INT32 i; + UINT16 i; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; name_length = strlen(name); void *val = M_AATreeGetString(wadfiles[wad]->startfolders, name); if (val != NULL) - return (uintptr_t)val; + return static_cast(reinterpret_cast(val)); for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) { @@ -1168,12 +1170,12 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum if (strlen(lump_p->fullname) == name_length) i++; - M_AATreeSetString(wadfiles[wad]->startfolders, name, (void *)(uintptr_t)i); + M_AATreeSetString(wadfiles[wad]->startfolders, name, reinterpret_cast(static_cast(i))); return i; } } - M_AATreeSetString(wadfiles[wad]->startfolders, name, (void *)INT16_MAX); + M_AATreeSetString(wadfiles[wad]->startfolders, name, reinterpret_cast(INT16_MAX)); return INT16_MAX; } @@ -1182,13 +1184,13 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum // Returns the position of the lumpinfo entry. UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) { - INT32 i; + UINT16 i; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; size_t name_length = strlen(name); void *val = M_AATreeGetString(wadfiles[wad]->endfolders, name); if (val != NULL) - return (uintptr_t)val; + return static_cast(reinterpret_cast(val)); for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) { @@ -1196,7 +1198,7 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) break; } - M_AATreeSetString(wadfiles[wad]->endfolders, name, (void *)(uintptr_t)i); + M_AATreeSetString(wadfiles[wad]->endfolders, name, reinterpret_cast(static_cast(i))); return i; } @@ -1204,7 +1206,7 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) // Returns lump position in PK3's lumpinfo, or INT16_MAX if not found. UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump) { - INT32 i; + UINT16 i; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; size_t name_length = strlen(name); @@ -1297,7 +1299,7 @@ lumpnum_t W_CheckNumForName(const char *name) // scan wad files backwards so patch lump files take precedence for (i = numwadfiles - 1; i >= 0; i--) { - check = W_CheckNumForNamePwad(name, (UINT16)i, 0); + check = W_CheckNumForNamePwad(name, static_cast(i), 0); if (check != INT16_MAX) break; //found it @@ -1339,7 +1341,7 @@ lumpnum_t W_CheckNumForLongName(const char *name) // scan wad files backwards so patch lump files take precedence for (i = numwadfiles - 1; i >= 0; i--) { - check = W_CheckNumForLongNamePwad(name, (UINT16)i, 0); + check = W_CheckNumForLongNamePwad(name, static_cast(i), 0); if (check != INT16_MAX) break; //found it @@ -1409,25 +1411,27 @@ lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, con { if (wadfiles[i]->type == RET_WAD) { - bsid = W_CheckNumForNamePwad(blockstart, (UINT16)i, 0); + bsid = W_CheckNumForNamePwad(blockstart, static_cast(i), 0); if (bsid == INT16_MAX) continue; // Start block doesn't exist? - beid = W_CheckNumForNamePwad(blockend, (UINT16)i, 0); + beid = W_CheckNumForNamePwad(blockend, static_cast(i), 0); if (beid == INT16_MAX) continue; // End block doesn't exist? - check = W_CheckNumForNamePwad(name, (UINT16)i, bsid); + check = W_CheckNumForNamePwad(name, static_cast(i), bsid); if (check < beid) - return (i<<16)+check; // found it, in our constraints + return (i << 16)+check; // found it, in our constraints } } + return LUMPERROR; } // Used by Lua. Case sensitive lump checking, quickly... UINT8 W_LumpExists(const char *name) { - INT32 i,j; + INT32 i, j; + for (i = numwadfiles - 1; i >= 0; i--) { lumpinfo_t *lump_p = wadfiles[i]->lumpinfo; @@ -1435,6 +1439,7 @@ UINT8 W_LumpExists(const char *name) if (fastcmp(lump_p->name, name)) return true; } + return false; } @@ -1594,13 +1599,13 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si fseek(handle, (long)(l->position + offset), SEEK_SET); // But let's not copy it yet. We support different compression formats on lumps, so we need to take that into account. - switch(wadfiles[wad]->lumpinfo[lump].compression) + switch (wadfiles[wad]->lumpinfo[lump].compression) { case CM_NOCOMPRESSION: // If it's uncompressed, we directly write the data into our destination, and return the bytes read. #ifdef NO_PNG_LUMPS { size_t bytesread = fread(dest, 1, size, handle); - ErrorIfPNG((UINT8 *)dest, bytesread, wadfiles[wad]->filename, l->fullname); + ErrorIfPNG(static_cast(dest), bytesread, wadfiles[wad]->filename, l->fullname); return bytesread; } #else @@ -1613,8 +1618,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si char *decData; // Lump's decompressed real data. size_t retval; // Helper var, lzf_decompress returns 0 when an error occurs. - rawData = Z_Malloc(l->disksize, PU_STATIC, NULL); - decData = Z_Malloc(l->size, PU_STATIC, NULL); + rawData = static_cast(Z_Malloc(l->disksize, PU_STATIC, NULL)); + decData = static_cast(Z_Malloc(l->size, PU_STATIC, NULL)); if (fread(rawData, 1, l->disksize, handle) < l->disksize) I_Error("wad %d, lump %d: cannot read compressed data", wad, lump); @@ -1641,7 +1646,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(rawData); Z_Free(decData); #ifdef NO_PNG_LUMPS - ErrorIfPNG((UINT8 *)dest, size, wadfiles[wad]->filename, l->fullname); + ErrorIfPNG(static_cast(dest), size, wadfiles[wad]->filename, l->fullname); #endif return size; #else @@ -1661,8 +1666,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si unsigned long rawSize = l->disksize; unsigned long decSize = size; - rawData = Z_Malloc(rawSize, PU_STATIC, NULL); - decData = dest; + rawData = static_cast(Z_Malloc(rawSize, PU_STATIC, NULL)); + decData = static_cast(dest); if (fread(rawData, 1, rawSize, handle) < rawSize) I_Error("wad %d, lump %d: cannot read compressed data", wad, lump); @@ -1698,7 +1703,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(rawData); #ifdef NO_PNG_LUMPS - ErrorIfPNG((UINT8 *)dest, size, wadfiles[wad]->filename, l->fullname); + ErrorIfPNG(static_cast(dest), size, wadfiles[wad]->filename, l->fullname); #endif return size; } @@ -1886,7 +1891,7 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0); dest = Z_Calloc(sizeof(patch_t), tag, &lumpcache[lump]); - Patch_Create((softwarepatch_t*)lumpdata, len, dest); + Patch_Create(static_cast(lumpdata), len, dest); Z_Free(lumpdata); } @@ -1908,7 +1913,7 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) if (!TestValidLump(wad, lump)) return NULL; - patch = (patch_t *)W_CacheSoftwarePatchNumPwad(wad, lump, tag); + patch = static_cast(W_CacheSoftwarePatchNumPwad(wad, lump, tag)); if (patch == NULL) return NULL; @@ -1919,7 +1924,7 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) Patch_CreateGL(patch); #endif - return (void *)patch; + return static_cast(patch); } void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag) @@ -1937,17 +1942,20 @@ void *W_GetCachedPatchNumPwad(UINT16 wad, UINT16 lump) #ifdef ROTSPRITE // Caches a rotsprite for patch rotation. -void *W_GetCachedRotPatchPwad(UINT16 wadnum, UINT16 lumpnum) +void *W_GetCachedRotPatchPwad(UINT16 wad, UINT16 lump) { - lumpcache_t *rotcache = wadfiles[wadnum]->rotcache; + if (!TestValidLump(wad, lump)) + return NULL; - if (!rotcache[lumpnum]) + lumpcache_t *rotcache = wadfiles[wad]->rotcache; + + if (!rotcache[lump]) { rotsprite_t *rspr = RotatedPatch_Create(ROTANGLES); - Z_SetUser(rspr, (void **)(&rotcache[lumpnum])); + Z_SetUser(rspr, static_cast(&rotcache[lump])); } - return (void *)(rotcache[lumpnum]); + return static_cast(rotcache[lump]); } #endif // ROTSPRITE @@ -1960,7 +1968,7 @@ void W_UnlockCachedPatch(void *patch) // have different lifetimes from software's. #ifdef HWRENDER if (rendermode == render_opengl) - HWR_UnlockCachedPatch((GLPatch_t *)((patch_t *)patch)->hardware); + HWR_UnlockCachedPatch(static_cast((static_cast(patch))->hardware)); else #endif Z_ChangeTag(patch, PU_PATCH); @@ -1995,13 +2003,13 @@ void *W_CachePatchNameRotated(const char *name, INT32 rotationangle, INT32 tag) if (rotationangle < 1 || rotationangle >= ROTANGLES) return W_CachePatchNum(num, tag); - rspr = (rotsprite_t *)W_GetCachedRotPatchPwad(WADFILENUM(num),LUMPNUM(num)); + rspr = static_cast(W_GetCachedRotPatchPwad(WADFILENUM(num), LUMPNUM(num))); if (rspr->patches[idx] == NULL) { INT32 xpivot = 0, ypivot = 0; - ptr = (patch_t *)W_CachePatchNum(num, PU_PATCH); + ptr = static_cast(W_CachePatchNum(num, PU_PATCH)); // >y pivot centered // >x pivot not centered @@ -2192,8 +2200,8 @@ static int W_VerifyPK3(FILE *fp, lumpchecklist_t *checklist, boolean status) fseek(fp, 0, SEEK_END); file_size = ftell(fp); - const long startpos = (file_size - (22 + 65536)); - if (!ResFindSignature(fp, pat_end, max(0l, startpos))) + const long startpos = file_size - (22 + 65536); + if (!ResFindSignature(fp, pat_end, std::max(0l, startpos))) return true; fseek(fp, -4, SEEK_CUR); @@ -2206,7 +2214,7 @@ static int W_VerifyPK3(FILE *fp, lumpchecklist_t *checklist, boolean status) fseek(fp, LONG(zend.cdiroffset), SEEK_SET); - CLEANUP(pfree) char *cdir = (char*)(malloc(LONG(zend.cdirsize))); + CLEANUP(pfree) char *cdir = static_cast(malloc(LONG(zend.cdirsize))); if (fread(cdir, 1, LONG(zend.cdirsize), fp) < (UINT32)(LONG(zend.cdirsize))) { @@ -2217,7 +2225,7 @@ static int W_VerifyPK3(FILE *fp, lumpchecklist_t *checklist, boolean status) for (i = 0; i < numlumps; i++) { - zentry_t *zentry = (zentry_t*)(cdir + offset); + zentry_t *zentry = reinterpret_cast(cdir + offset); char* fullname; char* trimname; char* dotpos; @@ -2229,8 +2237,8 @@ static int W_VerifyPK3(FILE *fp, lumpchecklist_t *checklist, boolean status) if (verified == true) { - fullname = (char*)(malloc(SHORT(zentry->namelen) + 1)); - strlcpy(fullname, (char*)(zentry + 1), SHORT(zentry->namelen) + 1); + fullname = static_cast(malloc(SHORT(zentry->namelen) + 1)); + strlcpy(fullname, reinterpret_cast(zentry + 1), SHORT(zentry->namelen) + 1); // Strip away file address and extension for the 8char name. if ((trimname = strrchr(fullname, '/')) != 0) @@ -2244,7 +2252,7 @@ static int W_VerifyPK3(FILE *fp, lumpchecklist_t *checklist, boolean status) dotpos = fullname + strlen(fullname); // Watch for files without extension. memset(lumpname, '\0', 9); // Making sure they're initialized to 0. Is it necessary? - strncpy(lumpname, trimname, min(8, dotpos - trimname)); + strncpy(lumpname, trimname, std::min(8, dotpos - trimname)); if (!W_VerifyName(lumpname, checklist, status)) verified = false; @@ -2439,8 +2447,8 @@ static int W_CheckPK3Contains(FILE *fp, lumpchecklist_t *checklist) fseek(fp, 0, SEEK_END); file_size = ftell(fp); - const long startpos = (file_size - (22 + 65536)); - if (!ResFindSignature(fp, pat_end, max(0l, startpos))) + const long startpos = file_size - (22 + 65536); + if (!ResFindSignature(fp, pat_end, std::max(0l, startpos))) return true; fseek(fp, -4, SEEK_CUR); @@ -2453,7 +2461,7 @@ static int W_CheckPK3Contains(FILE *fp, lumpchecklist_t *checklist) fseek(fp, LONG(zend.cdiroffset), SEEK_SET); - CLEANUP(pfree) char *cdir = (char*)(malloc(LONG(zend.cdirsize))); + CLEANUP(pfree) char *cdir = static_cast(malloc(LONG(zend.cdirsize))); if (fread(cdir, 1, LONG(zend.cdirsize), fp) < (UINT32)(LONG(zend.cdirsize))) { @@ -2464,7 +2472,7 @@ static int W_CheckPK3Contains(FILE *fp, lumpchecklist_t *checklist) for (i = 0; i < numlumps; i++) { - zentry_t *zentry = (zentry_t*)(cdir + offset); + zentry_t *zentry = reinterpret_cast(cdir + offset); CLEANUP(pfree) char* fullname = NULL; char* trimname; char* dotpos; @@ -2474,8 +2482,8 @@ static int W_CheckPK3Contains(FILE *fp, lumpchecklist_t *checklist) return false; } - fullname = (char*)(malloc(SHORT(zentry->namelen) + 1)); - strlcpy(fullname, (char*)(zentry + 1), SHORT(zentry->namelen) + 1); + fullname = static_cast(malloc(SHORT(zentry->namelen) + 1)); + strlcpy(fullname, reinterpret_cast(zentry + 1), SHORT(zentry->namelen) + 1); // Strip away file address and extension for the 8char name. if ((trimname = strrchr(fullname, '/')) != 0) @@ -2489,7 +2497,7 @@ static int W_CheckPK3Contains(FILE *fp, lumpchecklist_t *checklist) dotpos = fullname + strlen(fullname); // Watch for files without extension. memset(lumpname, '\0', 9); // Making sure they're initialized to 0. Is it necessary? - strncpy(lumpname, trimname, min(8, dotpos - trimname)); + strncpy(lumpname, trimname, std::min(8, dotpos - trimname)); if (W_NameStartsWith(lumpname, checklist)) { @@ -2611,15 +2619,15 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) size_t *vsizecache; // Remember that we're assuming that the WAD will have a specific set of lumps in a specific order. - UINT8 *wadData = (UINT8*)(W_CacheLumpNum(lumpnum, PU_LEVEL)); - filelump_t *fileinfo = (filelump_t *)(wadData + LONG(((wadinfo_t *)wadData)->infotableofs)); + UINT8 *wadData = static_cast(W_CacheLumpNum(lumpnum, PU_LEVEL)); + filelump_t *fileinfo = reinterpret_cast(wadData + LONG((reinterpret_cast(wadData))->infotableofs)); - i = LONG(((wadinfo_t *)wadData)->numlumps); - vsizecache = (size_t*)(Z_Malloc(sizeof(size_t)*i, PU_LEVEL, NULL)); + i = LONG((reinterpret_cast(wadData))->numlumps); + vsizecache = static_cast(Z_Malloc(sizeof(size_t)*i, PU_LEVEL, NULL)); for (realentry = 0; realentry < i; realentry++) { - vsizecache[realentry] = (size_t)(LONG(((filelump_t *)(fileinfo + realentry))->size)); + vsizecache[realentry] = static_cast(LONG((reinterpret_cast(fileinfo + realentry))->size)); if (!vsizecache[realentry]) continue; @@ -2627,7 +2635,7 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) numlumps++; } - vlumps = (virtlump_t*)(Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL)); + vlumps = static_cast(Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL)); // Build the lumps, skipping over empty entries. for (i = 0, realentry = 0; i < numlumps; realentry++) @@ -2647,7 +2655,7 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) // Play it safe with the name in this case. memcpy(vlumps[i].name, name, 8); vlumps[i].name[8] = '\0'; - vlumps[i].data = (UINT8*)( + vlumps[i].data = static_cast( Z_Malloc(vlumps[i].size, PU_LEVEL, NULL) // This is memory inefficient, sorry about that. ); memcpy(vlumps[i].data, wadData + LONG((fileinfo + realentry)->filepos), vlumps[i].size); @@ -2670,7 +2678,7 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) } numlumps++; - vlumps = (virtlump_t*)(Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL)); + vlumps = static_cast(Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL)); for (i = 0; i < numlumps; i++, lumpnum++) { // Check if it is map marker. It is not always first lump sadly, so we need to expect it anywhere @@ -2686,11 +2694,11 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) vlumps[i].size = W_LumpLength(lumpnum); memcpy(vlumps[i].name, name, 8); vlumps[i].name[8] = '\0'; - vlumps[i].data = (UINT8*)(W_CacheLumpNum(lumpnum, PU_LEVEL)); + vlumps[i].data = static_cast(W_CacheLumpNum(lumpnum, PU_LEVEL)); } } - vres = (virtres_t*)(Z_Malloc(sizeof(virtres_t), PU_LEVEL, NULL)); + vres = static_cast(Z_Malloc(sizeof(virtres_t), PU_LEVEL, NULL)); vres->vlumps = vlumps; vres->numlumps = numlumps; diff --git a/src/w_wad.h b/src/w_wad.h index 6240c31f3..9122df1b7 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -233,7 +233,7 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag); void *W_CacheSoftwarePatchNum(lumpnum_t lumpnum, INT32 tag); #ifdef ROTSPRITE -void *W_GetCachedRotPatchPwad(UINT16 wadnum, UINT16 lumpnum); // Get patch-based rotsprites from the cache. +void *W_GetCachedRotPatchPwad(UINT16 wad, UINT16 lump); // Get patch-based rotsprites from the cache. void *W_CachePatchNameRotated(const char *name, INT32 rotationangle, INT32 tag); #endif -- 2.54.0 From 699113fb25cdc4462de7e181b3e659c7d34e5d91 Mon Sep 17 00:00:00 2001 From: Alug Date: Thu, 4 Dec 2025 20:31:54 +0100 Subject: [PATCH 02/35] w_wad: replace lumpnumcache with unordered_map hash lookup needs testing if actually better but the old lumpnumcache was basically useless due to being incredibly small even just booting up the game we have around 1000 entries already this hopefully should still be faster than potentially going through ALL lumps in ALL wads we have loaded to find the lumpnums not sure if longnames should get any special handling like before, but i dont think it really matters if theyre long honestly thanks ceepeepeereference for the basis of this --- src/w_wad.cpp | 97 ++++++++++++++++++--------------------------------- 1 file changed, 34 insertions(+), 63 deletions(-) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 114e5c77c..3adf929a8 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -87,18 +87,32 @@ typedef struct } lumpchecklist_t; // Must be a power of two -#define LUMPNUMCACHESIZE 64 +#define LUMPNUMCACHESIZE 2048 // should be reasonable ig #define LUMPNUMCACHENAME 32 -typedef struct lumpnum_cache_s -{ - char lumpname[LUMPNUMCACHENAME]; - lumpnum_t lumpnum; - UINT32 hash; -} lumpnum_cache_t; +#include +#include +#include -static lumpnum_cache_t lumpnumcache[LUMPNUMCACHESIZE]; -static UINT16 lumpnumcacheindex = 0; +struct LumpnumNameHash +{ + std::size_t operator()(const std::string& str) const + { + // hopefully this is fast enough but should be + return static_cast(quickncasehash(str.c_str(), str.length())); + } +}; + +struct LumpnumStringEquals +{ + bool operator()(const std::string& str1, const std::string& str2) const + { + // fasticmp should be plenty fast and ignores case + return fasticmp(str1.c_str(), str2.c_str()); + } +}; + +static std::unordered_map lumpnumcache(LUMPNUMCACHESIZE); static const char pat_central[] = {0x50, 0x4b, 0x01, 0x02, 0x00}; static const char pat_end[] = {0x50, 0x4b, 0x05, 0x06, 0x00}; @@ -360,7 +374,7 @@ static inline INT32 W_MakeFileMD5(const char *filename, void *resblock) // Invalidates the cache of lump numbers. Call this whenever a wad is added. static void W_InvalidateLumpnumCache(void) { - memset(lumpnumcache, 0, sizeof(lumpnumcache)); + lumpnumcache.clear(); } UINT32 W_HashLumpName(const char *name) @@ -1222,61 +1236,18 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump) return INT16_MAX; } -static lumpnum_t CheckLumpInCache(const char *name, boolean longname) +static lumpnum_t CheckLumpInCache(const char *name) { - if (longname) - { - UINT32 hash = quickncasehash(name, 32); - - // Loop backwards so that we check most recent entries first - for (INT32 i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) - { - if (lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].hash == hash - && fasticmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name)) - { - lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1); - return lumpnumcache[lumpnumcacheindex].lumpnum; - } - } - } - else - { - UINT32 hash = quickncasehash(name, 8); - - // Loop backwards so that we check most recent entries first - for (INT32 i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) - { - if (lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].hash == hash - && lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname[8] == '\0' - && strnicmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name, 8) == 0) - { - lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1); - return lumpnumcache[lumpnumcacheindex].lumpnum; - } - } - } + auto it = lumpnumcache.find(name); + if (it != lumpnumcache.end()) + return it->second; return LUMPERROR; } -static void AddLumpToCache(lumpnum_t lumpnum, const char *name, boolean longname) +static void AddLumpToCache(lumpnum_t lumpnum, const char *name) { - if (longname && strlen(name) >= LUMPNUMCACHENAME) - return; - - lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1); - memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', LUMPNUMCACHENAME); - if (longname) - { - strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, LUMPNUMCACHENAME); - lumpnumcache[lumpnumcacheindex].hash = quickncasehash(name, LUMPNUMCACHENAME); - } - else - { - strncpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 8); - lumpnumcache[lumpnumcacheindex].hash = quickncasehash(name, 8); - } - lumpnumcache[lumpnumcacheindex].lumpnum = lumpnum; + lumpnumcache.insert({name, lumpnum}); } // @@ -1292,7 +1263,7 @@ lumpnum_t W_CheckNumForName(const char *name) return LUMPERROR; // Check the lumpnumcache first. - lumpnum_t cachenum = CheckLumpInCache(name, false); + lumpnum_t cachenum = CheckLumpInCache(name); if (cachenum != LUMPERROR) return cachenum; @@ -1313,7 +1284,7 @@ lumpnum_t W_CheckNumForName(const char *name) { // Update the cache. lumpnum_t lumpnum = (i << 16) | check; - AddLumpToCache(lumpnum, name, false); + AddLumpToCache(lumpnum, name); return lumpnum; } @@ -1334,7 +1305,7 @@ lumpnum_t W_CheckNumForLongName(const char *name) return LUMPERROR; // Check the lumpnumcache first. - lumpnum_t cachenum = CheckLumpInCache(name, true); + lumpnum_t cachenum = CheckLumpInCache(name); if (cachenum != LUMPERROR) return cachenum; @@ -1355,7 +1326,7 @@ lumpnum_t W_CheckNumForLongName(const char *name) { // Update the cache. lumpnum_t lumpnum = (i << 16) | check; - AddLumpToCache(lumpnum, name, true); + AddLumpToCache(lumpnum, name); return lumpnum; } -- 2.54.0 From 8091d825d5e942a86d9f25e71faaa52ff04e1bbd Mon Sep 17 00:00:00 2001 From: Alug Date: Fri, 5 Dec 2025 13:45:24 +0100 Subject: [PATCH 03/35] w_wad: use our lump fullnamelenght in W_CheckNumForFolderStartPK3 --- src/w_wad.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 3adf929a8..6d0353b98 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -1167,21 +1167,22 @@ UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlu // Look for the first lump from a folder. UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump) { - size_t name_length; UINT16 i; + size_t namelen; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; - name_length = strlen(name); + void *val = M_AATreeGetString(wadfiles[wad]->startfolders, name); if (val != NULL) return static_cast(reinterpret_cast(val)); + namelen = strlen(name); for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) { - if (strnicmp(name, lump_p->fullname, name_length) == 0) + if (strnicmp(name, lump_p->fullname, namelen) == 0) { /* SLADE is special and puts a single directory entry. Skip that. */ - if (strlen(lump_p->fullname) == name_length) + if (lump_p->fullnamelength == namelen) i++; M_AATreeSetString(wadfiles[wad]->startfolders, name, reinterpret_cast(static_cast(i))); @@ -1199,16 +1200,18 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) { UINT16 i; + size_t namelen; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; - size_t name_length = strlen(name); void *val = M_AATreeGetString(wadfiles[wad]->endfolders, name); if (val != NULL) return static_cast(reinterpret_cast(val)); + namelen = strlen(name); + for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) { - if (strnicmp(name, lump_p->fullname, name_length)) + if (strnicmp(name, lump_p->fullname, namelen)) break; } @@ -1221,12 +1224,14 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump) { UINT16 i; + size_t namelen; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; - size_t name_length = strlen(name); + + namelen = strlen(name); for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) { - if (!strnicmp(name, lump_p->fullname, name_length)) + if (!strnicmp(name, lump_p->fullname, namelen)) { return i; } -- 2.54.0 From 41c887f2bf5038917a479c9f9de61d8ec800e19c Mon Sep 17 00:00:00 2001 From: Alug Date: Fri, 5 Dec 2025 13:47:21 +0100 Subject: [PATCH 04/35] w_wad: comment out fullname lump hash for now its unused and not particulary useful eitherway, save us a few hash generates and a struct field lul --- src/w_wad.cpp | 6 +++--- src/w_wad.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 6d0353b98..09d74f993 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -424,7 +424,7 @@ static lumpinfo_t* ResGetLumpsStandalone(FILE* handle, UINT16* numlumps, const c strcpy(lumpinfo->fullname, lumpname); lumpinfo->fullname[lumpinfo->namelength] = '\0'; lumpinfo->fullnamelength = lumpinfo->namelength; - lumpinfo->hash.fullname = lumpinfo->hash.name; + //lumpinfo->hash.fullname = lumpinfo->hash.name; *numlumps = 1; return lumpinfo; @@ -535,7 +535,7 @@ static lumpinfo_t* ResGetLumpsWad(FILE* handle, UINT16* nlmp, const char* filena strncpy(lump_p->fullname, fileinfo->name, 8); lump_p->fullname[8] = '\0'; lump_p->fullnamelength = lump_p->namelength; - lump_p->hash.fullname = lump_p->hash.name; + //lump_p->hash.fullname = lump_p->hash.name; } *nlmp = numlumps; @@ -732,7 +732,7 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) lump_p->fullname = static_cast(Z_Calloc(SHORT(zentry->namelen) + 1, PU_STATIC, NULL)); strncpy(lump_p->fullname, fullname, SHORT(zentry->namelen)); lump_p->fullnamelength = zentry->namelen; - lump_p->hash.fullname = W_HashLumpName(lump_p->fullname); + //lump_p->hash.fullname = W_HashLumpName(lump_p->fullname); switch(SHORT(zentry->compression)) { diff --git a/src/w_wad.h b/src/w_wad.h index 9122df1b7..b045b9673 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -81,7 +81,7 @@ typedef struct struct { UINT32 name; // hash of name UINT32 longname; // hash of longname - UINT32 fullname; // hash of fullname + //UINT32 fullname; // hash of fullname } hash; } lumpinfo_t; -- 2.54.0 From fd1e41573170ce60dce049aedd981f1e009ddc94 Mon Sep 17 00:00:00 2001 From: Alug Date: Fri, 5 Dec 2025 14:00:04 +0100 Subject: [PATCH 05/35] w_wad: use unordered map to cache wad start and end folders --- src/w_wad.cpp | 31 ++++++++++++++++++------------- src/w_wad.h | 4 ++-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 09d74f993..8488fe4d6 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -114,6 +114,11 @@ struct LumpnumStringEquals static std::unordered_map lumpnumcache(LUMPNUMCACHESIZE); +// gotta wrap this in a struct due to external C linkage +struct FolderCache { + std::unordered_map map; +}; + static const char pat_central[] = {0x50, 0x4b, 0x01, 0x02, 0x00}; static const char pat_end[] = {0x50, 0x4b, 0x05, 0x06, 0x00}; @@ -143,8 +148,8 @@ void W_Shutdown(void) Z_Free(wad->lumpinfo[wad->numlumps].fullname); } - M_AATreeFree(wad->startfolders); - M_AATreeFree(wad->endfolders); + delete wad->startfolders; + delete wad->endfolders; Z_Free(wad->lumpinfo); Z_Free(wad); @@ -916,8 +921,8 @@ UINT16 W_InitFile(const char *filename, boolean local, boolean startup) fseek(handle, 0, SEEK_END); wadfile->filesize = static_cast(ftell(handle)); wadfile->type = type; - wadfile->startfolders = M_AATreeAlloc(AATREE_STRING); - wadfile->endfolders = M_AATreeAlloc(AATREE_STRING); + wadfile->startfolders = new FolderCache(); + wadfile->endfolders = new FolderCache(); // already generated, just copy it over memcpy(&wadfile->md5sum, &md5sum, 16); @@ -1171,10 +1176,10 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum size_t namelen; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; + auto it = wadfiles[wad]->startfolders->map.find(name); + if (it != wadfiles[wad]->startfolders->map.end()) + return it->second; - void *val = M_AATreeGetString(wadfiles[wad]->startfolders, name); - if (val != NULL) - return static_cast(reinterpret_cast(val)); namelen = strlen(name); for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) @@ -1185,12 +1190,12 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum if (lump_p->fullnamelength == namelen) i++; - M_AATreeSetString(wadfiles[wad]->startfolders, name, reinterpret_cast(static_cast(i))); + wadfiles[wad]->startfolders->map[name] = i; return i; } } - M_AATreeSetString(wadfiles[wad]->startfolders, name, reinterpret_cast(INT16_MAX)); + wadfiles[wad]->startfolders->map[name] = INT16_MAX; return INT16_MAX; } @@ -1203,9 +1208,9 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) size_t namelen; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; - void *val = M_AATreeGetString(wadfiles[wad]->endfolders, name); - if (val != NULL) - return static_cast(reinterpret_cast(val)); + auto it = wadfiles[wad]->endfolders->map.find(name); + if (it != wadfiles[wad]->endfolders->map.end()) + return it->second; namelen = strlen(name); @@ -1215,7 +1220,7 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) break; } - M_AATreeSetString(wadfiles[wad]->endfolders, name, reinterpret_cast(static_cast(i))); + wadfiles[wad]->endfolders->map[name] = i; return i; } diff --git a/src/w_wad.h b/src/w_wad.h index b045b9673..9ed733f78 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -137,8 +137,8 @@ typedef struct wadfile_s #ifdef ROTSPRITE lumpcache_t *rotcache; // Cache rotsprites for rotating patches. #endif - aatree_t *startfolders; - aatree_t *endfolders; + struct FolderCache *startfolders; + struct FolderCache *endfolders; UINT16 numlumps; // this wad's number of resources FILE *handle; UINT32 filesize; // for network -- 2.54.0 From b76ce836a0b0e54c3395707584927cfb78ff3fb1 Mon Sep 17 00:00:00 2001 From: Alug Date: Fri, 5 Dec 2025 14:51:58 +0100 Subject: [PATCH 06/35] w_wad: optimize W_LumpExists in two parts: - Use our lumpcache lookup first (our unordered map), BUT since it is case insensitive we extract our lump out of it to do another case sensitive name compare - In the manual lump seach falltthrough case, we can also check against the string lenghts and hash (its case insensitive but will still reject differing names, same with stringlenght) this improves the performance of the leaderboard map browser quite a bit and everything else using v.patchExists --- src/w_wad.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 8488fe4d6..27de24789 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -1412,13 +1412,38 @@ lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, con UINT8 W_LumpExists(const char *name) { INT32 i, j; + UINT32 hash; + size_t namelen; + + lumpnum_t cachenum = CheckLumpInCache(name); + if (cachenum != LUMPERROR) + { + // ok ok, we did find a lump in our lumpcache BUTT + // the lumpcache map is case insensitive + // extract the lumpinfo out of the lumpnum + // so we can do one more extra case ~sensitive~ name compare + // otherwise we gotta fall through to our manual lump search below + // lumpnum == (wadnum << 16) | lumpinfo id + UINT16 wadnum = (cachenum >> 16) & 0xFFFF; + UINT16 lumpid = cachenum & 0xFFFF; + lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[lumpid]; + if (fastcmp(lump_p->name, name)) + return true; + } + + namelen = strlen(name); + hash = W_HashLumpName(name); for (i = numwadfiles - 1; i >= 0; i--) { lumpinfo_t *lump_p = wadfiles[i]->lumpinfo; for (j = 0; j < wadfiles[i]->numlumps; ++j, ++lump_p) - if (fastcmp(lump_p->name, name)) + { + if (lump_p->namelength == namelen + && lump_p->hash.name == hash + && fastcmp(lump_p->name, name)) return true; + } } return false; -- 2.54.0 From 8f3b7158e3b574591aa739320c7fe7b7acf92a3a Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 7 Dec 2025 18:57:56 +0100 Subject: [PATCH 07/35] w_wad: kill longname support we dont use it for anything, we also cannot use it for anything. Due to massive amounts of unterminated string being passed to everything So we dont need to waste memory and resources --- src/w_wad.cpp | 26 +++++++++++++------------- src/w_wad.h | 12 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 27de24789..8afb38cab 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -144,7 +144,7 @@ void W_Shutdown(void) Z_Free(wad->filename); while (wad->numlumps--) { - Z_Free(wad->lumpinfo[wad->numlumps].longname); + //Z_Free(wad->lumpinfo[wad->numlumps].longname); Z_Free(wad->lumpinfo[wad->numlumps].fullname); } @@ -418,11 +418,11 @@ static lumpinfo_t* ResGetLumpsStandalone(FILE* handle, UINT16* numlumps, const c lumpinfo->hash.name = W_HashLumpName(lumpname); // Allocate the lump's long name. - lumpinfo->longname = static_cast(Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL)); + /*lumpinfo->longname = static_cast(Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL)); strcpy(lumpinfo->longname, lumpname); lumpinfo->longname[lumpinfo->namelength] = '\0'; lumpinfo->longnamelength = lumpinfo->namelength; - lumpinfo->hash.longname = lumpinfo->hash.name; + lumpinfo->hash.longname = lumpinfo->hash.name;*/ // Allocate the lump's full name. lumpinfo->fullname = static_cast(Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL)); @@ -529,11 +529,11 @@ static lumpinfo_t* ResGetLumpsWad(FILE* handle, UINT16* nlmp, const char* filena lump_p->hash.name = W_HashLumpName(lump_p->name); // Allocate the lump's long name. - lump_p->longname = static_cast(Z_Malloc(9 * sizeof(char), PU_STATIC, NULL)); + /*lump_p->longname = static_cast(Z_Malloc(9 * sizeof(char), PU_STATIC, NULL)); strncpy(lump_p->longname, fileinfo->name, 8); lump_p->longname[8] = '\0'; lump_p->longnamelength = lump_p->namelength; - lump_p->hash.longname = lump_p->hash.name; + lump_p->hash.longname = lump_p->hash.name;*/ // Allocate the lump's full name. lump_p->fullname = static_cast(Z_Malloc(9 * sizeof(char), PU_STATIC, NULL)); @@ -729,10 +729,10 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) lump_p->namelength = strlen(lump_p->name); lump_p->hash.name = W_HashLumpName(lump_p->name); - lump_p->longname = static_cast(Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL)); + /*lump_p->longname = static_cast(Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL)); strlcpy(lump_p->longname, trimname, dotpos - trimname + 1); lump_p->longnamelength = strlen(lump_p->longname); - lump_p->hash.longname = W_HashLumpName(lump_p->longname); + lump_p->hash.longname = W_HashLumpName(lump_p->longname);*/ lump_p->fullname = static_cast(Z_Calloc(SHORT(zentry->namelen) + 1, PU_STATIC, NULL)); strncpy(lump_p->fullname, fullname, SHORT(zentry->namelen)); @@ -1124,7 +1124,7 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump) // Should be the only version, but that's not possible until we fix // all the instances of non null-terminated strings in the codebase... // -UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump) +/*UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump) { UINT16 i; UINT32 hash; @@ -1156,7 +1156,7 @@ UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump) // not found. return INT16_MAX; -} +}*/ UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlump) { @@ -1306,7 +1306,7 @@ lumpnum_t W_CheckNumForName(const char *name) // Should be the only version, but that's not possible until we fix // all the instances of non null-terminated strings in the codebase... // -lumpnum_t W_CheckNumForLongName(const char *name) +/*lumpnum_t W_CheckNumForLongName(const char *name) { INT32 i; lumpnum_t check = INT16_MAX; @@ -1340,7 +1340,7 @@ lumpnum_t W_CheckNumForLongName(const char *name) return lumpnum; } -} +}*/ // // W_GetNumForName @@ -1365,7 +1365,7 @@ lumpnum_t W_GetNumForName(const char *name) // Should be the only version, but that's not possible until we fix // all the instances of non null-terminated strings in the codebase... // -lumpnum_t W_GetNumForLongName(const char *name) +/*lumpnum_t W_GetNumForLongName(const char *name) { lumpnum_t i; @@ -1375,7 +1375,7 @@ lumpnum_t W_GetNumForLongName(const char *name) I_Error("W_GetNumForLongName: %s not found!\n", name); return i; -} +}*/ // // W_CheckNumForNameInBlock diff --git a/src/w_wad.h b/src/w_wad.h index 9ed733f78..75c7e9c04 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -68,11 +68,11 @@ typedef struct unsigned long position; // filelump_t filepos unsigned long disksize; // filelump_t size char name[9]; // filelump_t name[] e.g. "LongEntr" - char *longname; // e.g. "LongEntryName" + //char *longname; // e.g. "LongEntryName" char *fullname; // e.g. "Folder/Subfolder/LongEntryName.extension" size_t namelength; // length of name - size_t longnamelength; // length of longname + //size_t longnamelength; // length of longname size_t fullnamelength; // length of fullname size_t size; // real (uncompressed) size @@ -80,7 +80,7 @@ typedef struct struct { UINT32 name; // hash of name - UINT32 longname; // hash of longname + //UINT32 longname; // hash of longname //UINT32 fullname; // hash of fullname } hash; } lumpinfo_t; @@ -180,7 +180,7 @@ const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump); const char *W_CheckNameForNum(lumpnum_t lumpnum); UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump); // checks only in one pwad -UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump); +//UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump); /* Find the first lump after F_START for instance. */ UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlump); @@ -190,9 +190,9 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump); lumpnum_t W_CheckNumForName(const char *name); -lumpnum_t W_CheckNumForLongName(const char *name); +//lumpnum_t W_CheckNumForLongName(const char *name); lumpnum_t W_GetNumForName(const char *name); // like W_CheckNumForName but I_Error on LUMPERROR -lumpnum_t W_GetNumForLongName(const char *name); +//lumpnum_t W_GetNumForLongName(const char *name); lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, const char *blockend); UINT8 W_LumpExists(const char *name); // Lua uses this. UINT8 W_CheckMultipleLumps(const char* lump, ...); // variadic version of above lmao -- 2.54.0 From 7dbb08315c820e4ee0ba90a3cbd1d43821110bc6 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 21 Jan 2026 20:48:04 +0100 Subject: [PATCH 08/35] fix cmake --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f09b95633..02dab08da 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,7 +54,7 @@ set(SRB2_CORE_SOURCES string.c tables.c v_video.c - w_wad.c + w_wad.cpp y_inter.c z_zone.cpp ) -- 2.54.0 From c84724da37c5700c8d83c46304e0bc22ce316f1c Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 8 Feb 2026 20:29:01 +0100 Subject: [PATCH 09/35] make lumpnumcache case sensitive + optimize + replace quickncase hash with FNV1a lumpnumcache can be case sensitive W_CheckNumForName should also look for uppercase stuff and W_LumpExists is case sensitive anyways this avoids having to always account for case (toupper/tolower) during hash and string comparisons and even allows us to even directly compare our strings instead of using fastcmp (std::strings allow this!) from some benchmarking externally this makes this faster quite a bit --- src/doomdef.h | 16 ---------------- src/m_misc.c | 16 ++++++++++++++++ src/m_misc.h | 1 + src/r_data.c | 6 +++--- src/s_sound.c | 4 ++-- src/w_wad.cpp | 31 ++++++++++--------------------- src/w_wad.h | 2 -- 7 files changed, 32 insertions(+), 44 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 23768d0f4..445b6951f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -516,22 +516,6 @@ extern boolean capslock; // i_system.c, replace getchar() once the keyboard has been appropriated INT32 I_GetKey(void); -/* http://www.cse.yorku.ca/~oz/hash.html */ -static inline -UINT32 quickncasehash (const char *p, size_t n) -{ - size_t i = 0; - UINT32 x = 5381; - - while (i < n && p[i]) - { - x = (x * 33) ^ tolower(p[i]); - i++; - } - - return x; -} - #ifndef __cplusplus #ifndef min // Double-Check with WATTCP-32's cdefs.h #define min(x, y) (((x) < (y)) ? (x) : (y)) diff --git a/src/m_misc.c b/src/m_misc.c index 8c7b34163..bd038a488 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1956,6 +1956,22 @@ UINT32 FNV1a_Hash(const char *message, size_t size) return hash; } +// Behaves like the old quickncasehash +// Stops either if it encounters null terminator +// or reaches size +UINT32 FNV1a_QuickCaseHash(const char *message, size_t size) +{ + UINT32 hash = FNV1A_OFFSET_BASIS; + + for (size_t i = 0; i < size && message[i]; i++) + { + hash ^= tolower(message[i]); + hash *= FNV1A_PRIME; + } + + return hash; +} + UINT32 FNV1a_HashString(const char *message) { UINT32 hash = FNV1A_OFFSET_BASIS; diff --git a/src/m_misc.h b/src/m_misc.h index ab7c8986f..813e8abf1 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -123,6 +123,7 @@ FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size); // Hashes some message using FNV-1a UINT32 FNV1a_Hash(const char *message, size_t size); +UINT32 FNV1a_QuickCaseHash(const char *message, size_t size); UINT32 FNV1a_HashString(const char *message); UINT32 FNV1a_HashLowercaseString(const char *message); diff --git a/src/r_data.c b/src/r_data.c index 386f0e700..a99218765 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -622,7 +622,7 @@ Rloadtextures (INT32 i, INT32 w) // Set texture properties. memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); - texture->hash = quickncasehash(texture->name, 8); + texture->hash = FNV1a_QuickCaseHash(texture->name, 8); texture->width = SHORT(patchlump.width); texture->height = SHORT(patchlump.height); @@ -1066,7 +1066,7 @@ static texture_t *R_ParseTexture(boolean actuallyLoadTexture) // Allocate memory for a zero-patch texture. Obviously, we'll be adding patches momentarily. resultTexture = (texture_t *)Z_Calloc(sizeof(texture_t), PU_STATIC, NULL); memcpy(resultTexture->name, newTextureName, 8); - resultTexture->hash = quickncasehash(newTextureName, 8); + resultTexture->hash = FNV1a_QuickCaseHash(newTextureName, 8); resultTexture->width = newTextureWidth; resultTexture->height = newTextureHeight; resultTexture->type = TEXTURETYPE_COMPOSITE; @@ -1756,7 +1756,7 @@ INT32 R_CheckTextureNumForName(const char *name) if (name[0] == '-') return 0; - hash = quickncasehash(name, 8); + hash = FNV1a_QuickCaseHash(name, 8); for (i = 0; i < tidcachelen; i++) if (tidcache[i].hash == hash && !strncasecmp(tidcache[i].name, name, 8)) diff --git a/src/s_sound.c b/src/s_sound.c index 7f931671d..206b5c37c 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1302,7 +1302,7 @@ static boolean ReadMusicDefFields(UINT16 wadnum, int line, char *stoken, musicde STRBUFCPY(def->name, value); strlwr(def->name); - def->hash = quickncasehash (def->name, 6); + def->hash = FNV1a_QuickCaseHash(def->name, 6); } (*defp) = def; @@ -1484,7 +1484,7 @@ void S_InitMusicDefs(void) // musicdef_t *S_FindMusicCredit(const char *musname) { - UINT32 hash = quickncasehash(musname, 6); + UINT32 hash = FNV1a_QuickCaseHash(musname, 6); musicdef_t *def; for (INT32 i = 0; i < nummusicdefs; ++i) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index bbf543f1d..44fece4a7 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -99,7 +99,7 @@ struct LumpnumNameHash std::size_t operator()(const std::string& str) const { // hopefully this is fast enough but should be - return static_cast(quickncasehash(str.c_str(), str.length())); + return static_cast(FNV1a_Hash(str.c_str(), str.length())); } }; @@ -107,8 +107,8 @@ struct LumpnumStringEquals { bool operator()(const std::string& str1, const std::string& str2) const { - // fasticmp should be plenty fast and ignores case - return fasticmp(str1.c_str(), str2.c_str()); + //return fastcmp(str1.c_str(), str2.c_str()); + return str1 == str2; } }; @@ -377,12 +377,12 @@ static inline INT32 W_MakeFileMD5(const char *filename, void *resblock) } // Invalidates the cache of lump numbers. Call this whenever a wad is added. -static void W_InvalidateLumpnumCache(void) +FUNCINLINE static ATTRINLINE void W_InvalidateLumpnumCache(void) { lumpnumcache.clear(); } -UINT32 W_HashLumpName(const char *name) +FUNCINLINE static ATTRINLINE UINT32 W_HashLumpName(const char *name) { return FNV1a_HashLowercaseString(name); } @@ -1246,7 +1246,7 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump) return INT16_MAX; } -static lumpnum_t CheckLumpInCache(const char *name) +FUNCINLINE static ATTRINLINE lumpnum_t CheckLumpInCache(const char *name) { auto it = lumpnumcache.find(name); if (it != lumpnumcache.end()) @@ -1255,9 +1255,9 @@ static lumpnum_t CheckLumpInCache(const char *name) return LUMPERROR; } -static void AddLumpToCache(lumpnum_t lumpnum, const char *name) +FUNCINLINE static ATTRINLINE void AddLumpToCache(lumpnum_t lumpnum, const char *name) { - lumpnumcache.insert({name, lumpnum}); + lumpnumcache.emplace(name, lumpnum); } // @@ -1415,21 +1415,10 @@ UINT8 W_LumpExists(const char *name) UINT32 hash; size_t namelen; + // Check the lumpnumcache first. lumpnum_t cachenum = CheckLumpInCache(name); if (cachenum != LUMPERROR) - { - // ok ok, we did find a lump in our lumpcache BUTT - // the lumpcache map is case insensitive - // extract the lumpinfo out of the lumpnum - // so we can do one more extra case ~sensitive~ name compare - // otherwise we gotta fall through to our manual lump search below - // lumpnum == (wadnum << 16) | lumpinfo id - UINT16 wadnum = (cachenum >> 16) & 0xFFFF; - UINT16 lumpid = cachenum & 0xFFFF; - lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[lumpid]; - if (fastcmp(lump_p->name, name)) - return true; - } + return cachenum; namelen = strlen(name); hash = W_HashLumpName(name); diff --git a/src/w_wad.h b/src/w_wad.h index 75c7e9c04..d21afb982 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -174,8 +174,6 @@ INT32 W_AddAutoloadedLocalFiles(char **filenames); #define W_FileHasFolders(wadfile) ((wadfile)->type == RET_PK3) -UINT32 W_HashLumpName(const char *name); - const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump); const char *W_CheckNameForNum(lumpnum_t lumpnum); -- 2.54.0 From 014a22e9336e4b5d34bdda2afbd61ca12de07c7a Mon Sep 17 00:00:00 2001 From: Alug Date: Tue, 24 Feb 2026 16:06:27 +0100 Subject: [PATCH 10/35] w_wad: replace unordered_map with verstable hashmap for lumpnumcache + convert back to C from C++ been rethinking this and been doing some digging online the unordered_map was just a quick and simple fix for performance issues in blan, but came with caveats: - unordered_map is simple to use but not particulary great in terms of performance during lookups especially - we needed to convert w_wad to c++ just for the lumpnumcache - due to needing to preserve the key since they gotta outlive the scope of the function that inserts things into the map, we effectively did a string copy on each map insert and lookup after some research came across verstable, which is basically almost the exact same as unordered map but in C while being extremely fast (even more with its builtin string hash and comparisor) only caveat with verstable is we need to rely on a new lib, though its just a single header file, and we gotta manage ensuring the input keys outlive functionscope ourselves, which we do with strdup in this case good thing, verstable allows to call a "destructor" specified by us whenever it removes or replaces keys in the map this also means we only need to copy strings on insert and not on lookup anymore all in all this is much faster while giving us the simple usage of c++ unordered_map while staying in C due to the strdup quirk i had to slightly modify verstable to allow to take const as inputs for the getter functions even if the keys themselves are not const marked with "KEY_GET_CONST" Verstable can be found here: https://github.com/JacksonAllan/Verstable and is licensed under MIT which is perfect! --- src/Sourcefile | 2 +- src/dedicated/i_system.c | 1 + src/sdl/i_system.cpp | 3 + src/verstable.h | 2119 ++++++++++++++++++++++++++++++++++++ src/{w_wad.cpp => w_wad.c} | 236 ++-- src/w_wad.h | 2 +- 6 files changed, 2248 insertions(+), 115 deletions(-) create mode 100644 src/verstable.h rename src/{w_wad.cpp => w_wad.c} (90%) diff --git a/src/Sourcefile b/src/Sourcefile index 2c4024f5f..08c18ebf0 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -45,7 +45,7 @@ sounds.c st_stuff.c tables.c v_video.c -w_wad.cpp +w_wad.c y_inter.c z_zone.cpp r_bsp.cpp diff --git a/src/dedicated/i_system.c b/src/dedicated/i_system.c index f6450134c..ddc17d6a5 100644 --- a/src/dedicated/i_system.c +++ b/src/dedicated/i_system.c @@ -1384,6 +1384,7 @@ static void I_Fork(void) INT32 I_StartupSystem(void) { + W_Startup(); I_StartupConsole(); #ifdef NEWSIGNALHANDLER // This is useful when debugging. It lets GDB attach to diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index 5310cc855..407e5c3a0 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -1886,6 +1886,9 @@ INT32 I_StartupSystem(void) SDL_version SDLlinked; SDL_VERSION(&SDLcompiled) SDL_GetVersion(&SDLlinked); + + W_Startup(); + I_StartupConsole(); #ifdef NEWSIGNALHANDLER // This is useful when debugging. It lets GDB attach to diff --git a/src/verstable.h b/src/verstable.h new file mode 100644 index 000000000..8c5a49dc3 --- /dev/null +++ b/src/verstable.h @@ -0,0 +1,2119 @@ +/*------------------------------------------------- VERSTABLE v2.2.1 --------------------------------------------------- + +Verstable is a C99-compatible, open-addressing hash table using quadratic probing and the following additions: + +* All keys that hash (i.e. "belong") to the same bucket (their "home bucket") are linked together by an 11-bit integer + specifying the quadratic displacement, relative to that bucket, of the next key in the chain. + +* If a chain of keys exists for a given bucket, then it always begins at that bucket. To maintain this policy, a 1-bit + flag is used to mark whether the key occupying a bucket belongs there. When inserting a new key, if the bucket it + belongs to is occupied by a key that does not belong there, then the occupying key is evicted and the new key takes + the bucket. + +* A 4-bit fragment of each key's hash code is also stored. + +* The aforementioned metadata associated with each bucket (the 4-bit hash fragment, the 1-bit flag, and the 11-bit link + to the next key in the chain) are stored together in a uint16_t array rather than in the bucket alongside the key and + (optionally) the value. + +One way to conceptualize this scheme is as a chained hash table in which overflowing keys are stored not in separate +memory allocations but in otherwise unused buckets. In this regard, it shares similarities with Malte Skarupke's Bytell +hash table (https://www.youtube.com/watch?v=M2fKMP47slQ) and traditional "coalesced hashing". + +Advantages of this scheme include: + +* Fast lookups impervious to load factor: If the table contains any key belonging to the lookup key's home bucket, then + that bucket contains the first in a traversable chain of all keys belonging to it. Hence, only the home bucket and + other buckets containing keys belonging to it are ever probed. Moreover, the stored hash fragments allow skipping most + non-matching keys in the chain without accessing the actual buckets array or calling the (potentially expensive) key + comparison function. + +* Fast insertions: Insertions are faster than they are in other schemes that move keys around (e.g. Robin Hood) because + they only move, at most, one existing key. + +* Fast, tombstone-free deletions: Deletions, which usually require tombstones in quadratic-probing hash tables, are + tombstone-free and only move, at most, one existing key. + +* Fast iteration: The separate metadata array allows keys in sparsely populated tables to be found without incurring the + frequent cache misses that would result from traversing the buckets array. + +Usage example: + + +---------------------------------------------------------+----------------------------------------------------------+ + | Using the generic macro API (C11 and later): | Using the prefixed functions API (C99 and later): | + +---------------------------------------------------------+----------------------------------------------------------+ + | #include | #include | + | | | + | // Instantiating a set template. | // Instantiating a set template. | + | #define NAME int_set | #define NAME int_set | + | #define KEY_TY int | #define KEY_TY int | + | #include "verstable.h" | #define HASH_FN vt_hash_integer | + | | #define CMPR_FN vt_cmpr_integer | + | // Instantiating a map template. | #include "verstable.h" | + | #define NAME int_int_map | | + | #define KEY_TY int | // Instantiating a map template. | + | #define VAL_TY int | #define NAME int_int_map | + | #include "verstable.h" | #define KEY_TY int | + | | #define VAL_TY int | + | int main( void ) | #define HASH_FN vt_hash_integer | + | { | #define CMPR_FN vt_cmpr_integer | + | // Set. | #include "verstable.h" | + | | | + | int_set our_set; | int main( void ) | + | vt_init( &our_set ); | { | + | | // Set. | + | // Inserting keys. | | + | for( int i = 0; i < 10; ++i ) | int_set our_set; | + | { | int_set_init( &our_set ); | + | int_set_itr itr = vt_insert( &our_set, i ); | | + | if( vt_is_end( itr ) ) | // Inserting keys. | + | { | for( int i = 0; i < 10; ++i ) | + | // Out of memory, so abort. | { | + | vt_cleanup( &our_set ); | int_set_itr itr = | + | return 1; | int_set_insert( &our_set, i ); | + | } | if( int_set_is_end( itr ) ) | + | } | { | + | | // Out of memory, so abort. | + | // Erasing keys. | int_set_cleanup( &our_set ); | + | for( int i = 0; i < 10; i += 3 ) | return 1; | + | vt_erase( &our_set, i ); | } | + | | } | + | // Retrieving keys. | | + | for( int i = 0; i < 10; ++i ) | // Erasing keys. | + | { | for( int i = 0; i < 10; i += 3 ) | + | int_set_itr itr = vt_get( &our_set, i ); | int_set_erase( &our_set, i ); | + | if( !vt_is_end( itr ) ) | | + | printf( "%d ", itr.data->key ); | // Retrieving keys. | + | } | for( int i = 0; i < 10; ++i ) | + | // Printed: 1 2 4 5 7 8 | { | + | | int_set_itr itr = int_set_get( &our_set, i ); | + | // Iteration. | if( !int_set_is_end( itr ) ) | + | for( | printf( "%d ", itr.data->key ); | + | int_set_itr itr = vt_first( &our_set ); | } | + | !vt_is_end( itr ); | // Printed: 1 2 4 5 7 8 | + | itr = vt_next( itr ) | | + | ) | // Iteration. | + | printf( "%d ", itr.data->key ); | for( | + | // Printed: 2 4 7 1 5 8 | int_set_itr itr = | + | | int_set_first( &our_set ); | + | vt_cleanup( &our_set ); | !int_set_is_end( itr ); | + | | itr = int_set_next( itr ) | + | // Map. | ) | + | | printf( "%d ", itr.data->key ); | + | int_int_map our_map; | // Printed: 2 4 7 1 5 8 | + | vt_init( &our_map ); | | + | | int_set_cleanup( &our_set ); | + | // Inserting keys and values. | | + | for( int i = 0; i < 10; ++i ) | // Map. | + | { | | + | int_int_map_itr itr = | int_int_map our_map; | + | vt_insert( &our_map, i, i + 1 ); | int_int_map_init( &our_map ); | + | if( vt_is_end( itr ) ) | | + | { | // Inserting keys and values. | + | // Out of memory, so abort. | for( int i = 0; i < 10; ++i ) | + | vt_cleanup( &our_map ); | { | + | return 1; | int_int_map_itr itr = | + | } | int_int_map_insert( &our_map, i, i + 1 ); | + | } | if( int_int_map_is_end( itr ) ) | + | | { | + | // Erasing keys and values. | // Out of memory, so abort. | + | for( int i = 0; i < 10; i += 3 ) | int_int_map_cleanup( &our_map ); | + | vt_erase( &our_map, i ); | return 1; | + | | } | + | // Retrieving keys and values. | } | + | for( int i = 0; i < 10; ++i ) | | + | { | // Erasing keys and values. | + | int_int_map_itr itr = vt_get( &our_map, i ); | for( int i = 0; i < 10; i += 3 ) | + | if( !vt_is_end( itr ) ) | int_int_map_erase( &our_map, i ); | + | printf( | | + | "%d:%d ", | // Retrieving keys and values. | + | itr.data->key, | for( int i = 0; i < 10; ++i ) | + | itr.data->val | { | + | ); | int_int_map_itr itr = | + | } | int_int_map_get( &our_map, i ); | + | // Printed: 1:2 2:3 4:5 5:6 7:8 8:9 | if( !int_int_map_is_end( itr ) ) | + | | printf( | + | // Iteration. | "%d:%d ", | + | for( | itr.data->key, | + | int_int_map_itr itr = vt_first( &our_map ); | itr.data->val | + | !vt_is_end( itr ); | ); | + | itr = vt_next( itr ) | } | + | ) | // Printed: 1:2 2:3 4:5 5:6 7:8 8:9 | + | printf( | | + | "%d:%d ", | // Iteration. | + | itr.data->key, | for( | + | itr.data->val | int_int_map_itr itr = | + | ); | int_int_map_first( &our_map ); | + | // Printed: 2:3 4:5 7:8 1:2 5:6 8:9 | !int_int_map_is_end( itr ); | + | | itr = int_int_map_next( itr ) | + | vt_cleanup( &our_map ); | ) | + | } | printf( | + | | "%d:%d ", | + | | itr.data->key, | + | | itr.data->val | + | | ); | + | | // Printed: 2:3 4:5 7:8 1:2 5:6 8:9 | + | | | + | | int_int_map_cleanup( &our_map ); | + | | } | + | | | + +---------------------------------------------------------+----------------------------------------------------------+ + +API: + + Instantiating a hash table template: + + Create a new hash table type in the following manner: + + #define NAME + #define KEY_TY + #include "verstable.h" + + The NAME macro specifies the name of hash table type that the library will declare, the prefix for the functions + associated with it, and the prefix for the associated iterator type. + + The KEY_TY macro specifies the key type. + + In C99, it is also always necessary to define HASH_FN and CMPR_FN (see below) before including the header. + + The following macros may also be defined before including the header: + + #define KEY_GET_CONST + + Forces the key getter to take a const pointer even if the input key is not marked const + + #define VAL_TY + + The type of the value associated with each key. + If this macro is defined, the hash table acts as a map associating keys with values. + Otherwise, it acts as a set containing only keys. + + #define HASH_FN + + The name of the existing function used to hash each key. + The function should have the signature uint64_t ( KEY_TY key ) and return a 64-bit hash code. + For best performance, the hash function should provide a high level of entropy across all bits. + There are two default hash functions: vt_hash_integer for all integer types up to 64 bits in size, and + vt_hash_string for NULL-terminated strings (i.e. char *). + When KEY_TY is one of such types and the compiler is in C11 mode or later, HASH_FN may be left undefined, in + which case the appropriate default function is inferred from KEY_TY. + Otherwise, HASH_FN must be defined. + + #define CMPR_FN + + The name of the existing function used to compare two keys. + The function should have the signature bool ( KEY_TY key_1, KEY_TY key_2 ) and return true if the two keys are + equal. + There are two default comparison functions: vt_cmpr_integer for all integer types up to 64 bits in size, and + vt_cmpr_string for NULL-terminated strings (i.e. char *). + As with the default hash functions, in C11 or later the appropriate default comparison function is inferred if + KEY_TY is one of such types and CMPR_FN is left undefined. + Otherwise, CMPR_FN must be defined. + + #define MAX_LOAD + + The floating-point load factor at which the hash table automatically doubles the size of its internal buckets + array. + The default is 0.9, i.e. 90%. + + #define KEY_DTOR_FN + + The name of the existing destructor function, with the signature void ( KEY_TY key ), called on a key when it is + erased from the table or replaced by a newly inserted key. + The API functions that may call the key destructor are NAME_insert, NAME_erase, NAME_erase_itr, NAME_clear, + and NAME_cleanup. + + #define VAL_DTOR_FN + + The name of the existing destructor function, with the signature void ( VAL_TY val ), called on a value when it + is erased from the table or replaced by a newly inserted value. + The API functions that may call the value destructor are NAME_insert, NAME_erase, NAME_erase_itr, NAME_clear, + and NAME_cleanup. + + #define CTX_TY + + The type of the hash table type's ctx (context) member. + This member only exists if CTX_TY was defined. + It is intended to be used in conjunction with MALLOC_FN and FREE_FN (see below). + + #define MALLOC_FN + + The name of the existing function used to allocate memory. + If CTX_TY was defined, the signature should be void *( size_t size, CTX_TY *ctx ), where size is the number of + bytes to allocate and ctx points to the table's ctx member. + Otherwise, the signature should be void *( size_t size ). + The default wraps stdlib.h's malloc. + + #define FREE_FN + + The name of the existing function used to free memory. + If CTX_TY was defined, the signature should be void ( void *ptr, size_t size, CTX_TY *ctx ), where ptr points to + the memory to free, size is the number of bytes that were allocated, and ctx points to the table's ctx member. + Otherwise, the signature should be void ( void *ptr, size_t size ). + The default wraps stdlib.h's free. + + #define HEADER_MODE + #define IMPLEMENTATION_MODE + + By default, all hash table functions are defined as static inline functions, the intent being that a given hash + table template should be instantiated once per translation unit; for best performance, this is the recommended + way to use the library. + However, it is also possible to separate the struct definitions and function declarations from the function + definitions such that one implementation can be shared across all translation units (as in a traditional header + and source file pair). + In that case, instantiate a template wherever it is needed by defining HEADER_MODE, along with only NAME, + KEY_TY, and (optionally) VAL_TY, CTX_TY, and header guards, and including the library, e.g.: + + #ifndef INT_INT_MAP_H + #define INT_INT_MAP_H + #define NAME int_int_map + #define KEY_TY int + #define VAL_TY int + #define HEADER_MODE + #include "verstable.h" + #endif + + In one source file, define IMPLEMENTATION_MODE, along with NAME, KEY_TY, and any of the aforementioned optional + macros, and include the library, e.g.: + + #define NAME int_int_map + #define KEY_TY int + #define VAL_TY int + #define HASH_FN vt_hash_integer // C99. + #define CMPR_FN vt_cmpr_integer // C99. + #define MAX_LOAD 0.8 + #define IMPLEMENTATION_MODE + #include "verstable.h" + + Including the library automatically undefines all the aforementioned macros after they have been used to instantiate + the template. + + Functions: + + The functions associated with a hash table type are all prefixed with the name the user supplied via the NAME macro. + In C11 and later, the generic "vt_"-prefixed macros may be used to automatically select the correct version of the + specified function based on the arguments. + + void NAME_init( NAME *table ) + void NAME_init( NAME *table, CTX_TY ctx ) + // C11 generic macro: vt_init. + + Initializes the table for use. + If CTX_TY was defined, ctx sets the table's ctx member. + + bool NAME_init_clone( NAME *table, NAME *source ) + bool NAME_init_clone( NAME *table, NAME *source, CTX_TY ctx ) + // C11 generic macro: vt_init_clone. + + Initializes the table as a shallow copy of the specified source table. + If CTX_TY was defined, ctx sets the table's ctx member. + Returns false in the case of memory allocation failure. + + size_t NAME_size( NAME *table ) // C11 generic macro: vt_size. + + Returns the number of keys currently in the table. + + size_t NAME_bucket_count( NAME *table ) // C11 generic macro: vt_bucket_count. + + Returns the table's current bucket count. + + NAME_itr NAME_insert( NAME *table, KEY_TY key ) + NAME_itr NAME_insert( NAME *table, KEY_TY key, VAL_TY val ) + // C11 generic macro: vt_insert. + + Inserts the specified key (and value, if VAL_TY was defined) into the hash table. + If the same key already exists, then the new key (and value) replaces the existing key (and value). + Returns an iterator to the new key, or an end iterator in the case of memory allocation failure. + + NAME_itr NAME_get_or_insert( NAME *table, KEY_TY key ) + NAME_itr NAME_get_or_insert( NAME *table, KEY_TY key, VAL_TY val ) + // C11 generic macro: vt_get_or_insert. + + Inserts the specified key (and value, if VAL_TY was defined) if it does not already exist in the table. + Returns an iterator to the new key if it was inserted, or an iterator to the existing key, or an end iterator if + the key did not exist but the new key could not be inserted because of memory allocation failure. + Determine whether the key was inserted by comparing the table's size before and after the call. + + NAME_itr NAME_get( NAME *table, KEY_TY key ) // C11 generic macro: vt_get. + + Returns a iterator to the specified key, or an end iterator if no such key exists. + + bool NAME_erase( NAME *table, KEY_TY key ) // C11 generic macro: vt_erase. + + Erases the specified key (and associated value, if VAL_TY was defined), if it exists. + Returns true if a key was erased. + + NAME_itr NAME_erase_itr( NAME *table, NAME_itr itr ) // C11 generic macro: vt_erase_itr. + + Erases the key (and associated value, if VAL_TY was defined) pointed to by the specified iterator. + Returns an iterator to the next key in the table, or an end iterator if the erased key was the last one. + + bool NAME_reserve( NAME *table, size_t size ) // C11 generic macro: vt_reserve. + + Ensures that the bucket count is large enough to support the specified key count (i.e. size) without rehashing. + Returns false if unsuccessful due to memory allocation failure. + + bool NAME_shrink( NAME *table ) // C11 generic macro: vt_shrink. + + Shrinks the bucket count to best accommodate the current size. + Returns false if unsuccessful due to memory allocation failure. + + NAME_itr NAME_first( NAME *table ) // C11 generic macro: vt_first. + + Returns an iterator to the first key in the table, or an end iterator if the table is empty. + + bool NAME_is_end( NAME_itr itr ) // C11 generic macro: vt_is_end. + + Returns true if the iterator is an end iterator. + + NAME_itr NAME_next( NAME_itr itr ) // C11 generic macro: vt_next. + + Returns an iterator to the key after the one pointed to by the specified iterator, or an end iterator if the + specified iterator points to the last key in the table. + + void NAME_clear( NAME *table ) // C11 generic macro: vt_clear. + + Erases all keys (and values, if VAL_TY was defined) in the table. + + void NAME_cleanup( NAME *table ) // C11 generic macro: vt_cleanup. + + Erases all keys (and values, if VAL_TY was defined) in the table, frees all memory associated with it, and + initializes it for reuse. + + Iterators: + + Access the key (and value, if VAL_TY was defined) that an iterator points to using the NAME_itr struct's data + member: + + itr.data->key + itr.data->val + + Functions that may insert new keys (NAME_insert and NAME_get_or_insert), erase keys (NAME_erase and NAME_erase_itr), + or reallocate the internal bucket array (NAME_reserve and NAME_shrink) invalidate all existing iterators. + To delete keys during iteration and resume iterating, use the return value of NAME_erase_itr. + +SRB2Kart - Saturn: + added KEY_GET_CONST to force the getters to take a const pointer even if the type of key to be inserted is not const + +Version history: + + 06/05/2025 2.2.1: Fixed incorrect signature of NAME_is_end in documentation. + 18/04/2025 2.2.0: Added const qualifier to the table parameter of NAME_size, NAME_bucket_count, NAME_get, and + NAME_first and to the source parameter of NAME_init_clone. + Added default support for const char * strings. + Added support for -Wextra. + Replaced FNV-1a with Wyhash as the default string hash function. + 18/06/2024 2.1.1: Fixed a bug affecting iteration on big-endian platforms under MSVC. + 27/05/2024 2.1.0: Replaced the Murmur3 mixer with the fast-hash mixer as the default integer hash function. + Fixed a bug that could theoretically cause a crash on rehash (triggerable in testing using + NAME_shrink with a maximum load factor significantly higher than 1.0). + 06/02/2024 2.0.0: Improved custom allocator support by introducing the CTX_TY option and allowing user-supplied free + functions to receive the allocation size. + Improved documentation. + Introduced various optimizations, including storing the buckets-array size mask instead of the + bucket count, eliminating empty-table checks, combining the buckets memory and metadata memory into + one allocation, and adding branch prediction macros. + Fixed a bug that caused a key to be used after destruction during erasure. + 12/12/2023 1.0.0: Initial release. + +License (MIT): + + Copyright (c) 2023-2025 Jackson L. Allan + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +/*--------------------------------------------------------------------------------------------------------------------*/ +/* Common header section */ +/*--------------------------------------------------------------------------------------------------------------------*/ + +#ifdef __GNUC__ + #if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) // >= GCC 3.1 + #define FORCEINLINE __attribute__((always_inline)) + #else + #define FORCEINLINE + #endif +#else + #define FORCEINLINE +#endif + +#ifndef VERSTABLE_H +#define VERSTABLE_H + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waggregate-return" + +#include +#include +#include +#include +#include +#include + +// Two-way concatenation macro. +#define VT_CAT_( a, b ) a##b +#define VT_CAT( a, b ) VT_CAT_( a, b ) + +// Branch optimization macros. +#ifdef __GNUC__ +#define VT_LIKELY( expression ) __builtin_expect( (bool)( expression ), true ) +#define VT_UNLIKELY( expression ) __builtin_expect( (bool)( expression ), false ) +#else +#define VT_LIKELY( expression ) ( expression ) +#define VT_UNLIKELY( expression ) ( expression ) +#endif + +// Masks for manipulating and extracting data from a bucket's uint16_t metadatum. +#define VT_EMPTY 0x0000 +#define VT_HASH_FRAG_MASK 0xF000 // 0b1111000000000000. +#define VT_IN_HOME_BUCKET_MASK 0x0800 // 0b0000100000000000. +#define VT_DISPLACEMENT_MASK 0x07FF // 0b0000011111111111, also denotes the displacement limit. Set to VT_LOAD to 1.0 + // to test proper handling of encroachment on the displacement limit during + // inserts. + +// Extracts a hash fragment from a uint64_t hash code. +// We take the highest four bits so that keys that map (via modulo) to the same bucket have distinct hash fragments. +static inline FORCEINLINE uint16_t vt_hashfrag( uint64_t hash ) +{ + return ( hash >> 48 ) & VT_HASH_FRAG_MASK; +} + +// Standard quadratic probing formula that guarantees that all buckets are visited when the bucket count is a power of +// two (at least in theory, because the displacement limit could terminate the search early when the bucket count is +// high). +static inline FORCEINLINE size_t vt_quadratic( uint16_t displacement ) +{ + return ( (size_t)displacement * displacement + displacement ) / 2; +} + +#define VT_MIN_NONZERO_BUCKET_COUNT 8 // Must be a power of two. + +// Function to find the left-most non-zero uint16_t in a uint64_t. +// This function is used when we scan four buckets at a time while iterating and relies on compiler intrinsics wherever +// possible. + +#if defined( __GNUC__ ) && ULLONG_MAX == 0xFFFFFFFFFFFFFFFF + +static inline FORCEINLINE int vt_first_nonzero_uint16( uint64_t val ) +{ + const uint16_t endian_checker = 0x0001; + if( *(const char *)&endian_checker ) // Little-endian (the compiler will optimize away the check at -O1 and above). + return __builtin_ctzll( val ) / 16; + + return __builtin_clzll( val ) / 16; +} + +#elif defined( _MSC_VER ) && ( defined( _M_X64 ) || defined( _M_ARM64 ) ) + +#include +#pragma intrinsic(_BitScanForward64) +#pragma intrinsic(_BitScanReverse64) + +static inline FORCEINLINE int vt_first_nonzero_uint16( uint64_t val ) +{ + unsigned long result; + + const uint16_t endian_checker = 0x0001; + if( *(const char *)&endian_checker ) + _BitScanForward64( &result, val ); + else + { + _BitScanReverse64( &result, val ); + result = 63 - result; + } + + return result / 16; +} + +#else + +static inline FORCEINLINE int vt_first_nonzero_uint16( uint64_t val ) +{ + int result = 0; + + uint32_t half; + memcpy( &half, &val, sizeof( uint32_t ) ); + if( !half ) + result += 2; + + uint16_t quarter; + memcpy( &quarter, (char *)&val + result * sizeof( uint16_t ), sizeof( uint16_t ) ); + if( !quarter ) + result += 1; + + return result; +} + +#endif + +// When the bucket count is zero, setting the metadata pointer to point to a VT_EMPTY placeholder, rather than NULL, +// allows us to avoid checking for a zero bucket count during insertion and lookup. +static const uint16_t vt_empty_placeholder_metadatum = VT_EMPTY; + +// Default hash and comparison functions. + +// Fast-hash, as described by https://jonkagstrom.com/bit-mixer-construction and +// https://code.google.com/archive/p/fast-hash. +// In testing, this hash function provided slightly better performance than the Murmur3 mixer. +static inline FORCEINLINE uint64_t vt_hash_integer( uint64_t key ) +{ + key ^= key >> 23; + key *= 0x2127599bf4325c37ull; + key ^= key >> 47; + return key; +} + +// For hashing strings, we use the public-domain Wyhash +// (https://github.com/wangyi-fudan/wyhash) with the following modifications: +// * We use a fixed seed and secret (the defaults suggested in the Wyhash repository). +// * We do not handle endianness, so the result will differ depending on the platform. +// * We omit the code optimized for 32-bit platforms. + +static inline FORCEINLINE void vt_wymum( uint64_t *a, uint64_t *b ) +{ +#if defined( __SIZEOF_INT128__ ) + __uint128_t r = *a; + r *= *b; + *a = (uint64_t)r; + *b = (uint64_t)( r >> 64 ); +#elif defined( _MSC_VER ) && defined( _M_X64 ) + *a = _umul128( *a, *b, b ); +#else + uint64_t ha = *a >> 32; + uint64_t hb = *b >> 32; + uint64_t la = (uint32_t)*a; + uint64_t lb = (uint32_t)*b; + uint64_t rh = ha * hb; + uint64_t rm0 = ha * lb; + uint64_t rm1 = hb * la; + uint64_t rl = la * lb; + uint64_t t = rl + ( rm0 << 32 ); + uint64_t c = t < rl; + uint64_t lo = t + ( rm1 << 32 ); + c += lo < t; + uint64_t hi = rh + ( rm0 >> 32 ) + ( rm1 >> 32 ) + c; + *a = lo; + *b = hi; +#endif +} + +static inline FORCEINLINE uint64_t vt_wymix( uint64_t a, uint64_t b ) +{ + vt_wymum( &a, &b ); + return a ^ b; +} + +static inline FORCEINLINE uint64_t vt_wyr8( const unsigned char *p ) +{ + uint64_t v; + memcpy( &v, p, 8 ); + return v; +} + +static inline FORCEINLINE uint64_t vt_wyr4( const unsigned char *p ) +{ + uint32_t v; + memcpy( &v, p, 4 ); + return v; +} + +static inline FORCEINLINE uint64_t vt_wyr3( const unsigned char *p, size_t k ) +{ + return ( ( (uint64_t)p[ 0 ] ) << 16 ) | ( ( (uint64_t)p[ k >> 1 ] ) << 8 ) | p[ k - 1 ]; +} + +static inline FORCEINLINE size_t vt_wyhash( const void *key, size_t len ) +{ + const unsigned char *p = (const unsigned char *)key; + uint64_t seed = 0xca813bf4c7abf0a9ull; + uint64_t a; + uint64_t b; + if( VT_LIKELY( len <= 16 ) ) + { + if( VT_LIKELY( len >= 4 ) ) + { + a = ( vt_wyr4( p ) << 32 ) | vt_wyr4( p + ( ( len >> 3 ) << 2 ) ); + b = ( vt_wyr4( p + len - 4 ) << 32 ) | vt_wyr4( p + len - 4 - ( ( len >> 3 ) << 2 ) ); + } + else if( VT_LIKELY( len > 0 ) ) + { + a = vt_wyr3( p, len ); + b = 0; + } + else + { + a = 0; + b = 0; + } + } + else + { + size_t i = len; + if( VT_UNLIKELY( i >= 48 ) ) + { + uint64_t see1 = seed; + uint64_t see2 = seed; + do{ + seed = vt_wymix( vt_wyr8( p ) ^ 0x8bb84b93962eacc9ull, vt_wyr8( p + 8 ) ^ seed ); + see1 = vt_wymix( vt_wyr8( p + 16 ) ^ 0x4b33a62ed433d4a3ull, vt_wyr8( p + 24 ) ^ see1 ); + see2 = vt_wymix( vt_wyr8( p + 32 ) ^ 0x4d5a2da51de1aa47ull, vt_wyr8( p + 40 ) ^ see2 ); + p += 48; + i -= 48; + } + while( VT_LIKELY( i >= 48 ) ); + seed ^= see1 ^ see2; + } + + while( VT_UNLIKELY( i > 16 ) ) + { + seed = vt_wymix( vt_wyr8( p ) ^ 0x8bb84b93962eacc9ull, vt_wyr8( p + 8 ) ^ seed ); + i -= 16; + p += 16; + } + + a = vt_wyr8( p + i - 16 ); + b = vt_wyr8( p + i - 8 ); + } + + a ^= 0x8bb84b93962eacc9ull; + b ^= seed; + vt_wymum( &a, &b ); + return (size_t)vt_wymix( a ^ 0x2d358dccaa6c78a5ull ^ len, b ^ 0x8bb84b93962eacc9ull ); +} + +static inline FORCEINLINE uint64_t vt_hash_string( const char *key ) +{ + return vt_wyhash( key, strlen( key ) ); +} + +static inline FORCEINLINE bool vt_cmpr_integer( uint64_t key_1, uint64_t key_2 ) +{ + return key_1 == key_2; +} + +static inline FORCEINLINE bool vt_cmpr_string( const char *key_1, const char *key_2 ) +{ + return strcmp( key_1, key_2 ) == 0; +} + +// Default allocation and free functions. + +static inline FORCEINLINE void *vt_malloc( size_t size ) +{ + return malloc( size ); +} + +static inline FORCEINLINE void vt_free( void *ptr, size_t size ) +{ + (void)size; + free( ptr ); +} + +static inline FORCEINLINE void *vt_malloc_with_ctx( size_t size, void *ctx ) +{ + (void)ctx; + return malloc( size ); +} + +static inline FORCEINLINE void vt_free_with_ctx( void *ptr, size_t size, void *ctx ) +{ + (void)size; + (void)ctx; + free( ptr ); +} + +// The rest of the common header section pertains to the C11 generic macro API. +// This interface is based on the extendible-_Generic mechanism documented in detail at +// https://github.com/JacksonAllan/CC/blob/main/articles/Better_C_Generics_Part_1_The_Extendible_Generic.md. +// In summary, instantiating a template also defines wrappers for the template's types and functions with names in the +// pattern of vt_table_NNNN and vt_init_NNNN, where NNNN is an automatically generated integer unique to the template +// instance in the current translation unit. +// These wrappers plug into _Generic-based API macros, which use preprocessor magic to automatically generate _Generic +// slots for every existing template instance. +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined( VT_NO_C11_GENERIC_API ) + +// Octal counter that supports up to 511 hash table templates. +#define VT_TEMPLATE_COUNT_D1 0 // Digit 1, i.e. least significant digit. +#define VT_TEMPLATE_COUNT_D2 0 +#define VT_TEMPLATE_COUNT_D3 0 + +// Four-way concatenation macro. +#define VT_CAT_4_( a, b, c, d ) a##b##c##d +#define VT_CAT_4( a, b, c, d ) VT_CAT_4_( a, b, c, d ) + +// Provides the current value of the counter as a three-digit octal number preceded by 0. +#define VT_TEMPLATE_COUNT VT_CAT_4( 0, VT_TEMPLATE_COUNT_D3, VT_TEMPLATE_COUNT_D2, VT_TEMPLATE_COUNT_D1 ) + +// _Generic-slot generation macros. + +#define VT_GENERIC_SLOT( ty, fn, n ) , VT_CAT( ty, n ): VT_CAT( fn, n ) +#define VT_R1_0( ty, fn, d3, d2 ) +#define VT_R1_1( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 0 ) ) +#define VT_R1_2( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 1 ) ) VT_R1_1( ty, fn, d3, d2 ) +#define VT_R1_3( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 2 ) ) VT_R1_2( ty, fn, d3, d2 ) +#define VT_R1_4( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 3 ) ) VT_R1_3( ty, fn, d3, d2 ) +#define VT_R1_5( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 4 ) ) VT_R1_4( ty, fn, d3, d2 ) +#define VT_R1_6( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 5 ) ) VT_R1_5( ty, fn, d3, d2 ) +#define VT_R1_7( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 6 ) ) VT_R1_6( ty, fn, d3, d2 ) +#define VT_R1_8( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 7 ) ) VT_R1_7( ty, fn, d3, d2 ) +#define VT_R2_0( ty, fn, d3 ) +#define VT_R2_1( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 0 ) +#define VT_R2_2( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 1 ) VT_R2_1( ty, fn, d3 ) +#define VT_R2_3( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 2 ) VT_R2_2( ty, fn, d3 ) +#define VT_R2_4( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 3 ) VT_R2_3( ty, fn, d3 ) +#define VT_R2_5( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 4 ) VT_R2_4( ty, fn, d3 ) +#define VT_R2_6( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 5 ) VT_R2_5( ty, fn, d3 ) +#define VT_R2_7( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 6 ) VT_R2_6( ty, fn, d3 ) +#define VT_R2_8( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 7 ) VT_R2_7( ty, fn, d3 ) +#define VT_R3_0( ty, fn ) +#define VT_R3_1( ty, fn ) VT_R2_8( ty, fn, 0 ) +#define VT_R3_2( ty, fn ) VT_R2_8( ty, fn, 1 ) VT_R3_1( ty, fn ) +#define VT_R3_3( ty, fn ) VT_R2_8( ty, fn, 2 ) VT_R3_2( ty, fn ) +#define VT_R3_4( ty, fn ) VT_R2_8( ty, fn, 3 ) VT_R3_3( ty, fn ) +#define VT_R3_5( ty, fn ) VT_R2_8( ty, fn, 4 ) VT_R3_4( ty, fn ) +#define VT_R3_6( ty, fn ) VT_R2_8( ty, fn, 5 ) VT_R3_5( ty, fn ) +#define VT_R3_7( ty, fn ) VT_R2_8( ty, fn, 6 ) VT_R3_6( ty, fn ) + +#define VT_GENERIC_SLOTS( ty, fn ) \ +VT_CAT( VT_R1_, VT_TEMPLATE_COUNT_D1 )( ty, fn, VT_TEMPLATE_COUNT_D3, VT_TEMPLATE_COUNT_D2 ) \ +VT_CAT( VT_R2_, VT_TEMPLATE_COUNT_D2 )( ty, fn, VT_TEMPLATE_COUNT_D3 ) \ +VT_CAT( VT_R3_, VT_TEMPLATE_COUNT_D3 )( ty, fn ) \ + +// Actual generic API macros. + +// vt_init must be handled as a special case because it could take one or two arguments, depending on whether CTX_TY +// was defined. +#define VT_ARG_3( _1, _2, _3, ... ) _3 +#define vt_init( ... ) VT_ARG_3( __VA_ARGS__, vt_init_with_ctx, vt_init_without_ctx, )( __VA_ARGS__ ) +#define vt_init_without_ctx( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_init_ ) )( table ) +#define vt_init_with_ctx( table, ... ) _Generic( *( table ) \ + VT_GENERIC_SLOTS( vt_table_, vt_init_ ) \ +)( table, __VA_ARGS__ ) \ + +#define vt_init_clone( table, ... ) _Generic( *( table ) \ + VT_GENERIC_SLOTS( vt_table_, vt_init_clone_ ) \ +)( table, __VA_ARGS__ ) \ + +#define vt_size( table )_Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_size_ ) )( table ) + +#define vt_bucket_count( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_bucket_count_ ) )( table ) + +#define vt_is_end( itr ) _Generic( itr VT_GENERIC_SLOTS( vt_table_itr_, vt_is_end_ ) )( itr ) + +#define vt_insert( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_insert_ ) )( table, __VA_ARGS__ ) + +#define vt_get_or_insert( table, ... ) _Generic( *( table ) \ + VT_GENERIC_SLOTS( vt_table_, vt_get_or_insert_ ) \ +)( table, __VA_ARGS__ ) \ + +#define vt_get( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_get_ ) )( table, __VA_ARGS__ ) + +#define vt_erase( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_erase_ ) )( table, __VA_ARGS__ ) + +#define vt_next( itr ) _Generic( itr VT_GENERIC_SLOTS( vt_table_itr_, vt_next_ ) )( itr ) + +#define vt_erase_itr( table, ... ) _Generic( *( table ) \ + VT_GENERIC_SLOTS( vt_table_, vt_erase_itr_ ) \ +)( table, __VA_ARGS__ ) \ + +#define vt_reserve( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_reserve_ ) )( table, __VA_ARGS__ ) + +#define vt_shrink( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_shrink_ ) )( table ) + +#define vt_first( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_first_ ) )( table ) + +#define vt_clear( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_clear_ ) )( table ) + +#define vt_cleanup( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_cleanup_ ) )( table ) + +#endif + +#endif + +/*--------------------------------------------------------------------------------------------------------------------*/ +/* Prefixed structs */ +/*--------------------------------------------------------------------------------------------------------------------*/ + +#ifndef IMPLEMENTATION_MODE + +typedef struct +{ + KEY_TY key; + #ifdef VAL_TY + VAL_TY val; + #endif +} VT_CAT( NAME, _bucket ); + +typedef struct +{ + VT_CAT( NAME, _bucket ) *data; + uint16_t *metadatum; + uint16_t *metadata_end; // Iterators carry an internal end pointer so that NAME_is_end does not need the table to be + // passed in as an argument. + // This also allows for the zero-bucket-count check to occur once in NAME_first, rather than + // repeatedly in NAME_is_end. + size_t home_bucket; // SIZE_MAX if home bucket is unknown. +} VT_CAT( NAME, _itr ); + +typedef struct +{ + size_t key_count; + size_t buckets_mask; // Rather than storing the bucket count directly, we store the bit mask used to reduce a hash + // code or displacement-derived bucket index to the buckets array, i.e. the bucket count minus + // one. + // Consequently, a zero bucket count (i.e. when .metadata points to the placeholder) constitutes + // a special case, represented by all bits unset (i.e. zero). + VT_CAT( NAME, _bucket ) *buckets; + uint16_t *metadata; // As described above, each metadatum consists of a 4-bit hash-code fragment (X), a 1-bit flag + // indicating whether the key in this bucket begins a chain associated with the bucket (Y), and + // an 11-bit value indicating the quadratic displacement of the next key in the chain (Z): + // XXXXYZZZZZZZZZZZ. + #ifdef CTX_TY + CTX_TY ctx; + #endif +} NAME; + +#endif + +/*--------------------------------------------------------------------------------------------------------------------*/ +/* Function prototypes */ +/*--------------------------------------------------------------------------------------------------------------------*/ + +#if defined( HEADER_MODE ) || defined( IMPLEMENTATION_MODE ) +#define VT_API_FN_QUALIFIERS +#else +#define VT_API_FN_QUALIFIERS static inline FORCEINLINE +#endif + +#ifndef IMPLEMENTATION_MODE + +VT_API_FN_QUALIFIERS void VT_CAT( NAME, _init )( + NAME * + #ifdef CTX_TY + , CTX_TY + #endif +); + +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _init_clone )( + NAME *, + const NAME * + #ifdef CTX_TY + , CTX_TY + #endif +); + +VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _size )( const NAME * ); + +VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _bucket_count )( const NAME * ); + +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _is_end )( VT_CAT( NAME, _itr ) ); + +VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert )( + NAME *, + KEY_TY + #ifdef VAL_TY + , VAL_TY + #endif +); + +VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get_or_insert )( + NAME *, + KEY_TY + #ifdef VAL_TY + , VAL_TY + #endif +); + +VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get )( + const NAME *table, +#ifdef KEY_GET_CONST + const KEY_TY key +#else + KEY_TY key +#endif +); + +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase )( NAME *, KEY_TY ); + +VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _next )( VT_CAT( NAME, _itr ) ); + +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _reserve )( NAME *, size_t ); + +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _shrink )( NAME * ); + +VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _first )( const NAME * ); + +VT_API_FN_QUALIFIERS void VT_CAT( NAME, _clear )( NAME * ); + +VT_API_FN_QUALIFIERS void VT_CAT( NAME, _cleanup )( NAME * ); + +// Not an API function, but must be prototyped anyway because it is called by the inline NAME_erase_itr below. +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase_itr_raw ) ( NAME *, VT_CAT( NAME, _itr ) ); + +// Erases the key pointed to by itr and returns an iterator to the next key in the table. +// This function must be inlined to ensure that the compiler optimizes away the NAME_fast_forward call if the returned +// iterator is discarded. +#ifdef __GNUC__ +static inline FORCEINLINE +#elif defined( _MSC_VER ) +static __forceinline +#else +static inline +#endif +VT_CAT( NAME, _itr ) VT_CAT( NAME, _erase_itr )( NAME *table, VT_CAT( NAME, _itr ) itr ) +{ + if( VT_CAT( NAME, _erase_itr_raw )( table, itr ) ) + return VT_CAT( NAME, _next )( itr ); + + return itr; +} + +#endif + +/*--------------------------------------------------------------------------------------------------------------------*/ +/* Function implementations */ +/*--------------------------------------------------------------------------------------------------------------------*/ + +#ifndef HEADER_MODE + +// Default settings. + +#ifndef MAX_LOAD +#define MAX_LOAD 0.9 +#endif + +#ifndef MALLOC_FN +#ifdef CTX_TY +#define MALLOC_FN vt_malloc_with_ctx +#else +#define MALLOC_FN vt_malloc +#endif +#endif + +#ifndef FREE_FN +#ifdef CTX_TY +#define FREE_FN vt_free_with_ctx +#else +#define FREE_FN vt_free +#endif +#endif + +#ifndef HASH_FN +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#ifdef _MSC_VER // In MSVC, the compound literal in the _Generic triggers a warning about unused local variables at /W4. +#define HASH_FN \ +_Pragma( "warning( push )" ) \ +_Pragma( "warning( disable: 4189 )" ) \ +_Generic( ( KEY_TY ){ 0 }, char *: vt_hash_string, const char*: vt_hash_string, default: vt_hash_integer ) \ +_Pragma( "warning( pop )" ) +#else +#define HASH_FN _Generic( ( KEY_TY ){ 0 }, \ + char *: vt_hash_string, \ + const char*: vt_hash_string, \ + default: vt_hash_integer \ +) +#endif +#else +#error Hash function inference is only available in C11 and later. In C99, you need to define HASH_FN manually to \ +vt_hash_integer, vt_hash_string, or your own custom function with the signature uint64_t ( KEY_TY ). +#endif +#endif + +#ifndef CMPR_FN +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#ifdef _MSC_VER +#define CMPR_FN \ +_Pragma( "warning( push )" ) \ +_Pragma( "warning( disable: 4189 )" ) \ +_Generic( ( KEY_TY ){ 0 }, char *: vt_cmpr_string, const char*: vt_cmpr_string, default: vt_cmpr_integer ) \ +_Pragma( "warning( pop )" ) +#else +#define CMPR_FN _Generic( ( KEY_TY ){ 0 }, \ + char *: vt_cmpr_string, \ + const char*: vt_cmpr_string, \ + default: vt_cmpr_integer \ +) +#endif +#else +#error Comparison function inference is only available in C11 and later. In C99, you need to define CMPR_FN manually \ +to vt_cmpr_integer, vt_cmpr_string, or your own custom function with the signature bool ( KEY_TY, KEY_TY ). +#endif +#endif + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + +VT_API_FN_QUALIFIERS void VT_CAT( NAME, _init )( + NAME *table + #ifdef CTX_TY + , CTX_TY ctx + #endif +) +{ + table->key_count = 0; + table->buckets_mask = 0x0000000000000000ull; + table->buckets = NULL; + table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum; + #ifdef CTX_TY + table->ctx = ctx; + #endif +} + +#pragma GCC diagnostic pop + +// For efficiency, especially in the case of a small table, the buckets array and metadata share the same dynamic memory +// allocation: +// +-----------------------------+-----+----------------+--------+ +// | Buckets | Pad | Metadata | Excess | +// +-----------------------------+-----+----------------+--------+ +// Any allocated metadata array requires four excess elements to ensure that iteration functions, which read four +// metadata at a time, never read beyond the end of it. +// This function returns the offset of the beginning of the metadata, i.e. the size of the buckets array plus the +// (usually zero) padding. +// It assumes that the bucket count is not zero. +static inline FORCEINLINE size_t VT_CAT( NAME, _metadata_offset )( NAME *table ) +{ + // Use sizeof, rather than alignof, for C99 compatibility. + return ( ( ( table->buckets_mask + 1 ) * sizeof( VT_CAT( NAME, _bucket ) ) + sizeof( uint16_t ) - 1 ) / + sizeof( uint16_t ) ) * sizeof( uint16_t ); +} + +// Returns the total allocation size, including the buckets array, padding, metadata, and excess metadata. +// As above, this function assumes that the bucket count is not zero. +static inline FORCEINLINE size_t VT_CAT( NAME, _total_alloc_size )( NAME *table ) +{ + return VT_CAT( NAME, _metadata_offset )( table ) + ( table->buckets_mask + 1 + 4 ) * sizeof( uint16_t ); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _init_clone )( + NAME *table, + const NAME *source + #ifdef CTX_TY + , CTX_TY ctx + #endif +) +{ + table->key_count = source->key_count; + table->buckets_mask = source->buckets_mask; + #ifdef CTX_TY + table->ctx = ctx; + #endif + + if( !source->buckets_mask ) + { + table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum; + table->buckets = NULL; + return true; + } + + void *allocation = MALLOC_FN( + VT_CAT( NAME, _total_alloc_size )( table ) + #ifdef CTX_TY + , &table->ctx + #endif + ); + + if( VT_UNLIKELY( !allocation ) ) + return false; + + table->buckets = (VT_CAT( NAME, _bucket ) *)allocation; + table->metadata = (uint16_t *)( (unsigned char *)allocation + VT_CAT( NAME, _metadata_offset )( table ) ); + memcpy( allocation, source->buckets, VT_CAT( NAME, _total_alloc_size )( table ) ); + + return true; +} + +#pragma GCC diagnostic pop + +VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _size )( const NAME *table ) +{ + return table->key_count; +} + +VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _bucket_count )( const NAME *table ) +{ + // If the bucket count is zero, buckets_mask will be zero, not the bucket count minus one. + // We account for this special case by adding (bool)buckets_mask rather than one. + return table->buckets_mask + (bool)table->buckets_mask; +} + +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _is_end )( VT_CAT( NAME, _itr ) itr ) +{ + return itr.metadatum == itr.metadata_end; +} + +// Finds the earliest empty bucket in which a key belonging to home_bucket can be placed, assuming that home_bucket +// is already occupied. +// The reason to begin the search at home_bucket, rather than the end of the existing chain, is that keys deleted from +// other chains might have freed up buckets that could fall in this chain before the final key. +// Returns true if an empty bucket within the range of the displacement limit was found, in which case the final two +// pointer arguments contain the index of the empty bucket and its quadratic displacement from home_bucket. +static inline FORCEINLINE bool VT_CAT( NAME, _find_first_empty )( + NAME *table, + size_t home_bucket, + size_t *empty, + uint16_t *displacement +) +{ + *displacement = 1; + size_t linear_dispacement = 1; + + while( true ) + { + *empty = ( home_bucket + linear_dispacement ) & table->buckets_mask; + if( table->metadata[ *empty ] == VT_EMPTY ) + return true; + + if( VT_UNLIKELY( ++*displacement == VT_DISPLACEMENT_MASK ) ) + return false; + + linear_dispacement += *displacement; + } +} + +// Finds the key in the chain beginning in home_bucket after which to link a new key with displacement_to_empty +// quadratic displacement and returns the index of the bucket containing that key. +// Although the new key could simply be linked to the end of the chain, keeping the chain ordered by displacement +// theoretically improves cache locality during lookups. +static inline FORCEINLINE size_t VT_CAT( NAME, _find_insert_location_in_chain )( + NAME *table, + size_t home_bucket, + uint16_t displacement_to_empty +) +{ + size_t candidate = home_bucket; + while( true ) + { + uint16_t displacement = table->metadata[ candidate ] & VT_DISPLACEMENT_MASK; + + if( displacement > displacement_to_empty ) + return candidate; + + candidate = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask; + } +} + +// Frees up a bucket occupied by a key not belonging there so that a new key belonging there can be placed there as the +// beginning of a new chain. +// This requires: +// * Finding the previous key in the chain to which the occupying key belongs by rehashing it and then traversing the +// chain. +// * Disconnecting the key from the chain. +// * Finding the appropriate empty bucket to which to move the key. +// * Moving the key (and value) data to the empty bucket. +// * Re-linking the key to the chain. +// Returns true if the eviction succeeded, or false if no empty bucket to which to evict the occupying key could be +// found within the displacement limit. +static inline FORCEINLINE bool VT_CAT( NAME, _evict )( NAME *table, size_t bucket ) +{ + // Find the previous key in chain. + size_t home_bucket = HASH_FN( table->buckets[ bucket ].key ) & table->buckets_mask; + size_t prev = home_bucket; + while( true ) + { + size_t next = ( home_bucket + vt_quadratic( table->metadata[ prev ] & VT_DISPLACEMENT_MASK ) ) & + table->buckets_mask; + + if( next == bucket ) + break; + + prev = next; + } + + // Disconnect the key from chain. + table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | ( table->metadata[ bucket ] & + VT_DISPLACEMENT_MASK ); + + // Find the empty bucket to which to move the key. + size_t empty; + uint16_t displacement; + if( VT_UNLIKELY( !VT_CAT( NAME, _find_first_empty )( table, home_bucket, &empty, &displacement ) ) ) + return false; + + // Find the key in the chain after which to link the moved key. + prev = VT_CAT( NAME, _find_insert_location_in_chain )( table, home_bucket, displacement ); + + // Move the key (and value) data. + table->buckets[ empty ] = table->buckets[ bucket ]; + + // Re-link the key to the chain from its new bucket. + table->metadata[ empty ] = ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) | ( table->metadata[ prev ] & + VT_DISPLACEMENT_MASK ); + table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | displacement; + + return true; +} + +// Returns an end iterator, i.e. any iterator for which .metadatum == .metadata_end. +// This function just cleans up the library code in functions that return an end iterator as a failure indicator. +static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( NAME, _end_itr )( void ) +{ + VT_CAT( NAME, _itr ) itr = { NULL, NULL, NULL, 0 }; + return itr; +} + +// Inserts a key, optionally replacing the existing key if it already exists. +// There are two main cases that must be handled: +// * If the key's home bucket is empty or occupied by a key that does not belong there, then the key is inserted there, +// evicting the occupying key if there is one. +// * Otherwise, the chain of keys beginning at the home bucket is (if unique is false) traversed in search of a matching +// key. +// If none is found, then the new key is inserted at the earliest available bucket, per quadratic probing from the +// home bucket, and then linked to the chain in a manner that maintains its quadratic order. +// The unique argument tells the function whether to skip searching for the key before inserting it (on rehashing, this +// step is unnecessary). +// The replace argument tells the function whether to replace an existing key. +// If replace is true, the function returns an iterator to the inserted key, or an end iterator if the key was not +// inserted because of the maximum load factor or displacement limit constraints. +// If replace is false, then the return value is as described above, except that if the key already exists, the function +// returns an iterator to the existing key. +static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert_raw )( + NAME *table, + KEY_TY key, + #ifdef VAL_TY + VAL_TY *val, + #endif + bool unique, + bool replace +) +{ + uint64_t hash = HASH_FN( key ); + uint16_t hashfrag = vt_hashfrag( hash ); + size_t home_bucket = hash & table->buckets_mask; + + // Case 1: The home bucket is empty or contains a key that doesn't belong there. + // This case also implicitly handles the case of a zero bucket count, since home_bucket will be zero and metadata[ 0 ] + // will be the empty placeholder. + // In that scenario, the zero buckets_mask triggers the below load-factor check. + if( !( table->metadata[ home_bucket ] & VT_IN_HOME_BUCKET_MASK ) ) + { + if( + // Load-factor check. + VT_UNLIKELY( table->key_count + 1 > VT_CAT( NAME, _bucket_count )( table ) * MAX_LOAD ) || + // Vacate the home bucket if it contains a key. + ( table->metadata[ home_bucket ] != VT_EMPTY && VT_UNLIKELY( !VT_CAT( NAME, _evict )( table, home_bucket ) ) ) + ) + return VT_CAT( NAME, _end_itr )(); + + table->buckets[ home_bucket ].key = key; + #ifdef VAL_TY + table->buckets[ home_bucket ].val = *val; + #endif + table->metadata[ home_bucket ] = hashfrag | VT_IN_HOME_BUCKET_MASK | VT_DISPLACEMENT_MASK; + + ++table->key_count; + + VT_CAT( NAME, _itr ) itr = { + table->buckets + home_bucket, + table->metadata + home_bucket, + table->metadata + table->buckets_mask + 1, // Iteration stopper (i.e. the first of the four excess metadata). + home_bucket + }; + return itr; + } + + // Case 2: The home bucket contains the beginning of a chain. + + // Optionally, check the existing chain. + if( !unique ) + { + size_t bucket = home_bucket; + while( true ) + { + if( + ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) == hashfrag && + VT_LIKELY( CMPR_FN( table->buckets[ bucket ].key, key ) ) + ) + { + if( replace ) + { + #ifdef KEY_DTOR_FN + KEY_DTOR_FN( table->buckets[ bucket ].key ); + #endif + table->buckets[ bucket ].key = key; + + #ifdef VAL_TY + #ifdef VAL_DTOR_FN + VAL_DTOR_FN( table->buckets[ bucket ].val ); + #endif + table->buckets[ bucket ].val = *val; + #endif + } + + VT_CAT( NAME, _itr ) itr = { + table->buckets + bucket, + table->metadata + bucket, + table->metadata + table->buckets_mask + 1, + home_bucket + }; + return itr; + } + + uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK; + if( displacement == VT_DISPLACEMENT_MASK ) + break; + + bucket = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask; + } + } + + size_t empty; + uint16_t displacement; + if( + VT_UNLIKELY( + // Load-factor check. + table->key_count + 1 > VT_CAT( NAME, _bucket_count )( table ) * MAX_LOAD || + // Find the earliest empty bucket, per quadratic probing. + !VT_CAT( NAME, _find_first_empty )( table, home_bucket, &empty, &displacement ) + ) + ) + return VT_CAT( NAME, _end_itr )(); + + // Insert the new key (and value) in the empty bucket and link it to the chain. + + size_t prev = VT_CAT( NAME, _find_insert_location_in_chain )( table, home_bucket, displacement ); + + table->buckets[ empty ].key = key; + #ifdef VAL_TY + table->buckets[ empty ].val = *val; + #endif + table->metadata[ empty ] = hashfrag | ( table->metadata[ prev ] & VT_DISPLACEMENT_MASK ); + table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | displacement; + + ++table->key_count; + + VT_CAT( NAME, _itr ) itr = { + table->buckets + empty, + table->metadata + empty, + table->metadata + table->buckets_mask + 1, + home_bucket + }; + return itr; +} + +// Resizes the bucket array. +// This function assumes that bucket_count is a power of two and large enough to accommodate all keys without violating +// the maximum load factor. +// Returns false in the case of allocation failure. +// As this function is called very rarely in _insert and _get_or_insert, ideally it should not be inlined into those +// functions. +// In testing, the no-inline approach showed a performance benefit when inserting existing keys (i.e. replacing). +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" // Silence warning about combining noinline with static inline FORCEINLINE. +__attribute__((noinline)) static inline +#elif defined( _MSC_VER ) +__declspec(noinline) static inline +#else +static inline +#endif +bool VT_CAT( NAME, _rehash )( NAME *table, size_t bucket_count ) +{ + // The attempt to resize the bucket array and rehash the keys must occur inside a loop that incrementally doubles the + // target bucket count because a failure could theoretically occur at any load factor due to the displacement limit. + while( true ) + { + NAME new_table = { + 0, + bucket_count - 1, + NULL, + NULL + #ifdef CTX_TY + , table->ctx + #endif + }; + + void *allocation = MALLOC_FN( + VT_CAT( NAME, _total_alloc_size )( &new_table ) + #ifdef CTX_TY + , &new_table.ctx + #endif + ); + + if( VT_UNLIKELY( !allocation ) ) + return false; + + new_table.buckets = (VT_CAT( NAME, _bucket ) *)allocation; + new_table.metadata = (uint16_t *)( (unsigned char *)allocation + VT_CAT( NAME, _metadata_offset )( &new_table ) ); + + memset( new_table.metadata, 0x00, ( bucket_count + 4 ) * sizeof( uint16_t ) ); + + // Iteration stopper at the end of the actual metadata array (i.e. the first of the four excess metadata). + new_table.metadata[ bucket_count ] = 0x01; + + for( size_t bucket = 0; bucket < VT_CAT( NAME, _bucket_count )( table ); ++bucket ) + if( table->metadata[ bucket ] != VT_EMPTY ) + { + VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )( + &new_table, + table->buckets[ bucket ].key, + #ifdef VAL_TY + &table->buckets[ bucket ].val, + #endif + true, + false + ); + + if( VT_UNLIKELY( VT_CAT( NAME, _is_end )( itr ) ) ) + break; + } + + // If a key could not be reinserted due to the displacement limit, double the bucket count and retry. + if( VT_UNLIKELY( new_table.key_count < table->key_count ) ) + { + FREE_FN( + new_table.buckets, + VT_CAT( NAME, _total_alloc_size )( &new_table ) + #ifdef CTX_TY + , &new_table.ctx + #endif + ); + + bucket_count *= 2; + continue; + } + + if( table->buckets_mask ) + FREE_FN( + table->buckets, + VT_CAT( NAME, _total_alloc_size )( table ) + #ifdef CTX_TY + , &table->ctx + #endif + ); + + *table = new_table; + return true; + } +} +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +// Inserts a key, replacing the existing key if it already exists. +// This function wraps insert_raw in a loop that handles growing and rehashing the table if a new key cannot be inserted +// because of the maximum load factor or displacement limit constraints. +// Returns an iterator to the inserted key, or an end iterator in the case of allocation failure. +VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert )( + NAME *table, + KEY_TY key + #ifdef VAL_TY + , VAL_TY val + #endif +) +{ + while( true ) + { + VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )( + table, + key, + #ifdef VAL_TY + &val, + #endif + false, + true + ); + + if( + // Lookup succeeded, in which case itr points to the found key. + VT_LIKELY( !VT_CAT( NAME, _is_end )( itr ) ) || + // Lookup failed and rehash also fails, in which case itr is an end iterator. + VT_UNLIKELY( + !VT_CAT( NAME, _rehash )( + table, table->buckets_mask ? VT_CAT( NAME, _bucket_count )( table ) * 2 : VT_MIN_NONZERO_BUCKET_COUNT + ) + ) + ) + return itr; + } +} + +// Same as NAME_insert, except that if the key already exists, no insertion occurs and the function returns an iterator +// to the existing key. +VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get_or_insert )( + NAME *table, + KEY_TY key + #ifdef VAL_TY + , VAL_TY val + #endif +) +{ + while( true ) + { + VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )( + table, + key, + #ifdef VAL_TY + &val, + #endif + false, + false + ); + + if( + // Lookup succeeded, in which case itr points to the found key. + VT_LIKELY( !VT_CAT( NAME, _is_end )( itr ) ) || + // Lookup failed and rehash also fails, in which case itr is an end iterator. + VT_UNLIKELY( + !VT_CAT( NAME, _rehash )( + table, table->buckets_mask ? VT_CAT( NAME, _bucket_count )( table ) * 2 : VT_MIN_NONZERO_BUCKET_COUNT + ) + ) + ) + return itr; + } +} + +// Returns an iterator pointing to the specified key, or an end iterator if the key does not exist. +VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get )( const NAME *table, +#ifdef KEY_GET_CONST + const KEY_TY key +#else + KEY_TY key +#endif + ) +{ + uint64_t hash = HASH_FN( key ); + size_t home_bucket = hash & table->buckets_mask; + + // If the home bucket is empty or contains a key that does not belong there, then our key does not exist. + // This check also implicitly handles the case of a zero bucket count, since home_bucket will be zero and + // metadata[ 0 ] will be the empty placeholder. + if( !( table->metadata[ home_bucket ] & VT_IN_HOME_BUCKET_MASK ) ) + return VT_CAT( NAME, _end_itr )(); + + // Traverse the chain of keys belonging to the home bucket. + uint16_t hashfrag = vt_hashfrag( hash ); + size_t bucket = home_bucket; + while( true ) + { + if( + ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) == hashfrag && + VT_LIKELY( CMPR_FN( table->buckets[ bucket ].key, key ) ) + ) + { + VT_CAT( NAME, _itr ) itr = { + table->buckets + bucket, + table->metadata + bucket, + table->metadata + table->buckets_mask + 1, + home_bucket + }; + return itr; + } + + uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK; + if( displacement == VT_DISPLACEMENT_MASK ) + return VT_CAT( NAME, _end_itr )(); + + bucket = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask; + } +} + +// Erases the key pointed to by the specified iterator. +// The erasure always occurs at the end of the chain to which the key belongs. +// If the key to be erased is not the last in the chain, it is swapped with the last so that erasure occurs at the end. +// This helps keep a chain's keys close to their home bucket for the sake of cache locality. +// Returns true if, in the case of iteration from first to end, NAME_next should now be called on the iterator to find +// the next key. +// This return value is necessary because at the iterator location, the erasure could result in an empty bucket, a +// bucket containing a moved key already visited during the iteration, or a bucket containing a moved key not yet +// visited. +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase_itr_raw )( NAME *table, VT_CAT( NAME, _itr ) itr ) +{ + --table->key_count; + size_t itr_bucket = itr.metadatum - table->metadata; + + // For now, we only call the value's destructor because the key may need to be hashed below to determine the home + // bucket. + #ifdef VAL_DTOR_FN + VAL_DTOR_FN( table->buckets[ itr_bucket ].val ); + #endif + + // Case 1: The key is the only one in its chain, so just remove it. + if( + table->metadata[ itr_bucket ] & VT_IN_HOME_BUCKET_MASK && + ( table->metadata[ itr_bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK + ) + { + #ifdef KEY_DTOR_FN + KEY_DTOR_FN( table->buckets[ itr_bucket ].key ); + #endif + table->metadata[ itr_bucket ] = VT_EMPTY; + return true; + } + + // Case 2 and 3 require that we know the key's home bucket, which the iterator may not have recorded. + if( itr.home_bucket == SIZE_MAX ) + { + if( table->metadata[ itr_bucket ] & VT_IN_HOME_BUCKET_MASK ) + itr.home_bucket = itr_bucket; + else + itr.home_bucket = HASH_FN( table->buckets[ itr_bucket ].key ) & table->buckets_mask; + } + + // The key can now be safely destructed for cases 2 and 3. + #ifdef KEY_DTOR_FN + KEY_DTOR_FN( table->buckets[ itr_bucket ].key ); + #endif + + // Case 2: The key is the last in a multi-key chain. + // Traverse the chain from the beginning and find the penultimate key. + // Then disconnect the key and erase. + if( ( table->metadata[ itr_bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK ) + { + size_t bucket = itr.home_bucket; + while( true ) + { + uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK; + size_t next = ( itr.home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask; + if( next == itr_bucket ) + { + table->metadata[ bucket ] |= VT_DISPLACEMENT_MASK; + table->metadata[ itr_bucket ] = VT_EMPTY; + return true; + } + + bucket = next; + } + } + + // Case 3: The chain has multiple keys, and the key is not the last one. + // Traverse the chain from the key to be erased and find the last and penultimate keys. + // Disconnect the last key from the chain, and swap it with the key to erase. + size_t bucket = itr_bucket; + while( true ) + { + size_t prev = bucket; + bucket = ( itr.home_bucket + vt_quadratic( table->metadata[ bucket ] & VT_DISPLACEMENT_MASK ) ) & + table->buckets_mask; + + if( ( table->metadata[ bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK ) + { + table->buckets[ itr_bucket ] = table->buckets[ bucket ]; + + table->metadata[ itr_bucket ] = ( table->metadata[ itr_bucket ] & ~VT_HASH_FRAG_MASK ) | ( + table->metadata[ bucket ] & VT_HASH_FRAG_MASK ); + + table->metadata[ prev ] |= VT_DISPLACEMENT_MASK; + table->metadata[ bucket ] = VT_EMPTY; + + // Whether the iterator should be advanced depends on whether the key moved to the iterator bucket came from + // before or after that bucket. + // In the former case, the iteration would already have hit the moved key, so the iterator should still be + // advanced. + if( bucket > itr_bucket ) + return false; + + return true; + } + } +} + +// Erases the specified key, if it exists. +// Returns true if a key was erased. +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase )( NAME *table, KEY_TY key ) +{ + VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _get)( table, key ); + if( VT_CAT( NAME, _is_end )( itr ) ) + return false; + + VT_CAT( NAME, _erase_itr_raw )( table, itr ); + return true; +} + +// Finds the first occupied bucket at or after the bucket pointed to by itr. +// This function scans four buckets at a time, ideally using intrinsics. +static inline FORCEINLINE void VT_CAT( NAME, _fast_forward )( VT_CAT( NAME, _itr ) *itr ) +{ + while( true ) + { + uint64_t metadata; + memcpy( &metadata, itr->metadatum, sizeof( uint64_t ) ); + if( metadata ) + { + int offset = vt_first_nonzero_uint16( metadata ); + itr->data += offset; + itr->metadatum += offset; + itr->home_bucket = SIZE_MAX; + return; + } + + itr->data += 4; + itr->metadatum += 4; + } +} + +VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _next )( VT_CAT( NAME, _itr ) itr ) +{ + ++itr.data; + ++itr.metadatum; + VT_CAT( NAME, _fast_forward )( &itr ); + return itr; +} + +// Returns the minimum bucket count required to accommodate a certain number of keys, which is governed by the maximum +// load factor. +static inline FORCEINLINE size_t VT_CAT( NAME, _min_bucket_count_for_size )( size_t size ) +{ + if( size == 0 ) + return 0; + + // Round up to a power of two. + size_t bucket_count = VT_MIN_NONZERO_BUCKET_COUNT; + while( size > bucket_count * MAX_LOAD ) + bucket_count *= 2; + + return bucket_count; +} + +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _reserve )( NAME *table, size_t size ) +{ + size_t bucket_count = VT_CAT( NAME, _min_bucket_count_for_size )( size ); + + if( bucket_count <= VT_CAT( NAME, _bucket_count )( table ) ) + return true; + + return VT_CAT( NAME, _rehash )( table, bucket_count ); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + +VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _shrink )( NAME *table ) +{ + size_t bucket_count = VT_CAT( NAME, _min_bucket_count_for_size )( table->key_count ); + + if( bucket_count == VT_CAT( NAME, _bucket_count )( table ) ) // Shrink unnecessary. + return true; + + if( bucket_count == 0 ) + { + FREE_FN( + table->buckets, + VT_CAT( NAME, _total_alloc_size )( table ) + #ifdef CTX_TY + , &table->ctx + #endif + ); + + table->buckets_mask = 0x0000000000000000ull; + table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum; + return true; + } + + return VT_CAT( NAME, _rehash )( table, bucket_count ); +} + +#pragma GCC diagnostic pop + +VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _first )( const NAME *table ) +{ + if( !table->key_count ) + return VT_CAT( NAME, _end_itr )(); + + VT_CAT( NAME, _itr ) itr = { table->buckets, table->metadata, table->metadata + table->buckets_mask + 1, SIZE_MAX }; + VT_CAT( NAME, _fast_forward )( &itr ); + return itr; +} + +VT_API_FN_QUALIFIERS void VT_CAT( NAME, _clear )( NAME *table ) +{ + if( !table->key_count ) + return; + + for( size_t i = 0; i < VT_CAT( NAME, _bucket_count )( table ); ++i ) + { + if( table->metadata[ i ] != VT_EMPTY ) + { + #ifdef KEY_DTOR_FN + KEY_DTOR_FN( table->buckets[ i ].key ); + #endif + #ifdef VAL_DTOR_FN + VAL_DTOR_FN( table->buckets[ i ].val ); + #endif + } + + table->metadata[ i ] = VT_EMPTY; + } + + table->key_count = 0; +} + +VT_API_FN_QUALIFIERS void VT_CAT( NAME, _cleanup )( NAME *table ) +{ + if( !table->buckets_mask ) + return; + + #if defined( KEY_DTOR_FN ) || defined( VAL_DTOR_FN ) + VT_CAT( NAME, _clear )( table ); + #endif + + FREE_FN( + table->buckets, + VT_CAT( NAME, _total_alloc_size )( table ) + #ifdef CTX_TY + , &table->ctx + #endif + ); + + VT_CAT( NAME, _init )( + table + #ifdef CTX_TY + , table->ctx + #endif + ); +} + +#endif + +/*--------------------------------------------------------------------------------------------------------------------*/ +/* Wrapper types and functions for the C11 generic API */ +/*--------------------------------------------------------------------------------------------------------------------*/ + +#if defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 201112L && \ + !defined( IMPLEMENTATION_MODE ) && \ + !defined( VT_NO_C11_GENERIC_API ) \ + +typedef NAME VT_CAT( vt_table_, VT_TEMPLATE_COUNT ); +typedef VT_CAT( NAME, _itr ) VT_CAT( vt_table_itr_, VT_TEMPLATE_COUNT ); + +static inline FORCEINLINE void VT_CAT( vt_init_, VT_TEMPLATE_COUNT )( + NAME *table + #ifdef CTX_TY + , CTX_TY ctx + #endif +) +{ + VT_CAT( NAME, _init )( + table + #ifdef CTX_TY + , ctx + #endif + ); +} + +static inline FORCEINLINE bool VT_CAT( vt_init_clone_, VT_TEMPLATE_COUNT )( + NAME *table, + const NAME* source + #ifdef CTX_TY + , CTX_TY ctx + #endif +) +{ + return VT_CAT( NAME, _init_clone )( + table, + source + #ifdef CTX_TY + , ctx + #endif + ); +} + +static inline FORCEINLINE size_t VT_CAT( vt_size_, VT_TEMPLATE_COUNT )( const NAME *table ) +{ + return VT_CAT( NAME, _size )( table ); +} + +static inline FORCEINLINE size_t VT_CAT( vt_bucket_count_, VT_TEMPLATE_COUNT )( const NAME *table ) +{ + return VT_CAT( NAME, _bucket_count )( table ); +} + +static inline FORCEINLINE bool VT_CAT( vt_is_end_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr ) +{ + return VT_CAT( NAME, _is_end )( itr ); +} + +static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_insert_, VT_TEMPLATE_COUNT )( + NAME *table, + KEY_TY key + #ifdef VAL_TY + , VAL_TY val + #endif +) +{ + return VT_CAT( NAME, _insert )( + table, + key + #ifdef VAL_TY + , val + #endif + ); +} + +static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_get_or_insert_, VT_TEMPLATE_COUNT )( + NAME *table, + KEY_TY key + #ifdef VAL_TY + , VAL_TY val + #endif +) +{ + return VT_CAT( NAME, _get_or_insert )( + table, + key + #ifdef VAL_TY + , val + #endif + ); +} + +static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_get_, VT_TEMPLATE_COUNT )( const NAME *table, KEY_TY key ) +{ + return VT_CAT( NAME, _get )( table, key ); +} + +static inline FORCEINLINE bool VT_CAT( vt_erase_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key ) +{ + return VT_CAT( NAME, _erase )( table, key ); +} + +static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_next_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr ) +{ + return VT_CAT( NAME, _next )( itr ); +} + +static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_erase_itr_, VT_TEMPLATE_COUNT )( NAME *table, VT_CAT( NAME, _itr ) itr ) +{ + return VT_CAT( NAME, _erase_itr )( table, itr ); +} + +static inline FORCEINLINE bool VT_CAT( vt_reserve_, VT_TEMPLATE_COUNT )( NAME *table, size_t bucket_count ) +{ + return VT_CAT( NAME, _reserve )( table, bucket_count ); +} + +static inline FORCEINLINE bool VT_CAT( vt_shrink_, VT_TEMPLATE_COUNT )( NAME *table ) +{ + return VT_CAT( NAME, _shrink )( table ); +} + +static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_first_, VT_TEMPLATE_COUNT )( const NAME *table ) +{ + return VT_CAT( NAME, _first )( table ); +} + +static inline FORCEINLINE void VT_CAT( vt_clear_, VT_TEMPLATE_COUNT )( NAME *table ) +{ + VT_CAT( NAME, _clear )( table ); +} + +static inline FORCEINLINE void VT_CAT( vt_cleanup_, VT_TEMPLATE_COUNT )( NAME *table ) +{ + VT_CAT( NAME, _cleanup )( table ); +} + +// Increment the template counter. +#if VT_TEMPLATE_COUNT_D1 == 0 +#undef VT_TEMPLATE_COUNT_D1 +#define VT_TEMPLATE_COUNT_D1 1 +#elif VT_TEMPLATE_COUNT_D1 == 1 +#undef VT_TEMPLATE_COUNT_D1 +#define VT_TEMPLATE_COUNT_D1 2 +#elif VT_TEMPLATE_COUNT_D1 == 2 +#undef VT_TEMPLATE_COUNT_D1 +#define VT_TEMPLATE_COUNT_D1 3 +#elif VT_TEMPLATE_COUNT_D1 == 3 +#undef VT_TEMPLATE_COUNT_D1 +#define VT_TEMPLATE_COUNT_D1 4 +#elif VT_TEMPLATE_COUNT_D1 == 4 +#undef VT_TEMPLATE_COUNT_D1 +#define VT_TEMPLATE_COUNT_D1 5 +#elif VT_TEMPLATE_COUNT_D1 == 5 +#undef VT_TEMPLATE_COUNT_D1 +#define VT_TEMPLATE_COUNT_D1 6 +#elif VT_TEMPLATE_COUNT_D1 == 6 +#undef VT_TEMPLATE_COUNT_D1 +#define VT_TEMPLATE_COUNT_D1 7 +#elif VT_TEMPLATE_COUNT_D1 == 7 +#undef VT_TEMPLATE_COUNT_D1 +#define VT_TEMPLATE_COUNT_D1 0 +#if VT_TEMPLATE_COUNT_D2 == 0 +#undef VT_TEMPLATE_COUNT_D2 +#define VT_TEMPLATE_COUNT_D2 1 +#elif VT_TEMPLATE_COUNT_D2 == 1 +#undef VT_TEMPLATE_COUNT_D2 +#define VT_TEMPLATE_COUNT_D2 2 +#elif VT_TEMPLATE_COUNT_D2 == 2 +#undef VT_TEMPLATE_COUNT_D2 +#define VT_TEMPLATE_COUNT_D2 3 +#elif VT_TEMPLATE_COUNT_D2 == 3 +#undef VT_TEMPLATE_COUNT_D2 +#define VT_TEMPLATE_COUNT_D2 4 +#elif VT_TEMPLATE_COUNT_D2 == 4 +#undef VT_TEMPLATE_COUNT_D2 +#define VT_TEMPLATE_COUNT_D2 5 +#elif VT_TEMPLATE_COUNT_D2 == 5 +#undef VT_TEMPLATE_COUNT_D2 +#define VT_TEMPLATE_COUNT_D2 6 +#elif VT_TEMPLATE_COUNT_D2 == 6 +#undef VT_TEMPLATE_COUNT_D2 +#define VT_TEMPLATE_COUNT_D2 7 +#elif VT_TEMPLATE_COUNT_D2 == 7 +#undef VT_TEMPLATE_COUNT_D2 +#define VT_TEMPLATE_COUNT_D2 0 +#if VT_TEMPLATE_COUNT_D3 == 0 +#undef VT_TEMPLATE_COUNT_D3 +#define VT_TEMPLATE_COUNT_D3 1 +#elif VT_TEMPLATE_COUNT_D3 == 1 +#undef VT_TEMPLATE_COUNT_D3 +#define VT_TEMPLATE_COUNT_D3 2 +#elif VT_TEMPLATE_COUNT_D3 == 2 +#undef VT_TEMPLATE_COUNT_D3 +#define VT_TEMPLATE_COUNT_D3 3 +#elif VT_TEMPLATE_COUNT_D3 == 3 +#undef VT_TEMPLATE_COUNT_D3 +#define VT_TEMPLATE_COUNT_D3 4 +#elif VT_TEMPLATE_COUNT_D3 == 4 +#undef VT_TEMPLATE_COUNT_D3 +#define VT_TEMPLATE_COUNT_D3 5 +#elif VT_TEMPLATE_COUNT_D3 == 5 +#undef VT_TEMPLATE_COUNT_D3 +#define VT_TEMPLATE_COUNT_D3 6 +#elif VT_TEMPLATE_COUNT_D3 == 6 +#undef VT_TEMPLATE_COUNT_D3 +#define VT_TEMPLATE_COUNT_D3 7 +#elif VT_TEMPLATE_COUNT_D3 == 7 +#error Sorry, the number of template instances is limited to 511. Define VT_NO_C11_GENERIC_API globally and use the \ +C99 prefixed function API to circumvent this restriction. +#endif +#endif +#endif + +#pragma GCC diagnostic pop + +#endif + +#undef NAME +#undef KEY_TY +#undef KEY_GET_CONST +#undef VAL_TY +#undef HASH_FN +#undef CMPR_FN +#undef MAX_LOAD +#undef KEY_DTOR_FN +#undef VAL_DTOR_FN +#undef CTX_TY +#undef MALLOC_FN +#undef FREE_FN +#undef HEADER_MODE +#undef IMPLEMENTATION_MODE +#undef VT_API_FN_QUALIFIERS + +#undef FORCEINLINE diff --git a/src/w_wad.cpp b/src/w_wad.c similarity index 90% rename from src/w_wad.cpp rename to src/w_wad.c index 9e2475095..cfc25bbaa 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.c @@ -90,34 +90,29 @@ typedef struct #define LUMPNUMCACHESIZE 2048 // should be reasonable ig #define LUMPNUMCACHENAME 32 -#include -#include -#include +#define NAME lumpnum_map +#define KEY_TY char * +#define KEY_GET_CONST +#define VAL_TY lumpnum_t +#define HASH_FN vt_hash_string +#define CMPR_FN vt_cmpr_string +#define KEY_DTOR_FN free +#include "verstable.h" -struct LumpnumNameHash -{ - std::size_t operator()(const std::string& str) const - { - // hopefully this is fast enough but should be - return static_cast(FNV1a_Hash(str.c_str(), str.length())); - } -}; +#define NAME folder_map +#define KEY_TY char * +#define KEY_GET_CONST +#define VAL_TY UINT16 +#define HASH_FN vt_hash_string +#define CMPR_FN vt_cmpr_string +#define KEY_DTOR_FN free +#include "verstable.h" -struct LumpnumStringEquals -{ - bool operator()(const std::string& str1, const std::string& str2) const - { - //return fastcmp(str1.c_str(), str2.c_str()); - return str1 == str2; - } -}; +static lumpnum_map lumpnumcache; -static std::unordered_map lumpnumcache(LUMPNUMCACHESIZE); - -// gotta wrap this in a struct due to external C linkage -struct FolderCache { - std::unordered_map map; -}; +typedef struct FolderCache { + folder_map map; +} FolderCache; static const char pat_central[] = {0x50, 0x4b, 0x01, 0x02, 0x00}; static const char pat_end[] = {0x50, 0x4b, 0x05, 0x06, 0x00}; @@ -135,6 +130,8 @@ wadfile_t *wadfiles[MAX_WADFILES] = {}; // 0 to numwadfiles-1 are valid // being ejected void W_Shutdown(void) { + lumpnum_map_cleanup(&lumpnumcache); + while (numwadfiles--) { wadfile_t *wad = wadfiles[numwadfiles]; @@ -150,14 +147,24 @@ void W_Shutdown(void) Z_Free(wad->lumpinfo[wad->numlumps].fullname); } - delete wad->startfolders; - delete wad->endfolders; + folder_map_cleanup(&wad->startfolders->map); + free(wad->startfolders); + folder_map_cleanup(&wad->endfolders->map); + free(wad->endfolders); Z_Free(wad->lumpinfo); Z_Free(wad); } } +// the lumpnum cache needs to be initialized before use +// call this as early as possible! +void W_Startup(void) +{ + lumpnum_map_init(&lumpnumcache); + lumpnum_map_reserve(&lumpnumcache, LUMPNUMCACHESIZE); +} + //=========================================================================== // LUMP BASED ROUTINES //=========================================================================== @@ -278,7 +285,7 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum) lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[posStart]; size_t length = len + 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name - char *name = static_cast(malloc(length + 1)); + char *name = (char *)(malloc(length + 1)); if (!name) I_Error("W_LoadDehackedLumpsPK3: Out of memory!\n"); @@ -314,7 +321,7 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum) if (memcmp(lump_p->name, "SOC_", 4) == 0) // Check for generic SOC lump { // shameless copy+paste of code from LUA_LoadLump size_t length = len + 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name - char *name = static_cast(malloc(length + 1)); + char *name = (char *)(malloc(length + 1)); if (!name) I_Error("W_LoadDehackedLumps: Out of memory!\n"); @@ -381,7 +388,7 @@ static inline INT32 W_MakeFileMD5(const char *filename, void *resblock) // Invalidates the cache of lump numbers. Call this whenever a wad is added. FUNCINLINE static ATTRINLINE void W_InvalidateLumpnumCache(void) { - lumpnumcache.clear(); + lumpnum_map_clear(&lumpnumcache); } FUNCINLINE static ATTRINLINE UINT32 W_HashLumpName(const char *name) @@ -410,24 +417,24 @@ static restype_t ResourceFileDetect(const char* filename) */ static lumpinfo_t* ResGetLumpsStandalone(FILE* handle, UINT16* numlumps, const char* lumpname) { - lumpinfo_t* lumpinfo = static_cast(Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL)); + lumpinfo_t* lumpinfo = (lumpinfo_t *)(Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL)); lumpinfo->position = 0; fseek(handle, 0, SEEK_END); - lumpinfo->size = static_cast(ftell(handle)); + lumpinfo->size = (size_t)(ftell(handle)); fseek(handle, 0, SEEK_SET); strlcpy(lumpinfo->name, lumpname, sizeof(lumpinfo->name)); lumpinfo->namelength = strlen(lumpinfo->name); lumpinfo->hash.name = W_HashLumpName(lumpname); // Allocate the lump's long name. - /*lumpinfo->longname = static_cast(Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL)); + /*lumpinfo->longname = (char *)(Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL)); strcpy(lumpinfo->longname, lumpname); lumpinfo->longname[lumpinfo->namelength] = '\0'; lumpinfo->longnamelength = lumpinfo->namelength; lumpinfo->hash.longname = lumpinfo->hash.name;*/ // Allocate the lump's full name. - lumpinfo->fullname = static_cast(Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL)); + lumpinfo->fullname = (char *)(Z_Malloc((lumpinfo->namelength + 1) * sizeof(char), PU_STATIC, NULL)); strcpy(lumpinfo->fullname, lumpname); lumpinfo->fullname[lumpinfo->namelength] = '\0'; lumpinfo->fullnamelength = lumpinfo->namelength; @@ -474,8 +481,8 @@ static lumpinfo_t* ResGetLumpsWad(FILE* handle, UINT16* nlmp, const char* filena header.infotableofs = LONG(header.infotableofs); // read wad file directory - i = header.numlumps * sizeof (*fileinfo); - fileinfov = fileinfo = static_cast(malloc(i)); + i = header.numlumps * sizeof(*fileinfo); + fileinfov = fileinfo = (filelump_t *)(malloc(i)); if (fseek(handle, header.infotableofs, SEEK_SET) == -1 || fread(fileinfo, 1, i, handle) < i) @@ -487,7 +494,7 @@ static lumpinfo_t* ResGetLumpsWad(FILE* handle, UINT16* nlmp, const char* filena numlumps = header.numlumps; // fill in lumpinfo for this wad - lump_p = lumpinfo = static_cast(Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL)); + lump_p = lumpinfo = (lumpinfo_t *)(Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL)); for (i = 0; i < numlumps; i++, lump_p++, fileinfo++) { @@ -531,14 +538,14 @@ static lumpinfo_t* ResGetLumpsWad(FILE* handle, UINT16* nlmp, const char* filena lump_p->hash.name = W_HashLumpName(lump_p->name); // Allocate the lump's long name. - /*lump_p->longname = static_cast(Z_Malloc(9 * sizeof(char), PU_STATIC, NULL)); + /*lump_p->longname = (char *)(Z_Malloc(9 * sizeof(char), PU_STATIC, NULL)); strncpy(lump_p->longname, fileinfo->name, 8); lump_p->longname[8] = '\0'; lump_p->longnamelength = lump_p->namelength; lump_p->hash.longname = lump_p->hash.name;*/ // Allocate the lump's full name. - lump_p->fullname = static_cast(Z_Malloc(9 * sizeof(char), PU_STATIC, NULL)); + lump_p->fullname = (char *)(Z_Malloc(9 * sizeof(char), PU_STATIC, NULL)); strncpy(lump_p->fullname, fileinfo->name, 8); lump_p->fullname[8] = '\0'; lump_p->fullnamelength = lump_p->namelength; @@ -559,24 +566,24 @@ static boolean ResFindSignature(FILE* handle, const char endPat[], UINT32 startp int c; fseek(handle, 0, SEEK_END); - size_t len = static_cast(ftell(handle)); + size_t len = (size_t)(ftell(handle)); fseek(handle, startpos, SEEK_SET); - size_t remaining = static_cast(len - startpos); - size_t chunkpos = static_cast(startpos); + size_t remaining = (size_t)(len - startpos); + size_t chunkpos = (size_t)(startpos); s = endPat; //128k buffers - size_t buffer_size = std::min(128 * 1024 * sizeof(char), remaining); - CLEANUP(pfree) char* buffer = static_cast(malloc(buffer_size)); + size_t buffer_size = min(128 * 1024 * sizeof(char), remaining); + CLEANUP(pfree) char* buffer = (char *)(malloc(buffer_size)); size_t bytes_read = 0; while ((bytes_read = fread(buffer, 1, buffer_size, handle)) > 0) { for (size_t i = 0; i < bytes_read; i++) { - c = static_cast(buffer[i]); + c = (int)(buffer[i]); if (*s != c && s > endPat) // No match? s = endPat; // We "reset" the counter by sending the s pointer back to the start of the array. @@ -588,7 +595,7 @@ static boolean ResFindSignature(FILE* handle, const char endPat[], UINT32 startp if (*s == 0x00) // The array pointer has reached the key char which marks the end. It means we have matched the signature. { //the original function would leave the FILE* seeked to the end of the match - size_t foundpos = static_cast(chunkpos + i + 1); + size_t foundpos = (size_t)(chunkpos + i + 1); fseek(handle, foundpos, SEEK_SET); return true; } @@ -666,7 +673,7 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) fseek(handle, 0, SEEK_END); const long startpos = ftell(handle) - (22 + 65536); - if (!ResFindSignature(handle, pat_end, std::max(0l, startpos))) + if (!ResFindSignature(handle, pat_end, max(0l, startpos))) { CONS_Alert(CONS_ERROR, "Missing central directory\n"); return NULL; @@ -681,11 +688,11 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) numlumps = SHORT(zend.entries); - lump_p = lumpinfo = static_cast(Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL)); + lump_p = lumpinfo = (lumpinfo_t *)(Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL)); fseek(handle, LONG(zend.cdiroffset), SEEK_SET); - CLEANUP(Z_Pfree) char *cdir = static_cast(Z_Malloc(LONG(zend.cdirsize), PU_STATIC, &cdir)); + CLEANUP(Z_Pfree) char *cdir = (char *)(Z_Malloc(LONG(zend.cdirsize), PU_STATIC, &cdir)); if (fread(cdir, 1, LONG(zend.cdirsize), handle) < (UINT32)(LONG(zend.cdirsize))) { @@ -698,7 +705,7 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) for (i = 0; i < numlumps; i++, lump_p++) { - zentry_t *zentry = reinterpret_cast(cdir + offset); + zentry_t *zentry = (zentry_t *)(cdir + offset); char* fullname; char* trimname; char* dotpos; @@ -714,7 +721,7 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) lump_p->disksize = LONG(zentry->compsize); lump_p->size = LONG(zentry->size); - fullname = static_cast(malloc(SHORT(zentry->namelen) + 1)); + fullname = (char *)(malloc(SHORT(zentry->namelen) + 1)); strncpy(fullname, (char *)(zentry + 1), SHORT(zentry->namelen)); fullname[zentry->namelen] = 0; @@ -728,16 +735,16 @@ static lumpinfo_t* ResGetLumpsZip(FILE* handle, UINT16* nlmp) dotpos = fullname + strlen(fullname); // Watch for files without extension. memset(lump_p->name, '\0', 9); // Making sure they're initialized to 0. Is it necessary? - strncpy(lump_p->name, trimname, std::min(8, dotpos - trimname)); + strncpy(lump_p->name, trimname, min(8, dotpos - trimname)); lump_p->namelength = strlen(lump_p->name); lump_p->hash.name = W_HashLumpName(lump_p->name); - /*lump_p->longname = static_cast(Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL)); + /*lump_p->longname = (char *)(Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL)); strlcpy(lump_p->longname, trimname, dotpos - trimname + 1); lump_p->longnamelength = strlen(lump_p->longname); lump_p->hash.longname = W_HashLumpName(lump_p->longname);*/ - lump_p->fullname = static_cast(Z_Calloc(SHORT(zentry->namelen) + 1, PU_STATIC, NULL)); + lump_p->fullname = (char *)(Z_Calloc(SHORT(zentry->namelen) + 1, PU_STATIC, NULL)); strncpy(lump_p->fullname, fullname, SHORT(zentry->namelen)); lump_p->fullnamelength = zentry->namelen; //lump_p->hash.fullname = W_HashLumpName(lump_p->fullname); @@ -915,17 +922,19 @@ UINT16 W_InitFile(const char *filename, boolean local, boolean startup) // // link wad file to search files // - wadfile = static_cast(Z_Calloc(sizeof (*wadfile), PU_STATIC, NULL)); + wadfile = (wadfile_t *)(Z_Calloc(sizeof (*wadfile), PU_STATIC, NULL)); wadfile->filename = Z_StrDup(filename); wadfile->handle = handle; - wadfile->numlumps = static_cast(numlumps); + wadfile->numlumps = (UINT16)(numlumps); wadfile->lumpinfo = lumpinfo; wadfile->important = important; fseek(handle, 0, SEEK_END); - wadfile->filesize = static_cast(ftell(handle)); + wadfile->filesize = (unsigned)(ftell(handle)); wadfile->type = type; - wadfile->startfolders = new FolderCache(); - wadfile->endfolders = new FolderCache(); + wadfile->startfolders = (FolderCache *)malloc(sizeof(FolderCache)); + folder_map_init(&wadfile->startfolders->map); + wadfile->endfolders = (FolderCache *)malloc(sizeof(FolderCache)); + folder_map_init(&wadfile->endfolders->map); // already generated, just copy it over memcpy(&wadfile->md5sum, &md5sum, 16); @@ -950,7 +959,7 @@ UINT16 W_InitFile(const char *filename, boolean local, boolean startup) #ifdef HWRENDER // Read shaders from file if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED)) - HWR_LoadCustomShadersFromFile(numwadfiles - 1, static_cast(type == RET_PK3)); + HWR_LoadCustomShadersFromFile(numwadfiles - 1, (boolean)(type == RET_PK3)); #endif // TODO: HACK ALERT - Load Lua & SOC stuff right here. I feel like this should be out of this place, but... Let's stick with this for now. @@ -1179,9 +1188,9 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum size_t namelen; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; - auto it = wadfiles[wad]->startfolders->map.find(name); - if (it != wadfiles[wad]->startfolders->map.end()) - return it->second; + folder_map_itr it = folder_map_get(&wadfiles[wad]->startfolders->map, name); + if (!folder_map_is_end(it)) + return it.data->val; namelen = strlen(name); @@ -1193,12 +1202,12 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum if (lump_p->fullnamelength == namelen) i++; - wadfiles[wad]->startfolders->map[name] = i; + folder_map_insert(&wadfiles[wad]->startfolders->map, strdup(name), i); return i; } } - wadfiles[wad]->startfolders->map[name] = INT16_MAX; + folder_map_insert(&wadfiles[wad]->startfolders->map, strdup(name), INT16_MAX); return INT16_MAX; } @@ -1211,9 +1220,9 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) size_t namelen; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; - auto it = wadfiles[wad]->endfolders->map.find(name); - if (it != wadfiles[wad]->endfolders->map.end()) - return it->second; + folder_map_itr it = folder_map_get(&wadfiles[wad]->endfolders->map, name); + if (!folder_map_is_end(it)) + return it.data->val; namelen = strlen(name); @@ -1223,7 +1232,7 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) break; } - wadfiles[wad]->endfolders->map[name] = i; + folder_map_insert(&wadfiles[wad]->endfolders->map, strdup(name), i); return i; } @@ -1251,16 +1260,17 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump) FUNCINLINE static ATTRINLINE lumpnum_t CheckLumpInCache(const char *name) { - auto it = lumpnumcache.find(name); - if (it != lumpnumcache.end()) - return it->second; + lumpnum_map_itr it = lumpnum_map_get(&lumpnumcache, name); + if (!lumpnum_map_is_end(it)) + return it.data->val; return LUMPERROR; } FUNCINLINE static ATTRINLINE void AddLumpToCache(lumpnum_t lumpnum, const char *name) { - lumpnumcache.emplace(name, lumpnum); + I_Assert(strlen(name) <= 8); // we dont have longnames, so assume this is a non terminated string + lumpnum_map_insert(&lumpnumcache, strdup(name), lumpnum); } // @@ -1283,7 +1293,7 @@ lumpnum_t W_CheckNumForName(const char *name) // scan wad files backwards so patch lump files take precedence for (i = numwadfiles - 1; i >= 0; i--) { - check = W_CheckNumForNamePwad(name, static_cast(i), 0); + check = W_CheckNumForNamePwad(name, (UINT16)(i), 0); if (check != INT16_MAX) break; //found it @@ -1325,7 +1335,7 @@ lumpnum_t W_CheckNumForName(const char *name) // scan wad files backwards so patch lump files take precedence for (i = numwadfiles - 1; i >= 0; i--) { - check = W_CheckNumForLongNamePwad(name, static_cast(i), 0); + check = W_CheckNumForLongNamePwad(name, (UINT16)(i), 0); if (check != INT16_MAX) break; //found it @@ -1395,14 +1405,14 @@ lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, con { if (wadfiles[i]->type == RET_WAD) { - bsid = W_CheckNumForNamePwad(blockstart, static_cast(i), 0); + bsid = W_CheckNumForNamePwad(blockstart, (UINT16)(i), 0); if (bsid == INT16_MAX) continue; // Start block doesn't exist? - beid = W_CheckNumForNamePwad(blockend, static_cast(i), 0); + beid = W_CheckNumForNamePwad(blockend, (UINT16)(i), 0); if (beid == INT16_MAX) continue; // End block doesn't exist? - check = W_CheckNumForNamePwad(name, static_cast(i), bsid); + check = W_CheckNumForNamePwad(name, (UINT16)(i), bsid); if (check < beid) return (i << 16)+check; // found it, in our constraints } @@ -1603,7 +1613,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si #ifdef NO_PNG_LUMPS { size_t bytesread = fread(dest, 1, size, handle); - ErrorIfPNG(static_cast(dest), bytesread, wadfiles[wad]->filename, l->fullname); + ErrorIfPNG((UINT8 *)(dest), bytesread, wadfiles[wad]->filename, l->fullname); return bytesread; } #else @@ -1616,8 +1626,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si char *decData; // Lump's decompressed real data. size_t retval; // Helper var, lzf_decompress returns 0 when an error occurs. - rawData = static_cast(Z_Malloc(l->disksize, PU_STATIC, NULL)); - decData = static_cast(Z_Malloc(l->size, PU_STATIC, NULL)); + rawData = (char *)(Z_Malloc(l->disksize, PU_STATIC, NULL)); + decData = (char *)(Z_Malloc(l->size, PU_STATIC, NULL)); if (fread(rawData, 1, l->disksize, handle) < l->disksize) I_Error("wad %d, lump %d: cannot read compressed data", wad, lump); @@ -1644,7 +1654,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(rawData); Z_Free(decData); #ifdef NO_PNG_LUMPS - ErrorIfPNG(static_cast(dest), size, wadfiles[wad]->filename, l->fullname); + ErrorIfPNG((UINT8 *)(dest), size, wadfiles[wad]->filename, l->fullname); #endif return size; #else @@ -1664,8 +1674,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si unsigned long rawSize = l->disksize; unsigned long decSize = size; - rawData = static_cast(Z_Malloc(rawSize, PU_STATIC, NULL)); - decData = static_cast(dest); + rawData = (UINT8 *)(Z_Malloc(rawSize, PU_STATIC, NULL)); + decData = (UINT8 *)(dest); if (fread(rawData, 1, rawSize, handle) < rawSize) I_Error("wad %d, lump %d: cannot read compressed data", wad, lump); @@ -1701,7 +1711,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(rawData); #ifdef NO_PNG_LUMPS - ErrorIfPNG(static_cast(dest), size, wadfiles[wad]->filename, l->fullname); + ErrorIfPNG((UINT8 *)(dest), size, wadfiles[wad]->filename, l->fullname); #endif return size; } @@ -1889,7 +1899,7 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0); dest = Z_Calloc(sizeof(patch_t), tag, &lumpcache[lump]); - Patch_Create(static_cast(lumpdata), len, dest); + Patch_Create((softwarepatch_t *)(lumpdata), len, dest); Z_Free(lumpdata); } @@ -1911,7 +1921,7 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) if (!TestValidLump(wad, lump)) return NULL; - patch = static_cast(W_CacheSoftwarePatchNumPwad(wad, lump, tag)); + patch = (patch_t *)(W_CacheSoftwarePatchNumPwad(wad, lump, tag)); if (patch == NULL) return NULL; @@ -1922,7 +1932,7 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) Patch_CreateGL(patch); #endif - return static_cast(patch); + return (void*)(patch); } void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag) @@ -1950,10 +1960,10 @@ void *W_GetCachedRotPatchPwad(UINT16 wad, UINT16 lump) if (!rotcache[lump]) { rotsprite_t *rspr = RotatedPatch_Create(ROTANGLES); - Z_SetUser(rspr, static_cast(&rotcache[lump])); + Z_SetUser(rspr, (void**)(&rotcache[lump])); } - return static_cast(rotcache[lump]); + return (void*)(rotcache[lump]); } #endif // ROTSPRITE @@ -1966,7 +1976,7 @@ void W_UnlockCachedPatch(void *patch) // have different lifetimes from software's. #ifdef HWRENDER if (rendermode == render_opengl) - HWR_UnlockCachedPatch(static_cast((static_cast(patch))->hardware)); + HWR_UnlockCachedPatch((GLPatch_t *)(((patch_t *)(patch))->hardware)); else #endif Z_ChangeTag(patch, PU_PATCH); @@ -2001,7 +2011,7 @@ void *W_CachePatchNameRotated(const char *name, INT32 rotationangle, INT32 tag) if (rotationangle < 1 || rotationangle >= ROTANGLES) return W_CachePatchNum(num, tag); - rspr = static_cast(W_GetCachedRotPatchPwad(WADFILENUM(num), LUMPNUM(num))); + rspr = (rotsprite_t *)(W_GetCachedRotPatchPwad(WADFILENUM(num), LUMPNUM(num))); if (rspr == NULL) return W_CachePatchNum(W_GetNumForName("MISSING"), tag); @@ -2010,7 +2020,7 @@ void *W_CachePatchNameRotated(const char *name, INT32 rotationangle, INT32 tag) { INT32 xpivot = 0, ypivot = 0; - ptr = static_cast(W_CachePatchNum(num, PU_PATCH)); + ptr = (patch_t *)(W_CachePatchNum(num, PU_PATCH)); if (ptr == NULL) return W_CachePatchNum(W_GetNumForName("MISSING"), tag); @@ -2204,7 +2214,7 @@ static int W_VerifyPK3(FILE *fp, lumpchecklist_t *checklist, boolean status) file_size = ftell(fp); const long startpos = file_size - (22 + 65536); - if (!ResFindSignature(fp, pat_end, std::max(0l, startpos))) + if (!ResFindSignature(fp, pat_end, max(0l, startpos))) return true; fseek(fp, -4, SEEK_CUR); @@ -2217,7 +2227,7 @@ static int W_VerifyPK3(FILE *fp, lumpchecklist_t *checklist, boolean status) fseek(fp, LONG(zend.cdiroffset), SEEK_SET); - CLEANUP(pfree) char *cdir = static_cast(malloc(LONG(zend.cdirsize))); + CLEANUP(pfree) char *cdir = (char *)(malloc(LONG(zend.cdirsize))); if (fread(cdir, 1, LONG(zend.cdirsize), fp) < (UINT32)(LONG(zend.cdirsize))) { @@ -2228,7 +2238,7 @@ static int W_VerifyPK3(FILE *fp, lumpchecklist_t *checklist, boolean status) for (i = 0; i < numlumps; i++) { - zentry_t *zentry = reinterpret_cast(cdir + offset); + zentry_t *zentry = (zentry_t *)(cdir + offset); char* fullname; char* trimname; char* dotpos; @@ -2240,7 +2250,7 @@ static int W_VerifyPK3(FILE *fp, lumpchecklist_t *checklist, boolean status) if (verified == true) { - fullname = static_cast(malloc(SHORT(zentry->namelen) + 1)); + fullname = (char *)(malloc(SHORT(zentry->namelen) + 1)); strncpy(fullname, (char *)(zentry + 1), SHORT(zentry->namelen)); fullname[zentry->namelen] = 0; @@ -2256,7 +2266,7 @@ static int W_VerifyPK3(FILE *fp, lumpchecklist_t *checklist, boolean status) dotpos = fullname + strlen(fullname); // Watch for files without extension. memset(lumpname, '\0', 9); // Making sure they're initialized to 0. Is it necessary? - strncpy(lumpname, trimname, std::min(8, dotpos - trimname)); + strncpy(lumpname, trimname, min(8, dotpos - trimname)); if (!W_VerifyName(lumpname, checklist, status)) verified = false; @@ -2452,7 +2462,7 @@ static int W_CheckPK3Contains(FILE *fp, lumpchecklist_t *checklist) file_size = ftell(fp); const long startpos = file_size - (22 + 65536); - if (!ResFindSignature(fp, pat_end, std::max(0l, startpos))) + if (!ResFindSignature(fp, pat_end, max(0l, startpos))) return true; fseek(fp, -4, SEEK_CUR); @@ -2465,7 +2475,7 @@ static int W_CheckPK3Contains(FILE *fp, lumpchecklist_t *checklist) fseek(fp, LONG(zend.cdiroffset), SEEK_SET); - CLEANUP(pfree) char *cdir = static_cast(malloc(LONG(zend.cdirsize))); + CLEANUP(pfree) char *cdir = (char *)(malloc(LONG(zend.cdirsize))); if (fread(cdir, 1, LONG(zend.cdirsize), fp) < (UINT32)(LONG(zend.cdirsize))) { @@ -2476,7 +2486,7 @@ static int W_CheckPK3Contains(FILE *fp, lumpchecklist_t *checklist) for (i = 0; i < numlumps; i++) { - zentry_t *zentry = reinterpret_cast(cdir + offset); + zentry_t *zentry = (zentry_t *)(cdir + offset); CLEANUP(pfree) char* fullname = NULL; char* trimname; char* dotpos; @@ -2486,7 +2496,7 @@ static int W_CheckPK3Contains(FILE *fp, lumpchecklist_t *checklist) return false; } - fullname = static_cast(malloc(SHORT(zentry->namelen) + 1)); + fullname = (char *)(malloc(SHORT(zentry->namelen) + 1)); strncpy(fullname, (char *)(zentry + 1), SHORT(zentry->namelen)); fullname[zentry->namelen] = 0; @@ -2502,7 +2512,7 @@ static int W_CheckPK3Contains(FILE *fp, lumpchecklist_t *checklist) dotpos = fullname + strlen(fullname); // Watch for files without extension. memset(lumpname, '\0', 9); // Making sure they're initialized to 0. Is it necessary? - strncpy(lumpname, trimname, std::min(8, dotpos - trimname)); + strncpy(lumpname, trimname, min(8, dotpos - trimname)); if (W_NameStartsWith(lumpname, checklist)) { @@ -2624,18 +2634,18 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) size_t *vsizecache; // Remember that we're assuming that the WAD will have a specific set of lumps in a specific order. - UINT8 *wadData = static_cast(W_CacheLumpNum(lumpnum, PU_LEVEL)); + UINT8 *wadData = (UINT8 *)(W_CacheLumpNum(lumpnum, PU_LEVEL)); if (wadData == NULL) I_Error("vres_GetMap: Invalid WadData!\n"); - filelump_t *fileinfo = reinterpret_cast(wadData + LONG((reinterpret_cast(wadData))->infotableofs)); + filelump_t *fileinfo = (filelump_t *)(wadData + LONG(((wadinfo_t *)(wadData))->infotableofs)); - i = LONG((reinterpret_cast(wadData))->numlumps); - vsizecache = static_cast(Z_Malloc(sizeof(size_t)*i, PU_LEVEL, NULL)); + i = LONG(((wadinfo_t *)(wadData))->numlumps); + vsizecache = (size_t *)(Z_Malloc(sizeof(size_t)*i, PU_LEVEL, NULL)); for (realentry = 0; realentry < i; realentry++) { - vsizecache[realentry] = static_cast(LONG((reinterpret_cast(fileinfo + realentry))->size)); + vsizecache[realentry] = (size_t)(LONG(((filelump_t *)(fileinfo + realentry))->size)); if (!vsizecache[realentry]) continue; @@ -2643,7 +2653,7 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) numlumps++; } - vlumps = static_cast(Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL)); + vlumps = (virtlump_t *)(Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL)); if (vlumps == NULL) I_Error("vres_GetMap: Out of memory!\n"); @@ -2665,7 +2675,7 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) // Play it safe with the name in this case. memcpy(vlumps[i].name, name, 8); vlumps[i].name[8] = '\0'; - vlumps[i].data = static_cast( + vlumps[i].data = (UINT8 *)( Z_Malloc(vlumps[i].size, PU_LEVEL, NULL) // This is memory inefficient, sorry about that. ); memcpy(vlumps[i].data, wadData + LONG((fileinfo + realentry)->filepos), vlumps[i].size); @@ -2692,7 +2702,7 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) } numlumps++; - vlumps = static_cast(Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL)); + vlumps = (virtlump_t *)(Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL)); if (vlumps == NULL) I_Error("vres_GetMap: Out of memory!\n"); @@ -2713,11 +2723,11 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) vlumps[i].size = W_LumpLength(lumpnum); memcpy(vlumps[i].name, name, 8); vlumps[i].name[8] = '\0'; - vlumps[i].data = static_cast(W_CacheLumpNum(lumpnum, PU_LEVEL)); + vlumps[i].data = (UINT8 *)(W_CacheLumpNum(lumpnum, PU_LEVEL)); } } - vres = static_cast(Z_Malloc(sizeof(virtres_t), PU_LEVEL, NULL)); + vres = (virtres_t *)(Z_Malloc(sizeof(virtres_t), PU_LEVEL, NULL)); vres->vlumps = vlumps; vres->numlumps = numlumps; diff --git a/src/w_wad.h b/src/w_wad.h index d21afb982..fc7f25fec 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -126,7 +126,6 @@ typedef enum restype RET_UNKNOWN, } restype_t; - typedef struct wadfile_s { char *filename; @@ -155,6 +154,7 @@ extern wadfile_t *wadfiles[MAX_WADFILES]; // ========================================================================= +void W_Startup(void); void W_Shutdown(void); // Opens a WAD file. Returns the FILE * handle for the file, or NULL if not found or could not be opened -- 2.54.0 From ba808307e95434707a8fdb0b345229f8e87cbbbc Mon Sep 17 00:00:00 2001 From: Alug Date: Tue, 24 Feb 2026 16:10:54 +0100 Subject: [PATCH 11/35] FORCEINLINE is apparently used by windows already lmao --- src/verstable.h | 102 ++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/verstable.h b/src/verstable.h index 8c5a49dc3..b3fe13f91 100644 --- a/src/verstable.h +++ b/src/verstable.h @@ -441,12 +441,12 @@ License (MIT): #ifdef __GNUC__ #if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) // >= GCC 3.1 - #define FORCEINLINE __attribute__((always_inline)) + #define FORCE_INLINE __attribute__((always_inline)) #else - #define FORCEINLINE + #define FORCE_INLINE #endif #else - #define FORCEINLINE + #define FORCE_INLINE #endif #ifndef VERSTABLE_H @@ -485,7 +485,7 @@ License (MIT): // Extracts a hash fragment from a uint64_t hash code. // We take the highest four bits so that keys that map (via modulo) to the same bucket have distinct hash fragments. -static inline FORCEINLINE uint16_t vt_hashfrag( uint64_t hash ) +static inline FORCE_INLINE uint16_t vt_hashfrag( uint64_t hash ) { return ( hash >> 48 ) & VT_HASH_FRAG_MASK; } @@ -493,7 +493,7 @@ static inline FORCEINLINE uint16_t vt_hashfrag( uint64_t hash ) // Standard quadratic probing formula that guarantees that all buckets are visited when the bucket count is a power of // two (at least in theory, because the displacement limit could terminate the search early when the bucket count is // high). -static inline FORCEINLINE size_t vt_quadratic( uint16_t displacement ) +static inline FORCE_INLINE size_t vt_quadratic( uint16_t displacement ) { return ( (size_t)displacement * displacement + displacement ) / 2; } @@ -506,7 +506,7 @@ static inline FORCEINLINE size_t vt_quadratic( uint16_t displacement ) #if defined( __GNUC__ ) && ULLONG_MAX == 0xFFFFFFFFFFFFFFFF -static inline FORCEINLINE int vt_first_nonzero_uint16( uint64_t val ) +static inline FORCE_INLINE int vt_first_nonzero_uint16( uint64_t val ) { const uint16_t endian_checker = 0x0001; if( *(const char *)&endian_checker ) // Little-endian (the compiler will optimize away the check at -O1 and above). @@ -521,7 +521,7 @@ static inline FORCEINLINE int vt_first_nonzero_uint16( uint64_t val ) #pragma intrinsic(_BitScanForward64) #pragma intrinsic(_BitScanReverse64) -static inline FORCEINLINE int vt_first_nonzero_uint16( uint64_t val ) +static inline FORCE_INLINE int vt_first_nonzero_uint16( uint64_t val ) { unsigned long result; @@ -539,7 +539,7 @@ static inline FORCEINLINE int vt_first_nonzero_uint16( uint64_t val ) #else -static inline FORCEINLINE int vt_first_nonzero_uint16( uint64_t val ) +static inline FORCE_INLINE int vt_first_nonzero_uint16( uint64_t val ) { int result = 0; @@ -567,7 +567,7 @@ static const uint16_t vt_empty_placeholder_metadatum = VT_EMPTY; // Fast-hash, as described by https://jonkagstrom.com/bit-mixer-construction and // https://code.google.com/archive/p/fast-hash. // In testing, this hash function provided slightly better performance than the Murmur3 mixer. -static inline FORCEINLINE uint64_t vt_hash_integer( uint64_t key ) +static inline FORCE_INLINE uint64_t vt_hash_integer( uint64_t key ) { key ^= key >> 23; key *= 0x2127599bf4325c37ull; @@ -581,7 +581,7 @@ static inline FORCEINLINE uint64_t vt_hash_integer( uint64_t key ) // * We do not handle endianness, so the result will differ depending on the platform. // * We omit the code optimized for 32-bit platforms. -static inline FORCEINLINE void vt_wymum( uint64_t *a, uint64_t *b ) +static inline FORCE_INLINE void vt_wymum( uint64_t *a, uint64_t *b ) { #if defined( __SIZEOF_INT128__ ) __uint128_t r = *a; @@ -609,32 +609,32 @@ static inline FORCEINLINE void vt_wymum( uint64_t *a, uint64_t *b ) #endif } -static inline FORCEINLINE uint64_t vt_wymix( uint64_t a, uint64_t b ) +static inline FORCE_INLINE uint64_t vt_wymix( uint64_t a, uint64_t b ) { vt_wymum( &a, &b ); return a ^ b; } -static inline FORCEINLINE uint64_t vt_wyr8( const unsigned char *p ) +static inline FORCE_INLINE uint64_t vt_wyr8( const unsigned char *p ) { uint64_t v; memcpy( &v, p, 8 ); return v; } -static inline FORCEINLINE uint64_t vt_wyr4( const unsigned char *p ) +static inline FORCE_INLINE uint64_t vt_wyr4( const unsigned char *p ) { uint32_t v; memcpy( &v, p, 4 ); return v; } -static inline FORCEINLINE uint64_t vt_wyr3( const unsigned char *p, size_t k ) +static inline FORCE_INLINE uint64_t vt_wyr3( const unsigned char *p, size_t k ) { return ( ( (uint64_t)p[ 0 ] ) << 16 ) | ( ( (uint64_t)p[ k >> 1 ] ) << 8 ) | p[ k - 1 ]; } -static inline FORCEINLINE size_t vt_wyhash( const void *key, size_t len ) +static inline FORCE_INLINE size_t vt_wyhash( const void *key, size_t len ) { const unsigned char *p = (const unsigned char *)key; uint64_t seed = 0xca813bf4c7abf0a9ull; @@ -693,41 +693,41 @@ static inline FORCEINLINE size_t vt_wyhash( const void *key, size_t len ) return (size_t)vt_wymix( a ^ 0x2d358dccaa6c78a5ull ^ len, b ^ 0x8bb84b93962eacc9ull ); } -static inline FORCEINLINE uint64_t vt_hash_string( const char *key ) +static inline FORCE_INLINE uint64_t vt_hash_string( const char *key ) { return vt_wyhash( key, strlen( key ) ); } -static inline FORCEINLINE bool vt_cmpr_integer( uint64_t key_1, uint64_t key_2 ) +static inline FORCE_INLINE bool vt_cmpr_integer( uint64_t key_1, uint64_t key_2 ) { return key_1 == key_2; } -static inline FORCEINLINE bool vt_cmpr_string( const char *key_1, const char *key_2 ) +static inline FORCE_INLINE bool vt_cmpr_string( const char *key_1, const char *key_2 ) { return strcmp( key_1, key_2 ) == 0; } // Default allocation and free functions. -static inline FORCEINLINE void *vt_malloc( size_t size ) +static inline FORCE_INLINE void *vt_malloc( size_t size ) { return malloc( size ); } -static inline FORCEINLINE void vt_free( void *ptr, size_t size ) +static inline FORCE_INLINE void vt_free( void *ptr, size_t size ) { (void)size; free( ptr ); } -static inline FORCEINLINE void *vt_malloc_with_ctx( size_t size, void *ctx ) +static inline FORCE_INLINE void *vt_malloc_with_ctx( size_t size, void *ctx ) { (void)ctx; return malloc( size ); } -static inline FORCEINLINE void vt_free_with_ctx( void *ptr, size_t size, void *ctx ) +static inline FORCE_INLINE void vt_free_with_ctx( void *ptr, size_t size, void *ctx ) { (void)size; (void)ctx; @@ -894,7 +894,7 @@ typedef struct #if defined( HEADER_MODE ) || defined( IMPLEMENTATION_MODE ) #define VT_API_FN_QUALIFIERS #else -#define VT_API_FN_QUALIFIERS static inline FORCEINLINE +#define VT_API_FN_QUALIFIERS static inline FORCE_INLINE #endif #ifndef IMPLEMENTATION_MODE @@ -966,7 +966,7 @@ VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase_itr_raw ) ( NAME *, VT_CAT( NAME, // This function must be inlined to ensure that the compiler optimizes away the NAME_fast_forward call if the returned // iterator is discarded. #ifdef __GNUC__ -static inline FORCEINLINE +static inline FORCE_INLINE #elif defined( _MSC_VER ) static __forceinline #else @@ -1083,7 +1083,7 @@ VT_API_FN_QUALIFIERS void VT_CAT( NAME, _init )( // This function returns the offset of the beginning of the metadata, i.e. the size of the buckets array plus the // (usually zero) padding. // It assumes that the bucket count is not zero. -static inline FORCEINLINE size_t VT_CAT( NAME, _metadata_offset )( NAME *table ) +static inline FORCE_INLINE size_t VT_CAT( NAME, _metadata_offset )( NAME *table ) { // Use sizeof, rather than alignof, for C99 compatibility. return ( ( ( table->buckets_mask + 1 ) * sizeof( VT_CAT( NAME, _bucket ) ) + sizeof( uint16_t ) - 1 ) / @@ -1092,7 +1092,7 @@ static inline FORCEINLINE size_t VT_CAT( NAME, _metadata_offset )( NAME *table ) // Returns the total allocation size, including the buckets array, padding, metadata, and excess metadata. // As above, this function assumes that the bucket count is not zero. -static inline FORCEINLINE size_t VT_CAT( NAME, _total_alloc_size )( NAME *table ) +static inline FORCE_INLINE size_t VT_CAT( NAME, _total_alloc_size )( NAME *table ) { return VT_CAT( NAME, _metadata_offset )( table ) + ( table->buckets_mask + 1 + 4 ) * sizeof( uint16_t ); } @@ -1163,7 +1163,7 @@ VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _is_end )( VT_CAT( NAME, _itr ) itr ) // other chains might have freed up buckets that could fall in this chain before the final key. // Returns true if an empty bucket within the range of the displacement limit was found, in which case the final two // pointer arguments contain the index of the empty bucket and its quadratic displacement from home_bucket. -static inline FORCEINLINE bool VT_CAT( NAME, _find_first_empty )( +static inline FORCE_INLINE bool VT_CAT( NAME, _find_first_empty )( NAME *table, size_t home_bucket, size_t *empty, @@ -1190,7 +1190,7 @@ static inline FORCEINLINE bool VT_CAT( NAME, _find_first_empty )( // quadratic displacement and returns the index of the bucket containing that key. // Although the new key could simply be linked to the end of the chain, keeping the chain ordered by displacement // theoretically improves cache locality during lookups. -static inline FORCEINLINE size_t VT_CAT( NAME, _find_insert_location_in_chain )( +static inline FORCE_INLINE size_t VT_CAT( NAME, _find_insert_location_in_chain )( NAME *table, size_t home_bucket, uint16_t displacement_to_empty @@ -1219,7 +1219,7 @@ static inline FORCEINLINE size_t VT_CAT( NAME, _find_insert_location_in_chain )( // * Re-linking the key to the chain. // Returns true if the eviction succeeded, or false if no empty bucket to which to evict the occupying key could be // found within the displacement limit. -static inline FORCEINLINE bool VT_CAT( NAME, _evict )( NAME *table, size_t bucket ) +static inline FORCE_INLINE bool VT_CAT( NAME, _evict )( NAME *table, size_t bucket ) { // Find the previous key in chain. size_t home_bucket = HASH_FN( table->buckets[ bucket ].key ) & table->buckets_mask; @@ -1261,7 +1261,7 @@ static inline FORCEINLINE bool VT_CAT( NAME, _evict )( NAME *table, size_t bucke // Returns an end iterator, i.e. any iterator for which .metadatum == .metadata_end. // This function just cleans up the library code in functions that return an end iterator as a failure indicator. -static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( NAME, _end_itr )( void ) +static inline FORCE_INLINE VT_CAT( NAME, _itr ) VT_CAT( NAME, _end_itr )( void ) { VT_CAT( NAME, _itr ) itr = { NULL, NULL, NULL, 0 }; return itr; @@ -1282,7 +1282,7 @@ static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( NAME, _end_itr )( void ) // inserted because of the maximum load factor or displacement limit constraints. // If replace is false, then the return value is as described above, except that if the key already exists, the function // returns an iterator to the existing key. -static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert_raw )( +static inline FORCE_INLINE VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert_raw )( NAME *table, KEY_TY key, #ifdef VAL_TY @@ -1415,7 +1415,7 @@ static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert_raw )( // In testing, the no-inline approach showed a performance benefit when inserting existing keys (i.e. replacing). #ifdef __GNUC__ #pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wattributes" // Silence warning about combining noinline with static inline FORCEINLINE. +#pragma GCC diagnostic ignored "-Wattributes" // Silence warning about combining noinline with static inline FORCE_INLINE. __attribute__((noinline)) static inline #elif defined( _MSC_VER ) __declspec(noinline) static inline @@ -1738,7 +1738,7 @@ VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase )( NAME *table, KEY_TY key ) // Finds the first occupied bucket at or after the bucket pointed to by itr. // This function scans four buckets at a time, ideally using intrinsics. -static inline FORCEINLINE void VT_CAT( NAME, _fast_forward )( VT_CAT( NAME, _itr ) *itr ) +static inline FORCE_INLINE void VT_CAT( NAME, _fast_forward )( VT_CAT( NAME, _itr ) *itr ) { while( true ) { @@ -1768,7 +1768,7 @@ VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _next )( VT_CAT( NAME, _ // Returns the minimum bucket count required to accommodate a certain number of keys, which is governed by the maximum // load factor. -static inline FORCEINLINE size_t VT_CAT( NAME, _min_bucket_count_for_size )( size_t size ) +static inline FORCE_INLINE size_t VT_CAT( NAME, _min_bucket_count_for_size )( size_t size ) { if( size == 0 ) return 0; @@ -1893,7 +1893,7 @@ VT_API_FN_QUALIFIERS void VT_CAT( NAME, _cleanup )( NAME *table ) typedef NAME VT_CAT( vt_table_, VT_TEMPLATE_COUNT ); typedef VT_CAT( NAME, _itr ) VT_CAT( vt_table_itr_, VT_TEMPLATE_COUNT ); -static inline FORCEINLINE void VT_CAT( vt_init_, VT_TEMPLATE_COUNT )( +static inline FORCE_INLINE void VT_CAT( vt_init_, VT_TEMPLATE_COUNT )( NAME *table #ifdef CTX_TY , CTX_TY ctx @@ -1908,7 +1908,7 @@ static inline FORCEINLINE void VT_CAT( vt_init_, VT_TEMPLATE_COUNT )( ); } -static inline FORCEINLINE bool VT_CAT( vt_init_clone_, VT_TEMPLATE_COUNT )( +static inline FORCE_INLINE bool VT_CAT( vt_init_clone_, VT_TEMPLATE_COUNT )( NAME *table, const NAME* source #ifdef CTX_TY @@ -1925,22 +1925,22 @@ static inline FORCEINLINE bool VT_CAT( vt_init_clone_, VT_TEMPLATE_COUNT )( ); } -static inline FORCEINLINE size_t VT_CAT( vt_size_, VT_TEMPLATE_COUNT )( const NAME *table ) +static inline FORCE_INLINE size_t VT_CAT( vt_size_, VT_TEMPLATE_COUNT )( const NAME *table ) { return VT_CAT( NAME, _size )( table ); } -static inline FORCEINLINE size_t VT_CAT( vt_bucket_count_, VT_TEMPLATE_COUNT )( const NAME *table ) +static inline FORCE_INLINE size_t VT_CAT( vt_bucket_count_, VT_TEMPLATE_COUNT )( const NAME *table ) { return VT_CAT( NAME, _bucket_count )( table ); } -static inline FORCEINLINE bool VT_CAT( vt_is_end_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr ) +static inline FORCE_INLINE bool VT_CAT( vt_is_end_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr ) { return VT_CAT( NAME, _is_end )( itr ); } -static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_insert_, VT_TEMPLATE_COUNT )( +static inline FORCE_INLINE VT_CAT( NAME, _itr ) VT_CAT( vt_insert_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key #ifdef VAL_TY @@ -1957,7 +1957,7 @@ static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_insert_, VT_TEMPLATE_C ); } -static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_get_or_insert_, VT_TEMPLATE_COUNT )( +static inline FORCE_INLINE VT_CAT( NAME, _itr ) VT_CAT( vt_get_or_insert_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key #ifdef VAL_TY @@ -1974,47 +1974,47 @@ static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_get_or_insert_, VT_TEM ); } -static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_get_, VT_TEMPLATE_COUNT )( const NAME *table, KEY_TY key ) +static inline FORCE_INLINE VT_CAT( NAME, _itr ) VT_CAT( vt_get_, VT_TEMPLATE_COUNT )( const NAME *table, KEY_TY key ) { return VT_CAT( NAME, _get )( table, key ); } -static inline FORCEINLINE bool VT_CAT( vt_erase_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key ) +static inline FORCE_INLINE bool VT_CAT( vt_erase_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key ) { return VT_CAT( NAME, _erase )( table, key ); } -static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_next_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr ) +static inline FORCE_INLINE VT_CAT( NAME, _itr ) VT_CAT( vt_next_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr ) { return VT_CAT( NAME, _next )( itr ); } -static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_erase_itr_, VT_TEMPLATE_COUNT )( NAME *table, VT_CAT( NAME, _itr ) itr ) +static inline FORCE_INLINE VT_CAT( NAME, _itr ) VT_CAT( vt_erase_itr_, VT_TEMPLATE_COUNT )( NAME *table, VT_CAT( NAME, _itr ) itr ) { return VT_CAT( NAME, _erase_itr )( table, itr ); } -static inline FORCEINLINE bool VT_CAT( vt_reserve_, VT_TEMPLATE_COUNT )( NAME *table, size_t bucket_count ) +static inline FORCE_INLINE bool VT_CAT( vt_reserve_, VT_TEMPLATE_COUNT )( NAME *table, size_t bucket_count ) { return VT_CAT( NAME, _reserve )( table, bucket_count ); } -static inline FORCEINLINE bool VT_CAT( vt_shrink_, VT_TEMPLATE_COUNT )( NAME *table ) +static inline FORCE_INLINE bool VT_CAT( vt_shrink_, VT_TEMPLATE_COUNT )( NAME *table ) { return VT_CAT( NAME, _shrink )( table ); } -static inline FORCEINLINE VT_CAT( NAME, _itr ) VT_CAT( vt_first_, VT_TEMPLATE_COUNT )( const NAME *table ) +static inline FORCE_INLINE VT_CAT( NAME, _itr ) VT_CAT( vt_first_, VT_TEMPLATE_COUNT )( const NAME *table ) { return VT_CAT( NAME, _first )( table ); } -static inline FORCEINLINE void VT_CAT( vt_clear_, VT_TEMPLATE_COUNT )( NAME *table ) +static inline FORCE_INLINE void VT_CAT( vt_clear_, VT_TEMPLATE_COUNT )( NAME *table ) { VT_CAT( NAME, _clear )( table ); } -static inline FORCEINLINE void VT_CAT( vt_cleanup_, VT_TEMPLATE_COUNT )( NAME *table ) +static inline FORCE_INLINE void VT_CAT( vt_cleanup_, VT_TEMPLATE_COUNT )( NAME *table ) { VT_CAT( NAME, _cleanup )( table ); } @@ -2116,4 +2116,4 @@ C99 prefixed function API to circumvent this restriction. #undef IMPLEMENTATION_MODE #undef VT_API_FN_QUALIFIERS -#undef FORCEINLINE +#undef FORCE_INLINE -- 2.54.0 From 7c24d92613ef93affeb3fd5b571df14560b64d76 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 25 Feb 2026 16:45:17 +0100 Subject: [PATCH 12/35] delet V_GetSkincolorChar this was unused and broken anyways think this came from snowy kart or smth --- src/v_video.c | 161 -------------------------------------------------- src/v_video.h | 2 - 2 files changed, 163 deletions(-) diff --git a/src/v_video.c b/src/v_video.c index fde09cc0b..9898e9c29 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -3309,167 +3309,6 @@ INT32 V_SubStringLengthToFit(const char *string, INT32 width, INT32 option) return max(i-1, 0); } -char V_GetSkincolorChar(INT32 color) -{ - char cstart = 0x80; - - switch (color) - { - case SKINCOLOR_WHITE: - case SKINCOLOR_SILVER: - case SKINCOLOR_SLATE: - cstart = 0x80; // White - break; - - case SKINCOLOR_GREY: - case SKINCOLOR_NICKEL: - case SKINCOLOR_BLACK: - case SKINCOLOR_SKUNK: - case SKINCOLOR_JET: - cstart = 0x86; // V_GRAYMAP - break; - - case SKINCOLOR_SEPIA: - case SKINCOLOR_BEIGE: - case SKINCOLOR_WALNUT: - case SKINCOLOR_BROWN: - case SKINCOLOR_LEATHER: - case SKINCOLOR_RUST: - case SKINCOLOR_WRISTWATCH: - cstart = 0x8e; // V_BROWNMAP - break; - - case SKINCOLOR_FAIRY: - case SKINCOLOR_SALMON: - case SKINCOLOR_PINK: - case SKINCOLOR_ROSE: - case SKINCOLOR_BRICK: - case SKINCOLOR_LEMONADE: - case SKINCOLOR_BUBBLEGUM: - case SKINCOLOR_LILAC: - cstart = 0x8d; // V_PINKMAP - break; - - case SKINCOLOR_CINNAMON: - case SKINCOLOR_RUBY: - case SKINCOLOR_RASPBERRY: - case SKINCOLOR_CHERRY: - case SKINCOLOR_RED: - case SKINCOLOR_CRIMSON: - case SKINCOLOR_MAROON: - case SKINCOLOR_FLAME: - case SKINCOLOR_SCARLET: - case SKINCOLOR_KETCHUP: - cstart = 0x85; // V_REDMAP - break; - - case SKINCOLOR_DAWN: - case SKINCOLOR_SUNSET: - case SKINCOLOR_CREAMSICLE: - case SKINCOLOR_ORANGE: - case SKINCOLOR_PUMPKIN: - case SKINCOLOR_ROSEWOOD: - case SKINCOLOR_BURGUNDY: - case SKINCOLOR_TANGERINE: - cstart = 0x87; // V_ORANGEMAP - break; - - case SKINCOLOR_PEACH: - case SKINCOLOR_CARAMEL: - case SKINCOLOR_CREAM: - cstart = 0x8f; // V_PEACHMAP - break; - - case SKINCOLOR_GOLD: - case SKINCOLOR_ROYAL: - case SKINCOLOR_BRONZE: - case SKINCOLOR_COPPER: - case SKINCOLOR_THUNDER: - cstart = 0x8a; // V_GOLDMAP - break; - - case SKINCOLOR_POPCORN: - case SKINCOLOR_QUARRY: - case SKINCOLOR_YELLOW: - case SKINCOLOR_MUSTARD: - case SKINCOLOR_CROCODILE: - case SKINCOLOR_OLIVE: - cstart = 0x82; // V_YELLOWMAP - break; - - case SKINCOLOR_ARTICHOKE: - case SKINCOLOR_VOMIT: - case SKINCOLOR_GARDEN: - case SKINCOLOR_TEA: - case SKINCOLOR_PISTACHIO: - cstart = 0x8b; // V_TEAMAP - break; - - case SKINCOLOR_LIME: - case SKINCOLOR_HANDHELD: - case SKINCOLOR_MOSS: - case SKINCOLOR_CAMOUFLAGE: - case SKINCOLOR_ROBOHOOD: - case SKINCOLOR_MINT: - case SKINCOLOR_GREEN: - case SKINCOLOR_PINETREE: - case SKINCOLOR_EMERALD: - case SKINCOLOR_SWAMP: - case SKINCOLOR_DREAM: - case SKINCOLOR_PLAGUE: - case SKINCOLOR_ALGAE: - cstart = 0x83; // V_GREENMAP - break; - - case SKINCOLOR_CARIBBEAN: - case SKINCOLOR_AZURE: - case SKINCOLOR_AQUA: - case SKINCOLOR_TEAL: - case SKINCOLOR_CYAN: - case SKINCOLOR_JAWZ: - case SKINCOLOR_CERULEAN: - case SKINCOLOR_NAVY: - case SKINCOLOR_SAPPHIRE: - cstart = 0x88; // V_SKYMAP - break; - - case SKINCOLOR_PIGEON: - case SKINCOLOR_PLATINUM: - case SKINCOLOR_STEEL: - cstart = 0x8c; // V_STEELMAP - break; - - case SKINCOLOR_PERIWINKLE: - case SKINCOLOR_BLUE: - case SKINCOLOR_BLUEBERRY: - case SKINCOLOR_NOVA: - cstart = 0x84; // V_BLUEMAP - break; - - case SKINCOLOR_ULTRAVIOLET: - case SKINCOLOR_PURPLE: - case SKINCOLOR_FUCHSIA: - cstart = 0x81; // V_PURPLEMAP - break; - - case SKINCOLOR_PASTEL: - case SKINCOLOR_MOONSLAM: - case SKINCOLOR_DUSK: - case SKINCOLOR_TOXIC: - case SKINCOLOR_MAUVE: - case SKINCOLOR_LAVENDER: - case SKINCOLOR_BYZANTIUM: - case SKINCOLOR_POMEGRANATE: - cstart = 0x89; // V_LAVENDERMAP - break; - - default: - break; - } - - return cstart; -} - INT32 V_SkinColorToHighlightcolor(skincolors_t color) { switch (color) diff --git a/src/v_video.h b/src/v_video.h index 7eea1468c..0a77c4bc2 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -287,8 +287,6 @@ INT32 V_ThinSubStringWidth(const char *string, INT32 length, INT32 option); // Find maximum length for substring taken from current string to fit into given width INT32 V_SubStringLengthToFit(const char *string, INT32 width, INT32 option); -char V_GetSkincolorChar(INT32 color); - INT32 V_SkinColorToHighlightcolor(skincolors_t color); // this is pretty dumb, but has to be done like this, otherwise the fps counter just disappears sometimes for no reason lol -- 2.54.0 From 3e327263c9e11a938cbebcad727f7a06e65c798e Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 25 Feb 2026 16:46:37 +0100 Subject: [PATCH 13/35] fix faulty papersprite flipping in opengl lol --- src/hardware/hw_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hardware/hw_main.cpp b/src/hardware/hw_main.cpp index e9aa0532d..15434ec20 100644 --- a/src/hardware/hw_main.cpp +++ b/src/hardware/hw_main.cpp @@ -4850,7 +4850,7 @@ static void HWR_ProjectSprite(mobj_t *thing) flip = sprframe->flip; // Will only be 0x00 or 0xFF if (papersprite && ang < ANGLE_180) - flip ^= 0xFFFF; + flip ^= 0xFF; } else { -- 2.54.0 From 1b22c3af19cd9460564a976e8520cc334353608d Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 25 Feb 2026 16:52:39 +0100 Subject: [PATCH 14/35] those should be boolean just for correctness! --- src/d_clisrv.c | 8 ++++---- src/d_clisrv.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0241f06e0..29dea3d19 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -173,11 +173,11 @@ static UINT16 resynch_delay[MAXNETNODES]; // delay time before the player can be static UINT32 resynch_status[MAXNETNODES]; // 0 bit means synched for that player, 1 means possibly desynched static UINT8 resynch_sent[MAXNETNODES][MAXPLAYERS]; // what synch packets have we attempted to send to the player static UINT8 resynch_inprogress[MAXNETNODES]; -static UINT8 resynch_local_inprogress = false; // WE are desynched and getting packets to fix it. -UINT8 hu_resynching = 0; +static boolean resynch_local_inprogress = false; // WE are desynched and getting packets to fix it. +boolean hu_resynching = false; #ifdef SATURNPAK -UINT8 hu_redownloadinggamestate = 0; +boolean hu_redownloadinggamestate = false; static UINT8 gamestate_resend_counter[MAXNETNODES]; #endif @@ -192,7 +192,7 @@ static UINT8 mynode; // my address pointofview server #ifdef SATURNPAK static boolean cl_redownloadinggamestate = false; -boolean is_client_saturn[MAXNETNODES]; +static boolean is_client_saturn[MAXNETNODES]; #endif #ifdef SATURNJOIN diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 430729bc2..396b2190c 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -654,9 +654,9 @@ void D_ResetTiccmds(void); tic_t GetLag(INT32 node); -extern UINT8 hu_resynching; +extern boolean hu_resynching; #ifdef SATURNPAK -extern UINT8 hu_redownloadinggamestate; +extern boolean hu_redownloadinggamestate; #endif extern boolean hu_stopped; // kart, true when the game is stopped for players due to a disconnecting or connecting player -- 2.54.0 From 2084cdc6d5679a779948d5002475b64866ec0d6b Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 25 Feb 2026 17:07:27 +0100 Subject: [PATCH 15/35] hw_md2: remove pointless float casts durs and tics are int32. and so is var2, anim_duration is uint16 but that still makes no sense lul --- src/hardware/hw_md2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 2fd33f65b..d9bf41645 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1336,8 +1336,8 @@ void HWR_DrawMD2(gl_vissprite_t *spr) if (spr->mobj->frame & FF_ANIMATE) { // set duration and tics to be the correct values for FF_ANIMATE states - durs = (float)spr->mobj->state->var2; - tics = (float)spr->mobj->anim_duration; + durs = spr->mobj->state->var2; + tics = (INT32)spr->mobj->anim_duration; } //FIXME: this is not yet correct -- 2.54.0 From 94026e43317cd66498022c7b2e687ffd557866c0 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 25 Feb 2026 17:44:56 +0100 Subject: [PATCH 16/35] makefile: comment out all the "Wno" flags we effectively fixed all of them, so this does not throw any warnings for me atleast in case we may screw up id rather have those warnings appear --- src/Makefile.d/versions.mk | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Makefile.d/versions.mk b/src/Makefile.d/versions.mk index c3989bb4f..cb13c286c 100644 --- a/src/Makefile.d/versions.mk +++ b/src/Makefile.d/versions.mk @@ -20,7 +20,8 @@ $(foreach v,$(join $(wordlist 2,$(_n),- $(gcc_versions)),\ CXXFLAGS+= -Wno-aggregate-return # -W -Wno-unused -WFLAGS:=-Wall -Wno-trigraphs -Wnull-dereference +WFLAGS:=-Wall -Wnull-dereference +#WFLAGS+=-Wno-trigraphs ifdef GCC152 WFLAGS+=-Wzero-as-null-pointer-constant endif @@ -31,7 +32,7 @@ ifndef RELAXWARNINGS WFLAGS+=-W #WFLAGS+=-Wno-sign-compare ifndef GCC295 - WFLAGS+=-Wno-div-by-zero +# WFLAGS+=-Wno-div-by-zero endif #WFLAGS+=-Wsystem-headers WFLAGS+=-Wfloat-equal @@ -62,7 +63,7 @@ endif endif WFLAGS+=-Wsign-compare ifdef GCC91 - WFLAGS+=-Wno-error=address-of-packed-member +# WFLAGS+=-Wno-error=address-of-packed-member endif ifdef GCC45 WFLAGS+=-Wlogical-op @@ -105,14 +106,14 @@ endif endif WFLAGS+=-Wformat-y2k ifdef GCC71 -WFLAGS+=-Wno-error=format-overflow=2 +#WFLAGS+=-Wno-error=format-overflow=2 endif WFLAGS+=-Wformat-security ifndef GCC29 #WFLAGS+=-Winit-self endif ifdef GCC46 -WFLAGS+=-Wno-suggest-attribute=noreturn +#WFLAGS+=-Wno-suggest-attribute=noreturn endif ifdef NOLDWARNING @@ -130,21 +131,21 @@ ifdef GCC44 #WFLAGS+=-Wno-error=array-bounds endif ifdef GCC46 - WFLAGS+=-Wno-error=suggest-attribute=noreturn +# WFLAGS+=-Wno-error=suggest-attribute=noreturn endif ifdef GCC54 - WFLAGS+=-Wno-logical-op -Wno-error=logical-op +# WFLAGS+=-Wno-logical-op -Wno-error=logical-op endif ifdef GCC61 - WFLAGS+=-Wno-tautological-compare -Wno-error=tautological-compare +# WFLAGS+=-Wno-tautological-compare -Wno-error=tautological-compare endif ifdef GCC71 WFLAGS+=-Wimplicit-fallthrough=4 endif ifdef GCC81 - WFLAGS+=-Wno-error=format-overflow - WFLAGS+=-Wno-format-overflow - WFLAGS+=-Wno-error=multistatement-macros +# WFLAGS+=-Wno-error=format-overflow +# WFLAGS+=-Wno-format-overflow +# WFLAGS+=-Wno-error=multistatement-macros endif ifneq (,$(filter $(CC) $(CXX),clang clang++)) @@ -183,6 +184,6 @@ endif # Lua ifdef GCC43 ifndef GCC44 -WFLAGS+=-Wno-logical-op +#WFLAGS+=-Wno-logical-op endif endif -- 2.54.0 From 1e51365cadaa3d6075b00eb8214a59618224755e Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 25 Feb 2026 17:43:08 +0100 Subject: [PATCH 17/35] add comment about K_KartUpdatePosition --- src/k_kart.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 0efd3a48b..2aa0377b2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6175,6 +6175,13 @@ void K_KartUpdatePosition(player_t *player) } } + // the player should NOT be checked against themselves! lol + // otherwise prevcheck will effectively be halved + // sadly this stuff is exposed to lua and atleast one script uses those so we cant fix it + // oh well + //if (player == &players[i]) + //continue; + const boolean isprevcheckpointi = mo->health == players[i].starpostnum; const boolean isnextcheckpointi = mo->health == (players[i].starpostnum + 1); -- 2.54.0 From fedccd25b997b5b79cc9f67ac9381c8a9a579238 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 25 Feb 2026 18:04:45 +0100 Subject: [PATCH 18/35] hw_cache: use verstable for HWR_PrecacheLevelFlats flatchecks + convert to C replaces the unordered_set and doesent require c++ --- src/hardware/{hw_cache.cpp => hw_cache.c} | 100 ++++++++++++---------- src/hardware/hw_data.h | 2 +- 2 files changed, 58 insertions(+), 44 deletions(-) rename src/hardware/{hw_cache.cpp => hw_cache.c} (92%) diff --git a/src/hardware/hw_cache.cpp b/src/hardware/hw_cache.c similarity index 92% rename from src/hardware/hw_cache.cpp rename to src/hardware/hw_cache.c index 09f4f1a7b..f7cfceecb 100644 --- a/src/hardware/hw_cache.cpp +++ b/src/hardware/hw_cache.c @@ -15,8 +15,6 @@ #ifdef HWRENDER -#include - #include "hw_main.h" #include "hw_glob.h" #include "hw_gl.h" @@ -100,7 +98,7 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm position = 0; } - position = std::max(((position * scale_y) + (FRACUNIT/2)) >> FRACBITS, 0); + position = max(((position * scale_y) + (FRACUNIT/2)) >> FRACBITS, 0); if (position + count >= pblockheight) count = pblockheight - position; @@ -164,7 +162,7 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap, fixed_t xfrac, xfracstep; fixed_t yfracstep, scale_y; const column_t *patchcol; - UINT8 *block = static_cast(mipmap->data); + UINT8 *block = (UINT8 *)(mipmap->data); INT32 bpp; INT32 blockmodulo; RGBA_t *palette; @@ -214,7 +212,7 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap, fixed_t xfrac, xfracstep; fixed_t yfracstep, scale_y; const column_t *patchcol; - UINT8 *block = static_cast(mipmap->data); + UINT8 *block = (UINT8 *)(mipmap->data); INT32 bpp; INT32 blockmodulo; INT32 width, height; @@ -290,7 +288,7 @@ static UINT8 *MakeBlock(GLMipmap_t *glMipmap) INT32 blocksize = (glMipmap->width * glMipmap->height); bpp = format2bpp(glMipmap->format); - block = static_cast(Z_Malloc(blocksize*bpp, PU_HWRCACHE, &(glMipmap->data))); + block = (UINT8 *)(Z_Malloc(blocksize*bpp, PU_HWRCACHE, &(glMipmap->data))); switch (bpp) { @@ -327,7 +325,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *gltex, boolean noe texture = textures[texnum]; gltex->mipmap.flags = TF_CHROMAKEYED | TF_WRAPXY; - gltex->mipmap.format = static_cast(textureformat); + gltex->mipmap.format = (GLTextureFormat_t)(textureformat); // hack the Legacy skies.. if (UNLIKELY(memcmp(texture->name, "SKY", 3) == 0 && @@ -346,7 +344,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *gltex, boolean noe colormap += COLORMAP_REMAPOFFSET; #endif - gltex->mipmap.colormap = static_cast(Z_Calloc(sizeof(*gltex->mipmap.colormap), PU_HWRPATCHCOLMIPMAP, NULL)); + gltex->mipmap.colormap = (GLColormap_t *)(Z_Calloc(sizeof(*gltex->mipmap.colormap), PU_HWRPATCHCOLMIPMAP, NULL)); gltex->mipmap.colormap->source = colormap; memcpy(gltex->mipmap.colormap->data, colormap, 256 * sizeof(UINT8)); @@ -379,7 +377,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *gltex, boolean noe // Composite the columns together. for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { - pdata = static_cast(W_CacheLumpNumPwad(patch->wad, patch->lump, PU_LEVEL)); + pdata = (UINT8 *)(W_CacheLumpNumPwad(patch->wad, patch->lump, PU_LEVEL)); realpatch = (softwarepatch_t *)pdata; HWR_DrawTexturePatchInCache(&gltex->mipmap, blockwidth, blockheight, texture, patch, realpatch); @@ -415,7 +413,7 @@ void HWR_MakePatch(const patch_t *patch, GLPatch_t *glPatch, GLMipmap_t *glMipma glMipmap->flags = 0; // setup the texture info - glMipmap->format = static_cast(patchformat); + glMipmap->format = (GLTextureFormat_t)(patchformat); glPatch->max_s = (float)patch->width / (float)glMipmap->width; glPatch->max_t = (float)patch->height / (float)glMipmap->height; @@ -457,7 +455,7 @@ static void HWR_FreeTextureData(patch_t *patch) if (!patch || !patch->hardware) return; - glPatch = static_cast(patch->hardware); + glPatch = (GLPatch_t *)(patch->hardware); if (vid.glstate == VID_GL_LIBRARY_LOADED) GL_DeleteTexture(glPatch->mipmap); @@ -473,7 +471,7 @@ void HWR_FreeTexture(patch_t *patch) if (patch->hardware) { - GLPatch_t *glPatch = static_cast(patch->hardware); + GLPatch_t *glPatch = (GLPatch_t *)(patch->hardware); HWR_FreeTextureColormaps(patch); @@ -499,7 +497,7 @@ static void HWR_FreeTextureColormaps(patch_t *patch) if (!patch) return; - pat = static_cast(patch->hardware); + pat = (GLPatch_t *)(patch->hardware); if (!pat) return; @@ -614,24 +612,20 @@ void HWR_FreeMapTextures(void) gl_numtextures = 0; } +#define NAME flatcheck +#define KEY_TY UINT32 +#define HASH_FN vt_hash_integer +#define CMPR_FN vt_cmpr_integer +#include "../verstable.h" + static void HWR_PrecacheLevelFlats(void) { // lookup for flats that may´ve been already loaded - // too lazy to make a hashtable or smth so unordered set will do :carbuncle: - std::unordered_set flatpresent; + flatcheck flatpresent; + flatcheck_init(&flatpresent); size_t i, j; INT32 k; - auto load_flat = [&](lumpnum_t lump, boolean noencoremap) - { - // flat already loaded? - if (flatpresent.count(lump) == 0) - { - HWR_GetFlat(lump, noencoremap); - flatpresent.insert(lump); - } - }; - // special case for encore #ifdef GLENCORE if (encoremap) @@ -649,14 +643,23 @@ static void HWR_PrecacheLevelFlats(void) const levelflat_t *levelflat = &levelflats[pic]; - load_flat(levelflat->lumpnum, R_NoEncore(sec, ceiling)); + if (flatcheck_is_end(flatcheck_get(&flatpresent, levelflat->lumpnum))) + { + HWR_GetFlat(levelflat->lumpnum, R_NoEncore(sec, ceiling)); + flatcheck_insert(&flatpresent, levelflat->lumpnum); + } if (levelflat->speed) // is it an animated flat ? { // start from 0, occasionally basenumlump != flat->lumpnum for (k = 0; k < levelflat->numpics; k++) { - load_flat(levelflat->baselumpnum + k, R_NoEncore(sec, ceiling)); + lumpnum_t lump = levelflat->baselumpnum + k; + if (flatcheck_is_end(flatcheck_get(&flatpresent, lump))) + { + HWR_GetFlat(lump, R_NoEncore(sec, ceiling)); + flatcheck_insert(&flatpresent, lump); + } } } } @@ -671,18 +674,29 @@ static void HWR_PrecacheLevelFlats(void) { const levelflat_t *levelflat = &levelflats[i]; - load_flat(levelflat->lumpnum, false); + if (flatcheck_is_end(flatcheck_get(&flatpresent, levelflat->lumpnum))) + { + HWR_GetFlat(levelflat->lumpnum, false); + flatcheck_insert(&flatpresent, levelflat->lumpnum); + } if (levelflat->speed) // is it an animated flat ? { // start from 0, occasionally basenumlump != flat->lumpnum for (k = 0; k < levelflat->numpics; k++) { - load_flat(levelflat->baselumpnum + k, false); + lumpnum_t lump = levelflat->baselumpnum + k; + if (flatcheck_is_end(flatcheck_get(&flatpresent, lump))) + { + HWR_GetFlat(lump, false); + flatcheck_insert(&flatpresent, lump); + } } } } } + + flatcheck_cleanup(&flatpresent); } static void HWR_PrecacheLevelTextures(void) @@ -692,7 +706,7 @@ static void HWR_PrecacheLevelTextures(void) size_t i, j, f; INT32 h; - texturepresent = static_cast(calloc(gl_numtextures, sizeof(*texturepresent))); + texturepresent = (char *)(calloc(gl_numtextures, sizeof(*texturepresent))); if (texturepresent == NULL) I_Error("%s: Out of memory looking up textures", "HWR_PrecacheLevel"); @@ -867,7 +881,7 @@ void HWR_LoadMapTextures(size_t pnumtextures) HWR_FreeMapTextures(); gl_numtextures = pnumtextures; - gl_textures = static_cast(calloc(gl_numtextures, sizeof(*gl_textures))); // *2 - 1 for encore-remapped texture and another for noencore texture (unused when not in encore), see: GLMapTextureSet_t + gl_textures = (GLMapTextureSet_t *)(calloc(gl_numtextures, sizeof(*gl_textures))); // *2 - 1 for encore-remapped texture and another for noencore texture (unused when not in encore), see: GLMapTextureSet_t if (gl_textures == NULL) I_Error("HWR_LoadMapTextures: ran out of memory for OpenGL textures. Sad!"); @@ -964,7 +978,7 @@ static void HWR_CacheFlat(GLMipmap_t *glMipmap, lumpnum_t flatlumpnum) W_ReadLump(flatlumpnum, Z_Malloc(size, PU_HWRCACHE, &glMipmap->data)); #ifdef GLENCORE - flat = static_cast(glMipmap->data); + flat = (UINT8 *)(glMipmap->data); for (steppy = 0; steppy < size; steppy++) { if (flat[steppy] == HWR_PATCHES_CHROMAKEY_COLORINDEX) @@ -1008,7 +1022,7 @@ void HWR_GetFlat(lumpnum_t flatlumpnum, boolean noencoremap) if (!noencoremap && encoremap) colormap += COLORMAP_REMAPOFFSET; #endif - glMipmap->colormap = static_cast(Z_Calloc(sizeof(*glMipmap->colormap), PU_HWRPATCHCOLMIPMAP, NULL)); + glMipmap->colormap = (GLColormap_t *)(Z_Calloc(sizeof(*glMipmap->colormap), PU_HWRPATCHCOLMIPMAP, NULL)); glMipmap->colormap->source = colormap; memcpy(glMipmap->colormap->data, colormap, 256 * sizeof(UINT8)); } @@ -1032,7 +1046,7 @@ void HWR_GetFlat(lumpnum_t flatlumpnum, boolean noencoremap) // --------------------+ static void HWR_LoadPatchMipmap(patch_t *patch, GLMipmap_t *glMipmap) { - GLPatch_t *glPatch = static_cast(patch->hardware); + GLPatch_t *glPatch = (GLPatch_t *)(patch->hardware); if (!glMipmap->downloaded) { @@ -1053,7 +1067,7 @@ static void HWR_LoadPatchMipmap(patch_t *patch, GLMipmap_t *glMipmap) // ----------------------+ static void HWR_UpdatePatchMipmap(patch_t *patch, GLMipmap_t *glMipmap) { - GLPatch_t *glPatch = static_cast(patch->hardware); + GLPatch_t *glPatch = (GLPatch_t *)(patch->hardware); HWR_MakePatch(patch, glPatch, glMipmap, true); // If hardware does not have the texture, then call GL_SetTexture to upload it @@ -1089,7 +1103,7 @@ void HWR_GetMappedPatch(patch_t *patch, const UINT8 *colormap) if (!patch->hardware) Patch_CreateGL(patch); - glPatch = static_cast(patch->hardware); + glPatch = (GLPatch_t *)(patch->hardware); // Blatant hack for encore colormapping aside... if (colormap == colormaps || colormap == NULL || colormap == (const UINT8*)(COLORMAP_REMAPOFFSET)) @@ -1125,12 +1139,12 @@ void HWR_GetMappedPatch(patch_t *patch, const UINT8 *colormap) // (it have a liste of mipmap) // this malloc is cleared in HWR_FreeColormapCache // (...) unfortunately z_malloc fragment alot the memory :(so malloc is better - newMipmap = static_cast(calloc(1, sizeof (*newMipmap))); + newMipmap = (GLMipmap_t *)(calloc(1, sizeof (*newMipmap))); if (newMipmap == NULL) I_Error("%s: Out of memory", "HWR_GetMappedPatch"); glMipmap->nextcolormap = newMipmap; - newMipmap->colormap = static_cast(Z_Calloc(sizeof(*newMipmap->colormap), PU_HWRPATCHCOLMIPMAP, NULL)); + newMipmap->colormap = (GLColormap_t *)(Z_Calloc(sizeof(*newMipmap->colormap), PU_HWRPATCHCOLMIPMAP, NULL)); newMipmap->colormap->source = colormap; memcpy(newMipmap->colormap->data, colormap, 256 * sizeof(UINT8)); @@ -1153,7 +1167,7 @@ patch_t *HWR_GetCachedGLPatchPwad(UINT16 wadnum, UINT16 lumpnum) { void *ptr = Z_Calloc(sizeof(patch_t), PU_PATCH, &lumpcache[lumpnum]); Patch_Create(NULL, 0, ptr); - Patch_AllocateHardwarePatch(static_cast(ptr)); + Patch_AllocateHardwarePatch((patch_t *)(ptr)); } return (patch_t *)(lumpcache[lumpnum]); @@ -1170,7 +1184,7 @@ static void HWR_DrawFadeMaskInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32 { INT32 i,j; fixed_t posx, posy, stepx, stepy; - UINT8 *block = static_cast(mipmap->data); // places the data directly into here + UINT8 *block = (UINT8 *)(mipmap->data); // places the data directly into here UINT8 *flat; UINT8 *dest, *src, texel; RGBA_t col; @@ -1326,7 +1340,7 @@ void HWR_SetPalette(RGBA_t *palette) static void HWR_SetPaletteLookup(RGBA_t *palette) { int r, g, b; - UINT8 *lut = static_cast(Z_Malloc( + UINT8 *lut = (UINT8 *)(Z_Malloc( HWR_PALETTE_LUT_SIZE*HWR_PALETTE_LUT_SIZE*HWR_PALETTE_LUT_SIZE*sizeof(UINT8), PU_STATIC, NULL)); #define STEP_SIZE (256/HWR_PALETTE_LUT_SIZE) @@ -1371,7 +1385,7 @@ void HWR_SetMapPalette(void) if (palsize < 768) // 256 * 3 I_Error("HWR_SetMapPalette: A programmer assumed palette lumps are at least 768 bytes long, but apparently this was a wrong assumption!\n"); - RGB_data = static_cast(W_CacheLumpNum(lumpnum, PU_CACHE)); + RGB_data = (UINT8 *)(W_CacheLumpNum(lumpnum, PU_CACHE)); // we got the RGB palette now, but we need it in RGBA format. for (i = 0; i < 256; i++) { @@ -1406,7 +1420,7 @@ static UINT32 HWR_CreateLightTable(UINT8 *lighttable) { UINT32 i, id; RGBA_t *palette = HWR_GetTexturePalette(); - RGBA_t *hw_lighttable = static_cast(Z_Malloc(256 * 32 * sizeof(RGBA_t), PU_STATIC, NULL)); + RGBA_t *hw_lighttable = (RGBA_t *)(Z_Malloc(256 * 32 * sizeof(RGBA_t), PU_STATIC, NULL)); // To make the palette index -> RGBA mapping easier for the shader, // the hardware lighttable is composed of RGBA colors instead of palette indices. diff --git a/src/hardware/hw_data.h b/src/hardware/hw_data.h index bb174b205..93ccfd4ac 100644 --- a/src/hardware/hw_data.h +++ b/src/hardware/hw_data.h @@ -54,7 +54,7 @@ struct GLMipmap_s UINT16 width, height; UINT32 downloaded; // The GPU has this texture. - struct GLMipmap_s *nextcolormap; + struct GLMipmap_s *nextcolormap; struct GLColormap_s *colormap; }; typedef struct GLMipmap_s GLMipmap_t; -- 2.54.0 From 1ec29c1c94579ffb9fc263bd202531b48daad8f0 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 25 Feb 2026 21:27:02 +0100 Subject: [PATCH 19/35] better flags for ASAN and UBSAN compile with -O1 and -fno-omit-frame-pointer -fno-common for ASAN --- src/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index be4f995db..372594384 100644 --- a/src/Makefile +++ b/src/Makefile @@ -269,13 +269,13 @@ endif ifdef USEASAN # compile with asan -opts+=-fsanitize=address +opts+=-O1 -fsanitize=address -fno-omit-frame-pointer -fno-common libs+=-fsanitize=address endif ifdef USEUBSAN -# compile with asan -opts+=-fsanitize=undefined +# compile with ubsan +opts+=-O1 -fsanitize=undefined -fno-omit-frame-pointer libs+=-fsanitize=undefined endif -- 2.54.0 From 3c70598aea71a51dd2abfe71f641e4e09ad11421 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 25 Feb 2026 22:38:36 +0100 Subject: [PATCH 20/35] lumpnumcache: W_LumpExists should return true or false whoops was oversight by me prob doesent really matter due to wraparound but its still incorrect --- src/w_wad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/w_wad.c b/src/w_wad.c index cfc25bbaa..5402e90a0 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1431,7 +1431,7 @@ UINT8 W_LumpExists(const char *name) // Check the lumpnumcache first. lumpnum_t cachenum = CheckLumpInCache(name); if (cachenum != LUMPERROR) - return cachenum; + return true; namelen = strlen(name); hash = W_HashLumpName(name); -- 2.54.0 From db4cd73912b65e26ea2542b845ae6b2671db8455 Mon Sep 17 00:00:00 2001 From: Alug Date: Thu, 26 Feb 2026 01:05:08 +0100 Subject: [PATCH 21/35] fix keepmusic resuming not resetting with "tunes" command this code sucks ass wtf was i cooking if this breaks again i will just wipe it lmao --- src/s_sound.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index f2ee1dc03..1e19c83fe 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1438,7 +1438,7 @@ void S_LoadMusicDefs(UINT16 wadnum) stoken = strtok(stoken, " "); if (stoken) { - if (! ReadMusicDefFields(wadnum, line, stoken, &def)) + if (!ReadMusicDefFields(wadnum, line, stoken, &def)) break; } @@ -1982,7 +1982,7 @@ static void S_SetKeepMusResume(void) { keepmusic.resume = 0; - if (fasticmp(music.name, mapmusic.name)) + if (fasticmp(music.name, keepmusic.name)) { keepmusic.resume = I_GetSongPosition(); } @@ -2078,9 +2078,12 @@ void S_InitMapMusic(void) S_ChangeMusicEx(mapmusic.name, mapmusic.flags, true, keepmusic.resume, 0, 500); } + keepmusic.resume = 0; return; } + keepmusic.resume = 0; + // Starting ambience should always be restarted // lug: but not when we keep the map music lol S_StopMusic(); @@ -2208,6 +2211,8 @@ static void Command_Tunes_f(void) mapmusic.position = position; mapmusic.resume = 0; + keepmusic.resume = 0; + S_ChangeMusicEx(mapmusic.name, mapmusic.flags, true, mapmusic.position, 0, 0); if (argc > 3) -- 2.54.0 From 6ff1cbf4b03c61ca2e4eac92834edec6347c7aee Mon Sep 17 00:00:00 2001 From: Alug Date: Thu, 26 Feb 2026 01:11:37 +0100 Subject: [PATCH 22/35] ok actually just remove keepmusic resume stuff well now it just broke with dynmusic this sucks, its probably fixable but the code was abysmal so shrug --- src/s_sound.c | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index 1e19c83fe..546c183a8 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -116,9 +116,7 @@ consvar_t cv_skipintromusic = {"skipintromusic", "No", CV_SAVE, CV_YesNo, NULL, boolean keepmapmusic = false; // keep the current music on map restart boolean skipintromus = false; // skip the intro fanfare -static boolean resumekeepmusic = false; static music_t keepmusic; -static void S_SetKeepMusResume(void); #ifdef HAVE_OPENMPT openmpt_module *openmpt_mhandle = NULL; @@ -1848,8 +1846,6 @@ void S_StopMusic(void) mapmusic.resume = (cv_resume.value && fasticmp(music.name, mapmusic.name)) ? I_GetSongPosition() : 0; - S_SetKeepMusResume(); - if (I_SongPaused()) I_ResumeSong(); @@ -1976,17 +1972,6 @@ void S_ResetKeepAndSpecialMus(void) keepmapmusic = skipintromus = false; } -// saves the current song position everytime a song stops -// so we can resume it in some cases -static void S_SetKeepMusResume(void) -{ - keepmusic.resume = 0; - - if (fasticmp(music.name, keepmusic.name)) - { - keepmusic.resume = I_GetSongPosition(); - } -} // determine if we should keep the music on a map restart // this gets called BEFORE the level gets loaded in G_DoLoadLevel @@ -1995,7 +1980,7 @@ void S_KeepMusic(void) static INT16 oldmap = -1; static boolean oldencore = false; - keepmapmusic = resumekeepmusic = false; + keepmapmusic = false; if (!cv_keepmusic.value) { @@ -2009,9 +1994,7 @@ void S_KeepMusic(void) if (oldmap == gamemap && oldencore == encoremode) { - const boolean musicchanged = S_CheckMusicException(); - resumekeepmusic = (musicchanged && keepmusic.resume); - keepmapmusic = (!musicchanged || resumekeepmusic); + keepmapmusic = (S_CheckMusicException() == false); } oldencore = encoremode; @@ -2072,18 +2055,9 @@ void S_InitMapMusic(void) if (keepmapmusic) { - // this is kinda silly, but we can use it to fade back into the map song at the saved point, should the current music be different from the map music - if (resumekeepmusic) - { - S_ChangeMusicEx(mapmusic.name, mapmusic.flags, true, keepmusic.resume, 0, 500); - } - - keepmusic.resume = 0; return; } - keepmusic.resume = 0; - // Starting ambience should always be restarted // lug: but not when we keep the map music lol S_StopMusic(); @@ -2211,8 +2185,6 @@ static void Command_Tunes_f(void) mapmusic.position = position; mapmusic.resume = 0; - keepmusic.resume = 0; - S_ChangeMusicEx(mapmusic.name, mapmusic.flags, true, mapmusic.position, 0, 0); if (argc > 3) -- 2.54.0 From 5577ba4c7ac98cb11339f4b416801ac100430c82 Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 1 Mar 2026 00:03:19 +0100 Subject: [PATCH 23/35] Autoload: dont add wads that contain maps --- src/d_main.c | 11 ++++++++++- src/w_wad.c | 13 ++++++++++++- src/w_wad.h | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 2832d6c6a..1a03b1972 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1124,9 +1124,18 @@ static void D_FindAddonsToAutoload(void) if (newline) *newline = '\0'; + // ok so theres still issues with players adding stuff to autoload by accident + // sometimes this stuff contains maps + // you can probably imagine how that goes in a netgame.... + if (W_CheckAutoLoadContainsMap(wadsToAutoload)) + { + CONS_Printf("Autoload: file %s contains map data! skipping...", wadsToAutoload); + continue; + } + if (!postload && W_CheckPostLoadList(wadsToAutoload)) { - CONS_Printf("forcing postload for file %s\n", wadsToAutoload); + CONS_Printf("Autoload: forcing postload for file %s\n", wadsToAutoload); postload = true; } diff --git a/src/w_wad.c b/src/w_wad.c index 8cc3fd214..4a1c1247a 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -2576,6 +2576,18 @@ static int W_CheckFileContains(const char *filename, lumpchecklist_t *checklist) return contains; } +int W_CheckAutoLoadContainsMap(const char *filename) +{ + // for now this checks for map marker + static lumpchecklist_t autoloadblacklist[] = + { + {"MAP", 3}, + {NULL, 0}, + }; + + return W_CheckFileContains(filename, autoloadblacklist); +} + int W_CheckPostLoadList(const char *filename) { static lumpchecklist_t postloadlist[] = @@ -2588,7 +2600,6 @@ int W_CheckPostLoadList(const char *filename) {"SOC_", 4}, {"MAINCFG", 7}, {"OBJCTCFG", 8}, - {"MAP", 3}, {NULL, 0}, }; diff --git a/src/w_wad.h b/src/w_wad.h index 6240c31f3..98c57173e 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -243,6 +243,7 @@ void W_VerifyFileMD5(UINT16 wadfilenum, const char *matchmd5); int W_VerifyNMUSlumps(const char *filename, FILE *handle, boolean exit_on_error); +int W_CheckAutoLoadContainsMap(const char *filename); int W_CheckPostLoadList(const char *filename); #ifdef __cplusplus -- 2.54.0 From 1cf04514b3b67685789182c83b95907be80a2154 Mon Sep 17 00:00:00 2001 From: Indev Date: Sun, 1 Mar 2026 02:31:11 +0300 Subject: [PATCH 24/35] Fix W_MakeFileMD5 printing incorrect time --- src/w_wad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/w_wad.c b/src/w_wad.c index 4a1c1247a..3c37bf185 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -339,7 +339,7 @@ static inline INT32 W_MakeFileMD5(const char *filename, void *resblock) if ((fhandle = fopen(filename, "rb")) != NULL) { - tic_t t = I_GetTime(); + precise_t t = I_GetPreciseTime(); CONS_Debug(DBG_SETUP, "Making MD5 for %s\n",filename); @@ -350,7 +350,7 @@ static inline INT32 W_MakeFileMD5(const char *filename, void *resblock) } CONS_Debug(DBG_SETUP, "MD5 calc for %s took %f seconds\n", - filename, (float)(I_GetTime() - t)/NEWTICRATE); + filename, (float)(I_GetPreciseTime() - t)/I_GetPrecisePrecision()); fclose(fhandle); return 0; -- 2.54.0 From 5ca3ab5966f2d221db802d0ed1c0d99d6a1e100a Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 1 Mar 2026 00:57:44 +0100 Subject: [PATCH 25/35] Autoload: fix incorrectly incrementing wad count those should only be incremented if we actually add something to the filearray and not instantly execute or error out --- src/d_main.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 1a03b1972..85ef4d226 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -130,8 +130,8 @@ static size_t startuppwadcount = 0; // autoloading static char *autoloadwadfiles[MAX_WADFILES]; static char *autoloadwadfilespost[MAX_WADFILES]; -static size_t autoloadcount = 0; -static size_t postloadcount = 0; +static size_t autoloadwadcount = 0; +static size_t postloadwadcount = 0; // boolean devparm = false; // started game with -devparm @@ -1037,7 +1037,7 @@ static INT32 D_DetectFileType(const char* filename) } // autoload that shit -static void D_AutoloadFile(const char *file, char **filearray, size_t index) +static void D_AutoloadFile(const char *file, char **filearray, size_t *index) { char *newfile; INT32 fileType = D_DetectFileType(file); @@ -1052,10 +1052,11 @@ static void D_AutoloadFile(const char *file, char **filearray, size_t index) { newfile = malloc(strlen(file) + 1); if (!newfile) - I_Error("No more free memory to AutoloadFile %s",file); + I_Error("D_AutoloadFile: No more free memory to autoload file %s", file); strcpy(newfile, file); - filearray[index] = newfile; + filearray[*index] = newfile; + (*index)++; } else { @@ -1098,7 +1099,7 @@ static void D_FindAddonsToAutoload(void) // If the file is found, run our shit if (!autoloadconfigfile) // nope outta here { - autoloadcount = postloadcount = 0; // so D_AddAutoloadFiles can skip everything since nothings there to autoload + autoloadwadcount = postloadwadcount = 0; // so D_AddAutoloadFiles can skip everything since nothings there to autoload return; } @@ -1142,18 +1143,16 @@ static void D_FindAddonsToAutoload(void) // LOAD IT if (!postload) { - D_AutoloadFile(wadsToAutoload, autoloadwadfiles, autoloadcount); - autoloadcount++; + D_AutoloadFile(wadsToAutoload, autoloadwadfiles, &autoloadwadcount); } else { - D_AutoloadFile(wadsToAutoload, autoloadwadfilespost, postloadcount); - postloadcount++; + D_AutoloadFile(wadsToAutoload, autoloadwadfilespost, &postloadwadcount); } } - autoloadwadfiles[autoloadcount] = NULL; - autoloadwadfilespost[postloadcount] = NULL; + autoloadwadfiles[autoloadwadcount] = NULL; + autoloadwadfilespost[postloadwadcount] = NULL; // we dont want memory leaks around here do we? fclose(autoloadconfigfile); @@ -1162,7 +1161,7 @@ static void D_FindAddonsToAutoload(void) static void D_AddAutoloadFiles(void) { // nothing to autoload - if (autoloadcount == 0) + if (autoloadwadcount == 0) return; CONS_Printf("D_AutoloadFile(): Loading autoloaded addons...\n"); @@ -1170,15 +1169,15 @@ static void D_AddAutoloadFiles(void) if (W_AddAutoloadedLocalFiles(autoloadwadfiles) == 0) CONS_Printf("D_AutoloadFile(): Are you sure you put in valid files or what?\n"); - D_CleanFile(autoloadwadfiles, autoloadcount); + D_CleanFile(autoloadwadfiles, autoloadwadcount); - autoloadcount = 0; + autoloadwadcount = 0; } void D_AddPostloadFiles(void) { // nothing to postload - if (postloadcount == 0 || !netgame) + if (postloadwadcount == 0 || !netgame) return; CONS_Printf("D_AddPostloadFiles(): Loading postloaded addons...\n"); @@ -1186,9 +1185,9 @@ void D_AddPostloadFiles(void) if (W_AddAutoloadedLocalFiles(autoloadwadfilespost) == 0) CONS_Printf("D_AddPostloadFiles(): Are you sure you put in valid files or what?\n"); - D_CleanFile(autoloadwadfilespost, postloadcount); + D_CleanFile(autoloadwadfilespost, postloadwadcount); - postloadcount = 0; + postloadwadcount = 0; } // ========================================================================== -- 2.54.0 From e8c7cc6a9b57ffba849ff6ccaa3935ea2633baa0 Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 1 Mar 2026 00:59:51 +0100 Subject: [PATCH 26/35] D_AutoloadFile: add overflow safeguard --- src/d_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/d_main.c b/src/d_main.c index 85ef4d226..c312e3f59 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1042,6 +1042,12 @@ static void D_AutoloadFile(const char *file, char **filearray, size_t *index) char *newfile; INT32 fileType = D_DetectFileType(file); + if (*index >= MAX_WADFILES) + { + CONS_Printf("D_AutoloadFile: Failed to add file %s! Too many autoloaded files\n", file); + return; + } + if (!fileType) { CONS_Printf("D_AutoloadFile: File %s is unknown or invalid\n", file); -- 2.54.0 From 1a9fa94cbd44d129af33b395bfe09acc3785f9fb Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 1 Mar 2026 01:01:20 +0100 Subject: [PATCH 27/35] Autoload: dont attempt to load directories lol --- src/d_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/d_main.c b/src/d_main.c index c312e3f59..0ecce2047 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1054,6 +1054,12 @@ static void D_AutoloadFile(const char *file, char **filearray, size_t *index) return; } + if (fileType == 1) + { + CONS_Printf("D_AutoloadFile: File %s is a directory\n", file); + return; + } + if (fileType <= 6) { newfile = malloc(strlen(file) + 1); -- 2.54.0 From 397fb071e5eb9edfc4ca10b92770ae48d95d3c4b Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 1 Mar 2026 21:24:09 +0100 Subject: [PATCH 28/35] fix compiling with valgrind support --- src/z_zone.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/z_zone.cpp b/src/z_zone.cpp index 9e5b4f41e..f25f99e43 100644 --- a/src/z_zone.cpp +++ b/src/z_zone.cpp @@ -523,11 +523,19 @@ void Z_CheckHeap(INT32 tag) #ifdef VALGRIND_MEMPOOL_EXISTS if (!VALGRIND_MEMPOOL_EXISTS(block)) { - I_Error("Z_CheckHeap %d: block %u" + I_Error("Z_CheckHeap :" +#ifdef ZDEBUG + " %s %d" +#endif + " block %u" #ifdef ZDEBUG " (owned by %s:%d)" #endif - " should not exist", i, blocknumon + " should not exist" +#ifdef ZDEBUG + , file, line +#endif + , blocknumon #ifdef ZDEBUG , block->ownerfile, block->ownerline #endif -- 2.54.0 From 82471339664b62115eb15cd9f1d44e7144acb4d5 Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 1 Mar 2026 21:32:25 +0100 Subject: [PATCH 29/35] w_wad: use strnlen to check lump length for map marker lump name may not be null terminated here, so strlen may read out of bounds garbo using strnlen ensures we read max 8 characters which is the name buffer size --- src/w_wad.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/w_wad.c b/src/w_wad.c index 3c37bf185..ef9bd471e 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -2660,11 +2660,12 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) { if (vsizecache[realentry] == 0) continue; + vlumps[i].size = vsizecache[realentry]; const char *name = (fileinfo + realentry)->name; - if (strlen(name) == 5 && memcmp(name, "MAP", 3) == 0) + if (strnlen(name, 8) == 5 && memcmp(name, "MAP", 3) == 0) { numlumps--; // We skip map marker, so 1 of entries becomes empty continue; // This will skip i++ so we will write to same entry -- 2.54.0 From f2b46fd1484e573babc3ab0f6e6da1843c942408 Mon Sep 17 00:00:00 2001 From: "X.organic" Date: Tue, 6 Apr 2021 01:01:33 +0200 Subject: [PATCH 30/35] Fix myhashfgets-related buffer overflows in deh_soc.c --- src/dehacked.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index a3513fbbe..6b09f0d5b 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -115,20 +115,25 @@ char *myfgets(char *buf, size_t bufsize, MYFILE *f) size_t i = 0; if (myfeof(f)) return NULL; + // we need one byte for a null terminated string bufsize--; + while (i < bufsize && !myfeof(f)) { char c = *f->curpos++; + if (c == '^') buf[i++] = myfget_color(f); else if (c == '\\') buf[i++] = myfget_hex(f); else if (c != '\r') buf[i++] = c; + if (c == '\n') break; } + buf[i] = '\0'; dbg_line++; @@ -138,25 +143,31 @@ char *myfgets(char *buf, size_t bufsize, MYFILE *f) static char *myhashfgets(char *buf, size_t bufsize, MYFILE *f) { size_t i = 0; + if (myfeof(f)) return NULL; + // we need one byte for a null terminated string bufsize--; + while (i < bufsize && !myfeof(f)) { char c = *f->curpos++; + if (c == '^') buf[i++] = myfget_color(f); else if (c == '\\') buf[i++] = myfget_hex(f); else if (c != '\r') buf[i++] = c; + if (c == '\n') // Ensure debug line is right... dbg_line++; + if (c == '#') break; } - i++; + buf[i] = '\0'; return buf; @@ -315,7 +326,10 @@ static void readPlayer(MYFILE *f, INT32 num) goto done; PlayerMenu[num].status = IT_CALL; - for (i = 0; i < MAXLINELEN-3; i++) + // A friendly neighborhood alias for brevity's sake + const size_t note_size = sizeof(description[num].notes); + + for (i = 0; i < (INT32)(MAXLINELEN-note_size-3); i++) { if (s[i] == '=') { @@ -323,10 +337,12 @@ static void readPlayer(MYFILE *f, INT32 num) break; } } + if (playertext) { - strcpy(description[num].notes, playertext); - strcat(description[num].notes, myhashfgets(playertext, sizeof (description[num].notes), f)); + strlcpy(description[num].notes, playertext, note_size); + strlcat(description[num].notes, + myhashfgets(playertext, note_size, f), note_size); } else strcpy(description[num].notes, ""); @@ -335,7 +351,7 @@ static void readPlayer(MYFILE *f, INT32 num) // It works down here, though. { INT32 numline = 0; - for (i = 0; (size_t)i < sizeof(description[num].notes)-1; i++) + for (i = 0; (size_t)i < note_size-1; i++) { if (numline < 20 && description[num].notes[i] == '\n') numline++; @@ -344,6 +360,7 @@ static void readPlayer(MYFILE *f, INT32 num) break; } } + description[num].notes[strlen(description[num].notes)-1] = '\0'; description[num].notes[i] = '\0'; continue; -- 2.54.0 From 07fc0eb03abd21ef36f96d345baf83f773ae7192 Mon Sep 17 00:00:00 2001 From: Alug Date: Mon, 2 Mar 2026 00:34:58 +0100 Subject: [PATCH 31/35] v_video: fix cv_palettenum bounds check mhhm yeah --- src/v_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v_video.c b/src/v_video.c index 9898e9c29..fcbeded5f 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -555,7 +555,7 @@ void V_SetPalette(INT32 palettenum) { palettenum = cv_palettenum.value; - if (palettenum * 256U > currentPaletteSize - 256) + if (palettenum * 256U > (currentPaletteSize - 256) / 3) { CONS_Alert(CONS_WARNING, "cv_palettenum %d out of range\n", palettenum); palettenum = 0; -- 2.54.0 From efe873218811aea5d718a96dc63726c96aeb4347 Mon Sep 17 00:00:00 2001 From: Alug Date: Mon, 2 Mar 2026 00:43:33 +0100 Subject: [PATCH 32/35] fix flashpals throwing random garbage into the renderer in some cases iirc, if the provided palette currently in use did not provide subpalettes, this will read past the bounds of allocated memory, which aint good lol i just dumped the white flashpal and threw it into there since idk if -file or autoload gets called before the palette gets initialised, which would carry a risk of copying more garbage, hence this big ass array --- src/v_video.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/src/v_video.c b/src/v_video.c index fcbeded5f..2aa680153 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -92,6 +92,97 @@ consvar_t cv_menucaps = {"menucaps", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NUL RGBA_t *pLocalPalette = NULL; RGBA_t *pGammaCorrectedPalette = NULL; +// huge ass array les gooooooo! +// this is basically the white flashpal byte for byte from our main palette lmao +static RGBA_t pFallbackFlashPal[] = { + {.s={253, 253, 253, 255}}, {.s={251, 251, 251, 255}}, {.s={249, 249, 249, 255}}, + {.s={247, 247, 247, 255}}, {.s={245, 245, 245, 255}}, {.s={243, 243, 243, 255}}, + {.s={241, 241, 241, 255}}, {.s={239, 239, 239, 255}}, {.s={237, 237, 237, 255}}, + {.s={235, 235, 235, 255}}, {.s={233, 233, 233, 255}}, {.s={231, 231, 231, 255}}, + {.s={229, 229, 229, 255}}, {.s={227, 227, 227, 255}}, {.s={225, 225, 225, 255}}, + {.s={223, 223, 223, 255}}, {.s={221, 221, 221, 255}}, {.s={219, 219, 219, 255}}, + {.s={217, 217, 217, 255}}, {.s={215, 215, 215, 255}}, {.s={213, 213, 213, 255}}, + {.s={211, 211, 211, 255}}, {.s={209, 209, 209, 255}}, {.s={207, 207, 207, 255}}, + {.s={205, 205, 205, 255}}, {.s={203, 203, 203, 255}}, {.s={201, 201, 201, 255}}, + {.s={199, 199, 199, 255}}, {.s={197, 197, 197, 255}}, {.s={195, 195, 195, 255}}, + {.s={193, 193, 193, 255}}, {.s={192, 192, 192, 255}}, {.s={239, 233, 227, 255}}, + {.s={237, 231, 225, 255}}, {.s={235, 229, 223, 255}}, {.s={233, 227, 221, 255}}, + {.s={231, 225, 219, 255}}, {.s={229, 223, 217, 255}}, {.s={227, 221, 215, 255}}, + {.s={225, 220, 214, 255}}, {.s={223, 218, 212, 255}}, {.s={220, 216, 210, 255}}, + {.s={218, 214, 208, 255}}, {.s={216, 212, 206, 255}}, {.s={214, 210, 204, 255}}, + {.s={212, 208, 202, 255}}, {.s={210, 206, 200, 255}}, {.s={208, 204, 198, 255}}, + {.s={239, 222, 210, 255}}, {.s={236, 220, 209, 255}}, {.s={234, 219, 208, 255}}, + {.s={232, 218, 207, 255}}, {.s={230, 216, 206, 255}}, {.s={227, 215, 205, 255}}, + {.s={225, 213, 204, 255}}, {.s={223, 212, 203, 255}}, {.s={221, 211, 202, 255}}, + {.s={218, 209, 201, 255}}, {.s={215, 208, 200, 255}}, {.s={212, 207, 199, 255}}, + {.s={210, 205, 198, 255}}, {.s={207, 203, 197, 255}}, {.s={204, 202, 196, 255}}, + {.s={202, 200, 195, 255}}, {.s={255, 250, 247, 255}}, {.s={255, 248, 244, 255}}, + {.s={255, 246, 241, 255}}, {.s={255, 244, 238, 255}}, {.s={255, 243, 236, 255}}, + {.s={255, 241, 233, 255}}, {.s={255, 239, 230, 255}}, {.s={255, 238, 228, 255}}, + {.s={255, 236, 224, 255}}, {.s={253, 234, 222, 255}}, {.s={251, 232, 220, 255}}, + {.s={249, 230, 218, 255}}, {.s={247, 228, 216, 255}}, {.s={245, 226, 214, 255}}, + {.s={243, 224, 212, 255}}, {.s={242, 223, 211, 255}}, {.s={255, 250, 246, 255}}, + {.s={255, 246, 237, 255}}, {.s={255, 242, 229, 255}}, {.s={255, 237, 220, 255}}, + {.s={255, 233, 213, 255}}, {.s={255, 229, 205, 255}}, {.s={255, 225, 198, 255}}, + {.s={255, 220, 192, 255}}, {.s={252, 218, 192, 255}}, {.s={248, 216, 192, 255}}, + {.s={245, 214, 192, 255}}, {.s={242, 213, 192, 255}}, {.s={238, 211, 192, 255}}, + {.s={235, 209, 192, 255}}, {.s={232, 207, 192, 255}}, {.s={229, 205, 192, 255}}, + {.s={255, 255, 251, 255}}, {.s={255, 255, 243, 255}}, {.s={255, 255, 235, 255}}, + {.s={255, 255, 227, 255}}, {.s={255, 255, 219, 255}}, {.s={255, 255, 211, 255}}, + {.s={255, 255, 203, 255}}, {.s={255, 255, 195, 255}}, {.s={255, 255, 192, 255}}, + {.s={243, 243, 192, 255}}, {.s={235, 235, 192, 255}}, {.s={227, 227, 192, 255}}, + {.s={219, 219, 192, 255}}, {.s={211, 211, 192, 255}}, {.s={203, 203, 192, 255}}, + {.s={195, 195, 192, 255}}, {.s={255, 255, 220, 255}}, {.s={250, 246, 213, 255}}, + {.s={245, 238, 208, 255}}, {.s={240, 230, 203, 255}}, {.s={235, 222, 199, 255}}, + {.s={230, 214, 196, 255}}, {.s={225, 208, 193, 255}}, {.s={220, 202, 192, 255}}, + {.s={255, 255, 255, 255}}, {.s={255, 247, 247, 255}}, {.s={255, 239, 239, 255}}, + {.s={255, 231, 231, 255}}, {.s={255, 223, 223, 255}}, {.s={255, 215, 215, 255}}, + {.s={255, 207, 207, 255}}, {.s={255, 199, 199, 255}}, {.s={255, 192, 192, 255}}, + {.s={251, 192, 192, 255}}, {.s={247, 192, 192, 255}}, {.s={243, 192, 192, 255}}, + {.s={239, 192, 192, 255}}, {.s={235, 192, 192, 255}}, {.s={231, 192, 192, 255}}, + {.s={227, 192, 192, 255}}, {.s={223, 192, 192, 255}}, {.s={219, 192, 192, 255}}, + {.s={215, 192, 192, 255}}, {.s={211, 192, 192, 255}}, {.s={207, 192, 192, 255}}, + {.s={203, 192, 192, 255}}, {.s={199, 192, 192, 255}}, {.s={195, 192, 192, 255}}, + {.s={255, 237, 237, 255}}, {.s={252, 232, 232, 255}}, {.s={249, 227, 227, 255}}, + {.s={246, 222, 222, 255}}, {.s={242, 218, 218, 255}}, {.s={239, 214, 214, 255}}, + {.s={236, 211, 211, 255}}, {.s={233, 207, 207, 255}}, {.s={227, 203, 192, 255}}, + {.s={225, 201, 192, 255}}, {.s={223, 199, 192, 255}}, {.s={220, 198, 192, 255}}, + {.s={218, 196, 192, 255}}, {.s={216, 194, 192, 255}}, {.s={214, 193, 192, 255}}, + {.s={213, 192, 192, 255}}, {.s={221, 255, 211, 255}}, {.s={219, 251, 210, 255}}, + {.s={217, 247, 209, 255}}, {.s={215, 243, 208, 255}}, {.s={214, 239, 206, 255}}, + {.s={212, 235, 205, 255}}, {.s={210, 231, 204, 255}}, {.s={208, 227, 203, 255}}, + {.s={206, 223, 201, 255}}, {.s={205, 219, 200, 255}}, {.s={203, 215, 199, 255}}, + {.s={201, 211, 198, 255}}, {.s={199, 207, 196, 255}}, {.s={197, 203, 195, 255}}, + {.s={195, 199, 194, 255}}, {.s={193, 195, 192, 255}}, {.s={246, 255, 233, 255}}, + {.s={241, 248, 228, 255}}, {.s={234, 241, 223, 255}}, {.s={228, 234, 218, 255}}, + {.s={222, 228, 213, 255}}, {.s={216, 221, 208, 255}}, {.s={210, 214, 203, 255}}, + {.s={204, 207, 199, 255}}, {.s={192, 255, 192, 255}}, {.s={192, 247, 192, 255}}, + {.s={192, 239, 192, 255}}, {.s={192, 231, 192, 255}}, {.s={192, 223, 192, 255}}, + {.s={192, 215, 192, 255}}, {.s={192, 207, 192, 255}}, {.s={192, 199, 192, 255}}, + {.s={255, 219, 255, 255}}, {.s={255, 192, 255, 255}}, {.s={247, 192, 247, 255}}, + {.s={239, 192, 239, 255}}, {.s={231, 192, 231, 255}}, {.s={223, 192, 223, 255}}, + {.s={215, 192, 215, 255}}, {.s={207, 192, 207, 255}}, {.s={249, 249, 252, 255}}, + {.s={240, 240, 247, 255}}, {.s={230, 230, 242, 255}}, {.s={221, 221, 238, 255}}, + {.s={212, 212, 233, 255}}, {.s={208, 208, 224, 255}}, {.s={203, 203, 214, 255}}, + {.s={198, 198, 204, 255}}, {.s={244, 251, 255, 255}}, {.s={239, 250, 255, 255}}, + {.s={234, 248, 255, 255}}, {.s={228, 246, 255, 255}}, {.s={223, 244, 255, 255}}, + {.s={218, 243, 255, 255}}, {.s={213, 241, 255, 255}}, {.s={207, 239, 255, 255}}, + {.s={205, 230, 247, 255}}, {.s={203, 227, 239, 255}}, {.s={201, 221, 231, 255}}, + {.s={199, 215, 223, 255}}, {.s={192, 239, 239, 255}}, {.s={192, 223, 223, 255}}, + {.s={192, 215, 215, 255}}, {.s={192, 207, 207, 255}}, {.s={249, 249, 255, 255}}, + {.s={240, 240, 255, 255}}, {.s={234, 234, 255, 255}}, {.s={226, 226, 255, 255}}, + {.s={220, 220, 255, 255}}, {.s={212, 212, 255, 255}}, {.s={204, 204, 255, 255}}, + {.s={197, 197, 255, 255}}, {.s={192, 192, 255, 255}}, {.s={192, 192, 249, 255}}, + {.s={192, 192, 242, 255}}, {.s={192, 192, 236, 255}}, {.s={192, 192, 230, 255}}, + {.s={192, 192, 224, 255}}, {.s={192, 192, 218, 255}}, {.s={192, 192, 212, 255}}, + {.s={192, 192, 211, 255}}, {.s={192, 192, 207, 255}}, {.s={192, 192, 205, 255}}, + {.s={192, 192, 201, 255}}, {.s={192, 192, 199, 255}}, {.s={192, 192, 195, 255}}, + {.s={192, 192, 193, 255}}, {.s={192, 255, 255, 255}}, {.s={243, 223, 243, 255}}, + {.s={237, 219, 237, 255}}, {.s={231, 215, 231, 255}}, {.s={225, 211, 225, 255}}, + {.s={219, 207, 219, 255}}, {.s={213, 203, 213, 255}}, {.s={207, 199, 207, 255}}, + {.s={201, 195, 201, 255}} +}; + static size_t currentPaletteSize; /* @@ -543,6 +634,7 @@ void V_ReloadPalette(void) // -------------+ void V_SetPalette(INT32 palettenum) { + RGBA_t *pal = NULL; if (!pLocalPalette) V_ReloadPalette(); @@ -563,15 +655,31 @@ void V_SetPalette(INT32 palettenum) } } + // if the palettenum goes beyond our current palette, then just fallback to the white flash pal + // our srb2 palette mostly just contains that as subpalettes, so everything uses this + // and makes it a good fallback case + // otherwise we will throw random garbage to the renderer which calls memcpy on it + // which is not really a good thing lol + // we probably could just memcpy from localpalette, but i dont trust -file nor autoload so + if (palettenum * 256U > (currentPaletteSize - 256) / 3) + { + CONS_Debug(DBG_RENDER, "palettenum %d out of range\n", palettenum); + pal = pFallbackFlashPal; + } + else + { + pal = &pLocalPalette[palettenum*256]; + } + #ifdef HWRENDER if (rendermode == render_opengl) - HWR_SetPalette(&pLocalPalette[palettenum*256]); + HWR_SetPalette(pal); #if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL) else #endif #endif if (rendermode != render_none) - I_SetPalette(&pLocalPalette[palettenum*256]); + I_SetPalette(pal); } void V_SetPaletteLump(const char *pal) -- 2.54.0 From 16cf4ede89f511967808b5e8737a9529ba891aa8 Mon Sep 17 00:00:00 2001 From: Alug Date: Mon, 2 Mar 2026 08:31:27 +0100 Subject: [PATCH 33/35] v_video: generate missing flashpals instead not sure if this is more sane than the huge ass array from before but atleast this is more accurate and can basically generate flashpals from any palette while also still working with all the colour and gamma adjustments prob needs some cleanup idk this is all rather messy lol --- src/hardware/hw_main.cpp | 4 +- src/hardware/hw_main.h | 6 - src/m_menudefs.c | 2 - src/st_stuff.c | 2 +- src/v_paldeltas.h | 525 +++++++++++++++++++++++++++++++++++++++ src/v_video.c | 196 +++++++-------- 6 files changed, 614 insertions(+), 121 deletions(-) create mode 100644 src/v_paldeltas.h diff --git a/src/hardware/hw_main.cpp b/src/hardware/hw_main.cpp index 15434ec20..e6ce072cf 100644 --- a/src/hardware/hw_main.cpp +++ b/src/hardware/hw_main.cpp @@ -242,7 +242,6 @@ consvar_t cv_glportals = {"gr_portals", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, consvar_t cv_glpaletterendering = {"gr_paletteshader", "Off", CV_CALL|CV_SAVE, CV_OnOff, CV_glpaletterendering_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_glpalettedepth = {"gr_palettedepth", "16 bits", CV_SAVE|CV_CALL, glpalettedepth_cons_t, CV_glpalettedepth_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_glflashpal = {"gr_flashpal", "On", CV_CALL|CV_SAVE, CV_OnOff, CV_glpaletterendering_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_gltexturedepth = {"gr_texturedepth", "32 bits", CV_CALL|CV_SAVE, gltexdepth_cons_t, CV_gltextureformat_OnChange, 0, NULL, NULL, 0, 0, NULL}; @@ -5913,7 +5912,6 @@ void HWR_AddCommands(void) CV_RegisterVar(&cv_glpaletterendering); CV_RegisterVar(&cv_glpalettedepth); - CV_RegisterVar(&cv_glflashpal); } // -------------------------------------------------------------------------- @@ -6070,7 +6068,7 @@ static void HWR_DoPostProcessor(void) { // Armageddon Blast Flash! // Could this even be considered postprocessor? - if (!HWR_PalRenderFlashpal()) + if (!HWR_ShouldUsePaletteRendering()) { const player_t *player = &players[displayplayers[viewssnum]]; diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 8b5284acb..8a501f727 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -208,7 +208,6 @@ extern consvar_t cv_glportals; extern consvar_t cv_glpaletterendering; extern consvar_t cv_glpalettedepth; -extern consvar_t cv_glflashpal; FUNCINLINE static ATTRINLINE boolean HWR_UseShader(void) { @@ -220,11 +219,6 @@ FUNCINLINE static ATTRINLINE boolean HWR_ShouldUsePaletteRendering(void) return (cv_glpaletterendering.value && (pLocalPalette != NULL) && HWR_UseShader()); } -FUNCINLINE static ATTRINLINE boolean HWR_PalRenderFlashpal(void) -{ - return (cv_glflashpal.value && HWR_ShouldUsePaletteRendering()); -} - // Returns a pointer to the palette which should be used for caching textures. FUNCINLINE static ATTRINLINE RGBA_t *HWR_GetTexturePalette(void) { diff --git a/src/m_menudefs.c b/src/m_menudefs.c index 00b927efd..aa3f186c8 100644 --- a/src/m_menudefs.c +++ b/src/m_menudefs.c @@ -1023,8 +1023,6 @@ static menuitem_t OP_OpenGLOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Fallback Player 3D Model", &cv_glfallbackplayermodel, 20}, {IT_STRING | IT_CVAR, NULL, "Shaders", &cv_glshaders, 25}, {IT_STRING | IT_CVAR, NULL, "Palette Rendering", &cv_glpaletterendering, 30}, - {IT_STRING | IT_CVAR, NULL, "Palette Rendering Flashpals",&cv_glflashpal, 35}, - //{IT_STRING | IT_CVAR, NULL, "Min Shader Brightness", &cv_glsecbright, 40}, {IT_STRING | IT_CVAR, NULL, "Texture Quality", &cv_gltexturedepth, 45}, {IT_STRING | IT_CVAR, NULL, "Texture Filter", &cv_glfiltermode, 50}, diff --git a/src/st_stuff.c b/src/st_stuff.c index 6dc7111f3..64bc4dc00 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -180,7 +180,7 @@ static void ST_doPaletteStuff(void) INT32 palette = 0; #ifdef HWRENDER - if (rendermode == render_opengl && !HWR_PalRenderFlashpal()) + if (rendermode == render_opengl && !HWR_ShouldUsePaletteRendering()) return; #endif diff --git a/src/v_paldeltas.h b/src/v_paldeltas.h new file mode 100644 index 000000000..30fc86dfc --- /dev/null +++ b/src/v_paldeltas.h @@ -0,0 +1,525 @@ +#ifndef H_PALDELTA +#define H_PALDELTA + +// those are the differences in palette indices between the main srb2 playpal and its white and nuke subpalettes +// this is probably not the best way of doing any of this, but it works lol + +static const INT16 pPaletteDeltaFlash[256][3] = { + { 6, 6, 6}, + { 12, 12, 12}, + { 18, 18, 18}, + { 24, 24, 24}, + { 30, 30, 30}, + { 36, 36, 36}, + { 42, 42, 42}, + { 48, 48, 48}, + { 54, 54, 54}, + { 60, 60, 60}, + { 66, 66, 66}, + { 72, 72, 72}, + { 78, 78, 78}, + { 84, 84, 84}, + { 90, 90, 90}, + { 95, 95, 95}, + { 101, 101, 101}, + { 107, 107, 107}, + { 113, 113, 113}, + { 119, 119, 119}, + { 125, 125, 125}, + { 131, 131, 131}, + { 137, 137, 137}, + { 143, 143, 143}, + { 149, 149, 149}, + { 155, 155, 155}, + { 161, 161, 161}, + { 167, 167, 167}, + { 173, 173, 173}, + { 179, 179, 179}, + { 185, 185, 185}, + { 191, 191, 191}, + { 48, 66, 84}, + { 54, 71, 89}, + { 60, 77, 95}, + { 66, 83, 100}, + { 72, 88, 105}, + { 79, 94, 111}, + { 85, 99, 117}, + { 91, 105, 123}, + { 96, 111, 129}, + { 102, 117, 135}, + { 108, 123, 141}, + { 114, 128, 146}, + { 120, 134, 152}, + { 127, 140, 158}, + { 133, 145, 163}, + { 140, 152, 170}, + { 48, 98, 134}, + { 57, 104, 137}, + { 63, 107, 140}, + { 69, 110, 143}, + { 75, 116, 146}, + { 84, 119, 149}, + { 90, 125, 152}, + { 95, 128, 155}, + { 101, 131, 158}, + { 110, 137, 161}, + { 119, 140, 164}, + { 128, 143, 167}, + { 134, 149, 170}, + { 143, 155, 173}, + { 152, 158, 176}, + { 158, 164, 179}, + { 0, 15, 24}, + { 0, 21, 33}, + { 0, 27, 42}, + { 0, 33, 51}, + { 0, 36, 57}, + { 0, 42, 66}, + { 0, 48, 75}, + { 0, 51, 81}, + { 0, 57, 93}, + { 6, 63, 98}, + { 12, 69, 104}, + { 18, 75, 110}, + { 24, 81, 116}, + { 30, 87, 122}, + { 36, 93, 128}, + { 39, 95, 131}, + { 0, 12, 26}, + { 0, 26, 52}, + { 0, 39, 78}, + { 0, 52, 102}, + { 0, 65, 127}, + { 0, 78, 150}, + { 0, 91, 172}, + { 0, 102, 191}, + { 9, 108, 191}, + { 19, 114, 191}, + { 29, 120, 191}, + { 39, 127, 191}, + { 48, 133, 191}, + { 58, 139, 191}, + { 68, 145, 191}, + { 78, 150, 191}, + { 0, 0, 12}, + { 0, 0, 36}, + { 0, 0, 60}, + { 0, 0, 84}, + { 0, 0, 107}, + { 0, 0, 131}, + { 0, 0, 155}, + { 0, 0, 179}, + { 0, 0, 191}, + { 36, 36, 191}, + { 60, 60, 191}, + { 84, 84, 191}, + { 107, 107, 191}, + { 131, 131, 191}, + { 155, 155, 191}, + { 179, 179, 191}, + { 0, 0, 104}, + { 15, 27, 125}, + { 30, 51, 140}, + { 45, 75, 155}, + { 60, 98, 167}, + { 75, 122, 176}, + { 90, 140, 185}, + { 104, 158, 191}, + { 0, 0, 0}, + { 0, 24, 24}, + { 0, 48, 48}, + { 0, 72, 72}, + { 0, 95, 95}, + { 0, 119, 119}, + { 0, 143, 143}, + { 0, 167, 167}, + { 0, 191, 191}, + { 12, 191, 191}, + { 24, 191, 191}, + { 36, 191, 191}, + { 48, 191, 191}, + { 60, 191, 191}, + { 72, 191, 191}, + { 84, 191, 191}, + { 95, 191, 191}, + { 107, 191, 191}, + { 119, 191, 191}, + { 131, 191, 191}, + { 143, 191, 191}, + { 155, 191, 191}, + { 167, 191, 191}, + { 179, 191, 191}, + { 0, 54, 54}, + { 9, 69, 69}, + { 18, 84, 84}, + { 27, 98, 98}, + { 39, 110, 110}, + { 48, 122, 122}, + { 57, 131, 131}, + { 66, 143, 143}, + { 85, 156, 191}, + { 91, 161, 191}, + { 96, 166, 191}, + { 102, 172, 191}, + { 108, 177, 191}, + { 114, 182, 191}, + { 120, 187, 191}, + { 127, 191, 191}, + { 101, 0, 131}, + { 106, 11, 134}, + { 111, 23, 138}, + { 117, 35, 142}, + { 123, 47, 145}, + { 129, 59, 149}, + { 134, 71, 153}, + { 140, 83, 157}, + { 145, 95, 160}, + { 151, 106, 164}, + { 157, 118, 168}, + { 162, 130, 172}, + { 168, 142, 175}, + { 173, 154, 179}, + { 179, 166, 183}, + { 185, 179, 187}, + { 24, 0, 65}, + { 42, 20, 80}, + { 61, 41, 95}, + { 79, 61, 110}, + { 97, 82, 124}, + { 115, 101, 139}, + { 135, 123, 154}, + { 153, 143, 169}, + { 191, 0, 191}, + { 191, 24, 191}, + { 191, 48, 191}, + { 191, 72, 191}, + { 191, 95, 191}, + { 191, 119, 191}, + { 191, 143, 191}, + { 191, 167, 191}, + { 0, 107, 0}, + { 0, 191, 0}, + { 24, 191, 24}, + { 48, 191, 48}, + { 72, 191, 72}, + { 95, 191, 95}, + { 119, 191, 119}, + { 143, 191, 143}, + { 16, 16, 9}, + { 44, 44, 22}, + { 73, 73, 36}, + { 101, 101, 51}, + { 127, 127, 66}, + { 142, 142, 93}, + { 156, 156, 121}, + { 170, 170, 151}, + { 31, 10, 0}, + { 48, 15, 0}, + { 64, 21, 0}, + { 79, 25, 0}, + { 95, 30, 0}, + { 111, 36, 0}, + { 127, 41, 0}, + { 143, 48, 0}, + { 149, 73, 24}, + { 155, 84, 48}, + { 161, 101, 72}, + { 167, 119, 95}, + { 191, 48, 48}, + { 191, 95, 95}, + { 191, 119, 119}, + { 191, 143, 143}, + { 18, 18, 0}, + { 42, 42, 0}, + { 61, 61, 0}, + { 86, 86, 0}, + { 104, 104, 0}, + { 129, 129, 0}, + { 154, 154, 0}, + { 172, 172, 0}, + { 191, 191, 0}, + { 191, 191, 18}, + { 191, 191, 36}, + { 191, 191, 55}, + { 191, 191, 74}, + { 191, 191, 92}, + { 191, 191, 110}, + { 191, 191, 129}, + { 191, 191, 131}, + { 191, 191, 143}, + { 191, 191, 149}, + { 191, 191, 161}, + { 191, 191, 167}, + { 191, 191, 179}, + { 191, 191, 185}, + { 191, 0, 0}, + { 36, 95, 36}, + { 54, 107, 54}, + { 72, 119, 72}, + { 90, 131, 90}, + { 107, 143, 107}, + { 125, 155, 125}, + { 143, 167, 143}, + { 161, 179, 161} +}; + +static const INT16 pPaletteDeltaNuke[256][3] = { + { 6,-133,-133}, + { 12,-125,-125}, + { 18,-117,-117}, + { 24,-109,-109}, + { 30,-101,-101}, + { 36, -93, -93}, + { 42, -85, -85}, + { 48, -77, -77}, + { 54, -69, -69}, + { 60, -61, -61}, + { 66, -53, -53}, + { 72, -45, -45}, + { 78, -37, -37}, + { 84, -29, -29}, + { 90, -21, -21}, + { 95, -14, -14}, + { 101, -6, -6}, + { 107, 2, 2}, + { 113, 10, 10}, + { 119, 18, 18}, + { 125, 26, 26}, + { 131, 34, 34}, + { 137, 42, 42}, + { 143, 50, 50}, + { 149, 58, 58}, + { 155, 66, 66}, + { 161, 74, 74}, + { 167, 82, 82}, + { 173, 90, 90}, + { 179, 98, 98}, + { 185, 106, 106}, + { 191, 113, 113}, + { 48, -53, -29}, + { 54, -46, -22}, + { 60, -38, -14}, + { 66, -30, -7}, + { 72, -23, 0}, + { 79, -15, 8}, + { 85, -8, 16}, + { 91, -1, 23}, + { 96, 7, 31}, + { 102, 15, 39}, + { 108, 23, 47}, + { 114, 30, 54}, + { 120, 38, 62}, + { 127, 46, 70}, + { 133, 53, 77}, + { 140, 62, 86}, + { 48, -10, 38}, + { 57, -2, 42}, + { 63, 2, 46}, + { 69, 6, 50}, + { 75, 14, 54}, + { 84, 18, 58}, + { 90, 26, 62}, + { 95, 30, 66}, + { 101, 34, 70}, + { 110, 42, 74}, + { 119, 46, 78}, + { 128, 50, 82}, + { 134, 58, 86}, + { 143, 66, 90}, + { 152, 70, 94}, + { 158, 78, 98}, + { 0,-121,-109}, + { 0,-113, -97}, + { 0,-105, -85}, + { 0, -97, -73}, + { 0, -93, -65}, + { 0, -85, -53}, + { 0, -77, -41}, + { 0, -73, -33}, + { 0, -65, -17}, + { 6, -57, -10}, + { 12, -49, -2}, + { 18, -41, 6}, + { 24, -33, 14}, + { 30, -25, 22}, + { 36, -17, 30}, + { 39, -14, 34}, + { 0,-124,-106}, + { 0,-106, -71}, + { 0, -89, -37}, + { 0, -71, -4}, + { 0, -54, 28}, + { 0, -37, 59}, + { 0, -20, 88}, + { 0, -4, 113}, + { 9, 4, 113}, + { 19, 12, 113}, + { 29, 20, 113}, + { 39, 28, 113}, + { 48, 36, 113}, + { 58, 44, 113}, + { 68, 52, 113}, + { 78, 59, 113}, + { 0,-141,-125}, + { 0,-141, -93}, + { 0,-141, -61}, + { 0,-141, -29}, + { 0,-141, 2}, + { 0,-141, 34}, + { 0,-141, 66}, + { 0,-141, 98}, + { 0,-141, 113}, + { 36, -93, 113}, + { 60, -61, 113}, + { 84, -29, 113}, + { 107, 2, 113}, + { 131, 34, 113}, + { 155, 66, 113}, + { 179, 98, 113}, + { 0,-141, -2}, + { 15,-105, 26}, + { 30, -73, 46}, + { 45, -41, 66}, + { 60, -10, 82}, + { 75, 22, 94}, + { 90, 46, 106}, + { 104, 70, 113}, + { 0,-141,-141}, + { 0,-109,-109}, + { 0, -77, -77}, + { 0, -45, -45}, + { 0, -14, -14}, + { 0, 18, 18}, + { 0, 50, 50}, + { 0, 82, 82}, + { 0, 113, 113}, + { 12, 113, 113}, + { 24, 113, 113}, + { 36, 113, 113}, + { 48, 113, 113}, + { 60, 113, 113}, + { 72, 113, 113}, + { 84, 113, 113}, + { 95, 113, 113}, + { 107, 113, 113}, + { 119, 113, 113}, + { 131, 113, 113}, + { 143, 113, 113}, + { 155, 113, 113}, + { 167, 113, 113}, + { 179, 113, 113}, + { 0, -69, -69}, + { 9, -49, -49}, + { 18, -29, -29}, + { 27, -10, -10}, + { 39, 6, 6}, + { 48, 22, 22}, + { 57, 34, 34}, + { 66, 50, 50}, + { 85, 67, 113}, + { 91, 74, 113}, + { 96, 81, 113}, + { 102, 88, 113}, + { 108, 95, 113}, + { 114, 102, 113}, + { 120, 108, 113}, + { 127, 113, 113}, + { 101,-141, 34}, + { 106,-126, 38}, + { 111,-110, 43}, + { 117, -94, 48}, + { 123, -78, 53}, + { 129, -62, 58}, + { 134, -46, 63}, + { 140, -30, 68}, + { 145, -14, 73}, + { 151, 1, 78}, + { 157, 17, 83}, + { 162, 33, 88}, + { 168, 49, 93}, + { 173, 65, 98}, + { 179, 81, 103}, + { 185, 98, 109}, + { 24,-141, -54}, + { 42,-114, -34}, + { 61, -86, -14}, + { 79, -59, 6}, + { 97, -32, 25}, + { 115, -6, 45}, + { 135, 23, 65}, + { 153, 50, 84}, + { 191,-141, 113}, + { 191,-109, 113}, + { 191, -77, 113}, + { 191, -45, 113}, + { 191, -14, 113}, + { 191, 18, 113}, + { 191, 50, 113}, + { 191, 82, 113}, + { 0, 2,-141}, + { 0, 113,-141}, + { 24, 113,-109}, + { 48, 113, -77}, + { 72, 113, -45}, + { 95, 113, -14}, + { 119, 113, 18}, + { 143, 113, 50}, + { 16,-119,-129}, + { 44, -82,-111}, + { 73, -43, -92}, + { 101, -6, -73}, + { 127, 29, -53}, + { 142, 48, -17}, + { 156, 67, 21}, + { 170, 86, 61}, + { 31,-127,-141}, + { 48,-121,-141}, + { 64,-113,-141}, + { 79,-107,-141}, + { 95,-100,-141}, + { 111, -93,-141}, + { 127, -86,-141}, + { 143, -77,-141}, + { 149, -43,-109}, + { 155, -29, -77}, + { 161, -6, -45}, + { 167, 18, -14}, + { 191, -77, -77}, + { 191, -14, -14}, + { 191, 18, 18}, + { 191, 50, 50}, + { 18,-117,-141}, + { 42, -84,-141}, + { 61, -59,-141}, + { 86, -26,-141}, + { 104, -2,-141}, + { 129, 31,-141}, + { 154, 64,-141}, + { 172, 89,-141}, + { 191, 113,-141}, + { 191, 113,-117}, + { 191, 113, -92}, + { 191, 113, -67}, + { 191, 113, -42}, + { 191, 113, -18}, + { 191, 113, 6}, + { 191, 113, 31}, + { 191, 113, 34}, + { 191, 113, 50}, + { 191, 113, 58}, + { 191, 113, 74}, + { 191, 113, 82}, + { 191, 113, 98}, + { 191, 113, 106}, + { 191,-141,-141}, + { 36, -14, -93}, + { 54, 2, -69}, + { 72, 18, -45}, + { 90, 34, -21}, + { 107, 50, 2}, + { 125, 66, 26}, + { 143, 82, 50}, + { 161, 98, 74} +}; + +#endif diff --git a/src/v_video.c b/src/v_video.c index 2aa680153..940dd190d 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -91,97 +91,7 @@ consvar_t cv_menucaps = {"menucaps", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NUL // local copy of the palette for V_GetColor() RGBA_t *pLocalPalette = NULL; RGBA_t *pGammaCorrectedPalette = NULL; - -// huge ass array les gooooooo! -// this is basically the white flashpal byte for byte from our main palette lmao -static RGBA_t pFallbackFlashPal[] = { - {.s={253, 253, 253, 255}}, {.s={251, 251, 251, 255}}, {.s={249, 249, 249, 255}}, - {.s={247, 247, 247, 255}}, {.s={245, 245, 245, 255}}, {.s={243, 243, 243, 255}}, - {.s={241, 241, 241, 255}}, {.s={239, 239, 239, 255}}, {.s={237, 237, 237, 255}}, - {.s={235, 235, 235, 255}}, {.s={233, 233, 233, 255}}, {.s={231, 231, 231, 255}}, - {.s={229, 229, 229, 255}}, {.s={227, 227, 227, 255}}, {.s={225, 225, 225, 255}}, - {.s={223, 223, 223, 255}}, {.s={221, 221, 221, 255}}, {.s={219, 219, 219, 255}}, - {.s={217, 217, 217, 255}}, {.s={215, 215, 215, 255}}, {.s={213, 213, 213, 255}}, - {.s={211, 211, 211, 255}}, {.s={209, 209, 209, 255}}, {.s={207, 207, 207, 255}}, - {.s={205, 205, 205, 255}}, {.s={203, 203, 203, 255}}, {.s={201, 201, 201, 255}}, - {.s={199, 199, 199, 255}}, {.s={197, 197, 197, 255}}, {.s={195, 195, 195, 255}}, - {.s={193, 193, 193, 255}}, {.s={192, 192, 192, 255}}, {.s={239, 233, 227, 255}}, - {.s={237, 231, 225, 255}}, {.s={235, 229, 223, 255}}, {.s={233, 227, 221, 255}}, - {.s={231, 225, 219, 255}}, {.s={229, 223, 217, 255}}, {.s={227, 221, 215, 255}}, - {.s={225, 220, 214, 255}}, {.s={223, 218, 212, 255}}, {.s={220, 216, 210, 255}}, - {.s={218, 214, 208, 255}}, {.s={216, 212, 206, 255}}, {.s={214, 210, 204, 255}}, - {.s={212, 208, 202, 255}}, {.s={210, 206, 200, 255}}, {.s={208, 204, 198, 255}}, - {.s={239, 222, 210, 255}}, {.s={236, 220, 209, 255}}, {.s={234, 219, 208, 255}}, - {.s={232, 218, 207, 255}}, {.s={230, 216, 206, 255}}, {.s={227, 215, 205, 255}}, - {.s={225, 213, 204, 255}}, {.s={223, 212, 203, 255}}, {.s={221, 211, 202, 255}}, - {.s={218, 209, 201, 255}}, {.s={215, 208, 200, 255}}, {.s={212, 207, 199, 255}}, - {.s={210, 205, 198, 255}}, {.s={207, 203, 197, 255}}, {.s={204, 202, 196, 255}}, - {.s={202, 200, 195, 255}}, {.s={255, 250, 247, 255}}, {.s={255, 248, 244, 255}}, - {.s={255, 246, 241, 255}}, {.s={255, 244, 238, 255}}, {.s={255, 243, 236, 255}}, - {.s={255, 241, 233, 255}}, {.s={255, 239, 230, 255}}, {.s={255, 238, 228, 255}}, - {.s={255, 236, 224, 255}}, {.s={253, 234, 222, 255}}, {.s={251, 232, 220, 255}}, - {.s={249, 230, 218, 255}}, {.s={247, 228, 216, 255}}, {.s={245, 226, 214, 255}}, - {.s={243, 224, 212, 255}}, {.s={242, 223, 211, 255}}, {.s={255, 250, 246, 255}}, - {.s={255, 246, 237, 255}}, {.s={255, 242, 229, 255}}, {.s={255, 237, 220, 255}}, - {.s={255, 233, 213, 255}}, {.s={255, 229, 205, 255}}, {.s={255, 225, 198, 255}}, - {.s={255, 220, 192, 255}}, {.s={252, 218, 192, 255}}, {.s={248, 216, 192, 255}}, - {.s={245, 214, 192, 255}}, {.s={242, 213, 192, 255}}, {.s={238, 211, 192, 255}}, - {.s={235, 209, 192, 255}}, {.s={232, 207, 192, 255}}, {.s={229, 205, 192, 255}}, - {.s={255, 255, 251, 255}}, {.s={255, 255, 243, 255}}, {.s={255, 255, 235, 255}}, - {.s={255, 255, 227, 255}}, {.s={255, 255, 219, 255}}, {.s={255, 255, 211, 255}}, - {.s={255, 255, 203, 255}}, {.s={255, 255, 195, 255}}, {.s={255, 255, 192, 255}}, - {.s={243, 243, 192, 255}}, {.s={235, 235, 192, 255}}, {.s={227, 227, 192, 255}}, - {.s={219, 219, 192, 255}}, {.s={211, 211, 192, 255}}, {.s={203, 203, 192, 255}}, - {.s={195, 195, 192, 255}}, {.s={255, 255, 220, 255}}, {.s={250, 246, 213, 255}}, - {.s={245, 238, 208, 255}}, {.s={240, 230, 203, 255}}, {.s={235, 222, 199, 255}}, - {.s={230, 214, 196, 255}}, {.s={225, 208, 193, 255}}, {.s={220, 202, 192, 255}}, - {.s={255, 255, 255, 255}}, {.s={255, 247, 247, 255}}, {.s={255, 239, 239, 255}}, - {.s={255, 231, 231, 255}}, {.s={255, 223, 223, 255}}, {.s={255, 215, 215, 255}}, - {.s={255, 207, 207, 255}}, {.s={255, 199, 199, 255}}, {.s={255, 192, 192, 255}}, - {.s={251, 192, 192, 255}}, {.s={247, 192, 192, 255}}, {.s={243, 192, 192, 255}}, - {.s={239, 192, 192, 255}}, {.s={235, 192, 192, 255}}, {.s={231, 192, 192, 255}}, - {.s={227, 192, 192, 255}}, {.s={223, 192, 192, 255}}, {.s={219, 192, 192, 255}}, - {.s={215, 192, 192, 255}}, {.s={211, 192, 192, 255}}, {.s={207, 192, 192, 255}}, - {.s={203, 192, 192, 255}}, {.s={199, 192, 192, 255}}, {.s={195, 192, 192, 255}}, - {.s={255, 237, 237, 255}}, {.s={252, 232, 232, 255}}, {.s={249, 227, 227, 255}}, - {.s={246, 222, 222, 255}}, {.s={242, 218, 218, 255}}, {.s={239, 214, 214, 255}}, - {.s={236, 211, 211, 255}}, {.s={233, 207, 207, 255}}, {.s={227, 203, 192, 255}}, - {.s={225, 201, 192, 255}}, {.s={223, 199, 192, 255}}, {.s={220, 198, 192, 255}}, - {.s={218, 196, 192, 255}}, {.s={216, 194, 192, 255}}, {.s={214, 193, 192, 255}}, - {.s={213, 192, 192, 255}}, {.s={221, 255, 211, 255}}, {.s={219, 251, 210, 255}}, - {.s={217, 247, 209, 255}}, {.s={215, 243, 208, 255}}, {.s={214, 239, 206, 255}}, - {.s={212, 235, 205, 255}}, {.s={210, 231, 204, 255}}, {.s={208, 227, 203, 255}}, - {.s={206, 223, 201, 255}}, {.s={205, 219, 200, 255}}, {.s={203, 215, 199, 255}}, - {.s={201, 211, 198, 255}}, {.s={199, 207, 196, 255}}, {.s={197, 203, 195, 255}}, - {.s={195, 199, 194, 255}}, {.s={193, 195, 192, 255}}, {.s={246, 255, 233, 255}}, - {.s={241, 248, 228, 255}}, {.s={234, 241, 223, 255}}, {.s={228, 234, 218, 255}}, - {.s={222, 228, 213, 255}}, {.s={216, 221, 208, 255}}, {.s={210, 214, 203, 255}}, - {.s={204, 207, 199, 255}}, {.s={192, 255, 192, 255}}, {.s={192, 247, 192, 255}}, - {.s={192, 239, 192, 255}}, {.s={192, 231, 192, 255}}, {.s={192, 223, 192, 255}}, - {.s={192, 215, 192, 255}}, {.s={192, 207, 192, 255}}, {.s={192, 199, 192, 255}}, - {.s={255, 219, 255, 255}}, {.s={255, 192, 255, 255}}, {.s={247, 192, 247, 255}}, - {.s={239, 192, 239, 255}}, {.s={231, 192, 231, 255}}, {.s={223, 192, 223, 255}}, - {.s={215, 192, 215, 255}}, {.s={207, 192, 207, 255}}, {.s={249, 249, 252, 255}}, - {.s={240, 240, 247, 255}}, {.s={230, 230, 242, 255}}, {.s={221, 221, 238, 255}}, - {.s={212, 212, 233, 255}}, {.s={208, 208, 224, 255}}, {.s={203, 203, 214, 255}}, - {.s={198, 198, 204, 255}}, {.s={244, 251, 255, 255}}, {.s={239, 250, 255, 255}}, - {.s={234, 248, 255, 255}}, {.s={228, 246, 255, 255}}, {.s={223, 244, 255, 255}}, - {.s={218, 243, 255, 255}}, {.s={213, 241, 255, 255}}, {.s={207, 239, 255, 255}}, - {.s={205, 230, 247, 255}}, {.s={203, 227, 239, 255}}, {.s={201, 221, 231, 255}}, - {.s={199, 215, 223, 255}}, {.s={192, 239, 239, 255}}, {.s={192, 223, 223, 255}}, - {.s={192, 215, 215, 255}}, {.s={192, 207, 207, 255}}, {.s={249, 249, 255, 255}}, - {.s={240, 240, 255, 255}}, {.s={234, 234, 255, 255}}, {.s={226, 226, 255, 255}}, - {.s={220, 220, 255, 255}}, {.s={212, 212, 255, 255}}, {.s={204, 204, 255, 255}}, - {.s={197, 197, 255, 255}}, {.s={192, 192, 255, 255}}, {.s={192, 192, 249, 255}}, - {.s={192, 192, 242, 255}}, {.s={192, 192, 236, 255}}, {.s={192, 192, 230, 255}}, - {.s={192, 192, 224, 255}}, {.s={192, 192, 218, 255}}, {.s={192, 192, 212, 255}}, - {.s={192, 192, 211, 255}}, {.s={192, 192, 207, 255}}, {.s={192, 192, 205, 255}}, - {.s={192, 192, 201, 255}}, {.s={192, 192, 199, 255}}, {.s={192, 192, 195, 255}}, - {.s={192, 192, 193, 255}}, {.s={192, 255, 255, 255}}, {.s={243, 223, 243, 255}}, - {.s={237, 219, 237, 255}}, {.s={231, 215, 231, 255}}, {.s={225, 211, 225, 255}}, - {.s={219, 207, 219, 255}}, {.s={213, 203, 213, 255}}, {.s={207, 199, 207, 255}}, - {.s={201, 195, 201, 255}} -}; +static RGBA_t *pFallbackFlashPal = NULL; static size_t currentPaletteSize; @@ -500,6 +410,58 @@ UINT32 V_GammaCorrect(UINT32 input, double power) return result.rgba; } +#include "v_paldeltas.h" +// remaps a palette with given "deltas" +// this is used with premade deltas between the srb2 palette and its both flashpals +// so we may approximate generate our own, should the current palette miss them +static void ApplyPaletteDelta(const RGBA_t *pal_in, RGBA_t *pal_out, const INT16 pal_delta[][3]) +{ + size_t i; + + for (i = 0; i < 256; i++) + { + INT16 r = (INT16)pal_in[i].s.red + pal_delta[i][0]; + INT16 g = (INT16)pal_in[i].s.green + pal_delta[i][1]; + INT16 b = (INT16)pal_in[i].s.blue + pal_delta[i][2]; + pal_out[i].s.red = (UINT8)CLAMP(r, 0 , 255); + pal_out[i].s.green = (UINT8)CLAMP(g, 0 , 255); + pal_out[i].s.blue = (UINT8)CLAMP(b, 0 , 255); + pal_out[i].s.alpha = 0xFF; // pal_in[i].s.alpha + } +} + +static void GenerateFlashPalettes(void) +{ + size_t i, palsize; + RGBA_t *pFlashPalGammaCorrectedPalette = NULL; + + // generate flashpalettes if the current palette does not provide any subpalettes + if (currentPaletteSize >= (14 * (256 * 3))) // 14 palettes are in playpal + return; + + palsize = (2*256); + + pFallbackFlashPal = Z_Malloc(sizeof(*pFallbackFlashPal)*palsize, PU_STATIC, NULL); + pFlashPalGammaCorrectedPalette = Z_Malloc(sizeof (*pFlashPalGammaCorrectedPalette)*palsize, PU_STATIC, NULL); + + // apply our deltas to the palettes to get the white and red "flashes" + ApplyPaletteDelta(pLocalPalette, pFallbackFlashPal, pPaletteDeltaFlash); + ApplyPaletteDelta(pLocalPalette, pFallbackFlashPal+256, pPaletteDeltaNuke); + + // apply colour and gamma correction if needed + if (Cubeapply) + { + for (i = 0; i < palsize; i++) + { + pFlashPalGammaCorrectedPalette[i].rgba = V_GammaDecode(pFallbackFlashPal[i].rgba); + V_CubeApply(&pFlashPalGammaCorrectedPalette[i]); + pFallbackFlashPal[i].rgba = V_GammaEncode(pFlashPalGammaCorrectedPalette[i].rgba); + } + } + + Z_Free(pFlashPalGammaCorrectedPalette); +} + // keep a copy of the palette so that we can get the RGB value for a color index at any time. static void LoadPalette(const char *lumpname) { @@ -519,6 +481,8 @@ static void LoadPalette(const char *lumpname) Z_Free(pLocalPalette); Z_Free(pGammaCorrectedPalette); + Z_Free(pFallbackFlashPal); + pFallbackFlashPal = NULL; pLocalPalette = Z_Malloc(sizeof (*pLocalPalette)*palsize, PU_STATIC, NULL); pGammaCorrectedPalette = Z_Malloc(sizeof (*pGammaCorrectedPalette)*palsize, PU_STATIC, NULL); @@ -538,12 +502,19 @@ static void LoadPalette(const char *lumpname) pLocalPalette[i].s.alpha = 0xFF; pGammaCorrectedPalette[i].rgba = V_GammaDecode(pLocalPalette[i].rgba); + } - if (!Cubeapply) - continue; + GenerateFlashPalettes(); - V_CubeApply(&pGammaCorrectedPalette[i]); - pLocalPalette[i].rgba = V_GammaEncode(pGammaCorrectedPalette[i].rgba); + // gotta apply any colour and gamma adjustments afterwards + // so we dont double apply it to the generated flashpals + if (Cubeapply) + { + for (i = 0; i < palsize; i++) + { + V_CubeApply(&pGammaCorrectedPalette[i]); + pLocalPalette[i].rgba = V_GammaEncode(pGammaCorrectedPalette[i].rgba); + } } } @@ -646,25 +617,32 @@ void V_SetPalette(INT32 palettenum) if (palettenum == 0) { palettenum = cv_palettenum.value; - - if (palettenum * 256U > (currentPaletteSize - 256) / 3) - { - CONS_Alert(CONS_WARNING, "cv_palettenum %d out of range\n", palettenum); - palettenum = 0; - } } } - // if the palettenum goes beyond our current palette, then just fallback to the white flash pal - // our srb2 palette mostly just contains that as subpalettes, so everything uses this - // and makes it a good fallback case - // otherwise we will throw random garbage to the renderer which calls memcpy on it - // which is not really a good thing lol - // we probably could just memcpy from localpalette, but i dont trust -file nor autoload so - if (palettenum * 256U > (currentPaletteSize - 256) / 3) + // in many cases custom palettes do not provide the needed subpalettes for flashpals to work + // before this would mean the renderer gets passed random data out of allocated memory bounds + // which was not very cool :chonkbuncle: + // gladly 2.1 only actually has 2 different subpalettes + // one for "white flash" and one red/pinkish one for nuke + // since we checked the palette earlier we should have generated those ourselves + if (((size_t)palettenum >= currentPaletteSize / (256 * 3))) { CONS_Debug(DBG_RENDER, "palettenum %d out of range\n", palettenum); - pal = pFallbackFlashPal; + + // if for some reason we did not generate flashpals + // just fall back to the main palette i guess? + if (pFallbackFlashPal != NULL) + { + if (palettenum == PAL_NUKE) + pal = &pFallbackFlashPal[256]; + else + pal = pFallbackFlashPal; + } + else + { + pal = pLocalPalette; + } } else { -- 2.54.0 From 6430150b63b9503e7e6cc97b3af13ec5125edbd5 Mon Sep 17 00:00:00 2001 From: Alug Date: Mon, 2 Mar 2026 17:53:16 +0100 Subject: [PATCH 34/35] fix parts of png info text for screenshots not being null terminated in some cases --- src/m_misc.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/m_misc.c b/src/m_misc.c index ee17e2098..97777a979 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -788,10 +788,10 @@ static void M_PNGText(png_structp png_ptr, png_infop png_info_ptr, PNG_CONST png else snprintf(locationtxt, 40, "Unknown"); - memset(png_infotext,0x00,sizeof (png_infotext)); + memset(png_infotext, 0x00, sizeof(png_infotext)); for (i = 0; i < SRB2PNGTXT; i++) - png_infotext[i].key = keytxt[i]; + png_infotext[i].key = keytxt[i]; png_infotext[0].text = titletxt; if (movie) @@ -804,9 +804,15 @@ static void M_PNGText(png_structp png_ptr, png_infop png_info_ptr, PNG_CONST png png_infotext[5].text = locationtxt; png_infotext[6].text = interfacetxt; png_infotext[7].text = rendermodetxt; - png_infotext[8].text = strncpy(ctrevision, comprevision, sizeof(ctrevision)-1); - png_infotext[9].text = strncpy(ctdate, compdate, sizeof(ctdate)-1); - png_infotext[10].text = strncpy(cttime, comptime, sizeof(cttime)-1); + strncpy(ctrevision, comprevision, sizeof(ctrevision)-1); + ctrevision[sizeof(ctrevision)-1] = '\0'; + png_infotext[8].text = ctrevision; + strncpy(ctdate, compdate, sizeof(ctdate)-1); + ctdate[sizeof(ctdate)-1] = '\0'; + png_infotext[9].text = ctdate; + strncpy(cttime, comptime, sizeof(cttime)-1); + cttime[sizeof(cttime)-1] = '\0'; + png_infotext[10].text = cttime; png_set_text(png_ptr, png_info_ptr, png_infotext, SRB2PNGTXT); #undef SRB2PNGTXT @@ -1564,6 +1570,7 @@ char *M_GetToken(const char *inputString) { startPos = endPos; } + if (stringToUse == NULL) return NULL; @@ -1634,7 +1641,8 @@ char *M_GetToken(const char *inputString) } // If the end of the string is reached, no token is to be read - if (startPos == stringLength) { + if (startPos == stringLength) + { endPos = stringLength; return NULL; } @@ -1644,7 +1652,7 @@ char *M_GetToken(const char *inputString) || stringToUse[startPos] == '}') { endPos = startPos + 1; - texturesToken = (char *)Z_Malloc(2*sizeof(char),PU_STATIC,NULL); + texturesToken = (char *)Z_Malloc(2*sizeof(char), PU_STATIC, NULL); texturesToken[0] = stringToUse[startPos]; texturesToken[1] = '\0'; return texturesToken; @@ -1855,8 +1863,10 @@ int M_PathParts(const char *path) int n; const char *p; const char *t; + if (path == NULL) return 0; + for (n = 0, p = path ;; ++n) { t = p; @@ -1869,6 +1879,7 @@ int M_PathParts(const char *path) break; } } + return n; } @@ -1911,7 +1922,9 @@ void M_MkdirEachUntil(const char *cpath, int start, int end, int mode) if (!( p = strchr(p, PATHSEP[0]) )) return; } + p += strspn(p, PATHSEP); + for (;;) { if (end > 0 && !--end) -- 2.54.0 From 9ee535066e8aa44a4fc07d0ebb21ac809ca33642 Mon Sep 17 00:00:00 2001 From: Alug Date: Tue, 3 Mar 2026 02:22:02 +0100 Subject: [PATCH 35/35] =?UTF-8?q?OpenGL:=20initialize=20all=20FSurfaceInfo?= =?UTF-8?q?=C2=B4s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit seen this a bunch of times now that occasionally something tries to access uninit values from a surface info since those are really small we just always init them now --- src/hardware/hw_draw.c | 14 +++++++------- src/hardware/hw_main.cpp | 22 ++++++++++------------ src/hardware/hw_md2.c | 5 ++--- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index 4797f21de..3602cef1b 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -222,7 +222,7 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p if (alphalevel) { - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff; switch (alphalevel) @@ -385,7 +385,7 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, // clip it since it is used for bunny scroll in doom I if (alphalevel) { - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff; switch (alphalevel) @@ -490,7 +490,7 @@ void HWR_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatlumpnum) void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength) { FOutVector v[4]; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; FBITFIELD poly_flags = PF_NoTexture|PF_Modulated|PF_NoDepthTest; v[0].x = v[3].x = -1.0f; @@ -543,7 +543,7 @@ void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength) void HWR_DrawConsoleBack(UINT32 color, INT32 height) { FOutVector v[4]; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; // setup some neat-o translucency effect if (!height) //cool hack 0 height is full height @@ -598,7 +598,7 @@ void HWR_drawAMline(const fline_t *fl, INT32 color) void HWR_DrawDiag(INT32 x, INT32 y, INT32 wh, INT32 color) { FOutVector v[4]; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; float fx, fy, fw, fh, fwait = 0; RGBA_t *palette; @@ -702,7 +702,7 @@ void HWR_DrawDiag(INT32 x, INT32 y, INT32 wh, INT32 color) void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, UINT32 color, INT32 options) { FOutVector v[4]; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; float fx, fy, fw, fh; if (w < 0 || h < 0) @@ -820,7 +820,7 @@ void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, UINT32 color, INT32 void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color) { FOutVector v[4]; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; float fx, fy, fw, fh; if (w < 0 || h < 0) diff --git a/src/hardware/hw_main.cpp b/src/hardware/hw_main.cpp index e6ce072cf..bf11f0c7c 100644 --- a/src/hardware/hw_main.cpp +++ b/src/hardware/hw_main.cpp @@ -550,7 +550,7 @@ static FUINT HWR_CalcSlopeLight(FUINT lightnum, pslope_t *slope, const sector_t // Render a floor or ceiling convex polygon static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, FBITFIELD PolyFlags, INT32 lightlevel, lumpnum_t lumpnum, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap) { - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; FOutVector *v3d; polyvertex_t *pv; pslope_t *slope = NULL; @@ -1375,7 +1375,7 @@ void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom extracolormap_t *colormap = gl_frontsector->extra_colormap; lightnum = HWR_CalcWallLight(lightnum, gl_curline, colormap); - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; Surf.PolyColor.s.alpha = 255; @@ -2674,7 +2674,7 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, float scrollx = 0.0f, scrolly = 0.0f; float anglef = 0.0f, cosangf = 0.0f, sinangf = 0.0f; angle_t angle = 0; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; fixed_t tempxsow, tempytow; size_t nrPlaneVerts; @@ -2841,9 +2841,8 @@ static void HWR_AddPolyObjectPlanes(void) light = R_GetPlaneLight(gl_frontsector, polyobjsector->floorheight, true); if (po_ptrs[i]->translucency > 0) { - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; FBITFIELD blendmode; - memset(&Surf, 0x00, sizeof(Surf)); blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf); HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->floorpic].lumpnum, po_ptrs[i], false, polyobjsector->floorheight, (light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel), Surf.PolyColor.s.alpha, polyobjsector, blendmode, (light == -1 ? gl_frontsector->extra_colormap : gl_frontsector->lightlist[light].extra_colormap)); @@ -2864,9 +2863,8 @@ static void HWR_AddPolyObjectPlanes(void) light = R_GetPlaneLight(gl_frontsector, polyobjsector->ceilingheight, true); if (po_ptrs[i]->translucency > 0) { - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; FBITFIELD blendmode; - memset(&Surf, 0x00, sizeof(Surf)); blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf); HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->ceilingpic].lumpnum, po_ptrs[i], true, polyobjsector->ceilingheight, (light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel), Surf.PolyColor.s.alpha, polyobjsector, blendmode, (light == -1 ? gl_frontsector->extra_colormap : gl_frontsector->lightlist[light].extra_colormap)); @@ -3418,7 +3416,7 @@ static void HWR_DrawSpriteShadow(gl_vissprite_t *spr, patch_t *gpatch, GLPatch_t { float this_scale = 1.0f; FOutVector swallVerts[4]; - FSurfaceInfo sSurf; + FSurfaceInfo sSurf = {}; FBITFIELD blendmode = 0; fixed_t floorheight, mobjfloor; pslope_t *floorslope; @@ -3654,7 +3652,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr, const boolean papersprite) FOutVector baseWallVerts[4]; // This is what the verts should end up as patch_t *gpatch; GLPatch_t *hwrpatch; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; extracolormap_t *colormap; INT32 lightlevel; boolean lightset = true; @@ -3939,7 +3937,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) FOutVector wallVerts[4]; patch_t *gpatch; // sprite patch converted to hardware GLPatch_t *hwrpatch; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; FBITFIELD blend = 0; INT32 shader = SHADER_NONE; @@ -4104,7 +4102,7 @@ static void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr) FOutVector wallVerts[4]; patch_t *gpatch; // sprite patch converted to hardware GLPatch_t *hwrpatch; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; INT32 shader = SHADER_NONE; @@ -6081,7 +6079,7 @@ static void HWR_DoPostProcessor(void) {-4.0f, -4.0f, 4.0f, 0.0f, 0.0f} }; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; // This won't change if the flash palettes are changed unfortunately, but it works for its purpose if (player->flashpal == PAL_NUKE) diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index d9bf41645..2aefecea1 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1099,7 +1099,7 @@ void HWR_DrawMD2(gl_vissprite_t *spr) INT32 frame = 0; INT32 nextFrame = -1; FTransform p; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; if (!cv_glmdls.value || spr->precip) return; @@ -1475,7 +1475,7 @@ void HWR_Draw2DModel(md2_t *md2, INT32 x, INT32 y, INT32 skinnum, skincolors_t c patch_t *gpatch, *blendgpatch; GLPatch_t *hwrPatch = NULL, *hwrBlendPatch = NULL; FTransform p; - FSurfaceInfo Surf; + FSurfaceInfo Surf = {}; if (!md2->model) { @@ -1526,7 +1526,6 @@ void HWR_Draw2DModel(md2_t *md2, INT32 x, INT32 y, INT32 skinnum, skincolors_t c if (blendgpatch) hwrBlendPatch = ((GLPatch_t *)blendgpatch->hardware); - memset(&Surf, 0x00, sizeof(FSurfaceInfo)); Surf.PolyColor.rgba = 0xFFFFFFFF; Surf.PolyFlags = PF_Occlude | PF_Modulated; -- 2.54.0