1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
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-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 * Handles WAD file header, directory, lump I/O.
30 *-----------------------------------------------------------------------------
37 #pragma implementation "w_wad.h"
43 #include "rockmacros.h"
48 // Location of each lump on disk.
49 lumpinfo_t
*lumpinfo IBSS_ATTR
;
50 int numlumps IBSS_ATTR
; // killough
51 void **lumpcache IBSS_ATTR
; // killough
53 static int *locktic
; // cph
55 static void W_ReportLocks(void)
58 lprintf(LO_DEBUG
, "W_ReportLocks:\nLump Size Locks Tics\n");
59 for (i
=0; i
<numlumps
; i
++) {
60 if (lumpinfo
[i
].locks
)
61 printf("%8.8s %6u %2d %6d\n", lumpinfo
[i
].name
,
62 W_LumpLength(i
), lumpinfo
[i
].locks
, gametic
- locktic
[i
]);
68 void W_PrintLump(FILE* fp
, void* p
) {
70 for (i
=0; i
<numlumps
; i
++)
71 if (lumpcache
[i
] == p
) {
72 fdprintf(fp
, " %8.8s %6u %2d %6d", lumpinfo
[i
].name
,
73 W_LumpLength(i
), lumpinfo
[i
].locks
, gametic
- locktic
[i
]);
76 fprintf(fp
, " not found");
82 static int W_Filelength(int handle
)
86 if (fstat(handle,&fileinfo) == -1)
87 I_Error("W_Filelength: Error fstating");
88 return fileinfo.st_size;
90 return filesize(handle
);
93 void ExtractFileBase (const char *path
, char *dest
)
95 const char *src
= path
+ strlen(path
) - 1;
98 // back up until a \ or the start
99 while (src
!= path
&& src
[-1] != ':' // killough 3/22/98: allow c:filename
104 // copy up to eight characters
108 while (*src
&& *src
!= '.' && ++length
<9)
109 *dest
++ = toupper(*src
++);
110 /* cph - length check removed, just truncate at 8 chars.
111 * If there are 8 or more chars, we'll copy 8, and no zero termination
116 // 1/18/98 killough: adds a default extension to a path
117 // Note: Backslashes are treated specially, for MS-DOS.
120 char *AddDefaultExtension(char *path
, const char *ext
)
124 while (p
-->path
&& *p
!='/' && *p
!='\\')
129 return strcat(path
,ext
);
133 // LUMP BASED ROUTINES.
138 // All files are optional, but at least one file must be
139 // found (PWAD, if all required lumps are present).
140 // Files with a .wad extension are wadlink files
141 // with multiple lumps.
142 // Other files are single lumps with the base filename
143 // for the lump name.
145 // Reload hack removed by Lee Killough
146 // CPhipps - source is an enum
149 static void W_AddFile(const char *filename
, wad_source_t source
)
150 // killough 1/31/98: static, const
158 filelump_t
*fileinfo
, *fileinfo2free
=NULL
; //killough
159 filelump_t singleinfo
;
161 // open the file and add to directory
163 handle
= open(filename
,O_RDONLY
);
166 if (handle
== -1 && D_NetGetWad(filename
)) // CPhipps
167 handle
= open(filename
,O_RDONLY
);
172 if ( strlen(filename
)<=4 || // add error check -- killough
173 (strcasecmp(filename
+strlen(filename
)-4 , ".lmp" ) &&
174 strcasecmp(filename
+strlen(filename
)-4 , ".gwa" ) )
176 I_Error("W_AddFile: couldn't open %s",filename
);
180 //jff 8/3/98 use logical output routine
181 printf (" adding %s\n",filename
);
182 startlump
= numlumps
;
184 if ( strlen(filename
)<=4 ||
186 strcasecmp(filename
+strlen(filename
)-4,".wad") &&
187 strcasecmp(filename
+strlen(filename
)-4,".gwa")
192 fileinfo
= &singleinfo
;
193 singleinfo
.filepos
= 0;
194 singleinfo
.size
= LONG(W_Filelength(handle
));
195 ExtractFileBase(filename
, singleinfo
.name
);
201 read(handle
, &header
, sizeof(header
));
202 if (strncmp(header
.identification
,"IWAD",4) &&
203 strncmp(header
.identification
,"PWAD",4))
204 I_Error("W_AddFile: Wad file %s doesn't have IWAD or PWAD id", filename
);
205 header
.numlumps
= LONG(header
.numlumps
);
206 header
.infotableofs
= LONG(header
.infotableofs
);
207 length
= header
.numlumps
*sizeof(filelump_t
);
208 fileinfo2free
= fileinfo
= malloc(length
); // killough
209 lseek(handle
, header
.infotableofs
, SEEK_SET
);
210 read(handle
, fileinfo
, length
);
211 numlumps
+= header
.numlumps
;
215 lumpinfo
= realloc(lumpinfo
, numlumps
*sizeof(lumpinfo_t
));
217 lump_p
= &lumpinfo
[startlump
];
218 for (i
=startlump
; (int)i
<numlumps
; i
++,lump_p
++, fileinfo
++)
220 lump_p
->handle
= handle
; // killough 4/25/98
221 lump_p
->position
= LONG(fileinfo
->filepos
);
222 lump_p
->size
= LONG(fileinfo
->size
);
223 #ifndef NO_PREDEFINED_LUMPS
224 lump_p
->data
= NULL
; // killough 1/31/98
226 lump_p
->namespace = ns_global
; // killough 4/17/98
227 strncpy (lump_p
->name
, fileinfo
->name
, 8);
228 lump_p
->source
= source
; // Ty 08/29/98
229 lump_p
->locks
= 0; // CPhipps - initialise locks
232 free(fileinfo2free
); // killough
235 // jff 1/23/98 Create routines to reorder the master directory
236 // putting all flats into one marked block, and all sprites into another.
237 // This will allow loading of sprites and flats from a PWAD with no
238 // other changes to code, particularly fast hashes of the lumps.
240 // killough 1/24/98 modified routines to be a little faster and smaller
242 static int IsMarker(const char *marker
, const char *name
)
244 return !strncasecmp(name
, marker
, 8) ||
245 (*name
== *marker
&& !strncasecmp(name
+1, marker
, 7));
248 // killough 4/17/98: add namespace tags
250 static void W_CoalesceMarkedResource(const char *start_marker
,
251 const char *end_marker
, int namespace)
253 lumpinfo_t
*marked
= malloc(sizeof(*marked
) * numlumps
);
254 size_t i
, num_marked
= 0, num_unmarked
= 0;
255 int is_marked
= 0, mark_end
= 0;
256 lumpinfo_t
*lump
= lumpinfo
;
258 for (i
=numlumps
; i
--; lump
++)
259 if (IsMarker(start_marker
, lump
->name
)) // start marker found
260 { // If this is the first start marker, add start marker to marked lumps
263 strncpy(marked
->name
, start_marker
, 8);
264 marked
->size
= 0; // killough 3/20/98: force size to be 0
265 marked
->namespace = ns_global
; // killough 4/17/98
268 is_marked
= 1; // start marking lumps
271 if (IsMarker(end_marker
, lump
->name
)) // end marker found
273 mark_end
= 1; // add end marker below
274 is_marked
= 0; // stop marking lumps
277 if (is_marked
) // if we are marking lumps,
278 { // move lump to marked list
279 marked
[num_marked
] = *lump
;
280 marked
[num_marked
++].namespace = namespace; // killough 4/17/98
283 lumpinfo
[num_unmarked
++] = *lump
; // else move down THIS list
285 // Append marked list to end of unmarked list
286 memcpy(lumpinfo
+ num_unmarked
, marked
, num_marked
* sizeof(*marked
));
288 free(marked
); // free marked list
290 numlumps
= num_unmarked
+ num_marked
; // new total number of lumps
292 if (mark_end
) // add end marker
294 lumpinfo
[numlumps
].size
= 0; // killough 3/20/98: force size to be 0
295 lumpinfo
[numlumps
].namespace = ns_global
; // killough 4/17/98
296 strncpy(lumpinfo
[numlumps
++].name
, end_marker
, 8);
300 // Hash function used for lump names.
301 // Must be mod'ed with table size.
302 // Can be used for any 8-character names.
305 unsigned W_LumpNameHash(const char *s
)
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]))
322 // Returns -1 if name not found.
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.
335 // killough 4/17/98: add namespace parameter to prevent collisions
336 // between different resources such as flats, sprites, colormaps
339 int (W_CheckNumForName
)(register const char *name
, register int namespace)
341 // Hash function maps the name to one of possibly numlump chains.
342 // It has been tuned so that the average chain length never exceeds 2.
349 i
= lumpinfo
[W_LumpNameHash(name
) % (unsigned) numlumps
].index
;
351 // We search along the chain until end, looking for case-insensitive
352 // matches which also match a namespace tag. Separate hash tables are
353 // not used for each namespace, because the performance benefit is not
354 // worth the overhead, considering namespace collisions are rare in
357 while (i
>= 0 && (strncasecmp(lumpinfo
[i
].name
, name
, 8) ||
358 lumpinfo
[i
].namespace != (unsigned)namespace))
359 i
= lumpinfo
[i
].next
;
361 // Return the matching lump, or -1 if none found.
367 // killough 1/31/98: Initialize lump hash table
370 static void W_InitLumpHash(void)
374 for (i
=0; i
<numlumps
; i
++)
375 lumpinfo
[i
].index
= -1; // mark slots empty
377 // Insert nodes to the beginning of each chain, in first-to-last
378 // lump order, so that the last lump of a given name appears first
379 // in any chain, observing pwad ordering rules. killough
381 for (i
=0; i
<numlumps
; i
++)
383 int j
= W_LumpNameHash(lumpinfo
[i
].name
) % (unsigned) numlumps
;
384 lumpinfo
[i
].next
= lumpinfo
[j
].index
; // Prepend to list
385 lumpinfo
[j
].index
= i
;
389 // End of lump hashing -- killough 1/31/98
394 // Calls W_CheckNumForName, but bombs out if not found.
396 int W_GetNumForName (const char* name
) // killough -- const added
398 int i
= W_CheckNumForName (name
);
400 I_Error("W_GetNumForName: %s not found", name
);
405 // Loads each of the files in the wadfiles array.
406 // All files are optional, but at least one file
408 // Files with a .wad extension are idlink files
409 // with multiple lumps.
410 // Other files are single lumps with the base filename
411 // for the lump name.
412 // Lump names can appear multiple times.
413 // The name searcher looks backwards, so a later file
414 // does override all earlier ones.
416 // CPhipps - modified to use the new wadfiles array
418 struct wadfile_info
*wadfiles
=NULL
;
420 size_t numwadfiles
= 0; // CPhipps - size of the wadfiles array (dynamic, no limit)
424 #ifndef NO_PREDEFINED_LUMPS
425 // killough 1/31/98: add predefined lumps first
427 numlumps
= num_predefined_lumps
;
429 // lumpinfo will be realloced as lumps are added
430 lumpinfo
= malloc(numlumps
*sizeof(*lumpinfo
));
432 memcpy(lumpinfo
, predefined_lumps
, numlumps
*sizeof(*lumpinfo
));
433 // Ty 08/29/98 - add source flag to the predefined lumps
436 for (i
=0;i
<numlumps
;i
++)
437 lumpinfo
[i
].source
= source_pre
;
440 // CPhipps - start with nothing
442 numlumps
= 0; lumpinfo
= malloc(0);
445 { // CPhipps - new wadfiles array used
446 // open all the files, load headers, and count lumps
448 for (i
=0; (size_t)i
<numwadfiles
; i
++)
449 W_AddFile(wadfiles
[i
].name
, wadfiles
[i
].src
);
452 I_Error ("W_Init: No files found");
455 // get all the sprites and flats into one marked block each
456 // killough 1/24/98: change interface to use M_START/M_END explicitly
457 // killough 4/17/98: Add namespace tags to each entry
459 W_CoalesceMarkedResource("S_START", "S_END", ns_sprites
);
460 W_CoalesceMarkedResource("F_START", "F_END", ns_flats
);
462 // killough 4/4/98: add colormap markers
463 W_CoalesceMarkedResource("C_START", "C_END", ns_colormaps
);
466 lumpcache
= calloc(sizeof *lumpcache
, numlumps
); // killough
469 I_Error ("W_Init: Couldn't allocate lumpcache");
471 // killough 1/31/98: initialize lump hash table
475 // cph - allocate space for lock time diagnostics
476 locktic
= malloc(sizeof(*locktic
)*numlumps
);
477 atexit(W_ReportLocks
);
483 // Returns the buffer size needed to load the given lump.
485 int W_LumpLength (int lump
)
487 if (lump
>= numlumps
)
488 I_Error ("W_LumpLength: %d >= numlumps",lump
);
489 return lumpinfo
[lump
].size
;
494 // Loads the lump into the given buffer,
495 // which must be >= W_LumpLength().
498 void W_ReadLump(int lump
, void *dest
)
500 lumpinfo_t
*l
= lumpinfo
+ lump
;
503 if (lump
>= numlumps
)
504 I_Error ("W_ReadLump: %d >= numlumps",lump
);
507 #ifndef NO_PREDEFINED_LUMPS
508 if (l
->data
) // killough 1/31/98: predefined lump data
509 memcpy(dest
, l
->data
, l
->size
);
516 if(gamestate
==GS_LEVEL
)
517 printf("Loading %s\n", lumpinfo
[lump
].name
);
520 // killough 1/31/98: Reload hack (-wart) removed
522 lseek(l
->handle
, l
->position
, SEEK_SET
);
523 c
= read(l
->handle
, dest
, l
->size
);
525 I_Error("W_ReadLump: only read %d of %d on lump %d", c
, l
->size
, lump
);
532 * killough 4/25/98: simplified
533 * CPhipps - modified for new lump locking scheme
537 void * (W_CacheLumpNum
)(int lump
, unsigned short locks
)
540 if ((unsigned)lump
>= (unsigned)numlumps
)
541 I_Error ("W_CacheLumpNum: %d >= numlumps",lump
);
544 if (!lumpcache
[lump
]) // read the lump in
545 W_ReadLump(lump
, Z_Malloc(W_LumpLength(lump
), PU_CACHE
, &lumpcache
[lump
]));
547 /* cph - if wasn't locked but now is, tell z_zone to hold it */
548 if (!lumpinfo
[lump
].locks
&& locks
) {
549 Z_ChangeTag(lumpcache
[lump
],PU_STATIC
);
551 locktic
[lump
] = gametic
;
554 lumpinfo
[lump
].locks
+= locks
;
557 if (!((lumpinfo
[lump
].locks
+1) & 0xf))
558 printf("W_CacheLumpNum: High lock on %s (%d)\n",
559 lumpinfo
[lump
].name
, lumpinfo
[lump
].locks
);
562 // CPhipps - if not locked, can't give you a pointer
563 return (locks
? lumpcache
[lump
] : NULL
);
567 * W_CacheLumpNumPadded
569 * Caches a lump and pads the memory following it.
570 * The thing returned is *only* guaranteed to be padded if
571 * the lump isn't already cached (otherwise, you get whatever is
572 * currently cached, which if it was cached by a previous call
573 * to this will also be padded)
576 void * W_CacheLumpNumPadded(int lump
, size_t len
, unsigned char pad
)
580 if ((unsigned)lump
>= (unsigned)numlumps
)
581 I_Error ("W_CacheLumpNum: %d >= numlumps",lump
);
584 if (!lumpcache
[lump
]) { /* read the lump in */
585 size_t lumplen
= W_LumpLength(lump
);
587 W_ReadLump(lump
, p
= Z_Malloc(len
, PU_CACHE
, &lumpcache
[lump
]));
588 memset(p
+lumplen
, pad
, len
-lumplen
);
591 /* cph - if wasn't locked but now is, tell z_zone to hold it */
592 if (!lumpinfo
[lump
].locks
&& locks
) {
593 Z_ChangeTag(lumpcache
[lump
],PU_STATIC
);
595 locktic
[lump
] = gametic
;
598 lumpinfo
[lump
].locks
+= locks
;
601 if (!((lumpinfo
[lump
].locks
+1) & 0xf))
602 printf("W_CacheLumpNum: High lock on %s (%d)\n",
603 lumpinfo
[lump
].name
, lumpinfo
[lump
].locks
);
606 return lumpcache
[lump
];
612 // CPhipps - this changes (should reduce) the number of locks on a lump
614 void (W_UnlockLumpNum
)(int lump
, signed short unlocks
)
617 if ((signed short)lumpinfo
[lump
].locks
< unlocks
)
618 printf("W_UnlockLumpNum: Excess unlocks on %s (%d-%d)\n",
619 lumpinfo
[lump
].name
, lumpinfo
[lump
].locks
, unlocks
);
621 lumpinfo
[lump
].locks
-= unlocks
;
622 // cph - Note: must only tell z_zone to make purgeable if currently locked,
623 // else it might already have been purged
624 if (unlocks
&& !lumpinfo
[lump
].locks
)
625 Z_ChangeTag(lumpcache
[lump
], PU_CACHE
);
628 // W_CacheLumpName macroized in w_wad.h -- killough
630 #ifndef NO_PREDEFINED_LUMPS
631 // WritePredefinedLumpWad
632 // Args: Filename - string with filename to write to
635 // If the user puts a -dumplumps switch on the command line, we will
636 // write all those predefined lumps above out into a pwad. User
637 // supplies the pwad name.
639 // killough 4/22/98: make endian-independent, remove tab chars
640 void WritePredefinedLumpWad(const char *filename
)
642 int handle
; // for file open
643 char filenam
[256]; // we may have to add ".wad" to the name they pass
645 if (!filename
|| !*filename
) // check for null pointer or empty name
646 return; // early return
648 AddDefaultExtension(strcpy(filenam
, filename
), ".wad");
650 // The following code writes a PWAD from the predefined lumps array
651 // How to write a PWAD will not be explained here.
652 #ifdef _MSC_VER // proff: In Visual C open is defined a bit different
653 if ( (handle
= open (filenam
, O_RDWR
| O_CREAT
| O_BINARY
, _S_IWRITE
|_S_IREAD
)) != -1)
655 if ( (handle
= open (filenam
, O_RDWR
| O_CREAT
| O_BINARY
, S_IWUSR
|S_IRUSR
)) != -1)
658 wadinfo_t header
= {"PWAD"};
659 size_t filepos
= sizeof(wadinfo_t
) + num_predefined_lumps
* sizeof(filelump_t
);
662 header
.numlumps
= LONG(num_predefined_lumps
);
663 header
.infotableofs
= LONG(sizeof(header
));
666 write(handle
, &header
, sizeof(header
));
669 for (i
=0;(size_t)i
<num_predefined_lumps
;i
++)
671 filelump_t fileinfo
= {0};
672 fileinfo
.filepos
= LONG(filepos
);
673 fileinfo
.size
= LONG(predefined_lumps
[i
].size
);
674 strncpy(fileinfo
.name
, predefined_lumps
[i
].name
, 8);
675 write(handle
, &fileinfo
, sizeof(fileinfo
));
676 filepos
+= predefined_lumps
[i
].size
;
680 for (i
=0;(size_t)i
<num_predefined_lumps
;i
++)
681 write(handle
, predefined_lumps
[i
].data
, predefined_lumps
[i
].size
);
684 I_Error("WritePredefinedLumpWad: Predefined lumps wad, %s written", filename
);
686 I_Error("WritePredefinedLumpWad: Can't open predefined lumps wad %s for output", filename
);