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