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 &copy) : 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 &copy)
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 &copy)
1323 {
1324 	Block = copy.Block;
1325 }
1326 
operator =(const FMemLump & copy)1327 FMemLump &FMemLump::operator = (const FMemLump &copy)
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