Add key bindings for pause, message refresh.
[chocolate-doom.git] / src / w_wad.c
blob9425705c11f6537e9cdfbcf609524389fbbc0960
1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // Copyright(C) 1993-1996 Id Software, Inc.
5 // Copyright(C) 2005 Simon Howard
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 // 02111-1307, USA.
22 // DESCRIPTION:
23 // Handles WAD file header, directory, lump I/O.
25 //-----------------------------------------------------------------------------
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
35 #include "doomdef.h"
36 #include "doomtype.h"
38 #include "i_swap.h"
39 #include "i_system.h"
40 #include "i_video.h"
41 #include "m_misc.h"
42 #include "z_zone.h"
44 #include "w_wad.h"
46 typedef struct
48 // Should be "IWAD" or "PWAD".
49 char identification[4];
50 int numlumps;
51 int infotableofs;
52 } PACKEDATTR wadinfo_t;
55 typedef struct
57 int filepos;
58 int size;
59 char name[8];
60 } PACKEDATTR filelump_t;
63 // GLOBALS
66 // Location of each lump on disk.
68 lumpinfo_t *lumpinfo;
69 unsigned int numlumps = 0;
71 // Hash table for fast lookups
73 static lumpinfo_t **lumphash;
75 static void ExtractFileBase(char *path, char *dest)
77 char* src;
78 int length;
80 src = path + strlen(path) - 1;
82 // back up until a \ or the start
83 while (src != path && *(src - 1) != DIR_SEPARATOR)
85 src--;
88 // copy up to eight characters
89 memset (dest,0,8);
90 length = 0;
92 while (*src && *src != '.')
94 if (++length == 9)
95 I_Error ("Filename base of %s >8 chars",path);
97 *dest++ = toupper((int)*src++);
101 // Hash function used for lump names.
103 unsigned int W_LumpNameHash(const char *s)
105 // This is the djb2 string hash function, modded to work on strings
106 // that have a maximum length of 8.
108 unsigned int result = 5381;
109 unsigned int i;
111 for (i=0; i < 8 && s[i] != '\0'; ++i)
113 result = ((result << 5) ^ result ) ^ toupper(s[i]);
116 return result;
120 // LUMP BASED ROUTINES.
124 // W_AddFile
125 // All files are optional, but at least one file must be
126 // found (PWAD, if all required lumps are present).
127 // Files with a .wad extension are wadlink files
128 // with multiple lumps.
129 // Other files are single lumps with the base filename
130 // for the lump name.
132 wad_file_t *W_AddFile (char *filename)
134 wadinfo_t header;
135 lumpinfo_t *lump_p;
136 unsigned int i;
137 wad_file_t *wad_file;
138 int length;
139 int startlump;
140 filelump_t *fileinfo;
141 filelump_t *filerover;
143 // open the file and add to directory
145 wad_file = W_OpenFile(filename);
147 if (wad_file == NULL)
149 printf (" couldn't open %s\n", filename);
150 return NULL;
153 startlump = numlumps;
155 if (strcasecmp(filename+strlen(filename)-3 , "wad" ) )
157 // single lump file
159 // fraggle: Swap the filepos and size here. The WAD directory
160 // parsing code expects a little-endian directory, so will swap
161 // them back. Effectively we're constructing a "fake WAD directory"
162 // here, as it would appear on disk.
164 fileinfo = Z_Malloc(sizeof(filelump_t), PU_STATIC, 0);
165 fileinfo->filepos = LONG(0);
166 fileinfo->size = LONG(wad_file->length);
168 // Name the lump after the base of the filename (without the
169 // extension).
171 ExtractFileBase (filename, fileinfo->name);
172 numlumps++;
174 else
176 // WAD file
177 W_Read(wad_file, 0, &header, sizeof(header));
179 if (strncmp(header.identification,"IWAD",4))
181 // Homebrew levels?
182 if (strncmp(header.identification,"PWAD",4))
184 I_Error ("Wad file %s doesn't have IWAD "
185 "or PWAD id\n", filename);
188 // ???modifiedgame = true;
191 header.numlumps = LONG(header.numlumps);
192 header.infotableofs = LONG(header.infotableofs);
193 length = header.numlumps*sizeof(filelump_t);
194 fileinfo = Z_Malloc(length, PU_STATIC, 0);
196 W_Read(wad_file, header.infotableofs, fileinfo, length);
197 numlumps += header.numlumps;
200 // Fill in lumpinfo
201 lumpinfo = realloc(lumpinfo, numlumps * sizeof(lumpinfo_t));
203 if (lumpinfo == NULL)
205 I_Error ("Couldn't realloc lumpinfo");
208 lump_p = &lumpinfo[startlump];
210 filerover = fileinfo;
212 for (i=startlump; i<numlumps; ++i)
214 lump_p->wad_file = wad_file;
215 lump_p->position = LONG(filerover->filepos);
216 lump_p->size = LONG(filerover->size);
217 lump_p->cache = NULL;
218 strncpy(lump_p->name, filerover->name, 8);
220 ++lump_p;
221 ++filerover;
224 Z_Free(fileinfo);
226 if (lumphash != NULL)
228 Z_Free(lumphash);
229 lumphash = NULL;
232 return wad_file;
238 // W_NumLumps
240 int W_NumLumps (void)
242 return numlumps;
248 // W_CheckNumForName
249 // Returns -1 if name not found.
252 int W_CheckNumForName (char* name)
254 lumpinfo_t *lump_p;
255 int i;
257 // Do we have a hash table yet?
259 if (lumphash != NULL)
261 int hash;
263 // We do! Excellent.
265 hash = W_LumpNameHash(name) % numlumps;
267 for (lump_p = lumphash[hash]; lump_p != NULL; lump_p = lump_p->next)
269 if (!strncasecmp(lump_p->name, name, 8))
271 return lump_p - lumpinfo;
275 else
277 // We don't have a hash table generate yet. Linear search :-(
279 // scan backwards so patch lump files take precedence
281 for (i=numlumps-1; i >= 0; --i)
283 if (!strncasecmp(lumpinfo[i].name, name, 8))
285 return i;
290 // TFB. Not found.
292 return -1;
299 // W_GetNumForName
300 // Calls W_CheckNumForName, but bombs out if not found.
302 int W_GetNumForName (char* name)
304 int i;
306 i = W_CheckNumForName (name);
308 if (i < 0)
310 I_Error ("W_GetNumForName: %s not found!", name);
313 return i;
318 // W_LumpLength
319 // Returns the buffer size needed to load the given lump.
321 int W_LumpLength (unsigned int lump)
323 if (lump >= numlumps)
325 I_Error ("W_LumpLength: %i >= numlumps", lump);
328 return lumpinfo[lump].size;
334 // W_ReadLump
335 // Loads the lump into the given buffer,
336 // which must be >= W_LumpLength().
338 void W_ReadLump(unsigned int lump, void *dest)
340 int c;
341 lumpinfo_t *l;
343 if (lump >= numlumps)
345 I_Error ("W_ReadLump: %i >= numlumps", lump);
348 l = lumpinfo+lump;
350 I_BeginRead ();
352 c = W_Read(l->wad_file, l->position, dest, l->size);
354 if (c < l->size)
356 I_Error ("W_ReadLump: only read %i of %i on lump %i",
357 c, l->size, lump);
360 I_EndRead ();
367 // W_CacheLumpNum
369 // Load a lump into memory and return a pointer to a buffer containing
370 // the lump data.
372 // 'tag' is the type of zone memory buffer to allocate for the lump
373 // (usually PU_STATIC or PU_CACHE). If the lump is loaded as
374 // PU_STATIC, it should be released back using W_ReleaseLumpNum
375 // when no longer needed (do not use Z_ChangeTag).
378 void *W_CacheLumpNum(int lumpnum, int tag)
380 byte *result;
381 lumpinfo_t *lump;
383 if ((unsigned)lumpnum >= numlumps)
385 I_Error ("W_CacheLumpNum: %i >= numlumps", lumpnum);
388 lump = &lumpinfo[lumpnum];
390 // Get the pointer to return. If the lump is in a memory-mapped
391 // file, we can just return a pointer to within the memory-mapped
392 // region. If the lump is in an ordinary file, we may already
393 // have it cached; otherwise, load it into memory.
395 if (lump->wad_file->mapped != NULL)
397 // Memory mapped file, return from the mmapped region.
399 result = lump->wad_file->mapped + lump->position;
401 else if (lump->cache != NULL)
403 // Already cached, so just switch the zone tag.
405 result = lump->cache;
406 Z_ChangeTag(lump->cache, tag);
408 else
410 // Not yet loaded, so load it now
412 lump->cache = Z_Malloc(W_LumpLength(lumpnum), tag, &lump->cache);
413 W_ReadLump (lumpnum, lump->cache);
414 result = lump->cache;
417 return result;
423 // W_CacheLumpName
425 void *W_CacheLumpName(char *name, int tag)
427 return W_CacheLumpNum(W_GetNumForName(name), tag);
431 // Release a lump back to the cache, so that it can be reused later
432 // without having to read from disk again, or alternatively, discarded
433 // if we run out of memory.
435 // Back in Vanilla Doom, this was just done using Z_ChangeTag
436 // directly, but now that we have WAD mmap, things are a bit more
437 // complicated ...
440 void W_ReleaseLumpNum(int lumpnum)
442 lumpinfo_t *lump;
444 if ((unsigned)lumpnum >= numlumps)
446 I_Error ("W_ReleaseLumpNum: %i >= numlumps", lumpnum);
449 lump = &lumpinfo[lumpnum];
451 if (lump->wad_file->mapped != NULL)
453 // Memory-mapped file, so nothing needs to be done here.
455 else
457 Z_ChangeTag(lump->cache, PU_CACHE);
461 void W_ReleaseLumpName(char *name)
463 W_ReleaseLumpNum(W_GetNumForName(name));
466 #if 0
469 // W_Profile
471 int info[2500][10];
472 int profilecount;
474 void W_Profile (void)
476 int i;
477 memblock_t* block;
478 void* ptr;
479 char ch;
480 FILE* f;
481 int j;
482 char name[9];
485 for (i=0 ; i<numlumps ; i++)
487 ptr = lumpinfo[i].cache;
488 if (!ptr)
490 ch = ' ';
491 continue;
493 else
495 block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
496 if (block->tag < PU_PURGELEVEL)
497 ch = 'S';
498 else
499 ch = 'P';
501 info[i][profilecount] = ch;
503 profilecount++;
505 f = fopen ("waddump.txt","w");
506 name[8] = 0;
508 for (i=0 ; i<numlumps ; i++)
510 memcpy (name,lumpinfo[i].name,8);
512 for (j=0 ; j<8 ; j++)
513 if (!name[j])
514 break;
516 for ( ; j<8 ; j++)
517 name[j] = ' ';
519 fprintf (f,"%s ",name);
521 for (j=0 ; j<profilecount ; j++)
522 fprintf (f," %c",info[i][j]);
524 fprintf (f,"\n");
526 fclose (f);
530 #endif
532 // Generate a hash table for fast lookups
534 void W_GenerateHashTable(void)
536 unsigned int i;
538 // Free the old hash table, if there is one
540 if (lumphash != NULL)
542 Z_Free(lumphash);
545 // Generate hash table
546 if (numlumps > 0)
548 lumphash = Z_Malloc(sizeof(lumpinfo_t *) * numlumps, PU_STATIC, NULL);
549 memset(lumphash, 0, sizeof(lumpinfo_t *) * numlumps);
551 for (i=0; i<numlumps; ++i)
553 unsigned int hash;
555 hash = W_LumpNameHash(lumpinfo[i].name) % numlumps;
557 // Hook into the hash table
559 lumpinfo[i].next = lumphash[hash];
560 lumphash[hash] = &lumpinfo[i];
564 // All done!