1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: fs_wad.cpp 4102 2009-11-13 00:22:29Z firebrand_kh $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include "gamedefs.h"
29 #include "fs_local.h"
30 #include "fwaddefs.h"
31 
32 // MACROS ------------------------------------------------------------------
33 
34 // TYPES -------------------------------------------------------------------
35 
36 struct lumpinfo_t
37 {
38 	VName			Name;
39 	vint32			Position;
40 	vint32			Size;
41 	EWadNamespace	Namespace;
42 };
43 
44 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
45 
46 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
47 
48 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
49 
50 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
51 
52 // PUBLIC DATA DEFINITIONS -------------------------------------------------
53 
54 // PRIVATE DATA DEFINITIONS ------------------------------------------------
55 
56 // CODE --------------------------------------------------------------------
57 
58 //==========================================================================
59 //
60 //	VWadFile::VWadFile
61 //
62 //==========================================================================
63 
VWadFile()64 VWadFile::VWadFile()
65 : Stream(NULL)
66 , NumLumps(0)
67 , LumpInfo(NULL)
68 {
69 }
70 
71 //==========================================================================
72 //
73 //	VWadFile::~VWadFile
74 //
75 //==========================================================================
76 
~VWadFile()77 VWadFile::~VWadFile()
78 {
79 	Close();
80 }
81 
82 //==========================================================================
83 //
84 //	VWadFile::Open
85 //
86 //==========================================================================
87 
Open(const VStr & FileName,const VStr & AGwaDir,bool FixVoices,VStream * InStream)88 void VWadFile::Open(const VStr& FileName, const VStr& AGwaDir, bool FixVoices,
89 	VStream* InStream)
90 {
91 	guard(VWadFile::Open);
92 	wadinfo_t		header;
93 	lumpinfo_t*		lump_p;
94 	int				i, j;
95 	int				length;
96 	filelump_t*		fileinfo;
97 	filelump_t*		fi_p;
98 
99 	Name = FileName;
100 	GwaDir = AGwaDir;
101 
102 	if (InStream)
103 	{
104 		Stream = InStream;
105 	}
106 	else
107 	{
108 		// open the file and add to directory
109 		Stream = FL_OpenSysFileRead(FileName);
110 		if (!Stream)
111 		{
112 			Sys_Error("Couldn't open %s", *FileName);
113 		}
114 	}
115 	GCon->Logf(NAME_Init, "adding %s", *FileName);
116 
117 	// WAD file
118 	Stream->Serialise(&header, sizeof(header));
119 	if (VStr::NCmp(header.identification, "IWAD", 4))
120 	{
121 		// Homebrew levels?
122 		if (VStr::NCmp(header.identification, "PWAD", 4))
123 		{
124 			Sys_Error ("Wad file %s doesn't have IWAD "
125 		 		"or PWAD id\n", *FileName);
126 		}
127 	}
128 	header.numlumps = LittleLong(header.numlumps);
129 	header.infotableofs = LittleLong(header.infotableofs);
130 	NumLumps = header.numlumps;
131 	//	Moved here to make static data less fragmented
132 	LumpInfo = new lumpinfo_t[NumLumps];
133 	length = header.numlumps * sizeof(filelump_t);
134 	fi_p = fileinfo = (filelump_t*)Z_Malloc(length);
135 	Stream->Seek(header.infotableofs);
136 	Stream->Serialise(fileinfo, length);
137 
138 	// Fill in lumpinfo
139 	lump_p = LumpInfo;
140 
141 	for (i = 0; i < NumLumps; i++, lump_p++, fileinfo++)
142 	{
143 		// Mac demo hexen.wad:  many (1784) of the lump names
144 		// have their first character with the high bit (0x80)
145 		// set.  I don't know the reason for that..  We must
146 		// clear the high bits for such Mac wad files to work
147 		// in this engine. This shouldn't break other wads.
148 		for (j = 0; j < 8; j++)
149 			fileinfo->name[j] &= 0x7f;
150 		lump_p->Name = VName(fileinfo->name, VName::AddLower8);
151 		lump_p->Position = LittleLong(fileinfo->filepos);
152 		lump_p->Size = LittleLong(fileinfo->size);
153 		lump_p->Namespace = WADNS_Global;
154 	}
155 
156 	Z_Free(fi_p);
157 
158 	//	Set up namespaces.
159 	InitNamespaces();
160 
161 	if (FixVoices)
162 	{
163 		FixVoiceNamespaces();
164 	}
165 	unguard;
166 }
167 
168 //==========================================================================
169 //
170 //	VWadFile::OpenSingleLump
171 //
172 //==========================================================================
173 
OpenSingleLump(const VStr & FileName)174 void VWadFile::OpenSingleLump(const VStr& FileName)
175 {
176 	guard(VWadFile::OpenSingleLump);
177 	// open the file and add to directory
178 	Stream = FL_OpenSysFileRead(FileName);
179 	if (!Stream)
180 	{
181 		Sys_Error("Couldn't open %s", *FileName);
182 	}
183 	GCon->Logf(NAME_Init, "adding %s", *FileName);
184 
185 	Name = FileName;
186 	GwaDir = VStr();
187 
188 	// single lump file
189 	NumLumps = 1;
190 	LumpInfo = new lumpinfo_t[1];
191 
192 	// Fill in lumpinfo
193 	LumpInfo->Name = VName(*FileName.ExtractFileBase(), VName::AddLower8);
194 	LumpInfo->Position = 0;
195 	LumpInfo->Size = Stream->TotalSize();
196 	LumpInfo->Namespace = WADNS_Global;
197 	unguard;
198 }
199 
200 //==========================================================================
201 //
202 //	VWadFile::Close
203 //
204 //==========================================================================
205 
Close()206 void VWadFile::Close()
207 {
208 	guard(VWadFile::Close);
209 	if (LumpInfo)
210 	{
211 		delete[] LumpInfo;
212 		LumpInfo = NULL;
213 	}
214 	NumLumps = 0;
215 	Name.Clean();
216 	GwaDir.Clean();
217 	if (Stream)
218 	{
219 		delete Stream;
220 		Stream = NULL;
221 	}
222 	unguard;
223 }
224 
225 //==========================================================================
226 //
227 //  VWadFile::CheckNumForName
228 //
229 //  Returns -1 if name not found.
230 //
231 //==========================================================================
232 
CheckNumForName(VName LumpName,EWadNamespace InNS)233 int VWadFile::CheckNumForName(VName LumpName, EWadNamespace InNS)
234 {
235 	guard(VWadFile::CheckNumForName);
236 	//	Special ZIP-file namespaces in WAD file are in global namespace.
237 	EWadNamespace NS = InNS;
238 	if (NS > WADNS_ZipSpecial)
239 	{
240 		NS = WADNS_Global;
241 	}
242 
243 	for (int i = NumLumps - 1; i >= 0; i--)
244 	{
245 		if (LumpInfo[i].Namespace == NS && LumpInfo[i].Name == LumpName)
246 		{
247 			return i;
248 		}
249 	}
250 
251 	// Not found.
252 	return -1;
253 	unguard;
254 }
255 
256 //==========================================================================
257 //
258 //  VWadFile::ReadFromLump
259 //
260 //  Loads part of the lump into the given buffer.
261 //
262 //==========================================================================
263 
ReadFromLump(int lump,void * dest,int pos,int size)264 void VWadFile::ReadFromLump(int lump, void* dest, int pos, int size)
265 {
266 	guard(VWadFile::ReadFromLump);
267 	if ((vuint32)lump >= (vuint32)NumLumps)
268 	{
269 		Sys_Error("VWadFile::ReadFromLump: %i >= numlumps", lump);
270 	}
271 
272 	lumpinfo_t &l = LumpInfo[lump];
273 
274 	if (pos >= l.Size)
275 	{
276 		return;
277 	}
278 
279 	Stream->Seek(l.Position + pos);
280 	Stream->Serialise(dest, size);
281 	unguard;
282 }
283 
284 //==========================================================================
285 //
286 //  VWadFile::InitNamespaces
287 //
288 //==========================================================================
289 
InitNamespaces()290 void VWadFile::InitNamespaces()
291 {
292 	guard(VWadFile::InitNamespaces);
293 	InitNamespace(WADNS_Sprites, NAME_s_start, NAME_s_end, NAME_ss_start, NAME_ss_end);
294 	InitNamespace(WADNS_Flats, NAME_f_start, NAME_f_end, NAME_ff_start, NAME_ff_end);
295 	InitNamespace(WADNS_ColourMaps, NAME_c_start, NAME_c_end, NAME_cc_start, NAME_cc_end);
296 	InitNamespace(WADNS_ACSLibrary, NAME_a_start, NAME_a_end, NAME_aa_start, NAME_aa_end);
297 	InitNamespace(WADNS_NewTextures, NAME_tx_start, NAME_tx_end);
298 	InitNamespace(WADNS_Voices, NAME_v_start, NAME_v_end, NAME_vv_start, NAME_vv_end);
299 	InitNamespace(WADNS_HiResTextures, NAME_hi_start, NAME_hi_end);
300 	unguard;
301 }
302 
303 //==========================================================================
304 //
305 //  VWadFile::InitNamespace
306 //
307 //==========================================================================
308 
InitNamespace(EWadNamespace NS,VName Start,VName End,VName AltStart,VName AltEnd)309 void VWadFile::InitNamespace(EWadNamespace NS, VName Start, VName End,
310 	VName AltStart, VName AltEnd)
311 {
312 	guard(VWadFile::InitNamespace);
313 	bool InNS = false;
314 	for (int i = 0; i < NumLumps; i++)
315 	{
316 		lumpinfo_t& L = LumpInfo[i];
317 
318 		//	Skip if lump is already in other namespace.
319 		if (L.Namespace != WADNS_Global)
320 			continue;
321 
322 		if (InNS)
323 		{
324 			//	Check for ending marker.
325 			if (L.Name == End || (AltEnd != NAME_None && L.Name == AltEnd))
326 			{
327 				InNS = false;
328 			}
329 			else
330 			{
331 				L.Namespace = NS;
332 			}
333 		}
334 		else
335 		{
336 			//	Check for starting marker.
337 			if (L.Name == Start || (AltStart != NAME_None && L.Name == AltStart))
338 			{
339 				InNS = true;
340 			}
341 		}
342 	}
343 	unguard;
344 }
345 
346 //==========================================================================
347 //
348 //  VWadFile::FixVoiceNamespaces
349 //
350 //==========================================================================
351 
FixVoiceNamespaces()352 void VWadFile::FixVoiceNamespaces()
353 {
354 	guard(VWadFile::FixVoiceNamespaces);
355 	for (int i = 0; i < NumLumps; i++)
356 	{
357 		lumpinfo_t& L = LumpInfo[i];
358 
359 		//	Skip if lump is already in other namespace.
360 		if (L.Namespace != WADNS_Global)
361 			continue;
362 
363 		const char* LName = *L.Name;
364 		if (LName[0] == 'v' && LName[1] == 'o' && LName[2] == 'c' &&
365 			LName[3] >= '0' && LName[3] <= '9' &&
366 			(LName[4] == 0 || (LName[4] >= '0' && LName[4] <= '9' &&
367 			(LName[5] == 0 || (LName[5] >= '0' && LName[5] <= '9' &&
368 			(LName[6] == 0 || (LName[6] >= '0' && LName[6] <= '9' &&
369 			(LName[7] == 0 || (LName[7] >= '0' && LName[7] <= '9')))))))))
370 		{
371 			L.Namespace = WADNS_Voices;
372 		}
373 	}
374 	unguard;
375 }
376 
377 //==========================================================================
378 //
379 //  VWadFile::LumpLength
380 //
381 //  Returns the buffer size needed to load the given lump.
382 //
383 //==========================================================================
384 
LumpLength(int lump)385 int VWadFile::LumpLength(int lump)
386 {
387 	guard(VWadFile::LumpLength);
388 	return LumpInfo[lump].Size;
389 	unguard;
390 }
391 
392 //==========================================================================
393 //
394 //  VWadFile::LumpName
395 //
396 //==========================================================================
397 
LumpName(int lump)398 VName VWadFile::LumpName(int lump)
399 {
400 	guard(VWadFile::LumpName);
401 	if (lump >= NumLumps)
402 	{
403 		return NAME_None;
404 	}
405 	return LumpInfo[lump].Name;
406 	unguard;
407 }
408 
409 //==========================================================================
410 //
411 //  VWadFile::IterateNS
412 //
413 //==========================================================================
414 
IterateNS(int Start,EWadNamespace NS)415 int VWadFile::IterateNS(int Start, EWadNamespace NS)
416 {
417 	guard(VWadFile::IterateNS);
418 	for (int li = Start; li < NumLumps; li++)
419 	{
420 		if (LumpInfo[li].Namespace == NS)
421 		{
422 			return li;
423 		}
424 	}
425 	return -1;
426 	unguard;
427 }
428 
429 //==========================================================================
430 //
431 //	VWadFile::BuildGLNodes
432 //
433 //==========================================================================
434 
BuildGLNodes(VSearchPath * GlWad)435 void VWadFile::BuildGLNodes(VSearchPath* GlWad)
436 {
437 	guard(VWadFile::BuildGLNodes);
438 #ifdef CLIENT
439 	VStr gwaname;
440 	if (GwaDir.IsNotEmpty())
441 	{
442 		FL_CreatePath(GwaDir);
443 		gwaname = GwaDir + "/" + Name.ExtractFileName();
444 	}
445 	else
446 		gwaname = Name;
447 	gwaname = gwaname.StripExtension() + ".gwa";
448 
449 	// Build GL nodes
450 	if (!GLBSP_BuildNodes(*Name, *gwaname))
451 	{
452 		Sys_Error("Node build failed");
453 	}
454 
455 	// Build PVS
456 	GLVis_BuildPVS(*Name, *gwaname);
457 
458 	// Add GWA file
459 	((VWadFile*)GlWad)->Open(gwaname, VStr(), false, NULL);
460 #endif
461 	unguard;
462 }
463 
464 //==========================================================================
465 //
466 //	VWadFile::BuildPVS
467 //
468 //==========================================================================
469 
BuildPVS(VSearchPath * BaseWad)470 void VWadFile::BuildPVS(VSearchPath* BaseWad)
471 {
472 	guard(VWadFile::BuildPVS);
473 #ifdef CLIENT
474 	VStr name = ((VWadFile*)BaseWad)->Name;
475 
476 	VStr glname = Name;
477 
478 	// Close old file
479 	Close();
480 
481 	// Build PVS
482 	GLVis_BuildPVS(*name, BaseWad != this ? *glname : NULL);
483 
484 	// Add GWA file
485 	Open(glname, VStr(), false, NULL);
486 #endif
487 	unguard;
488 }
489 
490 //==========================================================================
491 //
492 //  VWadFile::CreateLumpReaderNum
493 //
494 //==========================================================================
495 
CreateLumpReaderNum(int lump)496 VStream* VWadFile::CreateLumpReaderNum(int lump)
497 {
498 	guard(VWadFile::CreateLumpReaderNum);
499 	check((vuint32)lump < (vuint32)NumLumps);
500 	lumpinfo_t &l = LumpInfo[lump];
501 
502 	// read the lump in
503 	void* ptr = Z_Malloc(l.Size);
504 	if (l.Size)
505 	{
506 		Stream->Seek(l.Position);
507 		Stream->Serialise(ptr, l.Size);
508 	}
509 
510 
511 	//	Create stream.
512 	VStream* S = new VMemoryStream(ptr, l.Size);
513 	Z_Free(ptr);
514 	return S;
515 	unguard;
516 }
517 
518 //==========================================================================
519 //
520 //	VWadFile::RenameSprites
521 //
522 //==========================================================================
523 
RenameSprites(const TArray<VSpriteRename> & A,const TArray<VLumpRename> & LA)524 void VWadFile::RenameSprites(const TArray<VSpriteRename>& A,
525 	const TArray<VLumpRename>& LA)
526 {
527 	guard(VWadFile::RenameSprites);
528 	for (int i = 0; i < NumLumps; i++)
529 	{
530 		lumpinfo_t& L = LumpInfo[i];
531 		if (L.Namespace != WADNS_Sprites)
532 		{
533 			continue;
534 		}
535 		for (int j = 0; j < A.Num(); j++)
536 		{
537 			if ((*L.Name)[0] != A[j].Old[0] || (*L.Name)[1] != A[j].Old[1] ||
538 				(*L.Name)[2] != A[j].Old[2] || (*L.Name)[3] != A[j].Old[3])
539 			{
540 				continue;
541 			}
542 			char NewName[12];
543 			VStr::Cpy(NewName, *L.Name);
544 			NewName[0] = A[j].New[0];
545 			NewName[1] = A[j].New[1];
546 			NewName[2] = A[j].New[2];
547 			NewName[3] = A[j].New[3];
548 			L.Name = NewName;
549 		}
550 		for (int j = 0; j < LA.Num(); j++)
551 		{
552 			if (L.Name == LA[j].Old)
553 			{
554 				L.Name = LA[j].New;
555 			}
556 		}
557 	}
558 	unguard;
559 }
560 
CheckNumForFileName(VStr)561 int VWadFile::CheckNumForFileName(VStr)
562 {
563 	return -1;
564 }
565 
FileExists(const VStr &)566 bool VWadFile::FileExists(const VStr&)
567 {
568 	return false;
569 }
570 
OpenFileRead(const VStr &)571 VStream* VWadFile::OpenFileRead(const VStr&)
572 {
573 	return NULL;
574 }
575