ref: e33dbd82381ace644f7ac4574a354e5764591b55
dir: /src/w_wad.c/
// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 02111-1307, USA. // // DESCRIPTION: // Handles WAD file header, directory, lump I/O. // //----------------------------------------------------------------------------- #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "doomdef.h" #include "doomtype.h" #include "i_swap.h" #include "i_system.h" #include "i_video.h" #include "m_misc.h" #include "z_zone.h" #include "w_wad.h" typedef struct { // Should be "IWAD" or "PWAD". char identification[4]; int numlumps; int infotableofs; } PACKEDATTR wadinfo_t; typedef struct { int filepos; int size; char name[8]; } PACKEDATTR filelump_t; // // GLOBALS // // Location of each lump on disk. lumpinfo_t *lumpinfo; unsigned int numlumps = 0; // Hash table for fast lookups static lumpinfo_t **lumphash; static void ExtractFileBase(char *path, char *dest) { char* src; int length; src = path + strlen(path) - 1; // back up until a \ or the start while (src != path && *(src - 1) != DIR_SEPARATOR) { src--; } // copy up to eight characters memset (dest,0,8); length = 0; while (*src && *src != '.') { if (++length == 9) I_Error ("Filename base of %s >8 chars",path); *dest++ = toupper((int)*src++); } } // Hash function used for lump names. unsigned int W_LumpNameHash(const char *s) { // This is the djb2 string hash function, modded to work on strings // that have a maximum length of 8. unsigned int result = 5381; unsigned int i; for (i=0; i < 8 && s[i] != '\0'; ++i) { result = ((result << 5) ^ result ) ^ toupper(s[i]); } return result; } // // LUMP BASED ROUTINES. // // // W_AddFile // All files are optional, but at least one file must be // found (PWAD, if all required lumps are present). // Files with a .wad extension are wadlink files // with multiple lumps. // Other files are single lumps with the base filename // for the lump name. // // If filename starts with a tilde, the file is handled // specially to allow map reloads. // But: the reload feature is a fragile hack... unsigned int reloadlump; char* reloadname; wad_file_t *W_AddFile (char *filename) { wadinfo_t header; lumpinfo_t* lump_p; unsigned int i; wad_file_t *wad_file; int length; int startlump; filelump_t* fileinfo; filelump_t* filerover; wad_file_t *storehandle; // open the file and add to directory // handle reload indicator. if (filename[0] == '~') { filename++; reloadname = filename; reloadlump = numlumps; } wad_file = W_OpenFile(filename); if (wad_file == NULL) { printf (" couldn't open %s\n", filename); return NULL; } startlump = numlumps; if (strcasecmp(filename+strlen(filename)-3 , "wad" ) ) { // single lump file // fraggle: Swap the filepos and size here. The WAD directory // parsing code expects a little-endian directory, so will swap // them back. Effectively we're constructing a "fake WAD directory" // here, as it would appear on disk. fileinfo = Z_Malloc(sizeof(filelump_t), PU_STATIC, 0); fileinfo->filepos = LONG(0); fileinfo->size = LONG(wad_file->length); // Name the lump after the base of the filename (without the // extension). ExtractFileBase (filename, fileinfo->name); numlumps++; } else { // WAD file W_Read(wad_file, 0, &header, sizeof(header)); if (strncmp(header.identification,"IWAD",4)) { // Homebrew levels? if (strncmp(header.identification,"PWAD",4)) { I_Error ("Wad file %s doesn't have IWAD " "or PWAD id\n", filename); } // ???modifiedgame = true; } header.numlumps = LONG(header.numlumps); header.infotableofs = LONG(header.infotableofs); length = header.numlumps*sizeof(filelump_t); fileinfo = Z_Malloc(length, PU_STATIC, 0); W_Read(wad_file, header.infotableofs, fileinfo, length); numlumps += header.numlumps; } // Fill in lumpinfo lumpinfo = realloc (lumpinfo, numlumps*sizeof(lumpinfo_t)); if (!lumpinfo) I_Error ("Couldn't realloc lumpinfo"); lump_p = &lumpinfo[startlump]; storehandle = reloadname ? NULL : wad_file; for (i=startlump,filerover=fileinfo ; i<numlumps ; i++,lump_p++, filerover++) { lump_p->wad_file = storehandle; lump_p->position = LONG(filerover->filepos); lump_p->size = LONG(filerover->size); lump_p->cache = NULL; strncpy(lump_p->name, filerover->name, 8); } if (reloadname) { W_CloseFile(wad_file); } Z_Free(fileinfo); if (lumphash != NULL) { Z_Free(lumphash); lumphash = NULL; } return wad_file; } // // W_Reload // Flushes any of the reloadable lumps in memory // and reloads the directory. // void W_Reload (void) { wadinfo_t header; int lumpcount; lumpinfo_t* lump_p; unsigned int i; wad_file_t* wad_file; int length; filelump_t* fileinfo; if (reloadname == NULL) { return; } wad_file = W_OpenFile(reloadname); if (wad_file == NULL) { I_Error ("W_Reload: couldn't open %s", reloadname); } W_Read(wad_file, 0, &header, sizeof(header)); lumpcount = LONG(header.numlumps); header.infotableofs = LONG(header.infotableofs); length = lumpcount*sizeof(filelump_t); fileinfo = Z_Malloc(length, PU_STATIC, 0); W_Read(wad_file, header.infotableofs, fileinfo, length); // Fill in lumpinfo lump_p = &lumpinfo[reloadlump]; for (i=reloadlump; i<reloadlump+lumpcount; i++, lump_p++, fileinfo++) { if (lumpinfo[i].cache) { Z_Free (lumpinfo[i].cache); } lump_p->position = LONG(fileinfo->filepos); lump_p->size = LONG(fileinfo->size); } W_CloseFile(wad_file); Z_Free(fileinfo); } // // W_NumLumps // int W_NumLumps (void) { return numlumps; } // // W_CheckNumForName // Returns -1 if name not found. // int W_CheckNumForName (char* name) { lumpinfo_t *lump_p; int i; // Do we have a hash table yet? if (lumphash != NULL) { int hash; // We do! Excellent. hash = W_LumpNameHash(name) % numlumps; for (lump_p = lumphash[hash]; lump_p != NULL; lump_p = lump_p->next) { if (!strncasecmp(lump_p->name, name, 8)) { return lump_p - lumpinfo; } } } else { // We don't have a hash table generate yet. Linear search :-( // // scan backwards so patch lump files take precedence for (i=numlumps-1; i >= 0; --i) { if (!strncasecmp(lumpinfo[i].name, name, 8)) { return i; } } } // TFB. Not found. return -1; } // // W_GetNumForName // Calls W_CheckNumForName, but bombs out if not found. // int W_GetNumForName (char* name) { int i; i = W_CheckNumForName (name); if (i == -1) I_Error ("W_GetNumForName: %s not found!", name); return i; } // // W_LumpLength // Returns the buffer size needed to load the given lump. // int W_LumpLength (unsigned int lump) { if (lump >= numlumps) I_Error ("W_LumpLength: %i >= numlumps",lump); return lumpinfo[lump].size; } // // W_ReadLump // Loads the lump into the given buffer, // which must be >= W_LumpLength(). // void W_ReadLump(unsigned int lump, void *dest) { int c; lumpinfo_t* l; wad_file_t* wad_file; if (lump >= numlumps) { I_Error ("W_ReadLump: %i >= numlumps",lump); } l = lumpinfo+lump; I_BeginRead (); if (l->wad_file == NULL) { // reloadable file, so use open / read / close wad_file = W_OpenFile(reloadname); if (wad_file == NULL) { I_Error ("W_ReadLump: couldn't open %s",reloadname); } } else { wad_file = l->wad_file; } c = W_Read(wad_file, l->position, dest, l->size); if (c < l->size) { I_Error ("W_ReadLump: only read %i of %i on lump %i", c, l->size, lump); } if (l->wad_file == NULL) { W_CloseFile(wad_file); } I_EndRead (); } // // W_CacheLumpNum // // Load a lump into memory and return a pointer to a buffer containing // the lump data. // // 'tag' is the type of zone memory buffer to allocate for the lump // (usually PU_STATIC or PU_CACHE). If the lump is loaded as // PU_STATIC, it should be released back using W_ReleaseLumpNum // when no longer needed (do not use Z_ChangeTag). // void *W_CacheLumpNum(int lump, int tag) { byte* ptr; if ((unsigned)lump >= numlumps) I_Error ("W_CacheLumpNum: %i >= numlumps",lump); if (!lumpinfo[lump].cache) { // read the lump in //printf ("cache miss on lump %i\n",lump); ptr = Z_Malloc (W_LumpLength (lump), tag, &lumpinfo[lump].cache); W_ReadLump (lump, lumpinfo[lump].cache); } else { //printf ("cache hit on lump %i\n",lump); Z_ChangeTag (lumpinfo[lump].cache, tag); } return lumpinfo[lump].cache; } // // W_CacheLumpName // void *W_CacheLumpName(char *name, int tag) { return W_CacheLumpNum(W_GetNumForName(name), tag); } // // Release a lump back to the cache, so that it can be reused later // without having to read from disk again, or alternatively, discarded // if we run out of memory. // // Back in Vanilla Doom, this was just done using Z_ChangeTag // directly, but now that we have WAD mmap, things are a bit more // complicated ... // void W_ReleaseLumpNum(int lump) { if ((unsigned)lump >= numlumps) { I_Error ("W_ReleaseLumpNum: %i >= numlumps", lump); } Z_ChangeTag(lumpinfo[lump].cache, PU_CACHE); } void W_ReleaseLumpName(char *name) { W_ReleaseLumpNum(W_GetNumForName(name)); } #if 0 // // W_Profile // int info[2500][10]; int profilecount; void W_Profile (void) { int i; memblock_t* block; void* ptr; char ch; FILE* f; int j; char name[9]; for (i=0 ; i<numlumps ; i++) { ptr = lumpinfo[i].cache; if (!ptr) { ch = ' '; continue; } else { block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); if (block->tag < PU_PURGELEVEL) ch = 'S'; else ch = 'P'; } info[i][profilecount] = ch; } profilecount++; f = fopen ("waddump.txt","w"); name[8] = 0; for (i=0 ; i<numlumps ; i++) { memcpy (name,lumpinfo[i].name,8); for (j=0 ; j<8 ; j++) if (!name[j]) break; for ( ; j<8 ; j++) name[j] = ' '; fprintf (f,"%s ",name); for (j=0 ; j<profilecount ; j++) fprintf (f," %c",info[i][j]); fprintf (f,"\n"); } fclose (f); } #endif // Generate a hash table for fast lookups void W_GenerateHashTable(void) { unsigned int i; // Free the old hash table, if there is one if (lumphash != NULL) { Z_Free(lumphash); } // Generate hash table if (numlumps > 0) { lumphash = Z_Malloc(sizeof(lumpinfo_t *) * numlumps, PU_STATIC, NULL); memset(lumphash, 0, sizeof(lumpinfo_t *) * numlumps); for (i=0; i<numlumps; ++i) { unsigned int hash; hash = W_LumpNameHash(lumpinfo[i].name) % numlumps; // Hook into the hash table lumpinfo[i].next = lumphash[hash]; lumphash[hash] = &lumpinfo[i]; } } // All done! }