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