1 /* Emacs style mode select   -*- C++ -*-
2  *-----------------------------------------------------------------------------
3  *
4  *
5  *  PrBoom: a Doom port merged with LxDoom and LSDLDoom
6  *  based on BOOM, a modified and improved DOOM engine
7  *  Copyright (C) 1999 by
8  *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9  *  Copyright (C) 1999-2001 by
10  *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11  *  Copyright 2005, 2006 by
12  *  Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13  *
14  *  This program is free software; you can redistribute it and/or
15  *  modify it under the terms of the GNU General Public License
16  *  as published by the Free Software Foundation; either version 2
17  *  of the License, or (at your option) any later version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software
26  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27  *  02111-1307, USA.
28  *
29  * DESCRIPTION:
30  *      Handles WAD file header, directory, lump I/O.
31  *
32  *-----------------------------------------------------------------------------
33  */
34 
35 #include "config.h"
36 #ifdef _MSC_VER
37 #include <stddef.h>
38 #include <io.h>
39 #endif
40 #include <fcntl.h>
41 
42 #include "doomstat.h"
43 #include "d_net.h"
44 #include "doomtype.h"
45 #include "i_system.h"
46 
47 #include "w_wad.h"
48 #include "lprintf.h"
49 
50 #include <sys/stat.h>
51 
52 //
53 // GLOBALS
54 //
55 
56 // Location of each lump on disk.
57 lumpinfo_t *lumpinfo;
58 int        numlumps;         // killough
59 
ExtractFileBase(const char * path,char * dest)60 void ExtractFileBase (const char *path, char *dest)
61 {
62   const char *src = path + strlen(path) - 1;
63   int length;
64 
65   // back up until a \ or the start
66   while (src != path && src[-1] != ':' // killough 3/22/98: allow c:filename
67          && *(src-1) != '\\'
68          && *(src-1) != '/')
69   {
70     src--;
71   }
72 
73   // copy up to eight characters
74   memset(dest,0,8);
75   length = 0;
76 
77   while ((*src) && (*src != '.') && (++length<9))
78   {
79     *dest++ = toupper(*src);
80     (void)*src++;
81   }
82   /* cph - length check removed, just truncate at 8 chars.
83    * If there are 8 or more chars, we'll copy 8, and no zero termination
84    */
85 }
86 
87 //
88 // 1/18/98 killough: adds a default extension to a path
89 // Note: Backslashes are treated specially, for MS-DOS.
90 //
91 
AddDefaultExtension(char * path,const char * ext)92 char *AddDefaultExtension(char *path, const char *ext)
93 {
94   char *p = path;
95   while (*p++);
96   while (p-->path && *p!='/' && *p!='\\')
97     if (*p=='.')
98       return path;
99   if (*ext!='.')
100     strcat(path,".");
101   return strcat(path,ext);
102 }
103 
104 //
105 // LUMP BASED ROUTINES.
106 //
107 
108 //
109 // W_AddFile
110 // All files are optional, but at least one file must be
111 //  found (PWAD, if all required lumps are present).
112 // Files with a .wad extension are wadlink files
113 //  with multiple lumps.
114 // Other files are single lumps with the base filename
115 //  for the lump name.
116 //
117 // Reload hack removed by Lee Killough
118 // CPhipps - source is an enum
119 //
120 // proff - changed using pointer to wadfile_info_t
W_AddFile(wadfile_info_t * wadfile)121 static void W_AddFile(wadfile_info_t *wadfile)
122 // killough 1/31/98: static, const
123 {
124    wadinfo_t   header;
125    lumpinfo_t* lump_p;
126    unsigned    i;
127    int         length;
128    int         startlump;
129    filelump_t  *fileinfo = NULL;
130    filelump_t *fileinfo2free=NULL; //killough
131    filelump_t singleinfo;
132 #ifndef MEMORY_LOW
133    struct stat statbuf;
134 #endif
135 
136    // open the file and add to directory
137 
138 #ifdef MEMORY_LOW
139    wadfile->handle = open(wadfile->name, O_RDONLY | O_BINARY);
140 
141    if (wadfile->handle == -1)
142 #else
143       //precache into memory instead of reading from disk
144       wadfile->handle = fopen(wadfile->name, "rb");
145 
146    if (wadfile->handle == 0)
147 #endif
148    {
149       if (  strlen(wadfile->name)<=4 ||      // add error check -- killough
150             (strcasecmp(wadfile->name+strlen(wadfile->name)-4 , ".lmp" ) &&
151              strcasecmp(wadfile->name+strlen(wadfile->name)-4 , ".gwa" ) )
152          )
153          I_Error("W_AddFile: couldn't open %s",wadfile->name);
154       return;
155    }
156 
157 #ifndef MEMORY_LOW
158    stat(wadfile->name, &statbuf);
159    wadfile->length = statbuf.st_size;
160    wadfile->data = malloc(statbuf.st_size);
161    if ( fread(wadfile->data, statbuf.st_size, 1, wadfile->handle) != 1)
162      I_Error("W_AddFile: couldn't read wad data");
163 #endif
164 
165    //jff 8/3/98 use logical output routine
166    lprintf (LO_INFO," adding %s\n",wadfile->name);
167    startlump = numlumps;
168 
169    if (  strlen(wadfile->name)<=4 ||
170          (
171           strcasecmp(wadfile->name+strlen(wadfile->name)-4,".wad") &&
172           strcasecmp(wadfile->name+strlen(wadfile->name)-4,".gwa")
173          )
174       )
175    {
176       // single lump file
177       fileinfo = &singleinfo;
178       singleinfo.filepos = 0;
179 #ifdef MEMORY_LOW
180       singleinfo.size = doom_wtohl(I_Filelength(wadfile->handle));
181 #else
182       singleinfo.size = wadfile->length;
183 #endif
184       ExtractFileBase(wadfile->name, singleinfo.name);
185       numlumps++;
186    }
187    else
188    {
189       // WAD file
190 #ifdef MEMORY_LOW
191       I_Read(wadfile->handle, &header, sizeof(header));
192 #else
193       memcpy(&header, wadfile->data, sizeof(header));
194 #endif
195       if (strncmp(header.identification,"IWAD",4) &&
196             strncmp(header.identification,"PWAD",4))
197          I_Error("W_AddFile: Wad file %s doesn't have IWAD or PWAD id", wadfile->name);
198       header.numlumps = LONG(header.numlumps);
199       header.infotableofs = LONG(header.infotableofs);
200       length = header.numlumps*sizeof(filelump_t);
201       fileinfo2free = fileinfo = malloc(length);    // killough
202 #ifdef MEMORY_LOW
203       lseek(wadfile->handle, header.infotableofs, SEEK_SET);
204       I_Read(wadfile->handle, fileinfo, length);
205 #else
206       memcpy(fileinfo, &wadfile->data[header.infotableofs], length);
207 #endif
208       numlumps += header.numlumps;
209    }
210 
211    // Fill in lumpinfo
212    lumpinfo = realloc(lumpinfo, numlumps*sizeof(lumpinfo_t));
213 
214    lump_p = &lumpinfo[startlump];
215 
216    for (i=startlump ; (int)i<numlumps ; i++,lump_p++, fileinfo++)
217    {
218       lump_p->wadfile = wadfile;                    //  killough 4/25/98
219       lump_p->position = LONG(fileinfo->filepos);
220       lump_p->size = LONG(fileinfo->size);
221       lump_p->li_namespace = ns_global;              // killough 4/17/98
222       strncpy (lump_p->name, fileinfo->name, 8);
223       lump_p->source = wadfile->src;                    // Ty 08/29/98
224    }
225 
226    free(fileinfo2free);      // killough
227 }
228 
229 // jff 1/23/98 Create routines to reorder the master directory
230 // putting all flats into one marked block, and all sprites into another.
231 // This will allow loading of sprites and flats from a PWAD with no
232 // other changes to code, particularly fast hashes of the lumps.
233 //
234 // killough 1/24/98 modified routines to be a little faster and smaller
235 
IsMarker(const char * marker,const char * name)236 static int IsMarker(const char *marker, const char *name)
237 {
238   return !strncasecmp(name, marker, 8) ||
239     // doubled first character test for single-character prefixes only
240     // FF_* is valid alias for F_*, but HI_* should not allow HHI_*
241     (marker[1] == '_' && *name == *marker && !strncasecmp(name+1, marker, 7));
242 }
243 
244 // killough 4/17/98: add namespace tags
245 
W_CoalesceMarkedResource(const char * start_marker,const char * end_marker,lumpinfo_namespace_t li_namespace)246 static void W_CoalesceMarkedResource(const char *start_marker,
247                                      const char *end_marker,
248                                      lumpinfo_namespace_t li_namespace)
249 {
250   lumpinfo_t *marked = malloc(sizeof(*marked) * numlumps);
251   size_t i, num_marked = 0, num_unmarked = 0;
252   int is_marked = 0, mark_end = 0;
253   lumpinfo_t *lump = lumpinfo;
254 
255   for (i=numlumps; i--; lump++)
256     if (IsMarker(start_marker, lump->name))       // start marker found
257       { // If this is the first start marker, add start marker to marked lumps
258         if (!num_marked)
259           {
260             strncpy(marked->name, start_marker, 8);
261             marked->size = 0;  // killough 3/20/98: force size to be 0
262             marked->li_namespace = ns_global;        // killough 4/17/98
263             marked->wadfile = NULL;
264             num_marked = 1;
265           }
266         is_marked = 1;                            // start marking lumps
267       }
268     else
269       if (IsMarker(end_marker, lump->name))       // end marker found
270         {
271           mark_end = 1;                           // add end marker below
272           is_marked = 0;                          // stop marking lumps
273         }
274       else
275         if (is_marked)                            // if we are marking lumps,
276           {                                       // move lump to marked list
277             marked[num_marked] = *lump;
278             marked[num_marked++].li_namespace = li_namespace;  // killough 4/17/98
279           }
280         else
281           lumpinfo[num_unmarked++] = *lump;       // else move down THIS list
282 
283   // Append marked list to end of unmarked list
284   memcpy(lumpinfo + num_unmarked, marked, num_marked * sizeof(*marked));
285 
286   free(marked);                                   // free marked list
287 
288   numlumps = num_unmarked + num_marked;           // new total number of lumps
289 
290   if (mark_end)                                   // add end marker
291     {
292       lumpinfo[numlumps].size = 0;  // killough 3/20/98: force size to be 0
293       lumpinfo[numlumps].wadfile = NULL;
294       lumpinfo[numlumps].li_namespace = ns_global;   // killough 4/17/98
295       strncpy(lumpinfo[numlumps++].name, end_marker, 8);
296     }
297 }
298 
299 // Hash function used for lump names.
300 // Must be mod'ed with table size.
301 // Can be used for any 8-character names.
302 // by Lee Killough
303 
W_LumpNameHash(const char * s)304 unsigned W_LumpNameHash(const char *s)
305 {
306   unsigned hash;
307   if (!s[0]) return 0;
308   (void) ((hash =        toupper(s[0]), s[1]) &&
309           (hash = hash*3+toupper(s[1]), s[2]) &&
310           (hash = hash*2+toupper(s[2]), s[3]) &&
311           (hash = hash*2+toupper(s[3]), s[4]) &&
312           (hash = hash*2+toupper(s[4]), s[5]) &&
313           (hash = hash*2+toupper(s[5]), s[6]) &&
314           (hash = hash*2+toupper(s[6]),
315            hash = hash*2+toupper(s[7]))
316          );
317   return hash;
318 }
319 
320 //
321 // W_CheckNumForName
322 // Returns -1 if name not found.
323 //
324 // Rewritten by Lee Killough to use hash table for performance. Significantly
325 // cuts down on time -- increases Doom performance over 300%. This is the
326 // single most important optimization of the original Doom sources, because
327 // lump name lookup is used so often, and the original Doom used a sequential
328 // search. For large wads with > 1000 lumps this meant an average of over
329 // 500 were probed during every search. Now the average is under 2 probes per
330 // search. There is no significant benefit to packing the names into longwords
331 // with this new hashing algorithm, because the work to do the packing is
332 // just as much work as simply doing the string comparisons with the new
333 // algorithm, which minimizes the expected number of comparisons to under 2.
334 //
335 // killough 4/17/98: add namespace parameter to prevent collisions
336 // between different resources such as flats, sprites, colormaps
337 //
338 
339 // W_FindNumFromName, an iterative version of W_CheckNumForName
340 // returns list of lump numbers for a given name (latest first)
341 //
342 int (W_FindNumFromName)(const char *name, lumpinfo_namespace_t li_namespace, int i)
343 {
344   // Hash function maps the name to one of possibly numlump chains.
345   // It has been tuned so that the average chain length never exceeds 2.
346 
347   // proff 2001/09/07 - check numlumps==0, this happens when called before WAD loaded
348   if (numlumps == 0)
349     i = -1;
350   else
351   {
352     if (i < 0)
353       i = lumpinfo[W_LumpNameHash(name) % (unsigned) numlumps].index;
354     else
355       i = lumpinfo[i].next;
356 
357   // We search along the chain until end, looking for case-insensitive
358   // matches which also match a namespace tag. Separate hash tables are
359   // not used for each namespace, because the performance benefit is not
360   // worth the overhead, considering namespace collisions are rare in
361   // Doom wads.
362 
363   while (i >= 0 && (strncasecmp(lumpinfo[i].name, name, 8) ||
364                     lumpinfo[i].li_namespace != li_namespace))
365     i = lumpinfo[i].next;
366   }
367 
368   // Return the matching lump, or -1 if none found.
369 
370   return i;
371 }
372 
373 //
374 // killough 1/31/98: Initialize lump hash table
375 //
376 
W_HashLumps(void)377 void W_HashLumps(void)
378 {
379   int i;
380 
381   for (i=0; i<numlumps; i++)
382     lumpinfo[i].index = -1;                     // mark slots empty
383 
384   // Insert nodes to the beginning of each chain, in first-to-last
385   // lump order, so that the last lump of a given name appears first
386   // in any chain, observing pwad ordering rules. killough
387 
388   for (i=0; i<numlumps; i++)
389     {                                           // hash function:
390       int j = W_LumpNameHash(lumpinfo[i].name) % (unsigned) numlumps;
391       lumpinfo[i].next = lumpinfo[j].index;     // Prepend to list
392       lumpinfo[j].index = i;
393     }
394 }
395 
396 // End of lump hashing -- killough 1/31/98
397 
398 
399 
400 // W_GetNumForName
401 // Calls W_CheckNumForName, but bombs out if not found.
402 //
W_GetNumForName(const char * name)403 int W_GetNumForName (const char* name)     // killough -- const added
404 {
405   int i = W_CheckNumForName (name);
406   if (i == -1)
407     I_Error("W_GetNumForName: %.8s not found", name);
408   return i;
409 }
410 
411 // W_GetNameForNum
412 // Returns the name for the giiven lump number
413 //
W_GetNameForNum(const int lump)414 char* W_GetNameForNum (const int lump)
415 {
416   return (lump >=0 && lump < numlumps)? lumpinfo[lump].name : NULL;
417 }
418 
419 // W_ListNumFromName
420 // calls W_FindNumFromName and returns the lumps in ascending order
421 //
W_ListNumFromName(const char * name,int lump)422 int W_ListNumFromName(const char *name, int lump)
423 {
424   int i, next;
425 
426   for (i = -1; (next = W_FindNumFromName(name, i)) >= 0; i = next)
427     if (next == lump)
428       break;
429 
430   return i;
431 }
432 
433 
434 // W_Init
435 // Loads each of the files in the wadfiles array.
436 // All files are optional, but at least one file
437 //  must be found.
438 // Files with a .wad extension are idlink files
439 //  with multiple lumps.
440 // Other files are single lumps with the base filename
441 //  for the lump name.
442 // Lump names can appear multiple times.
443 // The name searcher looks backwards, so a later file
444 //  does override all earlier ones.
445 //
446 // CPhipps - modified to use the new wadfiles array
447 //
448 wadfile_info_t *wadfiles=NULL;
449 
450 size_t numwadfiles = 0; // CPhipps - size of the wadfiles array (dynamic, no limit)
451 
W_Init(void)452 void W_Init(void)
453 {
454   // CPhipps - start with nothing
455 
456   numlumps = 0; lumpinfo = NULL;
457 
458   { // CPhipps - new wadfiles array used
459     // open all the files, load headers, and count lumps
460     unsigned i;
461     for (i=0; i < numwadfiles; i++)
462       W_AddFile(&wadfiles[i]);
463   }
464 
465   if (!numlumps)
466     I_Error ("W_Init: No files found");
467 
468   //jff 1/23/98
469   // get all the sprites and flats into one marked block each
470   // killough 1/24/98: change interface to use M_START/M_END explicitly
471   // killough 4/17/98: Add namespace tags to each entry
472   // killough 4/4/98: add colormap markers
473   W_CoalesceMarkedResource("S_START", "S_END", ns_sprites);
474   W_CoalesceMarkedResource("F_START", "F_END", ns_flats);
475   W_CoalesceMarkedResource("C_START", "C_END", ns_colormaps);
476   W_CoalesceMarkedResource("B_START", "B_END", ns_prboom);
477   W_CoalesceMarkedResource("HI_START", "HI_END", ns_hires);
478 
479   // killough 1/31/98: initialize lump hash table
480   W_HashLumps();
481 
482   /* cph 2001/07/07 - separated cache setup */
483   lprintf(LO_INFO,"W_InitCache\n");
484   W_InitCache();
485 }
486 
W_Exit(void)487 void W_Exit(void)
488 {
489 	unsigned i;
490 	for (i = 0; i < numwadfiles; i++)
491    {
492 		if (wadfiles[i].handle)
493       {
494 #ifdef MEMORY_LOW
495          close(wadfiles[i].handle);
496 #else
497          fclose(wadfiles[i].handle);
498          free(wadfiles[i].data);
499 #endif
500       }
501 	}
502 }
503 
W_ReleaseAllWads(void)504 void W_ReleaseAllWads(void)
505 {
506 	unsigned i;
507 	W_DoneCache();
508 
509 	for(i = 0; i < numwadfiles; i++)
510 	{
511 		if(wadfiles[i].handle)
512 		{
513 #ifdef MEMORY_LOW
514 			close(wadfiles[i].handle);
515 #else
516          fclose(wadfiles[i].handle);
517 			free(wadfiles[i].data);
518 #endif
519 			wadfiles[i].handle = 0;
520 		}
521 	}
522 
523 	numwadfiles = 0;
524 	free(wadfiles);
525 	wadfiles = NULL;
526 	numlumps = 0;
527 	free(lumpinfo);
528 	lumpinfo = NULL;
529 }
530 
531 //
532 // W_LumpLength
533 // Returns the buffer size needed to load the given lump.
534 //
W_LumpLength(int lump)535 int W_LumpLength (int lump)
536 {
537   if (lump >= numlumps)
538     I_Error ("W_LumpLength: %i >= numlumps",lump);
539   return lumpinfo[lump].size;
540 }
541 
542 //
543 // W_ReadLump
544 // Loads the lump into the given buffer,
545 //  which must be >= W_LumpLength().
546 //
547 
W_ReadLump(int lump,void * dest)548 void W_ReadLump(int lump, void *dest)
549 {
550   lumpinfo_t *l = lumpinfo + lump;
551 
552     {
553       if (l->wadfile)
554       {
555 #ifdef MEMORY_LOW
556          lseek(l->wadfile->handle, l->position, SEEK_SET);
557          I_Read(l->wadfile->handle, dest, l->size);
558 #else
559          memcpy(dest, &l->wadfile->data[l->position], l->size);
560 #endif
561       }
562     }
563 }
564