Round 5: Warn those using 1.0 wadfiles to quit it.
[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 #ifndef HAVE_ALLOCA_H
22 #include <alloca.h>
23 #else
24 #define O_BINARY 0
25 #endif
26 #include "h2def.h"
27
28 // MACROS ------------------------------------------------------------------
29
30 #if defined(NeXT) || defined(__linux) || defined(__FreeBSD__)
31 // NeXT doesn't need a binary flag in open call
32 #define O_BINARY 0
33 #define strcmpi strcasecmp
34 #endif
35
36 // TYPES -------------------------------------------------------------------
37
38 typedef struct
39 {
40         char identification[4];
41         int numlumps;
42         int infotableofs;
43 } wadinfo_t;
44
45 typedef struct
46 {
47         int filepos;
48         int size;
49         char name[8];
50 } filelump_t;
51
52 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
53
54 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
55
56 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
57
58 void    W_MergeLumps(char *start, char *end);
59
60 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
61
62 // PUBLIC DATA DEFINITIONS -------------------------------------------------
63
64 lumpinfo_t *lumpinfo;
65 int numlumps;
66 void **lumpcache;
67
68 // PRIVATE DATA DEFINITIONS ------------------------------------------------
69
70 static lumpinfo_t *PrimaryLumpInfo;
71 static int PrimaryNumLumps;
72 static void **PrimaryLumpCache;
73 static lumpinfo_t *AuxiliaryLumpInfo;
74 static int AuxiliaryNumLumps;
75 static void **AuxiliaryLumpCache;
76 static int AuxiliaryHandle = 0;
77 boolean AuxiliaryOpened = false;
78
79 // CODE --------------------------------------------------------------------
80
81 #if defined(NeXT) || defined(__linux) || defined(__FreeBSD__)
82 //==========================================================================
83 //
84 // strupr
85 //
86 //==========================================================================
87
88 void strupr(char *s)
89 {
90     while(*s)
91         *s++ = toupper(*s);
92 }
93
94 //==========================================================================
95 //
96 // filelength
97 //
98 //==========================================================================
99
100 int filelength(int handle)
101 {
102     struct stat fileinfo;
103
104     if(fstat(handle, &fileinfo) == -1)
105         {
106                 I_Error("Error fstating");
107         }
108     return fileinfo.st_size;
109 }
110 #endif
111
112 //==========================================================================
113 //
114 // W_AddFile
115 //
116 // Files with a .wad extension are wadlink files with multiple lumps,
117 // other files are single lumps with the base filename for the lump name.
118 //
119 //==========================================================================
120
121 void W_AddFile(char *filename)
122 {
123         wadinfo_t header;
124         lumpinfo_t *lump_p;
125         unsigned i;
126         char path[128];
127         int handle, length;
128         int startlump;
129         filelump_t *fileinfo, singleinfo;
130         filelump_t *freeFileInfo;
131
132         /* Add support for HHEXEN_DATA envirionment variable */
133         snprintf (path, 128, "%s/%s", getenv("HHEXEN_DATA"), filename);
134         if((handle = open(path, O_RDONLY|O_BINARY)) == -1)
135         {
136                 /* Now try CWD */
137                 if((handle = open(filename, O_RDONLY|O_BINARY)) == -1)
138                 { // Didn't find file
139                         return;
140                 }
141         }
142         startlump = numlumps;
143         if(strcmpi(filename+strlen(filename)-3, "wad"))
144         { // Single lump file
145                 fileinfo = &singleinfo;
146                 freeFileInfo = NULL;
147                 singleinfo.filepos = 0;
148                 singleinfo.size = LONG(filelength(handle));
149                 M_ExtractFileBase(filename, singleinfo.name);
150                 numlumps++;
151         }
152         else
153         { // WAD file
154                 read(handle, &header, sizeof(header));
155                 if(strncmp(header.identification, "IWAD", 4))
156                 {
157                         if(strncmp(header.identification, "PWAD", 4))
158                         { // Bad file id
159                                 I_Error("Wad file %s doesn't have IWAD or PWAD id\n",
160                                         filename);
161                         }
162                 }
163                 header.numlumps = LONG(header.numlumps);
164                 header.infotableofs = LONG(header.infotableofs);
165                 length = header.numlumps*sizeof(filelump_t);
166 //              fileinfo = alloca(length);
167                 if(!(fileinfo = malloc(length)))
168                 {
169                         I_Error("W_AddFile:  fileinfo malloc failed\n");
170                 }
171                 freeFileInfo = fileinfo;
172                 lseek(handle, header.infotableofs, SEEK_SET);
173                 read(handle, fileinfo, length);
174                 numlumps += header.numlumps;
175         }
176
177         // Fill in lumpinfo
178         lumpinfo = realloc(lumpinfo, numlumps*sizeof(lumpinfo_t));
179         if(!lumpinfo)
180         {
181                 I_Error("Couldn't realloc lumpinfo");
182         }
183         lump_p = &lumpinfo[startlump];
184         for(i = startlump; i < numlumps; i++, lump_p++, fileinfo++)
185         {
186                 lump_p->handle = handle;
187                 lump_p->position = LONG(fileinfo->filepos);
188                 lump_p->size = LONG(fileinfo->size);
189                 strncpy(lump_p->name, fileinfo->name, 8);
190         }
191         if(freeFileInfo)
192         {
193                 free(freeFileInfo);
194         }
195 }
196
197 //==========================================================================
198 //
199 // W_InitMultipleFiles
200 //
201 // Pass a null terminated list of files to use.  All files are optional,
202 // but at least one file must be found.  Lump names can appear multiple
203 // times.  The name searcher looks backwards, so a later file can
204 // override an earlier one.
205 //
206 //==========================================================================
207
208 void W_InitMultipleFiles(char **filenames)
209 {
210         int size;
211
212         // Open all the files, load headers, and count lumps
213         numlumps = 0;
214         lumpinfo = malloc(1); // Will be realloced as lumps are added
215
216         for(; *filenames; filenames++)
217         {
218                 W_AddFile(*filenames);
219         }
220         if(!numlumps)
221         {
222                 I_Error("W_InitMultipleFiles: no files found");
223         }
224
225         // Merge lumps for flats and sprites
226         W_MergeLumps("S_START","S_END");
227         W_MergeLumps("F_START","F_END");
228
229         // Set up caching
230         size = numlumps*sizeof(*lumpcache);
231         lumpcache = malloc(size);
232         if(!lumpcache)
233         {
234                 I_Error("Couldn't allocate lumpcache");
235         }
236         memset(lumpcache, 0, size);
237
238         PrimaryLumpInfo = lumpinfo;
239         PrimaryLumpCache = lumpcache;
240         PrimaryNumLumps = numlumps;
241 }
242
243 //==========================================================================
244 //
245 // IsMarker
246 //
247 // From BOOM/xdoom.  Finds an S_START or SS_START marker
248 //
249 //==========================================================================
250
251 static int IsMarker(char *marker, char *name)
252 {
253         return !strncasecmp(name, marker, 8) ||
254                 (*name == *marker && !strncasecmp(name+1,marker,7));
255 }
256
257 //==========================================================================
258 //
259 // W_MergeLumps
260 //
261 // From xdoom/BOOM again.  Merges all sprite lumps into one big 'ol block
262 //
263 //==========================================================================
264
265 void W_MergeLumps(char *start, char *end)
266 {
267         lumpinfo_t *newlumpinfo;
268         int newlumps, oldlumps;
269         int in_block = 0;
270         int i;
271
272         newlumpinfo = (lumpinfo_t *) alloca(numlumps * sizeof(lumpinfo_t));
273         oldlumps = newlumps = 0;
274
275         for(i=0;i < numlumps;i++)
276         {
277                 //process lumps in global namespace
278                 if(!in_block)
279                 {
280                         //check for start of block
281                         if (IsMarker(start, lumpinfo[i].name))
282                         {
283                                 in_block=1;
284                                 if(!newlumps)
285                                 {
286                                         newlumps++;
287                                         memset(newlumpinfo[0].name,0,8);
288                     strcpy(newlumpinfo[0].name, start);
289                     newlumpinfo[0].handle = -1;
290                     newlumpinfo[0].position = newlumpinfo[0].size = 0;
291                 }
292             }
293             // else copy it
294             else
295             {
296                 lumpinfo[oldlumps++] = lumpinfo[i];
297             }
298         }
299         // process lumps in sprites or flats namespace
300         else
301         {
302             // check for end of block
303             if (IsMarker(end, lumpinfo[i].name))
304             {
305                 in_block = 0;
306             }
307             else if (i && lumpinfo[i].handle != lumpinfo[i-1].handle)
308             {
309                 in_block = 0;
310                 lumpinfo[oldlumps++] = lumpinfo[i];
311             }
312             else
313             {
314                 newlumpinfo[newlumps++] = lumpinfo[i];
315             }
316         }
317     }
318
319     // now copy the merged lumps to the end of the old list
320     if (newlumps)
321     {
322         if (oldlumps + newlumps > numlumps)
323             lumpinfo = realloc(lumpinfo, (oldlumps + newlumps) *
324                                  sizeof(lumpinfo_t));
325         memcpy(lumpinfo + oldlumps, newlumpinfo, sizeof(lumpinfo_t) * newlumps);
326
327         numlumps = oldlumps + newlumps;
328
329         memset(lumpinfo[numlumps].name, 0, 8);
330         strcpy(lumpinfo[numlumps].name, end);
331         lumpinfo[numlumps].handle = -1;
332         lumpinfo[numlumps].position = lumpinfo[numlumps].size = 0;
333         numlumps++;
334     }
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 */