ref: aa1aed334e860ab5241683f77dfd0215664efe5e
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; FILE *W_AddFile (char *filename) { wadinfo_t header; lumpinfo_t* lump_p; unsigned int i; FILE *handle; int length; int startlump; filelump_t* fileinfo; filelump_t* filerover; FILE *storehandle; // open the file and add to directory // handle reload indicator. if (filename[0] == '~') { filename++; reloadname = filename; reloadlump = numlumps; } if ( (handle = fopen(filename,"rb")) == 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(M_FileLength(handle)); // Name the lump after the base of the filename (without the // extension). ExtractFileBase (filename, fileinfo->name); numlumps++; } else { // WAD file fread (&header, sizeof(header), 1, handle); 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); fseek(handle, header.infotableofs, SEEK_SET); fread(fileinfo, length, 1, handle); 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 : handle; for (i=startlump,filerover=fileinfo ; i<numlumps ; i++,lump_p++, filerover++) { lump_p->handle = 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) fclose (handle); Z_Free(fileinfo); if (lumphash != NULL) { Z_Free(lumphash); lumphash = NULL; } return handle; } // // 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; FILE *handle; int length; filelump_t* fileinfo; if (!reloadname) return; if ( (handle = fopen(reloadname,"rb")) == NULL) I_Error ("W_Reload: couldn't open %s",reloadname); fread(&header, sizeof(header), 1, handle); lumpcount = LONG(header.numlumps); header.infotableofs = LONG(header.infotableofs); length = lumpcount*sizeof(filelump_t); fileinfo = Z_Malloc(length, PU_STATIC, 0); fseek(handle, header.infotableofs, SEEK_SET); fread(fileinfo, length, 1, handle); // 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); } fclose(handle); 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; FILE *handle; if (lump >= numlumps) I_Error ("W_ReadLump: %i >= numlumps",lump); l = lumpinfo+lump; I_BeginRead (); if (l->handle == NULL) { // reloadable file, so use open / read / close if ( (handle = fopen(reloadname,"rb")) == NULL) I_Error ("W_ReadLump: couldn't open %s",reloadname); } else handle = l->handle; fseek(handle, l->position, SEEK_SET); c = fread (dest, 1, l->size, handle); if (c < l->size) I_Error ("W_ReadLump: only read %i of %i on lump %i", c,l->size,lump); if (l->handle == NULL) fclose (handle); I_EndRead (); } // // W_CacheLumpNum // 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); } #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! }