Round 3: Some minor cross-platform compatibility changes (FreeBSD), still may not...
[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 <malloc.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <unistd.h> /* jim open() etc. */
19 #include <ctype.h>  /* jim toupper() */
20 #ifndef HAVE_ALLOCA_H
21 #include <alloca.h>
22 #else
23 #define O_BINARY 0
24 #endif
25 #include "h2def.h"
26
27 // MACROS ------------------------------------------------------------------
28
29 #if defined(NeXT) || defined(__linux) || defined(__FreeBSD__)
30 // NeXT doesn't need a binary flag in open call
31 #define O_BINARY 0
32 #define strcmpi strcasecmp
33 #endif
34
35 // TYPES -------------------------------------------------------------------
36
37 typedef struct
38 {
39         char identification[4];
40         int numlumps;
41         int infotableofs;
42 } wadinfo_t;
43
44 typedef struct
45 {
46         int filepos;
47         int size;
48         char name[8];
49 } filelump_t;
50
51 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
52
53 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
54
55 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
56
57 void    W_MergeLumps(char *start, char *end);
58
59 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
60
61 // PUBLIC DATA DEFINITIONS -------------------------------------------------
62
63 lumpinfo_t *lumpinfo;
64 int numlumps;
65 void **lumpcache;
66
67 // PRIVATE DATA DEFINITIONS ------------------------------------------------
68
69 static lumpinfo_t *PrimaryLumpInfo;
70 static int PrimaryNumLumps;
71 static void **PrimaryLumpCache;
72 static lumpinfo_t *AuxiliaryLumpInfo;
73 static int AuxiliaryNumLumps;
74 static void **AuxiliaryLumpCache;
75 static int AuxiliaryHandle = 0;
76 boolean AuxiliaryOpened = false;
77
78 // CODE --------------------------------------------------------------------
79
80 #if defined(NeXT) || defined(__linux) || defined(__FreeBSD__)
81 //==========================================================================
82 //
83 // strupr
84 //
85 //==========================================================================
86
87 void strupr(char *s)
88 {
89     while(*s)
90         *s++ = toupper(*s);
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 *) alloca(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 }
335                                         
336 //==========================================================================
337 //
338 // W_InitFile
339 //
340 // Initialize the primary from a single file.
341 //
342 //==========================================================================
343
344 void W_InitFile(char *filename)
345 {
346         char *names[2];
347
348         names[0] = filename;
349         names[1] = NULL;
350         W_InitMultipleFiles(names);
351 }
352
353 //==========================================================================
354 //
355 // W_OpenAuxiliary
356 //
357 //==========================================================================
358
359 void W_OpenAuxiliary(char *filename)
360 {
361         int i;
362         int size;
363         wadinfo_t header;
364         int handle;
365         int length;
366         filelump_t *fileinfo;
367         filelump_t *sourceLump;
368         lumpinfo_t *destLump;
369
370         if(AuxiliaryOpened)
371         {
372                 W_CloseAuxiliary();
373         }
374         if((handle = open(filename, O_RDONLY|O_BINARY)) == -1)
375         {
376                 I_Error("W_OpenAuxiliary: %s not found.", filename);
377                 return;
378         }
379         AuxiliaryHandle = handle;
380         read(handle, &header, sizeof(header));
381         if(strncmp(header.identification, "IWAD", 4))
382         {
383                 if(strncmp(header.identification, "PWAD", 4))
384                 { // Bad file id
385                         I_Error("Wad file %s doesn't have IWAD or PWAD id\n",
386                                 filename);
387                 }
388         }
389         header.numlumps = LONG(header.numlumps);
390         header.infotableofs = LONG(header.infotableofs);
391         length = header.numlumps*sizeof(filelump_t);
392         fileinfo = Z_Malloc(length, PU_STATIC, 0);
393         lseek(handle, header.infotableofs, SEEK_SET);
394         read(handle, fileinfo, length);
395         numlumps = header.numlumps;
396
397         // Init the auxiliary lumpinfo array
398         lumpinfo = Z_Malloc(numlumps*sizeof(lumpinfo_t), PU_STATIC, 0);
399         sourceLump = fileinfo;
400         destLump = lumpinfo;
401         for(i = 0; i < numlumps; i++, destLump++, sourceLump++)
402         {
403                 destLump->handle = handle;
404                 destLump->position = LONG(sourceLump->filepos);
405                 destLump->size = LONG(sourceLump->size);
406                 strncpy(destLump->name, sourceLump->name, 8);
407         }
408         Z_Free(fileinfo);
409
410         // Allocate the auxiliary lumpcache array
411         size = numlumps*sizeof(*lumpcache);
412         lumpcache = Z_Malloc(size, PU_STATIC, 0);
413         memset(lumpcache, 0, size);
414
415         AuxiliaryLumpInfo = lumpinfo;
416         AuxiliaryLumpCache = lumpcache;
417         AuxiliaryNumLumps = numlumps;
418         AuxiliaryOpened = true;
419 }
420
421 //==========================================================================
422 //
423 // W_CloseAuxiliary
424 //
425 //==========================================================================
426
427 void W_CloseAuxiliary(void)
428 {
429         int i;
430
431         if(AuxiliaryOpened)
432         {
433                 W_UseAuxiliary();
434                 for(i = 0; i < numlumps; i++)
435                 {
436                         if(lumpcache[i])
437                         {
438                                 Z_Free(lumpcache[i]);
439                         }
440                 }
441                 Z_Free(AuxiliaryLumpInfo);
442                 Z_Free(AuxiliaryLumpCache);
443                 W_CloseAuxiliaryFile();
444                 AuxiliaryOpened = false;
445         }
446         W_UsePrimary();
447 }
448
449 //==========================================================================
450 //
451 // W_CloseAuxiliaryFile
452 //
453 // WARNING: W_CloseAuxiliary() must be called before any further
454 // auxiliary lump processing.
455 //
456 //==========================================================================
457
458 void W_CloseAuxiliaryFile(void)
459 {
460         if(AuxiliaryHandle)
461         {
462                 close(AuxiliaryHandle);
463                 AuxiliaryHandle = 0;
464         }
465 }
466
467 //==========================================================================
468 //
469 // W_UsePrimary
470 //
471 //==========================================================================
472
473 void W_UsePrimary(void)
474 {
475         lumpinfo = PrimaryLumpInfo;
476         numlumps = PrimaryNumLumps;
477         lumpcache = PrimaryLumpCache;
478 }
479
480 //==========================================================================
481 //
482 // W_UseAuxiliary
483 //
484 //==========================================================================
485
486 void W_UseAuxiliary(void)
487 {
488         if(AuxiliaryOpened == false)
489         {
490                 I_Error("W_UseAuxiliary: WAD not opened.");
491         }
492         lumpinfo = AuxiliaryLumpInfo;
493         numlumps = AuxiliaryNumLumps;
494         lumpcache = AuxiliaryLumpCache;
495 }
496
497 //==========================================================================
498 //
499 // W_NumLumps
500 //
501 //==========================================================================
502
503 int     W_NumLumps(void)
504 {
505         return numlumps;
506 }
507
508 //==========================================================================
509 //
510 // W_CheckNumForName
511 //
512 // Returns -1 if name not found.
513 //
514 //==========================================================================
515
516 int W_CheckNumForName(char *name)
517 {
518         char name8[9];
519         int v1, v2;
520         lumpinfo_t *lump_p;
521
522         // Make the name into two integers for easy compares
523         strncpy(name8, name, 8);
524         name8[8] = 0; // in case the name was a full 8 chars
525         strupr(name8); // case insensitive
526         v1 = *(int *)name8;
527         v2 = *(int *)&name8[4];
528
529         // Scan backwards so patch lump files take precedence
530         lump_p = lumpinfo+numlumps;
531         while(lump_p-- != lumpinfo)
532         {
533                 if(*(int *)lump_p->name == v1 && *(int *)&lump_p->name[4] == v2)
534                 {
535                         return lump_p-lumpinfo;
536                 }
537         }
538         return -1;
539 }
540
541 //==========================================================================
542 //
543 // W_GetNumForName
544 //
545 // Calls W_CheckNumForName, but bombs out if not found.
546 //
547 //==========================================================================
548
549 int     W_GetNumForName (char *name)
550 {
551         int     i;
552
553         i = W_CheckNumForName(name);
554         if(i != -1)
555         {
556                 return i;
557         }
558 #ifdef DEMO_WAD
559         printf("W_GetNumForName: %s not found!  Assuming shareware wad.\n", name);
560         return 1;
561 #else
562         I_Error("W_GetNumForName: %s not found!", name);
563         return -1;
564 #endif
565 }
566
567 //==========================================================================
568 //
569 // W_LumpLength
570 //
571 // Returns the buffer size needed to load the given lump.
572 //
573 //==========================================================================
574
575 int W_LumpLength(int lump)
576 {
577         if(lump >= numlumps)
578         {
579                 I_Error("W_LumpLength: %i >= numlumps", lump);
580         }
581         return lumpinfo[lump].size;
582 }
583
584 //==========================================================================
585 //
586 // W_ReadLump
587 //
588 // Loads the lump into the given buffer, which must be >= W_LumpLength().
589 //
590 //==========================================================================
591
592 void W_ReadLump(int lump, void *dest)
593 {
594         int c;
595         lumpinfo_t *l;
596
597         if(lump >= numlumps)
598         {
599                 I_Error("W_ReadLump: %i >= numlumps", lump);
600         }
601         l = lumpinfo+lump;
602         //I_BeginRead();
603         lseek(l->handle, l->position, SEEK_SET);
604         c = read(l->handle, dest, l->size);
605         if(c < l->size)
606         {
607                 I_Error("W_ReadLump: only read %i of %i on lump %i",
608                         c, l->size, lump);
609         }
610         //I_EndRead();
611 }
612
613 //==========================================================================
614 //
615 // W_CacheLumpNum
616 //
617 //==========================================================================
618
619 void *W_CacheLumpNum(int lump, int tag)
620 {
621         byte *ptr;
622
623         if((unsigned)lump >= numlumps)
624         {
625                 I_Error("W_CacheLumpNum: %i >= numlumps", lump);
626         }
627         if(!lumpcache[lump])
628         { // Need to read the lump in
629                 ptr = Z_Malloc(W_LumpLength(lump), tag, &lumpcache[lump]);
630                 W_ReadLump(lump, lumpcache[lump]);
631         }
632         else
633         {
634                 Z_ChangeTag(lumpcache[lump], tag);
635         }
636         return lumpcache[lump];
637 }
638
639 //==========================================================================
640 //
641 // W_CacheLumpName
642 //
643 //==========================================================================
644
645 void *W_CacheLumpName(char *name, int tag)
646 {
647         return W_CacheLumpNum(W_GetNumForName(name), tag);
648 }
649
650 //==========================================================================
651 //
652 // W_Profile
653 //
654 //==========================================================================
655
656 // Ripped out for Heretic
657 /*
658 int     info[2500][10];
659 int     profilecount;
660
661 void W_Profile (void)
662 {
663         int             i;
664         memblock_t      *block;
665         void    *ptr;
666         char    ch;
667         FILE    *f;
668         int             j;
669         char    name[9];
670         
671         
672         for (i=0 ; i<numlumps ; i++)
673         {       
674                 ptr = lumpcache[i];
675                 if (!ptr)
676                 {
677                         ch = ' ';
678                         continue;
679                 }
680                 else
681                 {
682                         block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
683                         if (block->tag < PU_PURGELEVEL)
684                                 ch = 'S';
685                         else
686                                 ch = 'P';
687                 }
688                 info[i][profilecount] = ch;
689         }
690         profilecount++;
691         
692         f = fopen ("waddump.txt","w");
693         name[8] = 0;
694         for (i=0 ; i<numlumps ; i++)
695         {
696                 memcpy (name,lumpinfo[i].name,8);
697                 for (j=0 ; j<8 ; j++)
698                         if (!name[j])
699                                 break;
700                 for ( ; j<8 ; j++)
701                         name[j] = ' ';
702                 fprintf (f,"%s ",name);
703                 for (j=0 ; j<profilecount ; j++)
704                         fprintf (f,"    %c",info[i][j]);
705                 fprintf (f,"\n");
706         }
707         fclose (f);
708 }
709 */