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