1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: files.cpp 4297 2010-06-03 22:49:00Z 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 
31 // MACROS ------------------------------------------------------------------
32 
33 // TYPES -------------------------------------------------------------------
34 
35 struct version_t
36 {
37 	VStr			MainWad;
38 	VStr			GameDir;
39 	TArray<VStr>	AddFiles;
40 	int				ParmFound;
41 	bool			FixVoices;
42 };
43 
44 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
45 
46 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
47 
48 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
49 
50 static void SetupGameDir(const VStr& dirname);
51 
52 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
53 
54 // PUBLIC DATA DEFINITIONS -------------------------------------------------
55 
56 bool	fl_devmode = false;
57 VStr	fl_basedir;
58 VStr	fl_savedir;
59 VStr	fl_gamedir;
60 VStr	fl_mainwad;
61 
62 // PRIVATE DATA DEFINITIONS ------------------------------------------------
63 
64 TArray<VSearchPath*>	SearchPaths;
65 
66 TArray<VStr>			wadfiles;
67 static bool				bIwadAdded;
68 static TArray<VStr>		IWadDirs;
69 static int				IWadIndex;
70 
71 // CODE --------------------------------------------------------------------
72 
73 //==========================================================================
74 //
75 //	AddGameDir
76 //
77 //==========================================================================
78 
AddZipFile(VStr ZipName)79 static void AddZipFile(VStr ZipName)
80 {
81 	//	Add ZIP file.
82 	VZipFile* Zip = new VZipFile(ZipName);
83 	SearchPaths.Append(Zip);
84 
85 	//	Add all WAD files in the root of the ZIP file.
86 	TArray<VStr> Wads;
87 	Zip->ListWadFiles(Wads);
88 	for (int i = 0; i < Wads.Num(); i++)
89 	{
90 		VStr GwaName = Wads[i].StripExtension() + ".gwa";
91 		VStream* WadStrm = Zip->OpenFileRead(Wads[i]);
92 		VStream* GwaStrm = Zip->OpenFileRead(GwaName);
93 
94 		//	Decompress WAD and GWA files into a memory stream since
95 		// reading from ZIP will be very slow.
96 		size_t Len = WadStrm->TotalSize();
97 		vuint8* Buf = new vuint8[Len];
98 		WadStrm->Serialise(Buf, Len);
99 		delete WadStrm;
100 		WadStrm = NULL;
101 		WadStrm = new VMemoryStream(Buf, Len);
102 		delete[] Buf;
103 		Buf = NULL;
104 
105 		if (GwaStrm)
106 		{
107 			Len = GwaStrm->TotalSize();
108 			Buf = new vuint8[Len];
109 			GwaStrm->Serialise(Buf, Len);
110 			delete GwaStrm;
111 			GwaStrm = NULL;
112 			GwaStrm = new VMemoryStream(Buf, Len);
113 			delete[] Buf;
114 			Buf = NULL;
115 		}
116 
117 		W_AddFileFromZip(ZipName + ":" + Wads[i], WadStrm,
118 			ZipName + ":" + GwaName, GwaStrm);
119 	}
120 }
121 
122 //==========================================================================
123 //
124 //	AddGameDir
125 //
126 //==========================================================================
127 
cmpfunc(const void * v1,const void * v2)128 static int cmpfunc(const void* v1, const void* v2)
129 {
130 	return ((VStr*)v1)->StripExtension().ICmp(((VStr*)v2)->StripExtension());
131 }
132 
AddGameDir(const VStr & basedir,const VStr & dir)133 static void AddGameDir(const VStr& basedir, const VStr& dir)
134 {
135 	guard(AddGameDir);
136 	//	First add all .pk3 files in that directory.
137 	if (Sys_OpenDir(basedir + "/" + dir))
138 	{
139 		TArray<VStr> ZipFiles;
140 		for (VStr test = Sys_ReadDir(); test.IsNotEmpty(); test = Sys_ReadDir())
141 		{
142 			VStr ext = test.ExtractFileExtension().ToLower();
143 			if (ext == "pk3")
144 				ZipFiles.Append(test);
145 		}
146 		Sys_CloseDir();
147 		qsort(ZipFiles.Ptr(), ZipFiles.Num(), sizeof(VStr), cmpfunc);
148 		for (int i = 0; i < ZipFiles.Num(); i++)
149 		{
150 			AddZipFile(basedir + "/" + dir + "/" + ZipFiles[i]);
151 		}
152 	}
153 
154 	//	Then add wad##.wad files.
155 	VStr gwadir;
156 	if (fl_savedir.IsNotEmpty() && basedir != fl_savedir)
157 	{
158 		gwadir = fl_savedir + "/" + dir;
159 	}
160 
161 	for (int i = 0; i < 1024; i++)
162 	{
163 		VStr buf = basedir + "/" + dir + "/wad" + i + ".wad";
164 		if (!Sys_FileExists(buf))
165 			break;
166 		W_AddFile(buf, gwadir, false);
167 	}
168 
169 	//	Finally add directory itself.
170 	VFilesDir* info = new VFilesDir(basedir + "/" + dir);
171 	SearchPaths.Append(info);
172 	unguard;
173 }
174 
175 //==========================================================================
176 //
177 //	AddGameDir
178 //
179 //==========================================================================
180 
AddGameDir(const VStr & dir)181 static void AddGameDir(const VStr& dir)
182 {
183 	guard(AddGameDir);
184 	AddGameDir(fl_basedir, dir);
185 	if (fl_savedir.IsNotEmpty())
186 	{
187 		AddGameDir(fl_savedir, dir);
188 	}
189 	fl_gamedir = dir;
190 	unguard;
191 }
192 
193 //==========================================================================
194 //
195 //	FindMainWad
196 //
197 //==========================================================================
198 
FindMainWad(VStr MainWad)199 static VStr FindMainWad(VStr MainWad)
200 {
201 	//	First check in IWAD directories.
202 	for (int i = 0; i < IWadDirs.Num(); i++)
203 	{
204 		if (Sys_FileExists(IWadDirs[i] + "/" + MainWad))
205 		{
206 			return IWadDirs[i] + "/" + MainWad;
207 		}
208 	}
209 
210 	//	Then look in the save directory.
211 	if (fl_savedir.IsNotEmpty() && Sys_FileExists(fl_savedir + "/" + MainWad))
212 	{
213 		return fl_savedir + "/" + MainWad;
214 	}
215 
216 	//	Finally in base directory.
217 	if (Sys_FileExists(fl_basedir + "/" + MainWad))
218 	{
219 		return fl_basedir + "/" + MainWad;
220 	}
221 
222 	return VStr();
223 }
224 
225 //==========================================================================
226 //
227 //	ParseBase
228 //
229 //==========================================================================
230 
ParseBase(const VStr & name)231 static void ParseBase(const VStr& name)
232 {
233 	guard(ParseBase);
234 	TArray<version_t>	games;
235 	bool				select_game;
236 	VStr				UseName;
237 
238 	if (fl_savedir.IsNotEmpty() && Sys_FileExists(fl_savedir + "/" + name))
239 	{
240 		UseName = fl_savedir + "/" + name;
241 	}
242 	else if (Sys_FileExists(fl_basedir + "/" + name))
243 	{
244 		UseName = fl_basedir + "/" + name;
245 	}
246 	else
247 	{
248 		return;
249 	}
250 
251 	select_game = false;
252 	VScriptParser* sc = new VScriptParser(UseName, FL_OpenSysFileRead(UseName));
253 	while (!sc->AtEnd())
254 	{
255 		version_t &dst = games.Alloc();
256 		dst.ParmFound = 0;
257 		dst.FixVoices = false;
258 		sc->Expect("game");
259 		sc->ExpectString();
260 		dst.GameDir = sc->String;
261 		if (sc->Check("iwad"))
262 		{
263 			sc->ExpectString();
264 			dst.MainWad = sc->String;
265 		}
266 		while (sc->Check("addfile"))
267 		{
268 			sc->ExpectString();
269 			dst.AddFiles.Append(sc->String);
270 		}
271 		if (sc->Check("param"))
272 		{
273 			sc->ExpectString();
274 			dst.ParmFound = GArgs.CheckParm(*sc->String);
275 			if (dst.ParmFound)
276 			{
277 				select_game = true;
278 			}
279 		}
280 		if (sc->Check("fixvoices"))
281 		{
282 			dst.FixVoices = true;
283 		}
284 		sc->Expect("end");
285 	}
286 	delete sc;
287 	sc = NULL;
288 
289 	for (int gi = games.Num() - 1; gi >= 0; gi--)
290 	{
291 		version_t& G = games[gi];
292 		if (select_game && !G.ParmFound)
293 		{
294 			continue;
295 		}
296 		if (fl_mainwad.IsNotEmpty())
297 		{
298 			if (G.MainWad.IsEmpty() || G.MainWad == fl_mainwad || select_game)
299 			{
300 				if (!bIwadAdded)
301 				{
302 					IWadIndex = SearchPaths.Num();
303 					VStr MainWadPath = FindMainWad(fl_mainwad);
304 					W_AddFile(MainWadPath, fl_savedir, G.FixVoices);
305 					bIwadAdded = true;
306 				}
307 				for (int j = 0; j < G.AddFiles.Num(); j++)
308 				{
309 					W_AddFile(fl_basedir + "/" + G.AddFiles[j], fl_savedir,
310 						false);
311 				}
312 				SetupGameDir(G.GameDir);
313 				return;
314 			}
315 			continue;
316 		}
317 		if (G.MainWad.IsEmpty())
318 		{
319 			continue;
320 		}
321 
322 		//	Look for the main wad file.
323 		VStr MainWadPath = FindMainWad(G.MainWad);
324 		if (MainWadPath.IsNotEmpty())
325 		{
326 			fl_mainwad = G.MainWad;
327 			if (!bIwadAdded)
328 			{
329 				IWadIndex = SearchPaths.Num();
330 				W_AddFile(MainWadPath, fl_savedir, G.FixVoices);
331 				bIwadAdded = true;
332 			}
333 			for (int j = 0; j < G.AddFiles.Num(); j++)
334 			{
335 				VStr FName = FindMainWad(G.AddFiles[j]);
336 				if (FName.IsEmpty())
337 				{
338 					Sys_Error("Required file %s not found", *G.AddFiles[j]);
339 				}
340 				W_AddFile(FName, fl_savedir, false);
341 			}
342 			SetupGameDir(G.GameDir);
343 			return;
344 		}
345 	}
346 
347 	if (select_game)
348 		Sys_Error("Main wad file not found.");
349 	else
350 		Sys_Error("Game mode indeterminate.");
351 	unguard;
352 }
353 
354 //==========================================================================
355 //
356 //	SetupGameDir
357 //
358 //==========================================================================
359 
SetupGameDir(const VStr & dirname)360 static void SetupGameDir(const VStr& dirname)
361 {
362 	guard(SetupGameDir);
363 	ParseBase(dirname + "/base.txt");
364 	AddGameDir(dirname);
365 	unguard;
366 }
367 
368 //==========================================================================
369 //
370 //	RenameSprites
371 //
372 //==========================================================================
373 
RenameSprites()374 static void RenameSprites()
375 {
376 	guard(RenameSprites);
377 	VStream* Strm = FL_OpenFileRead("sprite_rename.txt");
378 	if (!Strm)
379 	{
380 		return;
381 	}
382 
383 	VScriptParser* sc = new VScriptParser("sprite_rename.txt", Strm);
384 	TArray<VSpriteRename> Renames;
385 	TArray<VSpriteRename> AlwaysRenames;
386 	TArray<VLumpRename> LumpRenames;
387 	TArray<VLumpRename> AlwaysLumpRenames;
388 	while (!sc->AtEnd())
389 	{
390 		bool Always = sc->Check("always");
391 
392 		if (sc->Check("lump"))
393 		{
394 			sc->ExpectString();
395 			VStr Old = sc->String.ToLower();
396 			sc->ExpectString();
397 			VStr New = sc->String.ToLower();
398 			VLumpRename& R = Always ? AlwaysLumpRenames.Alloc() :
399 				LumpRenames.Alloc();
400 			R.Old = *Old;
401 			R.New = *New;
402 			continue;
403 		}
404 
405 		sc->ExpectString();
406 		if (sc->String.Length() != 4)
407 		{
408 			sc->Error("Sprite name must be 4 chars long");
409 		}
410 		VStr Old = sc->String.ToLower();
411 
412 		sc->ExpectString();
413 		if (sc->String.Length() != 4)
414 		{
415 			sc->Error("Sprite name must be 4 chars long");
416 		}
417 		VStr New = sc->String.ToLower();
418 
419 		VSpriteRename& R = Always ? AlwaysRenames.Alloc() : Renames.Alloc();
420 		R.Old[0] = Old[0];
421 		R.Old[1] = Old[1];
422 		R.Old[2] = Old[2];
423 		R.Old[3] = Old[3];
424 		R.New[0] = New[0];
425 		R.New[1] = New[1];
426 		R.New[2] = New[2];
427 		R.New[3] = New[3];
428 	}
429 	delete sc;
430 	sc = NULL;
431 
432 	bool RenameAll = !!GArgs.CheckParm("-oldsprites");
433 	for (int i = 0; i < SearchPaths.Num(); i++)
434 	{
435 		if (RenameAll || i == IWadIndex)
436 		{
437 			SearchPaths[i]->RenameSprites(Renames, LumpRenames);
438 		}
439 		SearchPaths[i]->RenameSprites(AlwaysRenames, AlwaysLumpRenames);
440 	}
441 	unguard;
442 }
443 
444 //==========================================================================
445 //
446 //	FL_Init
447 //
448 //==========================================================================
449 
FL_Init()450 void FL_Init()
451 {
452 	guard(FL_Init);
453 	const char* p;
454 
455 	//	Set up base directory (main data files).
456 	fl_basedir = ".";
457 	p = GArgs.CheckValue("-basedir");
458 	if (p)
459 	{
460 		fl_basedir = p;
461 	}
462 
463 	//	Set up save directory (files written by engine).
464 	p = GArgs.CheckValue("-savedir");
465 	if (p)
466 	{
467 		fl_savedir = p;
468 	}
469 #if !defined(_WIN32)
470 	else
471 	{
472 		const char* HomeDir = getenv("HOME");
473 		if (HomeDir)
474 		{
475 			fl_savedir = VStr(HomeDir) + "/.vavoom";
476 		}
477 	}
478 #endif
479 
480 	//	Set up additional directories where to look for IWAD files.
481 	int iwp = GArgs.CheckParm("-iwaddir");
482 	if (iwp)
483 	{
484 		while (++iwp != GArgs.Count() && GArgs[iwp][0] != '-' && GArgs[iwp][0] != '+')
485 		{
486 			IWadDirs.Append(GArgs[iwp]);
487 		}
488 	}
489 
490 	AddGameDir("basev/common");
491 
492 	p = GArgs.CheckValue("-iwad");
493 	if (p)
494 	{
495 		fl_mainwad = p;
496 	}
497 
498 	p = GArgs.CheckValue("-devgame");
499 	if (p)
500 	{
501 		fl_devmode = true;
502 	}
503 	else
504 	{
505 		p = GArgs.CheckValue("-game");
506 	}
507 
508 	if (p)
509 	{
510 		SetupGameDir(p);
511 	}
512 	else
513 	{
514 		ParseBase("basev/games.txt");
515 #ifdef DEVELOPER
516 		//  I need progs to be loaded from files
517 		fl_devmode = true;
518 #endif
519 	}
520 
521 	int fp = GArgs.CheckParm("-file");
522 	if (fp)
523 	{
524 		while (++fp != GArgs.Count() && GArgs[fp][0] != '-' && GArgs[fp][0] != '+')
525 		{
526 			VStr Ext = VStr(GArgs[fp]).ExtractFileExtension().ToLower();
527 			if (Ext == "pk3" || Ext == "zip")
528 				AddZipFile(GArgs[fp]);
529 			else
530 				W_AddFile(GArgs[fp], VStr(), false);
531 		}
532 	}
533 
534 	RenameSprites();
535 	unguard;
536 }
537 
538 //==========================================================================
539 //
540 //	FL_Shutdown
541 //
542 //==========================================================================
543 
FL_Shutdown()544 void FL_Shutdown()
545 {
546 	guard(FL_Shutdown);
547 	for (int i = 0; i < SearchPaths.Num(); i++)
548 	{
549 		delete SearchPaths[i];
550 		SearchPaths[i] = NULL;
551 	}
552 	SearchPaths.Clear();
553 	fl_basedir.Clean();
554 	fl_savedir.Clean();
555 	fl_gamedir.Clean();
556 	fl_mainwad.Clean();
557 	wadfiles.Clear();
558 	IWadDirs.Clear();
559 	unguard;
560 }
561 
562 //==========================================================================
563 //
564 //	FL_FileExists
565 //
566 //==========================================================================
567 
FL_FileExists(const VStr & fname)568 bool FL_FileExists(const VStr& fname)
569 {
570 	guard(FL_FileExists);
571 	for (int i = SearchPaths.Num() - 1; i >= 0 ; i--)
572 	{
573 		if (SearchPaths[i]->FileExists(fname))
574 		{
575 			return true;
576 		}
577 	}
578 	return false;
579 	unguard;
580 }
581 
582 //==========================================================================
583 //
584 //	FL_CreatePath
585 //
586 //==========================================================================
587 
FL_CreatePath(const VStr & Path)588 void FL_CreatePath(const VStr& Path)
589 {
590 	guard(FL_CreatePath);
591 	VStr Temp = Path;
592 	for (size_t i = 3; i <= Temp.Length(); i++)
593 	{
594 		if (Temp[i] == '/' || Temp[i] == '\\' || Temp[i] == 0)
595 		{
596 			char Save = Temp[i];
597 			Temp[i] = 0;
598 			if (!Sys_DirExists(Temp))
599 				Sys_CreateDirectory(Temp);
600 			Temp[i] = Save;
601 		}
602 	}
603 	unguard;
604 }
605 
606 //==========================================================================
607 //
608 //	FL_OpenFileRead
609 //
610 //==========================================================================
611 
FL_OpenFileRead(const VStr & Name)612 VStream* FL_OpenFileRead(const VStr& Name)
613 {
614 	guard(FL_OpenFileRead);
615 	for (int i = SearchPaths.Num() - 1; i >= 0; i--)
616 	{
617 		VStream* Strm = SearchPaths[i]->OpenFileRead(Name);
618 		if (Strm)
619 		{
620 			return Strm;
621 		}
622 	}
623 	return NULL;
624 	unguard;
625 }
626 
627 //==========================================================================
628 //
629 //	FL_OpenSysFileRead
630 //
631 //==========================================================================
632 
FL_OpenSysFileRead(const VStr & Name)633 VStream* FL_OpenSysFileRead(const VStr& Name)
634 {
635 	guard(FL_OpenSysFileRead);
636 	FILE *File = fopen(*Name, "rb");
637 	if (!File)
638 	{
639 		return NULL;
640 	}
641 	return new VStreamFileReader(File, GCon);
642 	unguard;
643 }
644 
645 //==========================================================================
646 //
647 //	VStreamFileWriter
648 //
649 //==========================================================================
650 
651 class VStreamFileWriter : public VStream
652 {
653 public:
VStreamFileWriter(FILE * InFile,FOutputDevice * InError)654 	VStreamFileWriter(FILE *InFile, FOutputDevice *InError)
655 		: File(InFile), Error(InError)
656 	{
657 		guard(VStreamFileWriter::VStreamFileReader);
658 		bLoading = false;
659 		unguard;
660 	}
~VStreamFileWriter()661 	~VStreamFileWriter()
662 	{
663 		guard(VStreamFileWriter::~VStreamFileWriter);
664 		if (File)
665 			Close();
666 		unguard;
667 	}
Seek(int InPos)668 	void Seek(int InPos)
669 	{
670 		//guard(VStreamFileWriter::Seek);
671 		if (fseek(File, InPos, SEEK_SET))
672 		{
673 			bError = true;
674 			//Error->Logf( TEXT("seek Failed %i/%i: %i %i"), InPos, Size, Pos, ferror(File) );
675 		}
676 		//unguard;
677 	}
Tell()678 	int Tell()
679 	{
680 		return ftell(File);
681 	}
TotalSize()682 	int TotalSize()
683 	{
684 		int CurPos = ftell(File);
685 		fseek(File, 0, SEEK_END);
686 		int Size = ftell(File);
687 		fseek(File, CurPos, SEEK_SET);
688 		return Size;
689 	}
AtEnd()690 	bool AtEnd()
691 	{
692 		return !!feof(File);
693 	}
Close()694 	bool Close()
695 	{
696 		guardSlow(VStreamFileWriter::Close);
697 		if (File && fclose(File))
698 		{
699 			bError = true;
700 			Error->Logf("fclose failed");
701 		}
702 		File = NULL;
703 		return !bError;
704 		unguardSlow;
705 	}
Serialise(void * V,int Length)706 	void Serialise(void* V, int Length)
707 	{
708 		guardSlow(VStreamFileWriter::Serialise);
709 		if (fwrite(V, Length, 1, File) != 1)
710 		{
711 			bError = true;
712 			Error->Logf("fwrite failed: Length=%i Error=%i", Length, ferror(File));
713 		}
714 		unguardSlow;
715 	}
Flush()716 	void Flush()
717 	{
718 		if (fflush(File))
719 		{
720 			bError = true;
721 			Error->Logf("WriteFailed");
722 		}
723 	}
724 protected:
725 	FILE *File;
726 	FOutputDevice *Error;
727 };
728 
729 //==========================================================================
730 //
731 //	FL_OpenFileWrite
732 //
733 //==========================================================================
734 
FL_OpenFileWrite(const VStr & Name)735 VStream* FL_OpenFileWrite(const VStr& Name)
736 {
737 	guard(FL_OpenFileWrite);
738 	VStr TmpName;
739 
740 	if (fl_savedir.IsNotEmpty())
741 		TmpName = fl_savedir + "/" + fl_gamedir + "/" + Name;
742 	else
743 		TmpName = fl_basedir + "/" + fl_gamedir + "/" + Name;
744 	FL_CreatePath(TmpName.ExtractFilePath());
745 	FILE *File = fopen(*TmpName, "wb");
746 	if (!File)
747 	{
748 		return NULL;
749 	}
750 	return new VStreamFileWriter(File, GCon);
751 	unguard;
752 }
753 
754 //==========================================================================
755 //
756 //	VStreamFileReader
757 //
758 //==========================================================================
759 
VStreamFileReader(FILE * InFile,FOutputDevice * InError)760 VStreamFileReader::VStreamFileReader(FILE* InFile, FOutputDevice *InError)
761 	: File(InFile), Error(InError)
762 {
763 	guard(VStreamFileReader::VStreamFileReader);
764 	fseek(File, 0, SEEK_SET);
765 	bLoading = true;
766 	unguard;
767 }
~VStreamFileReader()768 VStreamFileReader::~VStreamFileReader()
769 {
770 	guard(VStreamFileReader::~VStreamFileReader);
771 	if (File)
772 		Close();
773 	unguard;
774 }
Seek(int InPos)775 void VStreamFileReader::Seek(int InPos)
776 {
777 	guard(VStreamFileReader::Seek);
778 	if (fseek(File, InPos, SEEK_SET))
779 	{
780 		bError = true;
781 		//Error->Logf("seek Failed %i/%i: %i %i", InPos, Size, Pos, ferror(File) );
782 	}
783 	unguard;
784 }
Tell()785 int VStreamFileReader::Tell()
786 {
787 	return ftell(File);
788 }
TotalSize()789 int VStreamFileReader::TotalSize()
790 {
791 	int CurPos = ftell(File);
792 	fseek(File, 0, SEEK_END);
793 	int Size = ftell(File);
794 	fseek(File, CurPos, SEEK_SET);
795 	return Size;
796 }
AtEnd()797 bool VStreamFileReader::AtEnd()
798 {
799 	return Tell() >= TotalSize();
800 }
Close()801 bool VStreamFileReader::Close()
802 {
803 	guardSlow(VStreamFileReader::Close);
804 	if (File)
805 		fclose(File);
806 	File = NULL;
807 	return !bError;
808 	unguardSlow;
809 }
Serialise(void * V,int Length)810 void VStreamFileReader::Serialise(void* V, int Length)
811 {
812 	guardSlow(VStreamFileReader::Serialise);
813 	if (fread(V, Length, 1, File) != 1)
814 	{
815 		bError = true;
816 		Error->Logf("fread failed: Length=%i Error=%i", Length, ferror(File));
817 	}
818 	unguardSlow;
819 }
820