diff --git a/src/doomdef.h b/src/doomdef.h index 208b6cc69..9149a2203 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -516,6 +516,22 @@ 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 min // Double-Check with WATTCP-32's cdefs.h #define min(x, y) (((x) < (y)) ? (x) : (y)) #endif diff --git a/src/p_setup.c b/src/p_setup.c index 9ae36afc9..2435390cb 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3452,6 +3452,8 @@ UINT16 P_PartialAddWadFile(const char *wadfilename, boolean local) if (!devparm && digmreplaces) CONS_Printf(M_GetText("%s digital musics replaced\n"), sizeu1(digmreplaces)); + R_LoadTexturesPwad(wadnum); + // // search for sprite replacements // @@ -3548,7 +3550,7 @@ boolean P_MultiSetupWadFiles(boolean fullsetup) if (partadd_stage == 1) { // Reload all textures, unconditionally for better or worse. - R_LoadTextures(); + //R_LoadTextures(); if (fullsetup) ++partadd_stage; diff --git a/src/r_data.c b/src/r_data.c index 772d76925..0900ed2c1 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -129,6 +129,7 @@ INT16 *hicolormaps; // test a 32k colormap remaps high -> high // Painfully simple texture id cacheing to make maps load faster. :3 static struct { char name[9]; + UINT32 hash; INT32 id; } *tidcache = NULL; static INT32 tidcachelen = 0; @@ -374,144 +375,63 @@ void R_FlushTextureCache(void) int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum); void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); -// -// R_LoadTextures -// Initializes the texture list with the textures from the world map. -// #define TX_START "TX_START" #define TX_END "TX_END" -void R_LoadTextures(void) + +static INT32 +Rloadtextures (INT32 i, INT32 w) { - INT32 i, k, w; UINT16 j; + INT32 k; UINT16 texstart, texend, texturesLumpPos; - patch_t *patchlump; + patch_t patchlump; texpatch_t *patch; texture_t *texture; - // Free previous memory before numtextures change. - if (numtextures) + // Get the lump numbers for the markers in the WAD, if they exist. + if (W_FileHasFolders(wadfiles[w])) { - for (i = 0; i < numtextures; i++) - { - Z_Free(textures[i]); - Z_Free(texturecache[i]); - } - Z_Free(texturetranslation); - Z_Free(textures); - } - - // Load patches and textures. - - // Get the number of textures to check. - // NOTE: Make SURE the system does not process - // the markers. - // This system will allocate memory for all duplicate/patched textures even if it never uses them, - // but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures. - for (w = 0, numtextures = 0; w < numwadfiles; w++) - { - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1; - texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); - } - + texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); while (texturesLumpPos != INT16_MAX) { - numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos); + R_ParseTEXTURESLump(w, texturesLumpPos, &i); texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); } - - // Add all the textures between TX_START and TX_END - if (texstart != INT16_MAX && texend != INT16_MAX) - { - // PK3s have subfolders, so we can't just make a simple sum - if (W_FileHasFolders(wadfiles[w])) - { - for (j = texstart; j < texend; j++) - { - if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it - numtextures++; - } - } - else - { - numtextures += (UINT32)(texend - texstart); - } - } + } + else + { + texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); + texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + if (texturesLumpPos != INT16_MAX) + R_ParseTEXTURESLump(w, texturesLumpPos, &i); } - // If no textures found by this point, bomb out - if (!numtextures) - I_Error("No textures detected in any WADs!\n"); - - // Allocate memory and initialize to 0 for all the textures we are initialising. - // There are actually 5 buffers allocated in one for convenience. - textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL); - - // Allocate texture column offset table. - texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); - // Allocate texture referencing cache. - texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); - // Allocate texture width mask table. - texturewidthmask = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); - // Allocate texture height mask table. - textureheight = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4)); - // Create translation table for global animation. - texturetranslation = Z_Malloc((numtextures + 1) * sizeof(*texturetranslation), PU_STATIC, NULL); - - for (i = 0; i < numtextures; i++) - texturetranslation[i] = i; - - for (i = 0, w = 0; w < numwadfiles; w++) + if (!( texstart == INT16_MAX || texend == INT16_MAX )) { - // Get the lump numbers for the markers in the WAD, if they exist. - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - while (texturesLumpPos != INT16_MAX) - { - R_ParseTEXTURESLump(w, texturesLumpPos, &i); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); - } - } - else - { - texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1; - texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - if (texturesLumpPos != INT16_MAX) - R_ParseTEXTURESLump(w, texturesLumpPos, &i); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) - continue; - // Work through each lump between the markers in the WAD. for (j = 0; j < (texend - texstart); j++) { + UINT16 wadnum = (UINT16)w; + lumpnum_t lumpnum = texstart + j; + if (W_FileHasFolders(wadfiles[w])) { - if (W_IsLumpFolder(w, texstart + j)) // Check if lump is a folder + if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder continue; // If it is then SKIP IT } - patchlump = W_CacheLumpNumPwad((UINT16)w, texstart + j, PU_CACHE); + W_ReadLumpHeaderPwad(wadnum, lumpnum, &patchlump, 8, 0); // #define PNG_HEADER_SIZE (8) //CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height); texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); // Set texture properties. M_Memcpy(texture->name, W_CheckNameForNumPwad((UINT16)w, texstart + j), sizeof(texture->name)); - texture->width = SHORT(patchlump->width); - texture->height = SHORT(patchlump->height); + texture->hash = quickncasehash(texture->name, 8); + texture->width = SHORT(patchlump.width); + texture->height = SHORT(patchlump.height); texture->patchcount = 1; texture->holes = false; @@ -522,8 +442,6 @@ void R_LoadTextures(void) patch->wad = (UINT16)w; patch->lump = texstart + j; - Z_Unlock(patchlump); - k = 1; while (k << 1 <= texture->width) k <<= 1; @@ -534,12 +452,186 @@ void R_LoadTextures(void) } } + return i; +} + +static INT32 +count_range +( const char * marker_start, + const char * marker_end, + const char * folder, + UINT16 wadnum) +{ + UINT16 j; + UINT16 texstart, texend; + INT32 count = 0; + + // Count flats + if (W_FileHasFolders(wadfiles[wadnum])) + { + texstart = W_CheckNumForFolderStartPK3(folder, wadnum, 0); + texend = W_CheckNumForFolderEndPK3(folder, wadnum, texstart); + } + else + { + texstart = W_CheckNumForMarkerStartPwad(marker_start, wadnum, 0); + texend = W_CheckNumForNamePwad(marker_end, wadnum, texstart); + } + + if (texstart != INT16_MAX && texend != INT16_MAX) + { + // PK3s have subfolders, so we can't just make a simple sum + if (W_FileHasFolders(wadfiles[wadnum])) + { + for (j = texstart; j < texend; j++) + { + if (!W_IsLumpFolder(wadnum, j)) // Check if lump is a folder; if not, then count it + count++; + } + } + else // Add all the textures between markers + { + count += (texend - texstart); + } + } + + return count; +} + +static INT32 R_CountTextures(UINT16 wadnum) +{ + UINT16 texturesLumpPos; + INT32 count = 0; + + // Load patches and textures. + + // Get the number of textures to check. + // NOTE: Make SURE the system does not process + // the markers. + // This system will allocate memory for all duplicate/patched textures even if it never uses them, + // but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures. + + // Count the textures from TEXTURES lumps + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", wadnum, 0); + + while (texturesLumpPos != INT16_MAX) + { + count += R_CountTexturesInTEXTURESLump(wadnum, texturesLumpPos); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", wadnum, texturesLumpPos + 1); + } + + // Count single-patch textures + count += count_range(TX_START, TX_END, "textures/", wadnum); + + return count; +} + +static void +recallocuser +( void * user, + size_t old, + size_t new) +{ + char *p = Z_Realloc(*(void**)user, + new, PU_STATIC, user); + + if (new > old) + memset(&p[old], 0, (new - old)); +} + +static void R_AllocateTextures(INT32 add) +{ + const INT32 newtextures = (numtextures + add); + const size_t newsize = newtextures * sizeof (void*); + const size_t oldsize = numtextures * sizeof (void*); + + INT32 i; + + // Allocate memory and initialize to 0 for all the textures we are initialising. + recallocuser(&textures, oldsize, newsize); + + // Allocate texture column offset table. + recallocuser(&texturecolumnofs, oldsize, newsize); + // Allocate texture referencing cache. + recallocuser(&texturecache, oldsize, newsize); + // Allocate texture width table. + recallocuser(&texturewidthmask, oldsize, newsize); + // Allocate texture height table. + recallocuser(&textureheight, oldsize, newsize); + // Create translation table for global animation. + Z_Realloc(texturetranslation, (newtextures + 1) * sizeof(*texturetranslation), PU_STATIC, &texturetranslation); + + for (i = 0; i < numtextures; ++i) + { + // R_FlushTextureCache relies on the user for + // Z_Free, texturecache has been reallocated so the + // user is now garbage memory. + Z_SetUser(texturecache[i], + (void**)&texturecache[i]); + } + + while (i < newtextures) + { + texturetranslation[i] = i; + i++; + } +} + +static INT32 R_DefineTextures(INT32 i, UINT16 w) +{ + return Rloadtextures(i, w); +} + +static void R_FinishLoadingTextures(INT32 add) +{ + numtextures += add; + #ifdef HWRENDER if (rendermode == render_opengl) HWR_LoadTextures(numtextures); #endif } +// +// R_LoadTextures +// Initializes the texture list with the textures from the world map. +// +void R_LoadTextures(void) +{ + INT32 i, w; + INT32 newtextures = 0; + + for (w = 0; w < numwadfiles; w++) + { + newtextures += R_CountTextures((UINT16)w); + } + + // If no textures found by this point, bomb out + if (!newtextures) + I_Error("No textures detected in any WADs!\n"); + + R_AllocateTextures(newtextures); + + for (i = 0, w = 0; w < numwadfiles; w++) + { + i = R_DefineTextures(i, w); + } + + R_FinishLoadingTextures(newtextures); +} + +void R_LoadTexturesPwad(UINT16 wadnum) +{ + INT32 newtextures = R_CountTextures(wadnum); + + if (!newtextures) + return; + + R_AllocateTextures(newtextures); + R_DefineTextures(numtextures, wadnum); + R_FinishLoadingTextures(newtextures); +} + static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch) { char *texturesToken; @@ -778,6 +870,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); M_Memcpy(resultTexture->name, newTextureName, 8); + resultTexture->hash = quickncasehash(newTextureName, 8); resultTexture->width = newTextureWidth; resultTexture->height = newTextureHeight; } @@ -1556,19 +1649,22 @@ void R_ClearTextureNumCache(boolean btell) INT32 R_CheckTextureNumForName(const char *name) { INT32 i; + UINT32 hash; // "NoTexture" marker. if (name[0] == '-') return 0; + hash = quickncasehash(name, 8); + for (i = 0; i < tidcachelen; i++) - if (!strncasecmp(tidcache[i].name, name, 8)) + if (tidcache[i].hash == hash && !strncasecmp(tidcache[i].name, name, 8)) return tidcache[i].id; // Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier //for (i = 0; i < numtextures; i++) <- old for (i = (numtextures - 1); i >= 0; i--) // <- new - if (!strncasecmp(textures[i]->name, name, 8)) + if (textures[i]->hash == hash && !strncasecmp(textures[i]->name, name, 8)) { tidcachelen++; Z_Realloc(tidcache, tidcachelen * sizeof(*tidcache), PU_STATIC, &tidcache); @@ -1577,6 +1673,7 @@ INT32 R_CheckTextureNumForName(const char *name) #ifndef ZDEBUG CONS_Debug(DBG_SETUP, "texture #%s: %s\n", sizeu1(tidcachelen), tidcache[tidcachelen-1].name); #endif + tidcache[tidcachelen-1].hash = hash; tidcache[tidcachelen-1].id = i; return i; } diff --git a/src/r_data.h b/src/r_data.h index ec1146322..102bb5314 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -40,6 +40,7 @@ typedef struct { // Keep name for switch changing, etc. char name[8]; + UINT32 hash; INT16 width, height; boolean holes; @@ -63,6 +64,7 @@ extern CV_PossibleValue_t Color_cons_t[]; // Load TEXTURE1/TEXTURE2/PNAMES definitions, create lookup tables void R_LoadTextures(void); +void R_LoadTexturesPwad(UINT16 wadnum); void R_FlushTextureCache(void); INT32 R_GetTextureNum(INT32 texnum); diff --git a/src/w_wad.c b/src/w_wad.c index 86536c03f..2a52c4dd3 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -371,6 +371,7 @@ static lumpinfo_t* ResGetLumpsStandalone (FILE* handle, UINT16* numlumps, const lumpinfo->size = ftell(handle); fseek(handle, 0, SEEK_SET); strlcpy(lumpinfo->name, lumpname, 9); + lumpinfo->hash = quickncasehash(lumpname, 8); // Allocate the lump's full and long name. lumpinfo->fullname = Z_StrDup(lumpname); @@ -462,6 +463,7 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen lump_p->compression = CM_NOCOMPRESSION; memset(lump_p->name, 0x00, 9); strncpy(lump_p->name, fileinfo->name, 8); + lump_p->hash = quickncasehash(lump_p->name, 8); // Allocate the lump's long name. lump_p->longname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); @@ -636,6 +638,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) 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)); + lump_p->hash = quickncasehash(lump_p->name, 8); lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL); strlcpy(lump_p->longname, trimname, dotpos - trimname + 1); @@ -953,6 +956,7 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump) { UINT16 i; static char uname[9]; + UINT32 hash; if (!TestValidLump(wad,0)) return INT16_MAX; @@ -960,6 +964,7 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump) memset(uname, 0, sizeof uname); strncpy(uname, name, sizeof(uname)-1); strupr(uname); + hash = quickncasehash(uname, 8); // // scan forward @@ -970,7 +975,7 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump) { lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) - if (memcmp(lump_p->name, uname, sizeof(uname) - 1) == 0) + if (lump_p->hash == hash && memcmp(lump_p->name, uname, sizeof(uname) - 1) == 0) return i; } @@ -1178,15 +1183,20 @@ lumpnum_t W_CheckNumForLongName(const char *name) // TODO: Make it search through cache first, maybe...? lumpnum_t W_CheckNumForMap(const char *name) { + UINT32 hash = quickncasehash(name, 8); UINT16 lumpNum, end; UINT32 i; + lumpinfo_t *p; for (i = numwadfiles - 1; i < numwadfiles; i--) { if (wadfiles[i]->type == RET_WAD) { for (lumpNum = 0; lumpNum < wadfiles[i]->numlumps; lumpNum++) - if (!strncmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8)) + { + p = wadfiles[i]->lumpinfo + lumpNum; + if (p->hash == hash && !strncmp(name, p->name, 8)) return (i<<16) + lumpNum; + } } else if (wadfiles[i]->type == RET_PK3) { @@ -1197,12 +1207,11 @@ lumpnum_t W_CheckNumForMap(const char *name) continue; // Now look for the specified map. for (; lumpNum < end; lumpNum++) - if (!strnicmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8)) - { - const char *extension = strrchr(wadfiles[i]->lumpinfo[lumpNum].fullname, '.'); - if (!(extension && stricmp(extension, ".wad"))) - return (i<<16) + lumpNum; - } + { + p = wadfiles[i]->lumpinfo + lumpNum; + if (p->hash == hash && !strnicmp(name, p->name, 8)) + return (i<<16) + lumpNum; + } } } return LUMPERROR; diff --git a/src/w_wad.h b/src/w_wad.h index 84ce9775e..c0f47708e 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -69,6 +69,7 @@ typedef struct unsigned long position; // filelump_t filepos unsigned long disksize; // filelump_t size char name[9]; // filelump_t name[] e.g. "LongEntr" + UINT32 hash; char *longname; // e.g. "LongEntryName" char *fullname; // e.g. "Folder/Subfolder/LongEntryName.extension" size_t size; // real (uncompressed) size