1 /*
2 ** w_wad.cpp
3 **
4 **---------------------------------------------------------------------------
5 ** Copyright 1998-2009 Randy Heit
6 ** Copyright 2005-2009 Christoph Oelckers
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 **
34 */
35
36
37 // HEADER FILES ------------------------------------------------------------
38
39 #include <stdlib.h>
40 #include <ctype.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <string.h>
44
45 #include "w_wad.h"
46 #include "w_zip.h"
47 #include "m_crc32.h"
48 #include "doomerrors.h"
49 #include "resourcefiles/resourcefile.h"
50 #include "zdoomsupport.h"
51 #include "filesys.h"
52
53 // Work around missing defines for ECWolf
54 #ifndef PATH_MAX
55 #define PATH_MAX 260
56 #endif
57 #define TEXTCOLOR_RED
58
59 // MACROS ------------------------------------------------------------------
60
61 #define NULL_INDEX (0xffffffff)
62
63 //
64 // WADFILE I/O related stuff.
65 //
66 struct FWadCollection::LumpRecord
67 {
68 int wadnum;
69 FResourceLump *lump;
70 };
71
72 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
73
74 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
75
76 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
77
78 static void PrintLastError ();
79
80 // PUBLIC DATA DEFINITIONS -------------------------------------------------
81
82 FWadCollection Wads;
83
84 // PRIVATE DATA DEFINITIONS ------------------------------------------------
85
86 // CODE --------------------------------------------------------------------
87
88 //==========================================================================
89 //
90 // uppercoppy
91 //
92 // [RH] Copy up to 8 chars, upper-casing them in the process
93 //==========================================================================
94
uppercopy(char * to,const char * from)95 void uppercopy (char *to, const char *from)
96 {
97 int i;
98
99 for (i = 0; i < 8 && from[i]; i++)
100 to[i] = toupper (from[i]);
101 for (; i < 8; i++)
102 to[i] = 0;
103 }
104
FWadCollection()105 FWadCollection::FWadCollection ()
106 : FirstLumpIndex(NULL), NextLumpIndex(NULL),
107 FirstLumpIndex_FullName(NULL), NextLumpIndex_FullName(NULL),
108 NumLumps(0)
109 {
110 }
111
~FWadCollection()112 FWadCollection::~FWadCollection ()
113 {
114 DeleteAll();
115 }
116
DeleteAll()117 void FWadCollection::DeleteAll ()
118 {
119 if (FirstLumpIndex != NULL)
120 {
121 delete[] FirstLumpIndex;
122 FirstLumpIndex = NULL;
123 }
124 if (NextLumpIndex != NULL)
125 {
126 delete[] NextLumpIndex;
127 NextLumpIndex = NULL;
128 }
129 if (FirstLumpIndex_FullName != NULL)
130 {
131 delete[] FirstLumpIndex_FullName;
132 FirstLumpIndex_FullName = NULL;
133 }
134 if (NextLumpIndex_FullName != NULL)
135 {
136 delete[] NextLumpIndex_FullName;
137 NextLumpIndex_FullName = NULL;
138 }
139
140 LumpInfo.Clear();
141 NumLumps = 0;
142
143 // we must count backward to enssure that embedded WADs are deleted before
144 // the ones that contain their data.
145 for (int i = Files.Size() - 1; i >= 0; --i)
146 {
147 delete Files[i];
148 }
149 Files.Clear();
150 }
151
152 //==========================================================================
153 //
154 // W_InitMultipleFiles
155 //
156 // Pass a null terminated list of files to use. All files are optional,
157 // but at least one file must be found. Lump names can appear multiple
158 // times. The name searcher looks backwards, so a later file can
159 // override an earlier one.
160 //
161 //==========================================================================
162
InitMultipleFiles(TArray<FString> & filenames)163 void FWadCollection::InitMultipleFiles (TArray<FString> &filenames)
164 {
165 int numfiles;
166
167 // open all the files, load headers, and count lumps
168 DeleteAll();
169 numfiles = 0;
170
171 for(unsigned i=0;i<filenames.Size(); i++)
172 {
173 int baselump = NumLumps;
174 AddFile (filenames[i]);
175 }
176
177 NumLumps = LumpInfo.Size();
178 if (NumLumps == 0)
179 {
180 I_FatalError ("W_InitMultipleFiles: no files found");
181 }
182 RenameSprites();
183
184 // [RH] Set up hash table
185 FirstLumpIndex = new DWORD[NumLumps];
186 NextLumpIndex = new DWORD[NumLumps];
187 FirstLumpIndex_FullName = new DWORD[NumLumps];
188 NextLumpIndex_FullName = new DWORD[NumLumps];
189 InitHashChains ();
190 LumpInfo.ShrinkToFit();
191 Files.ShrinkToFit();
192 }
193
194 //-----------------------------------------------------------------------
195 //
196 // Adds an external file to the lump list but not to the hash chains
197 // It's just a simple means to assign a lump number to some file so that
198 // the texture manager can read from it.
199 //
200 //-----------------------------------------------------------------------
201
AddExternalFile(const char * filename)202 int FWadCollection::AddExternalFile(const char *filename)
203 {
204 FResourceLump *lump = new FExternalLump(filename);
205
206 FWadCollection::LumpRecord *lumprec = &LumpInfo[LumpInfo.Reserve(1)];
207 lumprec->lump = lump;
208 lumprec->wadnum = -1;
209 return LumpInfo.Size()-1; // later
210 }
211
212 //==========================================================================
213 //
214 // W_AddFile
215 //
216 // Files with a .wad extension are wadlink files with multiple lumps,
217 // other files are single lumps with the base filename for the lump name.
218 //
219 // [RH] Removed reload hack
220 //==========================================================================
221
AddFile(const char * filename,FileReader * wadinfo)222 void FWadCollection::AddFile (const char *filename, FileReader *wadinfo)
223 {
224 int startlump;
225 bool isdir = false;
226
227 if (wadinfo == NULL)
228 {
229 // Does this exist? If so, is it a directory?
230 File info(filename);
231 if (!info.exists())
232 {
233 Printf(TEXTCOLOR_RED "Could not stat %s\n", filename);
234 PrintLastError();
235 return;
236 }
237 isdir = info.isDirectory();
238
239 if (!isdir)
240 {
241 try
242 {
243 wadinfo = new FileReader(filename);
244 }
245 catch (CRecoverableError &err)
246 { // Didn't find file
247 Printf (TEXTCOLOR_RED "%s\n", err.GetMessage());
248 PrintLastError ();
249 return;
250 }
251 }
252 }
253
254 Printf (" adding %s", filename);
255 startlump = NumLumps;
256
257 FResourceFile *resfile;
258
259 if (!isdir)
260 resfile = FResourceFile::OpenResourceFile(filename, wadinfo);
261 else
262 resfile = FResourceFile::OpenDirectory(filename);
263
264 if (resfile != NULL)
265 {
266 DWORD lumpstart = LumpInfo.Size();
267
268 resfile->SetFirstLump(lumpstart);
269
270 Files.Push(resfile);
271
272 // [ECWolf] Do this first.
273 bool noEmbedded = true;
274 for (DWORD i=0; i < resfile->LumpCount(); i++)
275 {
276 FResourceLump *lump = resfile->GetLump(i);
277 if (lump->Flags & LUMPF_EMBEDDED)
278 {
279 // Should be ecwolf.<something>
280 FindEmbeddedWolfData(resfile, filename, lump->FullName+7);
281
282 char path[256];
283
284 mysnprintf(path, 256, "%s:", filename);
285 char *wadstr = path + strlen(path);
286
287 FileReader *embedded = lump->NewReader();
288 strcpy(wadstr, lump->FullName);
289
290 AddFile(path, embedded);
291
292 noEmbedded = false;
293 break;
294 }
295 }
296
297 if(noEmbedded)
298 {
299 for (DWORD i=0; i < resfile->LumpCount(); i++)
300 {
301 FResourceLump *lump = resfile->GetLump(i);
302 FWadCollection::LumpRecord *lump_p = &LumpInfo[LumpInfo.Reserve(1)];
303
304 lump_p->lump = lump;
305 lump_p->wadnum = Files.Size()-1;
306 }
307 }
308 return;
309 }
310 }
311
312 //==========================================================================
313 //
314 // Looks for "vanilla" wolf data embedded into an archive. This is only
315 // intended so that Wolf4SDL/vanilla mods can include a compatibility patch
316 // which is automatically loaded.
317 //
318 //==========================================================================
319
FindEmbeddedWolfData(FResourceFile * res,const char * filename,const char * ext)320 void FWadCollection::FindEmbeddedWolfData(FResourceFile *res, const char* filename, const char* ext)
321 {
322 enum
323 {
324 FILE_AUDIOHED,
325 FILE_AUDIOT,
326 FILE_GAMEMAPS,
327 FILE_MAPHEAD,
328 FILE_VGADICT,
329 FILE_VGAGRAPH,
330 FILE_VGAHEAD,
331 FILE_VSWAP,
332
333 NUM_FILES
334 };
335
336 struct
337 {
338 const char* name;
339 bool found;
340 int lump;
341 } data[NUM_FILES] =
342 {
343 { "audiohed.", false },
344 { "audiot.", false },
345 { "gamemaps.", false },
346 { "maphead.", false },
347 { "vgadict.", false },
348 { "vgagraph.", false },
349 { "vgahead.", false },
350 { "vswap.", false }
351 };
352 unsigned int count = 0;
353
354 for(DWORD i = 0; i < res->LumpCount(); ++i)
355 {
356 FResourceLump *lump = res->GetLump(i);
357
358 FString name(lump->FullName);
359 for(unsigned int j = 0; j < NUM_FILES; ++j)
360 {
361 if(data[j].found)
362 continue;
363
364 FString expected = FString(data[j].name) + ext;
365 if(name.CompareNoCase(expected) == 0)
366 {
367 data[j].found = true;
368 data[j].lump = i;
369 break;
370 }
371 }
372 }
373
374 // [BL] HACK: In order to mimize changes to ZDoom code, we're doing
375 // something horrible here. We're going to pass an array of FileReaders
376 // into AddFile and when we open the respective archive, just know that
377 // there are extra pointers.
378 FileReader *readers[3];
379 FString fname;
380
381 if(data[FILE_AUDIOHED].found && data[FILE_AUDIOT].found)
382 {
383 readers[0] = res->GetLump(data[FILE_AUDIOT].lump)->NewReader();
384 readers[1] = res->GetLump(data[FILE_AUDIOHED].lump)->NewReader();
385
386 fname.Format("%s:%s%s", filename, data[FILE_AUDIOT].name, ext);
387 AddFile(fname, reinterpret_cast<FileReader*>(&readers));
388 ++count;
389 }
390
391 if(data[FILE_GAMEMAPS].found && data[FILE_MAPHEAD].found)
392 {
393 readers[0] = res->GetLump(data[FILE_GAMEMAPS].lump)->NewReader();
394 readers[1] = res->GetLump(data[FILE_MAPHEAD].lump)->NewReader();
395
396 fname.Format("%s:%s%s", filename, data[FILE_GAMEMAPS].name, ext);
397 AddFile(fname, reinterpret_cast<FileReader*>(&readers));
398 ++count;
399 }
400
401 if(data[FILE_VGADICT].found && data[FILE_VGAGRAPH].found && data[FILE_VGAHEAD].found)
402 {
403 readers[0] = res->GetLump(data[FILE_VGAGRAPH].lump)->NewReader();
404 readers[1] = res->GetLump(data[FILE_VGAHEAD].lump)->NewReader();
405 readers[2] = res->GetLump(data[FILE_VGADICT].lump)->NewReader();
406
407 fname.Format("%s:%s%s", filename, data[FILE_VGAGRAPH].name, ext);
408 AddFile(fname, reinterpret_cast<FileReader*>(&readers));
409 ++count;
410 }
411
412 // This one can be handled normally. :)
413 if(data[FILE_VSWAP].found)
414 {
415 fname.Format("%s:%s%s", filename, data[FILE_VSWAP].name, ext);
416 AddFile(fname, res->GetLump(data[FILE_VSWAP].lump)->NewReader());
417 ++count;
418 }
419
420 if(count == 0)
421 I_Error("Attempt to load embedded compatibility patch without data.");
422 }
423
424 //==========================================================================
425 //
426 // W_CheckIfWadLoaded
427 //
428 // Returns true if the specified wad is loaded, false otherwise.
429 // If a fully-qualified path is specified, then the wad must match exactly.
430 // Otherwise, any wad with that name will work, whatever its path.
431 // Returns the wads index if found, or -1 if not.
432 //
433 //==========================================================================
434
CheckIfWadLoaded(const char * name)435 int FWadCollection::CheckIfWadLoaded (const char *name)
436 {
437 unsigned int i;
438
439 if (strrchr (name, '/') != NULL)
440 {
441 for (i = 0; i < Files.Size(); ++i)
442 {
443 if (stricmp (GetWadFullName (i), name) == 0)
444 {
445 return i;
446 }
447 }
448 }
449 else
450 {
451 for (i = 0; i < Files.Size(); ++i)
452 {
453 if (stricmp (GetWadName (i), name) == 0)
454 {
455 return i;
456 }
457 }
458 }
459 return -1;
460 }
461
462 //==========================================================================
463 //
464 // W_NumLumps
465 //
466 //==========================================================================
467
GetNumLumps() const468 int FWadCollection::GetNumLumps () const
469 {
470 return NumLumps;
471 }
472
473 //==========================================================================
474 //
475 // GetNumFiles
476 //
477 //==========================================================================
478
GetNumWads() const479 int FWadCollection::GetNumWads () const
480 {
481 return Files.Size();
482 }
483
484 //==========================================================================
485 //
486 // W_CheckNumForName
487 //
488 // Returns -1 if name not found. The version with a third parameter will
489 // look exclusively in the specified wad for the lump.
490 //
491 // [RH] Changed to use hash lookup ala BOOM instead of a linear search
492 // and namespace parameter
493 //==========================================================================
494
CheckNumForName(const char * name,int space)495 int FWadCollection::CheckNumForName (const char *name, int space)
496 {
497 union
498 {
499 char uname[8];
500 QWORD qname;
501 };
502 DWORD i;
503
504 if (name == NULL)
505 {
506 return -1;
507 }
508
509 // Let's not search for names that are longer than 8 characters and contain path separators
510 // They are almost certainly full path names passed to this function.
511 if (strlen(name) > 8 && strpbrk(name, "/."))
512 {
513 return -1;
514 }
515
516 uppercopy (uname, name);
517 i = FirstLumpIndex[LumpNameHash (uname) % NumLumps];
518
519 while (i != NULL_INDEX)
520 {
521 FResourceLump *lump = LumpInfo[i].lump;
522
523 if (lump->qwName == qname)
524 {
525 if (lump->Namespace == space) break;
526 // If the lump is from one of the special namespaces exclusive to Zips
527 // the check has to be done differently:
528 // If we find a lump with this name in the global namespace that does not come
529 // from a Zip return that. WADs don't know these namespaces and single lumps must
530 // work as well.
531 if (space > ns_specialzipdirectory && lump->Namespace == ns_global &&
532 !(lump->Flags & LUMPF_ZIPFILE)) break;
533 }
534 i = NextLumpIndex[i];
535 }
536
537 return i != NULL_INDEX ? i : -1;
538 }
539
CheckNumForName(const char * name,int space,int wadnum,bool exact)540 int FWadCollection::CheckNumForName (const char *name, int space, int wadnum, bool exact)
541 {
542 FResourceLump *lump;
543 union
544 {
545 char uname[8];
546 QWORD qname;
547 };
548 DWORD i;
549
550 if (wadnum < 0)
551 {
552 return CheckNumForName (name, space);
553 }
554
555 uppercopy (uname, name);
556 i = FirstLumpIndex[LumpNameHash (uname) % NumLumps];
557
558 // If exact is true if will only find lumps in the same WAD, otherwise
559 // also those in earlier WADs.
560
561 while (i != NULL_INDEX &&
562 (lump = LumpInfo[i].lump, lump->qwName != qname ||
563 lump->Namespace != space ||
564 (exact? (LumpInfo[i].wadnum != wadnum) : (LumpInfo[i].wadnum > wadnum)) ))
565 {
566 i = NextLumpIndex[i];
567 }
568
569 return i != NULL_INDEX ? i : -1;
570 }
571
572 //==========================================================================
573 //
574 // W_GetNumForName
575 //
576 // Calls W_CheckNumForName, but bombs out if not found.
577 //
578 //==========================================================================
579
GetNumForName(const char * name,int space)580 int FWadCollection::GetNumForName (const char *name, int space)
581 {
582 int i;
583
584 i = CheckNumForName (name, space);
585
586 if (i == -1)
587 I_Error ("W_GetNumForName: %s not found!", name);
588
589 return i;
590 }
591
592
593 //==========================================================================
594 //
595 // W_CheckNumForFullName
596 //
597 // Same as above but looks for a fully qualified name from a .zip
598 // These don't care about namespaces though because those are part
599 // of the path.
600 //
601 //==========================================================================
602
CheckNumForFullName(const char * name,bool trynormal,int namespc)603 int FWadCollection::CheckNumForFullName (const char *name, bool trynormal, int namespc)
604 {
605 DWORD i;
606
607 if (name == NULL)
608 {
609 return -1;
610 }
611
612 i = FirstLumpIndex_FullName[MakeKey (name) % NumLumps];
613
614 while (i != NULL_INDEX && stricmp(name, LumpInfo[i].lump->FullName))
615 {
616 i = NextLumpIndex_FullName[i];
617 }
618
619 if (i != NULL_INDEX) return i;
620
621 if (trynormal && strlen(name) <= 8 && !strpbrk(name, "./"))
622 {
623 return CheckNumForName(name, namespc);
624 }
625 return -1;
626 }
627
CheckNumForFullName(const char * name,int wadnum)628 int FWadCollection::CheckNumForFullName (const char *name, int wadnum)
629 {
630 DWORD i;
631
632 if (wadnum < 0)
633 {
634 return CheckNumForFullName (name);
635 }
636
637 i = FirstLumpIndex_FullName[MakeKey (name) % NumLumps];
638
639 while (i != NULL_INDEX &&
640 (stricmp(name, LumpInfo[i].lump->FullName) || LumpInfo[i].wadnum != wadnum))
641 {
642 i = NextLumpIndex_FullName[i];
643 }
644
645 return i != NULL_INDEX ? i : -1;
646 }
647
648 //==========================================================================
649 //
650 // W_GetNumForFullName
651 //
652 // Calls W_CheckNumForFullName, but bombs out if not found.
653 //
654 //==========================================================================
655
GetNumForFullName(const char * name)656 int FWadCollection::GetNumForFullName (const char *name)
657 {
658 int i;
659
660 i = CheckNumForFullName (name);
661
662 if (i == -1)
663 I_Error ("GetNumForFullName: %s not found!", name);
664
665 return i;
666 }
667
668 //==========================================================================
669 //
670 // W_LumpLength
671 //
672 // Returns the buffer size needed to load the given lump.
673 //
674 //==========================================================================
675
LumpLength(int lump) const676 int FWadCollection::LumpLength (int lump) const
677 {
678 if ((size_t)lump >= NumLumps)
679 {
680 I_Error ("W_LumpLength: %i >= NumLumps",lump);
681 }
682
683 return LumpInfo[lump].lump->LumpSize;
684 }
685
686 //==========================================================================
687 //
688 // GetLumpOffset
689 //
690 // Returns the offset from the beginning of the file to the lump.
691 // Returns -1 if the lump is compressed or can't be read directly
692 //
693 //==========================================================================
694
GetLumpOffset(int lump)695 int FWadCollection::GetLumpOffset (int lump)
696 {
697 if ((size_t)lump >= NumLumps)
698 {
699 I_Error ("GetLumpOffset: %i >= NumLumps",lump);
700 }
701
702 return LumpInfo[lump].lump->GetFileOffset();
703 }
704
705 //==========================================================================
706 //
707 // GetLumpOffset
708 //
709 //==========================================================================
710
GetLumpFlags(int lump)711 int FWadCollection::GetLumpFlags (int lump)
712 {
713 if ((size_t)lump >= NumLumps)
714 {
715 return 0;
716 }
717
718 return LumpInfo[lump].lump->Flags;
719 }
720
721 //==========================================================================
722 //
723 // W_LumpNameHash
724 //
725 // NOTE: s should already be uppercase, in contrast to the BOOM version.
726 //
727 // Hash function used for lump names.
728 // Must be mod'ed with table size.
729 // Can be used for any 8-character names.
730 //
731 //==========================================================================
732
LumpNameHash(const char * s)733 DWORD FWadCollection::LumpNameHash (const char *s)
734 {
735 const DWORD *table = GetCRCTable ();;
736 DWORD hash = 0xffffffff;
737 int i;
738
739 for (i = 8; i > 0 && *s; --i, ++s)
740 {
741 hash = CRC1 (hash, *s, table);
742 }
743 return hash ^ 0xffffffff;
744 }
745
746 //==========================================================================
747 //
748 // W_InitHashChains
749 //
750 // Prepares the lumpinfos for hashing.
751 // (Hey! This looks suspiciously like something from Boom! :-)
752 //
753 //==========================================================================
754
InitHashChains(void)755 void FWadCollection::InitHashChains (void)
756 {
757 char name[8];
758 unsigned int i, j;
759
760 // Mark all buckets as empty
761 memset (FirstLumpIndex, 255, NumLumps*sizeof(FirstLumpIndex[0]));
762 memset (NextLumpIndex, 255, NumLumps*sizeof(NextLumpIndex[0]));
763 memset (FirstLumpIndex_FullName, 255, NumLumps*sizeof(FirstLumpIndex_FullName[0]));
764 memset (NextLumpIndex_FullName, 255, NumLumps*sizeof(NextLumpIndex_FullName[0]));
765
766 // Now set up the chains
767 for (i = 0; i < (unsigned)NumLumps; i++)
768 {
769 uppercopy (name, LumpInfo[i].lump->Name);
770 j = LumpNameHash (name) % NumLumps;
771 NextLumpIndex[i] = FirstLumpIndex[j];
772 FirstLumpIndex[j] = i;
773
774 // Do the same for the full paths
775 if (LumpInfo[i].lump->FullName!=NULL)
776 {
777 j = MakeKey(LumpInfo[i].lump->FullName) % NumLumps;
778 NextLumpIndex_FullName[i] = FirstLumpIndex_FullName[j];
779 FirstLumpIndex_FullName[j] = i;
780 }
781 }
782 }
783
784 //==========================================================================
785 //
786 // RenameSprites
787 //
788 // Renames sprites in IWADs so that unique actors can have unique sprites,
789 // making it possible to import any actor from any game into any other
790 // game without jumping through hoops to resolve duplicate sprite names.
791 // You just need to know what the sprite's new name is.
792 //
793 //==========================================================================
794
RenameSprites()795 void FWadCollection::RenameSprites ()
796 {
797 }
798
799 //==========================================================================
800 //
801 // W_FindLump
802 //
803 // Find a named lump. Specifically allows duplicates for merging of e.g.
804 // SNDINFO lumps.
805 //
806 //==========================================================================
807
FindLump(const char * name,int * lastlump,bool anyns)808 int FWadCollection::FindLump (const char *name, int *lastlump, bool anyns)
809 {
810 union
811 {
812 char name8[8];
813 QWORD qname;
814 };
815 LumpRecord *lump_p;
816
817 uppercopy (name8, name);
818
819 assert(lastlump != NULL && *lastlump >= 0);
820 lump_p = &LumpInfo[*lastlump];
821 while (lump_p < &LumpInfo[NumLumps])
822 {
823 FResourceLump *lump = lump_p->lump;
824
825 if ((anyns || lump->Namespace == ns_global) && lump->qwName == qname)
826 {
827 int lump = int(lump_p - &LumpInfo[0]);
828 *lastlump = lump + 1;
829 return lump;
830 }
831 lump_p++;
832 }
833
834 *lastlump = NumLumps;
835 return -1;
836 }
837
838 //==========================================================================
839 //
840 // W_FindLumpMulti
841 //
842 // Find a named lump. Specifically allows duplicates for merging of e.g.
843 // SNDINFO lumps. Returns everything having one of the passed names.
844 //
845 //==========================================================================
846
FindLumpMulti(const char ** names,int * lastlump,bool anyns,int * nameindex)847 int FWadCollection::FindLumpMulti (const char **names, int *lastlump, bool anyns, int *nameindex)
848 {
849 LumpRecord *lump_p;
850
851 assert(lastlump != NULL && *lastlump >= 0);
852 lump_p = &LumpInfo[*lastlump];
853 while (lump_p < &LumpInfo[NumLumps])
854 {
855 FResourceLump *lump = lump_p->lump;
856
857 if (anyns || lump->Namespace == ns_global)
858 {
859
860 for(const char **name = names; *name != NULL; name++)
861 {
862 if (!strnicmp(*name, lump->Name, 8))
863 {
864 int lump = int(lump_p - &LumpInfo[0]);
865 *lastlump = lump + 1;
866 if (nameindex != NULL) *nameindex = int(name - names);
867 return lump;
868 }
869 }
870 }
871 lump_p++;
872 }
873
874 *lastlump = NumLumps;
875 return -1;
876 }
877
878 //==========================================================================
879 //
880 // W_CheckLumpName
881 //
882 //==========================================================================
883
CheckLumpName(int lump,const char * name)884 bool FWadCollection::CheckLumpName (int lump, const char *name)
885 {
886 if ((size_t)lump >= NumLumps)
887 return false;
888
889 return !strnicmp (LumpInfo[lump].lump->Name, name, 8);
890 }
891
892 //==========================================================================
893 //
894 // W_GetLumpName
895 //
896 //==========================================================================
897
GetLumpName(char * to,int lump) const898 void FWadCollection::GetLumpName (char *to, int lump) const
899 {
900 if ((size_t)lump >= NumLumps)
901 *to = 0;
902 else
903 uppercopy (to, LumpInfo[lump].lump->Name);
904 }
905
906 //==========================================================================
907 //
908 // FWadCollection :: GetLumpFullName
909 //
910 // Returns the lump's full name if it has one or its short name if not.
911 //
912 //==========================================================================
913
GetLumpFullName(int lump) const914 const char *FWadCollection::GetLumpFullName (int lump) const
915 {
916 if ((size_t)lump >= NumLumps)
917 return NULL;
918 else if (LumpInfo[lump].lump->FullName != NULL)
919 return LumpInfo[lump].lump->FullName;
920 else
921 return LumpInfo[lump].lump->Name;
922 }
923
924 //==========================================================================
925 //
926 // FWadCollection :: GetLumpFullPath
927 //
928 // Returns the name of the lump's wad prefixed to the lump's full name.
929 //
930 //==========================================================================
931
GetLumpFullPath(int lump) const932 FString FWadCollection::GetLumpFullPath(int lump) const
933 {
934 FString foo;
935
936 if ((size_t) lump < NumLumps)
937 {
938 foo << GetWadName(LumpInfo[lump].wadnum) << ':' << GetLumpFullName(lump);
939 }
940 return foo;
941 }
942
943 //==========================================================================
944 //
945 // GetLumpNamespace
946 //
947 //==========================================================================
948
GetLumpNamespace(int lump) const949 int FWadCollection::GetLumpNamespace (int lump) const
950 {
951 if ((size_t)lump >= NumLumps)
952 return ns_global;
953 else
954 return LumpInfo[lump].lump->Namespace;
955 }
956
957 //==========================================================================
958 //
959 // FWadCollection :: GetLumpIndexNum
960 //
961 // Returns the index number for this lump. This is *not* the lump's position
962 // in the lump directory, but rather a special value that RFF can associate
963 // with files. Other archive types will return 0, since they don't have it.
964 //
965 //==========================================================================
966
GetLumpIndexNum(int lump) const967 int FWadCollection::GetLumpIndexNum(int lump) const
968 {
969 if ((size_t)lump >= NumLumps)
970 return 0;
971 else
972 return LumpInfo[lump].lump->GetIndexNum();
973 }
974
975 //==========================================================================
976 //
977
978 // W_GetLumpFile
979 //
980 //==========================================================================
981
GetLumpFile(int lump) const982 int FWadCollection::GetLumpFile (int lump) const
983 {
984 if ((size_t)lump >= LumpInfo.Size())
985 return -1;
986 return LumpInfo[lump].wadnum;
987 }
988
989 //==========================================================================
990 //
991 // W_ReadLump
992 //
993 // Loads the lump into the given buffer, which must be >= W_LumpLength().
994 //
995 //==========================================================================
996
ReadLump(int lump,void * dest)997 void FWadCollection::ReadLump (int lump, void *dest)
998 {
999 FWadLump lumpr = OpenLumpNum (lump);
1000 long size = lumpr.GetLength ();
1001 long numread = lumpr.Read (dest, size);
1002
1003 if (numread != size)
1004 {
1005 I_Error ("W_ReadLump: only read %ld of %ld on lump %i\n",
1006 numread, size, lump);
1007 }
1008 }
1009
1010 //==========================================================================
1011 //
1012 // ReadLump - variant 2
1013 //
1014 // Loads the lump into a newly created buffer and returns it.
1015 //
1016 //==========================================================================
1017
ReadLump(int lump)1018 FMemLump FWadCollection::ReadLump (int lump)
1019 {
1020 return FMemLump(FString(ELumpNum(lump)));
1021 }
1022
1023 //==========================================================================
1024 //
1025 // OpenLumpNum
1026 //
1027 // Returns a copy of the file object for a lump's wad and positions its
1028 // file pointer at the beginning of the lump.
1029 //
1030 //==========================================================================
1031
OpenLumpNum(int lump)1032 FWadLump FWadCollection::OpenLumpNum (int lump)
1033 {
1034 if ((unsigned)lump >= (unsigned)LumpInfo.Size())
1035 {
1036 I_Error ("W_OpenLumpNum: %u >= NumLumps", lump);
1037 }
1038
1039 return FWadLump(LumpInfo[lump].lump);
1040 }
1041
1042 //==========================================================================
1043 //
1044 // ReopenLumpNum
1045 //
1046 // Similar to OpenLumpNum, except a new, independant file object is created
1047 // for the lump's wad. Use this when you won't read the lump's data all at
1048 // once (e.g. for streaming an Ogg Vorbis stream from a wad as music).
1049 //
1050 //==========================================================================
1051
ReopenLumpNum(int lump)1052 FWadLump *FWadCollection::ReopenLumpNum (int lump)
1053 {
1054 if ((unsigned)lump >= (unsigned)LumpInfo.Size())
1055 {
1056 I_Error ("W_ReopenLumpNum: %u >= NumLumps", lump);
1057 }
1058
1059 return new FWadLump(LumpInfo[lump].lump, true);
1060 }
1061
1062 //==========================================================================
1063 //
1064 // GetFileReader
1065 //
1066 // Retrieves the FileReader object to access the given WAD
1067 // Careful: This is only useful for real WAD files!
1068 //
1069 //==========================================================================
1070
GetFileReader(int wadnum)1071 FileReader *FWadCollection::GetFileReader(int wadnum)
1072 {
1073 if ((DWORD)wadnum >= Files.Size())
1074 {
1075 return NULL;
1076 }
1077 return Files[wadnum]->GetReader();
1078 }
1079
1080 //==========================================================================
1081 //
1082 // W_GetWadName
1083 //
1084 // Returns the name of the given wad.
1085 //
1086 //==========================================================================
1087
GetWadName(int wadnum) const1088 const char *FWadCollection::GetWadName (int wadnum) const
1089 {
1090 const char *name, *slash;
1091
1092 if ((DWORD)wadnum >= Files.Size())
1093 {
1094 return NULL;
1095 }
1096
1097 name = Files[wadnum]->Filename;
1098 slash = strrchr (name, '/');
1099 return slash != NULL ? slash+1 : name;
1100 }
1101
1102 //==========================================================================
1103 //
1104 //
1105 //==========================================================================
1106
GetFirstLump(int wadnum) const1107 int FWadCollection::GetFirstLump (int wadnum) const
1108 {
1109 if ((DWORD)wadnum >= Files.Size())
1110 {
1111 return 0;
1112 }
1113
1114 return Files[wadnum]->GetFirstLump();
1115 }
1116
1117 //==========================================================================
1118 //
1119 //
1120 //==========================================================================
1121
GetLastLump(int wadnum) const1122 int FWadCollection::GetLastLump (int wadnum) const
1123 {
1124 if ((DWORD)wadnum >= Files.Size())
1125 {
1126 return 0;
1127 }
1128
1129 return Files[wadnum]->GetFirstLump() + Files[wadnum]->LumpCount() - 1;
1130 }
1131
1132 //==========================================================================
1133 //
1134 // W_GetWadFullName
1135 //
1136 // Returns the name of the given wad, including any path
1137 //
1138 //==========================================================================
1139
GetWadFullName(int wadnum) const1140 const char *FWadCollection::GetWadFullName (int wadnum) const
1141 {
1142 if ((unsigned int)wadnum >= Files.Size())
1143 {
1144 return NULL;
1145 }
1146
1147 return Files[wadnum]->Filename;
1148 }
1149
1150
1151 //==========================================================================
1152 //
1153 // IsUncompressedFile
1154 //
1155 // Returns true when the lump is available as an uncompressed portion of
1156 // a file. The music player can play such lumps by streaming but anything
1157 // else has to be loaded into memory first.
1158 //
1159 //==========================================================================
1160
IsUncompressedFile(int lump) const1161 bool FWadCollection::IsUncompressedFile(int lump) const
1162 {
1163 if ((unsigned)lump >= (unsigned)NumLumps)
1164 {
1165 I_Error ("IsUncompressedFile: %u >= NumLumps",lump);
1166 }
1167
1168 FResourceLump *l = LumpInfo[lump].lump;
1169 FileReader *f = l->GetReader();
1170
1171 // We can access the file only if we get the FILE pointer from the FileReader here.
1172 // Any other case means it won't work.
1173 return (f != NULL && f->GetFile() != NULL);
1174 }
1175
1176 //==========================================================================
1177 //
1178 // IsEncryptedFile
1179 //
1180 // Returns true if the first 256 bytes of the lump are encrypted for Blood.
1181 //
1182 //==========================================================================
1183
IsEncryptedFile(int lump) const1184 bool FWadCollection::IsEncryptedFile(int lump) const
1185 {
1186 if ((unsigned)lump >= (unsigned)NumLumps)
1187 {
1188 return false;
1189 }
1190 return !!(LumpInfo[lump].lump->Flags & LUMPF_BLOODCRYPT);
1191 }
1192
1193
1194 // FWadLump -----------------------------------------------------------------
1195
FWadLump()1196 FWadLump::FWadLump ()
1197 : FileReader(), Lump(NULL)
1198 {
1199 }
1200
FWadLump(const FWadLump & copy)1201 FWadLump::FWadLump (const FWadLump ©) : FileReader()
1202 {
1203 // This must be defined isn't called.
1204 File = copy.File;
1205 Length = copy.Length;
1206 FilePos = copy.FilePos;
1207 StartPos = copy.StartPos;
1208 CloseOnDestruct = false;
1209 if ((Lump = copy.Lump)) Lump->CacheLump();
1210 }
1211
1212 #ifdef _DEBUG
operator =(const FWadLump & copy)1213 FWadLump & FWadLump::operator= (const FWadLump ©)
1214 {
1215 // Only the debug build actually calls this!
1216 File = copy.File;
1217 Length = copy.Length;
1218 FilePos = copy.FilePos;
1219 StartPos = copy.StartPos;
1220 CloseOnDestruct = false;
1221 if ((Lump = copy.Lump)) Lump->CacheLump();
1222 return *this;
1223 }
1224 #endif
1225
1226
FWadLump(FResourceLump * lump,bool alwayscache)1227 FWadLump::FWadLump(FResourceLump *lump, bool alwayscache)
1228 : FileReader()
1229 {
1230 FileReader *f = lump->GetReader();
1231
1232 if (f != NULL && f->GetFile() != NULL && !alwayscache)
1233 {
1234 // Uncompressed lump in a file
1235 File = f->GetFile();
1236 Length = lump->LumpSize;
1237 StartPos = FilePos = lump->GetFileOffset();
1238 Lump = NULL;
1239 }
1240 else
1241 {
1242 File = NULL;
1243 Length = lump->LumpSize;
1244 StartPos = FilePos = 0;
1245 Lump = lump;
1246 Lump->CacheLump();
1247 }
1248 }
1249
~FWadLump()1250 FWadLump::~FWadLump()
1251 {
1252 if (Lump != NULL)
1253 {
1254 Lump->ReleaseCache();
1255 }
1256 }
1257
Seek(long offset,int origin)1258 long FWadLump::Seek (long offset, int origin)
1259 {
1260 if (Lump != NULL)
1261 {
1262 switch (origin)
1263 {
1264 case SEEK_CUR:
1265 offset += FilePos;
1266 break;
1267
1268 case SEEK_END:
1269 offset += Length;
1270 break;
1271
1272 default:
1273 break;
1274 }
1275 FilePos = offset < 0 ? 0 : (offset > Length ? Length : offset);
1276 return 0;
1277 }
1278 return FileReader::Seek(offset, origin);
1279 }
1280
Read(void * buffer,long len)1281 long FWadLump::Read (void *buffer, long len)
1282 {
1283 long numread;
1284 long startread = FilePos;
1285
1286 if (Lump != NULL)
1287 {
1288 if (FilePos + len > Length)
1289 {
1290 len = Length - FilePos;
1291 }
1292 memcpy(buffer, Lump->Cache + FilePos, len);
1293 FilePos += len;
1294 numread = len;
1295 }
1296 else
1297 {
1298 numread = FileReader::Read(buffer, len);
1299 }
1300 return numread;
1301 }
1302
Gets(char * strbuf,int len)1303 char *FWadLump::Gets(char *strbuf, int len)
1304 {
1305 if (Lump != NULL)
1306 {
1307 return GetsFromBuffer(Lump->Cache, strbuf, len);
1308 }
1309 else
1310 {
1311 return FileReader::Gets(strbuf, len);
1312 }
1313 return strbuf;
1314 }
1315
1316 // FMemLump -----------------------------------------------------------------
1317
FMemLump()1318 FMemLump::FMemLump ()
1319 {
1320 }
1321
FMemLump(const FMemLump & copy)1322 FMemLump::FMemLump (const FMemLump ©)
1323 {
1324 Block = copy.Block;
1325 }
1326
operator =(const FMemLump & copy)1327 FMemLump &FMemLump::operator = (const FMemLump ©)
1328 {
1329 Block = copy.Block;
1330 return *this;
1331 }
1332
FMemLump(const FString & source)1333 FMemLump::FMemLump (const FString &source)
1334 : Block (source)
1335 {
1336 }
1337
~FMemLump()1338 FMemLump::~FMemLump ()
1339 {
1340 }
1341
FString(ELumpNum lumpnum)1342 FString::FString (ELumpNum lumpnum)
1343 {
1344 FWadLump lumpr = Wads.OpenLumpNum ((int)lumpnum);
1345 long size = lumpr.GetLength ();
1346 AllocBuffer (1 + size);
1347 long numread = lumpr.Read (&Chars[0], size);
1348 Chars[size] = '\0';
1349
1350 if (numread != size)
1351 {
1352 I_Error ("ConstructStringFromLump: Only read %ld of %ld bytes on lump %i (%s)\n",
1353 numread, size, lumpnum, Wads.GetLumpFullName((int)lumpnum));
1354 }
1355 }
1356
1357 //==========================================================================
1358 //
1359 // PrintLastError
1360 //
1361 //==========================================================================
1362
1363 #ifdef _WIN32
1364 //#define WIN32_LEAN_AND_MEAN
1365 //#include <windows.h>
1366
1367 extern "C" {
1368 __declspec(dllimport) unsigned long __stdcall FormatMessageA(
1369 unsigned long dwFlags,
1370 const void *lpSource,
1371 unsigned long dwMessageId,
1372 unsigned long dwLanguageId,
1373 char **lpBuffer,
1374 unsigned long nSize,
1375 va_list *Arguments
1376 );
1377 __declspec(dllimport) void * __stdcall LocalFree (void *);
1378 __declspec(dllimport) unsigned long __stdcall GetLastError ();
1379 }
1380
PrintLastError()1381 static void PrintLastError ()
1382 {
1383 char *lpMsgBuf;
1384 FormatMessageA(0x1300 /*FORMAT_MESSAGE_ALLOCATE_BUFFER |
1385 FORMAT_MESSAGE_FROM_SYSTEM |
1386 FORMAT_MESSAGE_IGNORE_INSERTS*/,
1387 NULL,
1388 GetLastError(),
1389 1 << 10 /*MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)*/, // Default language
1390 &lpMsgBuf,
1391 0,
1392 NULL
1393 );
1394 Printf (TEXTCOLOR_RED " %s\n", lpMsgBuf);
1395 // Free the buffer.
1396 LocalFree( lpMsgBuf );
1397 }
1398 #else
1399 #include <cerrno>
PrintLastError()1400 static void PrintLastError ()
1401 {
1402 Printf (TEXTCOLOR_RED " %s\n", strerror(errno));
1403 }
1404 #endif
1405