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