Round 2: Added --help and --version, as well as HHEXEN_DATA environment variable...
[theoddone33/hhexen.git] / base / w_wad.c
1
2 //**************************************************************************
3 //**
4 //** w_wad.c : Heretic 2 : Raven Software, Corp.
5 //**
6 //** $RCSfile$
7 //** $Revision$
8 //** $Date$
9 //** $Author$
10 //**
11 //**************************************************************************
12
13 // HEADER FILES ------------------------------------------------------------
14
15 #ifdef __linux
16 #include <malloc.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <unistd.h> /* jim open() etc. */
20 #include <ctype.h>  /* jim toupper() */
21 #else
22 #ifdef NeXT
23 #include <libc.h>
24 #include <ctype.h>
25 #else
26 #include <malloc.h>
27 #include <io.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #endif
31 #endif
32 #include <alloca.h>
33 #include "h2def.h"
34
35 // MACROS ------------------------------------------------------------------
36
37 #if defined(NeXT) || defined(__linux)
38 // NeXT doesn't need a binary flag in open call
39 #define O_BINARY 0
40 #define strcmpi strcasecmp
41 #endif
42
43 // TYPES -------------------------------------------------------------------
44
45 typedef struct
46 {
47         char identification[4];
48         int numlumps;
49         int infotableofs;
50 } wadinfo_t;
51
52 typedef struct
53 {
54         int filepos;
55         int size;
56         char name[8];
57 } filelump_t;
58
59 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
60
61 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
62
63 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
64
65 void    W_MergeLumps(char *start, char *end);
66
67 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
68
69 // PUBLIC DATA DEFINITIONS -------------------------------------------------
70
71 lumpinfo_t *lumpinfo;
72 int numlumps;
73 void **lumpcache;
74
75 // PRIVATE DATA DEFINITIONS ------------------------------------------------
76
77 static lumpinfo_t *PrimaryLumpInfo;
78 static int PrimaryNumLumps;
79 static void **PrimaryLumpCache;
80 static lumpinfo_t *AuxiliaryLumpInfo;
81 static int AuxiliaryNumLumps;
82 static void **AuxiliaryLumpCache;
83 static int AuxiliaryHandle = 0;
84 boolean AuxiliaryOpened = false;
85
86 // CODE --------------------------------------------------------------------
87
88 #if defined(NeXT) || defined(__linux)
89 //==========================================================================
90 //
91 // strupr
92 //
93 //==========================================================================
94
95 void strupr(char *s)
96 {
97     while(*s)
98         *s++ = toupper(*s);
99 }
100
101 //==========================================================================
102 //
103 // filelength
104 //
105 //==========================================================================
106
107 int filelength(int handle)
108 {
109     struct stat fileinfo;
110
111     if(fstat(handle, &fileinfo) == -1)
112         {
113                 I_Error("Error fstating");
114         }
115     return fileinfo.st_size;
116 }
117 #endif
118
119 //==========================================================================
120 //
121 // W_AddFile
122 //
123 // Files with a .wad extension are wadlink files with multiple lumps,
124 // other files are single lumps with the base filename for the lump name.
125 //
126 //==========================================================================
127
128 void W_AddFile(char *filename)
129 {
130         wadinfo_t header;
131         lumpinfo_t *lump_p;
132         unsigned i;
133         char path[128];
134         int handle, length;
135         int startlump;
136         filelump_t *fileinfo, singleinfo;
137         filelump_t *freeFileInfo;
138
139         /* Add support for HHEXEN_DATA envirionment variable */
140         snprintf (path, 128, "%s/%s", getenv("HHEXEN_DATA"), filename);
141         if((handle = open(path, O_RDONLY|O_BINARY)) == -1)
142         {
143                 /* Now try CWD */
144                 if((handle = open(filename, O_RDONLY|O_BINARY)) == -1)
145                 { // Didn't find file
146                         return;
147                 }
148         }
149         startlump = numlumps;
150         if(strcmpi(filename+strlen(filename)-3, "wad"))
151         { // Single lump file
152                 fileinfo = &singleinfo;
153                 freeFileInfo = NULL;
154                 singleinfo.filepos = 0;
155                 singleinfo.size = LONG(filelength(handle));
156                 M_ExtractFileBase(filename, singleinfo.name);
157                 numlumps++;
158         }
159         else
160         { // WAD file
161                 read(handle, &header, sizeof(header));
162                 if(strncmp(header.identification, "IWAD", 4))
163                 {
164                         if(strncmp(header.identification, "PWAD", 4))
165                         { // Bad file id
166                                 I_Error("Wad file %s doesn't have IWAD or PWAD id\n",
167                                         filename);
168                         }
169                 }
170                 header.numlumps = LONG(header.numlumps);
171                 header.infotableofs = LONG(header.infotableofs);
172                 length = header.numlumps*sizeof(filelump_t);
173 //              fileinfo = alloca(length);
174                 if(!(fileinfo = malloc(length)))
175                 {
176                         I_Error("W_AddFile:  fileinfo malloc failed\n");
177                 }
178                 freeFileInfo = fileinfo;
179                 lseek(handle, header.infotableofs, SEEK_SET);
180                 read(handle, fileinfo, length);
181                 numlumps += header.numlumps;
182         }
183
184         // Fill in lumpinfo
185         lumpinfo = realloc(lumpinfo, numlumps*sizeof(lumpinfo_t));
186         if(!lumpinfo)
187         {
188                 I_Error("Couldn't realloc lumpinfo");
189         }
190         lump_p = &lumpinfo[startlump];
191         for(i = startlump; i < numlumps; i++, lump_p++, fileinfo++)
192         {
193                 lump_p->handle = handle;
194                 lump_p->position = LONG(fileinfo->filepos);
195                 lump_p->size = LONG(fileinfo->size);
196                 strncpy(lump_p->name, fileinfo->name, 8);
197         }
198         if(freeFileInfo)
199         {
200                 free(freeFileInfo);
201         }
202 }
203
204 //==========================================================================
205 //
206 // W_InitMultipleFiles
207 //
208 // Pass a null terminated list of files to use.  All files are optional,
209 // but at least one file must be found.  Lump names can appear multiple
210 // times.  The name searcher looks backwards, so a later file can
211 // override an earlier one.
212 //
213 //==========================================================================
214
215 void W_InitMultipleFiles(char **filenames)
216 {
217         int size;
218
219         // Open all the files, load headers, and count lumps
220         numlumps = 0;
221         lumpinfo = malloc(1); // Will be realloced as lumps are added
222
223         for(; *filenames; filenames++)
224         {
225                 W_AddFile(*filenames);
226         }
227         if(!numlumps)
228         {
229                 I_Error("W_InitMultipleFiles: no files found");
230         }
231
232         // Merge lumps for flats and sprites
233         W_MergeLumps("S_START","S_END");
234         W_MergeLumps("F_START","F_END");
235
236         // Set up caching
237         size = numlumps*sizeof(*lumpcache);
238         lumpcache = malloc(size);
239         if(!lumpcache)
240         {
241                 I_Error("Couldn't allocate lumpcache");
242         }
243         memset(lumpcache, 0, size);
244
245         PrimaryLumpInfo = lumpinfo;
246         PrimaryLumpCache = lumpcache;
247         PrimaryNumLumps = numlumps;
248 }
249
250 //==========================================================================
251 //
252 // IsMarker
253 //
254 // From BOOM/xdoom.  Finds an S_START or SS_START marker
255 //
256 //==========================================================================
257
258 static int IsMarker(char *marker, char *name)
259 {
260         return !strncasecmp(name, marker, 8) ||
261                 (*name == *marker && !strncasecmp(name+1,marker,7));
262 }
263
264 //==========================================================================
265 //
266 // W_MergeLumps
267 //
268 // From xdoom/BOOM again.  Merges all sprite lumps into one big 'ol block
269 //
270 //==========================================================================
271
272 void W_MergeLumps(char *start, char *end)
273 {
274         lumpinfo_t *newlumpinfo;
275         int newlumps, oldlumps;
276         int in_block = 0;
277         int i;
278
279         newlumpinfo = (lumpinfo_t *) alloca(numlumps * sizeof(lumpinfo_t));
280         oldlumps = newlumps = 0;
281
282         for(i=0;i < numlumps;i++)
283         {
284                 //process lumps in global namespace
285                 if(!in_block)
286                 {
287                         //check for start of block
288                         if (IsMarker(start, lumpinfo[i].name))
289                         {
290                                 in_block=1;
291                                 if(!newlumps)
292                                 {
293                                         newlumps++;
294                                         memset(newlumpinfo[0].name,0,8);
295                     strcpy(newlumpinfo[0].name, start);
296                     newlumpinfo[0].handle = -1;
297                     newlumpinfo[0].position = newlumpinfo[0].size = 0;
298                 }
299             }
300             // else copy it
301             else
302             {
303                 lumpinfo[oldlumps++] = lumpinfo[i];
304             }
305         }
306         // process lumps in sprites or flats namespace
307         else
308         {
309             // check for end of block
310             if (IsMarker(end, lumpinfo[i].name))
311             {
312                 in_block = 0;
313             }
314             else if (i && lumpinfo[i].handle != lumpinfo[i-1].handle)
315             {
316                 in_block = 0;
317                 lumpinfo[oldlumps++] = lumpinfo[i];
318             }
319             else
320             {
321                 newlumpinfo[newlumps++] = lumpinfo[i];
322             }
323         }
324     }
325
326     // now copy the merged lumps to the end of the old list
327     if (newlumps)
328     {
329         if (oldlumps + newlumps > numlumps)
330             lumpinfo = realloc(lumpinfo, (oldlumps + newlumps) *
331                                  sizeof(lumpinfo_t));
332         memcpy(lumpinfo + oldlumps, newlumpinfo, sizeof(lumpinfo_t) * newlumps);
333
334         numlumps = oldlumps + newlumps;
335
336         memset(lumpinfo[numlumps].name, 0, 8);
337         strcpy(lumpinfo[numlumps].name, end);
338         lumpinfo[numlumps].handle = -1;
339         lumpinfo[numlumps].position = lumpinfo[numlumps].size = 0;
340         numlumps++;
341     }
342 }
343                                         
344 //==========================================================================
345 //
346 // W_InitFile
347 //
348 // Initialize the primary from a single file.
349 //
350 //==========================================================================
351
352 void W_InitFile(char *filename)
353 {
354         char *names[2];
355
356         names[0] = filename;
357         names[1] = NULL;
358         W_InitMultipleFiles(names);
359 }
360
361 //==========================================================================
362 //
363 // W_OpenAuxiliary
364 //
365 //==========================================================================
366
367 void W_OpenAuxiliary(char *filename)
368 {
369         int i;
370         int size;
371         wadinfo_t header;
372         int handle;
373         int length;
374         filelump_t *fileinfo;
375         filelump_t *sourceLump;
376         lumpinfo_t *destLump;
377
378         if(AuxiliaryOpened)
379         {
380                 W_CloseAuxiliary();
381         }
382         if((handle = open(filename, O_RDONLY|O_BINARY)) == -1)
383         {
384                 I_Error("W_OpenAuxiliary: %s not found.", filename);
385                 return;
386         }
387         AuxiliaryHandle = handle;
388         read(handle, &header, sizeof(header));
389         if(strncmp(header.identification, "IWAD", 4))
390         {
391                 if(strncmp(header.identification, "PWAD", 4))
392                 { // Bad file id
393                         I_Error("Wad file %s doesn't have IWAD or PWAD id\n",
394                                 filename);
395                 }
396         }
397         header.numlumps = LONG(header.numlumps);
398         header.infotableofs = LONG(header.infotableofs);
399         length = header.numlumps*sizeof(filelump_t);
400         fileinfo = Z_Malloc(length, PU_STATIC, 0);
401         lseek(handle, header.infotableofs, SEEK_SET);
402         read(handle, fileinfo, length);
403         numlumps = header.numlumps;
404
405         // Init the auxiliary lumpinfo array
406         lumpinfo = Z_Malloc(numlumps*sizeof(lumpinfo_t), PU_STATIC, 0);
407         sourceLump = fileinfo;
408         destLump = lumpinfo;
409         for(i = 0; i < numlumps; i++, destLump++, sourceLump++)
410         {
411                 destLump->handle = handle;
412                 destLump->position = LONG(sourceLump->filepos);
413                 destLump->size = LONG(sourceLump->size);
414                 strncpy(destLump->name, sourceLump->name, 8);
415         }
416         Z_Free(fileinfo);
417
418         // Allocate the auxiliary lumpcache array
419         size = numlumps*sizeof(*lumpcache);
420         lumpcache = Z_Malloc(size, PU_STATIC, 0);
421         memset(lumpcache, 0, size);
422
423         AuxiliaryLumpInfo = lumpinfo;
424         AuxiliaryLumpCache = lumpcache;
425         AuxiliaryNumLumps = numlumps;
426         AuxiliaryOpened = true;
427 }
428
429 //==========================================================================
430 //
431 // W_CloseAuxiliary
432 //
433 //==========================================================================
434
435 void W_CloseAuxiliary(void)
436 {
437         int i;
438
439         if(AuxiliaryOpened)
440         {
441                 W_UseAuxiliary();
442                 for(i = 0; i < numlumps; i++)
443                 {
444                         if(lumpcache[i])
445                         {
446                                 Z_Free(lumpcache[i]);
447                         }
448                 }
449                 Z_Free(AuxiliaryLumpInfo);
450                 Z_Free(AuxiliaryLumpCache);
451                 W_CloseAuxiliaryFile();
452                 AuxiliaryOpened = false;
453         }
454         W_UsePrimary();
455 }
456
457 //==========================================================================
458 //
459 // W_CloseAuxiliaryFile
460 //
461 // WARNING: W_CloseAuxiliary() must be called before any further
462 // auxiliary lump processing.
463 //
464 //==========================================================================
465
466 void W_CloseAuxiliaryFile(void)
467 {
468         if(AuxiliaryHandle)
469         {
470                 close(AuxiliaryHandle);
471                 AuxiliaryHandle = 0;
472         }
473 }
474
475 //==========================================================================
476 //
477 // W_UsePrimary
478 //
479 //==========================================================================
480
481 void W_UsePrimary(void)
482 {
483         lumpinfo = PrimaryLumpInfo;
484         numlumps = PrimaryNumLumps;
485         lumpcache = PrimaryLumpCache;
486 }
487
488 //==========================================================================
489 //
490 // W_UseAuxiliary
491 //
492 //==========================================================================
493
494 void W_UseAuxiliary(void)
495 {
496         if(AuxiliaryOpened == false)
497         {
498                 I_Error("W_UseAuxiliary: WAD not opened.");
499         }
500         lumpinfo = AuxiliaryLumpInfo;
501         numlumps = AuxiliaryNumLumps;
502         lumpcache = AuxiliaryLumpCache;
503 }
504
505 //==========================================================================
506 //
507 // W_NumLumps
508 //
509 //==========================================================================
510
511 int     W_NumLumps(void)
512 {
513         return numlumps;
514 }
515
516 //==========================================================================
517 //
518 // W_CheckNumForName
519 //
520 // Returns -1 if name not found.
521 //
522 //==========================================================================
523
524 int W_CheckNumForName(char *name)
525 {
526         char name8[9];
527         int v1, v2;
528         lumpinfo_t *lump_p;
529
530         // Make the name into two integers for easy compares
531         strncpy(name8, name, 8);
532         name8[8] = 0; // in case the name was a full 8 chars
533         strupr(name8); // case insensitive
534         v1 = *(int *)name8;
535         v2 = *(int *)&name8[4];
536
537         // Scan backwards so patch lump files take precedence
538         lump_p = lumpinfo+numlumps;
539         while(lump_p-- != lumpinfo)
540         {
541                 if(*(int *)lump_p->name == v1 && *(int *)&lump_p->name[4] == v2)
542                 {
543                         return lump_p-lumpinfo;
544                 }
545         }
546         return -1;
547 }
548
549 //==========================================================================
550 //
551 // W_GetNumForName
552 //
553 // Calls W_CheckNumForName, but bombs out if not found.
554 //
555 //==========================================================================
556
557 int     W_GetNumForName (char *name)
558 {
559         int     i;
560
561         i = W_CheckNumForName(name);
562         if(i != -1)
563         {
564                 return i;
565         }
566 #ifdef DEMO_WAD
567         printf("W_GetNumForName: %s not found!  Assuming shareware wad.\n", name);
568         return 1;
569 #else
570         I_Error("W_GetNumForName: %s not found!", name);
571         return -1;
572 #endif
573 }
574
575 //==========================================================================
576 //
577 // W_LumpLength
578 //
579 // Returns the buffer size needed to load the given lump.
580 //
581 //==========================================================================
582
583 int W_LumpLength(int lump)
584 {
585         if(lump >= numlumps)
586         {
587                 I_Error("W_LumpLength: %i >= numlumps", lump);
588         }
589         return lumpinfo[lump].size;
590 }
591
592 //==========================================================================
593 //
594 // W_ReadLump
595 //
596 // Loads the lump into the given buffer, which must be >= W_LumpLength().
597 //
598 //==========================================================================
599
600 void W_ReadLump(int lump, void *dest)
601 {
602         int c;
603         lumpinfo_t *l;
604
605         if(lump >= numlumps)
606         {
607                 I_Error("W_ReadLump: %i >= numlumps", lump);
608         }
609         l = lumpinfo+lump;
610         //I_BeginRead();
611         lseek(l->handle, l->position, SEEK_SET);
612         c = read(l->handle, dest, l->size);
613         if(c < l->size)
614         {
615                 I_Error("W_ReadLump: only read %i of %i on lump %i",
616                         c, l->size, lump);
617         }
618         //I_EndRead();
619 }
620
621 //==========================================================================
622 //
623 // W_CacheLumpNum
624 //
625 //==========================================================================
626
627 void *W_CacheLumpNum(int lump, int tag)
628 {
629         byte *ptr;
630
631         if((unsigned)lump >= numlumps)
632         {
633                 I_Error("W_CacheLumpNum: %i >= numlumps", lump);
634         }
635         if(!lumpcache[lump])
636         { // Need to read the lump in
637                 ptr = Z_Malloc(W_LumpLength(lump), tag, &lumpcache[lump]);
638                 W_ReadLump(lump, lumpcache[lump]);
639         }
640         else
641         {
642                 Z_ChangeTag(lumpcache[lump], tag);
643         }
644         return lumpcache[lump];
645 }
646
647 //==========================================================================
648 //
649 // W_CacheLumpName
650 //
651 //==========================================================================
652
653 void *W_CacheLumpName(char *name, int tag)
654 {
655         return W_CacheLumpNum(W_GetNumForName(name), tag);
656 }
657
658 //==========================================================================
659 //
660 // W_Profile
661 //
662 //==========================================================================
663
664 // Ripped out for Heretic
665 /*
666 int     info[2500][10];
667 int     profilecount;
668
669 void W_Profile (void)
670 {
671         int             i;
672         memblock_t      *block;
673         void    *ptr;
674         char    ch;
675         FILE    *f;
676         int             j;
677         char    name[9];
678         
679         
680         for (i=0 ; i<numlumps ; i++)
681         {       
682                 ptr = lumpcache[i];
683                 if (!ptr)
684                 {
685                         ch = ' ';
686                         continue;
687                 }
688                 else
689                 {
690                         block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
691                         if (block->tag < PU_PURGELEVEL)
692                                 ch = 'S';
693                         else
694                                 ch = 'P';
695                 }
696                 info[i][profilecount] = ch;
697         }
698         profilecount++;
699         
700         f = fopen ("waddump.txt","w");
701         name[8] = 0;
702         for (i=0 ; i<numlumps ; i++)
703         {
704                 memcpy (name,lumpinfo[i].name,8);
705                 for (j=0 ; j<8 ; j++)
706                         if (!name[j])
707                                 break;
708                 for ( ; j<8 ; j++)
709                         name[j] = ' ';
710                 fprintf (f,"%s ",name);
711                 for (j=0 ; j<profilecount ; j++)
712                         fprintf (f,"    %c",info[i][j]);
713                 fprintf (f,"\n");
714         }
715         fclose (f);
716 }
717 */