1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 //
21 // files.c
22 //
23
24 #include "common.h"
25 #include "../include/minizip/unzip.h"
26
27 #define FS_MAX_PAKS 1024
28 #define FS_MAX_HASHSIZE 1024
29 #define FS_MAX_FILEINDICES 1024
30
31 cVar_t *fs_basedir;
32 cVar_t *fs_cddir;
33 cVar_t *fs_game;
34 cVar_t *fs_gamedircvar;
35 cVar_t *fs_defaultPaks;
36
37 /*
38 =============================================================================
39
40 IN-MEMORY PAK FILES
41
42 =============================================================================
43 */
44
45 typedef struct mPackFile_s {
46 char fileName[MAX_QPATH];
47
48 int filePos;
49 int fileLen;
50
51 struct mPackFile_s *hashNext;
52 } mPackFile_t;
53
54 typedef struct mPack_s {
55 char name[MAX_OSPATH];
56
57 // Standard
58 FILE *pak;
59
60 // Compressed
61 unzFile *pkz;
62
63 // Information
64 int numFiles;
65 mPackFile_t *files;
66
67 struct mPackFile_s *fileHashTree[FS_MAX_HASHSIZE];
68 } mPack_t;
69
70 /*
71 =============================================================================
72
73 FILESYSTEM FUNCTIONALITY
74
75 =============================================================================
76 */
77
78 typedef struct fsLink_s {
79 struct fsLink_s *next;
80 char *from;
81 int fromLength;
82 char *to;
83 } fsLink_t;
84
85 typedef struct fsPath_s {
86 char pathName[MAX_OSPATH];
87 char gamePath[MAX_OSPATH];
88 mPack_t *package;
89
90 struct fsPath_s *next;
91 } fsPath_t;
92
93 static char fs_gameDir[MAX_OSPATH];
94
95 static fsLink_t *fs_fileLinks;
96
97 static fsPath_t *fs_searchPaths;
98 static fsPath_t **fs_invSearchPaths;
99 static int fs_numInvSearchPaths;
100 static fsPath_t *fs_baseSearchPath; // Without gamedirs
101
102 /*
103 =============================================================================
104
105 FILE INDEXING
106
107 =============================================================================
108 */
109
110 typedef struct fsHandleIndex_s {
111 char name[MAX_QPATH];
112
113 qBool inUse;
114 fsOpenMode_t openMode;
115
116 // One of these is always NULL
117 FILE *regFile;
118 unzFile *pkzFile;
119 } fsHandleIndex_t;
120
121 static fsHandleIndex_t fs_fileIndices[FS_MAX_FILEINDICES];
122
123 /*
124 =============================================================================
125
126 ZLIB FUNCTIONS
127
128 =============================================================================
129 */
130
131 /*
132 ================
133 FS_ZLibDecompress
134 ================
135 */
FS_ZLibDecompress(byte * in,int inLen,byte * out,int outLen,int wbits)136 int FS_ZLibDecompress (byte *in, int inLen, byte *out, int outLen, int wbits)
137 {
138 z_stream zs;
139 int result;
140
141 memset (&zs, 0, sizeof (zs));
142
143 zs.next_in = in;
144 zs.avail_in = 0;
145
146 zs.next_out = out;
147 zs.avail_out = outLen;
148
149 result = inflateInit2 (&zs, wbits);
150 if (result != Z_OK) {
151 Sys_Error ("Error on inflateInit %d\nMessage: %s\n", result, zs.msg);
152 return 0;
153 }
154
155 zs.avail_in = inLen;
156
157 result = inflate (&zs, Z_FINISH);
158 if (result != Z_STREAM_END) {
159 Sys_Error ("Error on inflate %d\nMessage: %s\n", result, zs.msg);
160 zs.total_out = 0;
161 }
162
163 result = inflateEnd (&zs);
164 if (result != Z_OK) {
165 Sys_Error ("Error on inflateEnd %d\nMessage: %s\n", result, zs.msg);
166 return 0;
167 }
168
169 return zs.total_out;
170 }
171
172
173 /*
174 ================
175 FS_ZLibCompressChunk
176 ================
177 */
FS_ZLibCompressChunk(byte * in,int inLen,byte * out,int outLen,int method,int wbits)178 int FS_ZLibCompressChunk (byte *in, int inLen, byte *out, int outLen, int method, int wbits)
179 {
180 z_stream zs;
181 int result;
182
183 zs.next_in = in;
184 zs.avail_in = inLen;
185 zs.total_in = 0;
186
187 zs.next_out = out;
188 zs.avail_out = outLen;
189 zs.total_out = 0;
190
191 zs.msg = NULL;
192 zs.state = NULL;
193 zs.zalloc = Z_NULL;
194 zs.zfree = Z_NULL;
195 zs.opaque = NULL;
196
197 zs.data_type = Z_BINARY;
198 zs.adler = 0;
199 zs.reserved = 0;
200
201 result = deflateInit2 (&zs, method, Z_DEFLATED, wbits, 9, Z_DEFAULT_STRATEGY);
202 if (result != Z_OK)
203 return 0;
204
205 result = deflate(&zs, Z_FINISH);
206 if (result != Z_STREAM_END)
207 return 0;
208
209 result = deflateEnd(&zs);
210 if (result != Z_OK)
211 return 0;
212
213 return zs.total_out;
214 }
215
216 /*
217 =============================================================================
218
219 HELPER FUNCTIONS
220
221 =============================================================================
222 */
223
224 /*
225 ================
226 __FileLen
227 ================
228 */
__FileLen(FILE * f)229 static int __FileLen (FILE *f)
230 {
231 int pos;
232 int end;
233
234 pos = ftell (f);
235 fseek (f, 0, SEEK_END);
236 end = ftell (f);
237 fseek (f, pos, SEEK_SET);
238
239 return end;
240 }
241
242
243 /*
244 ============
245 FS_CreatePath
246
247 Creates any directories needed to store the given filename
248 ============
249 */
FS_CreatePath(char * path)250 void FS_CreatePath (char *path)
251 {
252 char *ofs;
253
254 for (ofs=path+1 ; *ofs ; ofs++) {
255 if (*ofs == '/') {
256 // Create the directory
257 *ofs = 0;
258 Sys_Mkdir (path);
259 *ofs = '/';
260 }
261 }
262 }
263
264
265 /*
266 ================
267 FS_CopyFile
268 ================
269 */
FS_CopyFile(char * src,char * dst)270 void FS_CopyFile (char *src, char *dst)
271 {
272 FILE *f1, *f2;
273 int l;
274 byte buffer[65536];
275
276 if (fs_developer->intVal)
277 Com_Printf (0, "FS_CopyFile (%s, %s)\n", src, dst);
278
279 f1 = fopen (src, "rb");
280 if (!f1)
281 return;
282 f2 = fopen (dst, "wb");
283 if (!f2) {
284 fclose (f1);
285 return;
286 }
287
288 for ( ; ; ) {
289 l = fread (buffer, 1, sizeof (buffer), f1);
290 if (!l)
291 break;
292 fwrite (buffer, 1, l, f2);
293 }
294
295 fclose (f1);
296 fclose (f2);
297 }
298
299 /*
300 =============================================================================
301
302 FILE HANDLING
303
304 =============================================================================
305 */
306
307 /*
308 =================
309 FS_GetFreeHandle
310 =================
311 */
FS_GetFreeHandle(fsHandleIndex_t ** handle)312 static fileHandle_t FS_GetFreeHandle (fsHandleIndex_t **handle)
313 {
314 fileHandle_t i;
315 fsHandleIndex_t *hIndex;
316
317 for (i=0, hIndex=fs_fileIndices ; i<FS_MAX_FILEINDICES ; hIndex++, i++) {
318 if (hIndex->inUse)
319 continue;
320
321 hIndex->inUse = qTrue;
322 *handle = hIndex;
323 return i+1;
324 }
325
326 Com_Error (ERR_FATAL, "FS_GetFreeHandle: no free handles!");
327 return 0;
328 }
329
330
331 /*
332 =================
333 FS_GetHandle
334 =================
335 */
FS_GetHandle(fileHandle_t fileNum)336 static fsHandleIndex_t *FS_GetHandle (fileHandle_t fileNum)
337 {
338 fsHandleIndex_t *hIndex;
339
340 if (fileNum < 0 || fileNum > FS_MAX_FILEINDICES)
341 Com_Error (ERR_FATAL, "FS_GetHandle: invalid file number");
342
343 hIndex = &fs_fileIndices[fileNum-1];
344 if (!hIndex->inUse)
345 Com_Error (ERR_FATAL, "FS_GetHandle: invalid handle index");
346
347 return hIndex;
348 }
349
350
351 /*
352 ============
353 FS_FileLength
354
355 Returns the TOTAL file size, not the position.
356 Make sure to move the position back after moving to the beginning of the file for the size lookup.
357 ============
358 */
FS_FileLength(fileHandle_t fileNum)359 int FS_FileLength (fileHandle_t fileNum)
360 {
361 fsHandleIndex_t *handle;
362
363 handle = FS_GetHandle (fileNum);
364 if (handle->regFile) {
365 return __FileLen (handle->regFile);
366 }
367 else if (handle->pkzFile) {
368 // FIXME
369 return -1;
370 }
371
372 // Shouldn't happen...
373 assert (0);
374 return -1;
375 }
376
377
378 /*
379 ============
380 FS_Tell
381
382 Returns the current file position.
383 ============
384 */
FS_Tell(fileHandle_t fileNum)385 int FS_Tell (fileHandle_t fileNum)
386 {
387 fsHandleIndex_t *handle;
388
389 handle = FS_GetHandle (fileNum);
390 if (handle->regFile)
391 return ftell (handle->regFile);
392 else if (handle->pkzFile)
393 return unztell (handle->pkzFile);
394
395 // Shouldn't happen...
396 assert (0);
397 return 0;
398 }
399
400
401 /*
402 =================
403 FS_Read
404
405 Properly handles partial reads
406 =================
407 */
408 void CDAudio_Stop (void);
FS_Read(void * buffer,int len,fileHandle_t fileNum)409 int FS_Read (void *buffer, int len, fileHandle_t fileNum)
410 {
411 fsHandleIndex_t *handle;
412 int remaining;
413 int read;
414 byte *buf;
415 qBool tried;
416
417 handle = FS_GetHandle (fileNum);
418 if (handle->openMode != FS_MODE_READ_BINARY)
419 Com_Error (ERR_FATAL, "FS_Read: %s: was not opened in read mode", handle->name);
420
421 // Read in chunks for progress bar
422 remaining = len;
423 buf = (byte *)buffer;
424
425 tried = qFalse;
426 if (handle->regFile) {
427 // File
428 while (remaining) {
429 read = fread (buf, 1, remaining, handle->regFile);
430 switch (read) {
431 case 0:
432 // We might have been trying to read from a CD
433 if (!tried) {
434 tried = qTrue;
435 #ifndef DEDICATED_ONLY
436 if (!dedicated->intVal)
437 CDAudio_Stop ();
438 #endif
439 }
440 else {
441 if (fs_developer->intVal)
442 Com_Printf (0, "FS_Read: 0 bytes read from \"%s\"", handle->name);
443 return len - remaining;
444 }
445 break;
446
447 case -1:
448 Com_Error (ERR_FATAL, "FS_Read: -1 bytes read from \"%s\"", handle->name);
449 break;
450 }
451
452 // Do some progress bar thing here
453 remaining -= read;
454 buf += read;
455 }
456
457 return len;
458 }
459 else if (handle->pkzFile) {
460 // Zip file
461 while (remaining) {
462 read = unzReadCurrentFile (handle->pkzFile, buf, remaining);
463 switch (read) {
464 case 0:
465 // We might have been trying to read from a CD
466 if (!tried) {
467 tried = qTrue;
468 #ifndef DEDICATED_ONLY
469 if (!dedicated->intVal)
470 CDAudio_Stop ();
471 #endif
472 }
473 else {
474 if (fs_developer->intVal)
475 Com_Printf (0, "FS_Read: 0 bytes read from \"%s\"", handle->name);
476 return len - remaining;
477 }
478 break;
479
480 case -1:
481 Com_Error (ERR_FATAL, "FS_Read: -1 bytes read from \"%s\"", handle->name);
482 break;
483 }
484
485 // Do some progress bar thing here
486 remaining -= read;
487 buf += read;
488 }
489
490 return len;
491 }
492
493 // Shouldn't happen...
494 assert (0);
495 return 0;
496 }
497
498
499 /*
500 =================
501 FS_Write
502 =================
503 */
FS_Write(void * buffer,int size,fileHandle_t fileNum)504 int FS_Write (void *buffer, int size, fileHandle_t fileNum)
505 {
506 fsHandleIndex_t *handle;
507 int remaining;
508 int write;
509 byte *buf;
510
511 handle = FS_GetHandle (fileNum);
512 switch (handle->openMode) {
513 case FS_MODE_WRITE_BINARY:
514 case FS_MODE_APPEND_BINARY:
515 case FS_MODE_WRITE_TEXT:
516 case FS_MODE_APPEND_TEXT:
517 break;
518 default:
519 Com_Error (ERR_FATAL, "FS_Write: %s: was no opened in append/write mode", handle->name);
520 break;
521 }
522 if (size < 0)
523 Com_Error (ERR_FATAL, "FS_Write: size < 0");
524
525 // Write
526 remaining = size;
527 buf = (byte *)buffer;
528
529 if (handle->regFile) {
530 // File
531 while (remaining) {
532 write = fwrite (buf, 1, remaining, handle->regFile);
533
534 switch (write) {
535 case 0:
536 if (fs_developer->intVal)
537 Com_Printf (PRNT_ERROR, "FS_Write: 0 bytes written to %s\n", handle->name);
538 return size - remaining;
539
540 case -1:
541 Com_Error (ERR_FATAL, "FS_Write: -1 bytes written to %s", handle->name);
542 break;
543 }
544
545 remaining -= write;
546 buf += write;
547 }
548
549 return size;
550 }
551 else if (handle->pkzFile) {
552 // Zip file
553 Com_Error (ERR_FATAL, "FS_Write: can't write to zip file %s", handle->name);
554 }
555
556 // Shouldn't happen...
557 assert (0);
558 return 0;
559 }
560
561
562 /*
563 =================
564 FS_Seek
565 =================
566 */
FS_Seek(fileHandle_t fileNum,int offset,fsSeekOrigin_t seekOrigin)567 void FS_Seek (fileHandle_t fileNum, int offset, fsSeekOrigin_t seekOrigin)
568 {
569 fsHandleIndex_t *handle;
570 unz_file_info info;
571 int remaining, r, len;
572 byte dummy[0x8000];
573
574 handle = FS_GetHandle (fileNum);
575 if (handle->regFile) {
576 // Seek through a regular file
577 switch (seekOrigin) {
578 case FS_SEEK_SET:
579 fseek (handle->regFile, offset, SEEK_SET);
580 break;
581
582 case FS_SEEK_CUR:
583 fseek (handle->regFile, offset, SEEK_CUR);
584 break;
585
586 case FS_SEEK_END:
587 fseek (handle->regFile, offset, SEEK_END);
588 break;
589
590 default:
591 Com_Error (ERR_FATAL, "FS_Seek: bad origin (%i)", seekOrigin);
592 break;
593 }
594 }
595 else if (handle->pkzFile) {
596 // Seek through a zip
597 switch (seekOrigin) {
598 case FS_SEEK_SET:
599 remaining = offset;
600 break;
601
602 case FS_SEEK_CUR:
603 remaining = offset + unztell (handle->pkzFile);
604 break;
605
606 case FS_SEEK_END:
607 unzGetCurrentFileInfo (handle->pkzFile, &info, NULL, 0, NULL, 0, NULL, 0);
608
609 remaining = offset + info.uncompressed_size;
610 break;
611
612 default:
613 Com_Error (ERR_FATAL, "FS_Seek: bad origin (%i)", seekOrigin);
614 }
615
616 // Reopen the file
617 unzCloseCurrentFile (handle->pkzFile);
618 unzOpenCurrentFile (handle->pkzFile);
619
620 // Skip until the desired offset is reached
621 while (remaining) {
622 len = remaining;
623 if (len > sizeof (dummy))
624 len = sizeof (dummy);
625
626 r = unzReadCurrentFile (handle->pkzFile, dummy, len);
627 if (r <= 0)
628 break;
629
630 remaining -= r;
631 }
632 }
633 else
634 assert (0);
635 }
636
637
638 /*
639 ==============
640 FS_OpenFileAppend
641 ==============
642 */
FS_OpenFileAppend(fsHandleIndex_t * handle,qBool binary)643 static int FS_OpenFileAppend (fsHandleIndex_t *handle, qBool binary)
644 {
645 char path[MAX_OSPATH];
646
647 Q_snprintfz (path, sizeof (path), "%s/%s", fs_gameDir, handle->name);
648
649 // Create the path if it doesn't exist
650 FS_CreatePath (path);
651
652 // Open
653 if (binary)
654 handle->regFile = fopen (path, "ab");
655 else
656 handle->regFile = fopen (path, "at");
657
658 // Return length
659 if (handle->regFile) {
660 if (fs_developer->intVal)
661 Com_Printf (0, "FS_OpenFileAppend: \"%s\"", path);
662 return __FileLen (handle->regFile);
663 }
664
665 // Failed to open
666 if (fs_developer->intVal)
667 Com_Printf (0, "FS_OpenFileAppend: couldn't open \"%s\"", path);
668 return -1;
669 }
670
671
672 /*
673 ==============
674 FS_OpenFileWrite
675 ==============
676 */
FS_OpenFileWrite(fsHandleIndex_t * handle,qBool binary)677 static int FS_OpenFileWrite (fsHandleIndex_t *handle, qBool binary)
678 {
679 char path[MAX_OSPATH];
680
681 Q_snprintfz (path, sizeof (path), "%s/%s", fs_gameDir, handle->name);
682
683 // Create the path if it doesn't exist
684 FS_CreatePath (path);
685
686 // Open
687 if (binary)
688 handle->regFile = fopen (path, "wb");
689 else
690 handle->regFile = fopen (path, "wt");
691
692 // Return length
693 if (handle->regFile) {
694 if (fs_developer->intVal)
695 Com_Printf (0, "FS_OpenFileWrite: \"%s\"", path);
696 return 0;
697 }
698
699 // Failed to open
700 if (fs_developer->intVal)
701 Com_Printf (0, "FS_OpenFileWrite: couldn't open \"%s\"", path);
702 return -1;
703 }
704
705
706 /*
707 ===========
708 FS_OpenFileRead
709
710 Finds the file in the search path.
711 returns filesize and an open FILE *
712 Used for streaming data out of either a pak file or
713 a seperate file.
714 ===========
715 */
716 qBool fs_fileFromPak = qFalse;
FS_OpenFileRead(fsHandleIndex_t * handle)717 static int FS_OpenFileRead (fsHandleIndex_t *handle)
718 {
719 fsPath_t *searchPath;
720 char netPath[MAX_OSPATH];
721 mPack_t *package;
722 mPackFile_t *searchFile;
723 fsLink_t *link;
724 uint32 hashValue;
725
726 fs_fileFromPak = qFalse;
727 // Check for links first
728 for (link=fs_fileLinks ; link ; link=link->next) {
729 if (!strncmp (handle->name, link->from, link->fromLength)) {
730 Q_snprintfz (netPath, sizeof (netPath), "%s%s", link->to, handle->name+link->fromLength);
731
732 // Open
733 handle->regFile = fopen (netPath, "rb");
734
735 // Return length
736 if (fs_developer->intVal)
737 Com_Printf (0, "FS_OpenFileRead: link file: %s\n", netPath);
738 if (handle->regFile)
739 return __FileLen (handle->regFile);
740
741 // Failed to load
742 return -1;
743 }
744 }
745
746 // Calculate hash value
747 hashValue = Com_HashFileName (handle->name, FS_MAX_HASHSIZE);
748
749 // Search through the path, one element at a time
750 for (searchPath=fs_searchPaths ; searchPath ; searchPath=searchPath->next) {
751 // Is the element a pak file?
752 if (searchPath->package) {
753 // Look through all the pak file elements
754 package = searchPath->package;
755
756 for (searchFile=package->fileHashTree[hashValue] ; searchFile ; searchFile=searchFile->hashNext) {
757 if (Q_stricmp (searchFile->fileName, handle->name))
758 continue;
759
760 // Found it!
761 fs_fileFromPak = qTrue;
762
763 if (package->pak) {
764 if (fs_developer->intVal)
765 Com_Printf (0, "FS_OpenFileRead: pack file %s : %s\n", package->name, handle->name);
766
767 // Open a new file on the pakfile
768 handle->regFile = fopen (package->name, "rb");
769 if (handle->regFile) {
770 fseek (handle->regFile, searchFile->filePos, SEEK_SET);
771 return searchFile->fileLen;
772 }
773 }
774 else if (package->pkz) {
775 if (fs_developer->intVal)
776 Com_Printf (0, "FS_OpenFileRead: pkz file %s : %s\n", package->name, handle->name);
777
778 handle->pkzFile = unzOpen (package->name);
779 if (handle->pkzFile) {
780 if (unzSetOffset (handle->pkzFile, searchFile->filePos) == UNZ_OK) {
781 if (unzOpenCurrentFile (handle->pkzFile) == UNZ_OK)
782 return searchFile->fileLen;
783 }
784
785 // Failed to locate/open
786 unzClose (handle->pkzFile);
787 }
788 }
789
790 Com_Error (ERR_FATAL, "FS_OpenFileRead: couldn't reopen \"%s\"", handle->name);
791 }
792 }
793 else {
794 // Check a file in the directory tree
795 Q_snprintfz (netPath, sizeof (netPath), "%s/%s", searchPath->pathName, handle->name);
796
797 handle->regFile = fopen (netPath, "rb");
798 if (handle->regFile) {
799 // Found it!
800 if (fs_developer->intVal)
801 Com_Printf (0, "FS_OpenFileRead: %s\n", netPath);
802 return __FileLen (handle->regFile);
803 }
804 }
805 }
806
807 if (fs_developer->intVal)
808 Com_Printf (0, "FS_OpenFileRead: can't find %s\n", handle->name);
809
810 return -1;
811 }
812
813
814 /*
815 ===========
816 FS_OpenFile
817 ===========
818 */
FS_OpenFile(char * fileName,fileHandle_t * fileNum,fsOpenMode_t openMode)819 int FS_OpenFile (char *fileName, fileHandle_t *fileNum, fsOpenMode_t openMode)
820 {
821 fsHandleIndex_t *handle;
822 int fileSize;
823
824 *fileNum = FS_GetFreeHandle (&handle);
825
826 Q_strncpyz (handle->name, fileName, sizeof (handle->name));
827 handle->openMode = openMode;
828
829 // Open under the desired mode
830 switch (openMode) {
831 case FS_MODE_APPEND_BINARY:
832 fileSize = FS_OpenFileAppend (handle, qTrue);
833 break;
834 case FS_MODE_APPEND_TEXT:
835 fileSize = FS_OpenFileAppend (handle, qFalse);
836 break;
837
838 case FS_MODE_READ_BINARY:
839 fileSize = FS_OpenFileRead (handle);
840 break;
841
842 case FS_MODE_WRITE_BINARY:
843 fileSize = FS_OpenFileWrite (handle, qTrue);
844 break;
845 case FS_MODE_WRITE_TEXT:
846 fileSize = FS_OpenFileWrite (handle, qFalse);
847 break;
848
849 default:
850 Com_Error (ERR_FATAL, "FS_OpenFile: %s: invalid mode '%i'", handle->name, openMode);
851 break;
852 }
853
854 // Failed
855 if (fileSize == -1) {
856 memset (handle, 0, sizeof (fsHandleIndex_t));
857 *fileNum = 0;
858 }
859
860 return fileSize;
861 }
862
863
864 /*
865 ==============
866 FS_CloseFile
867 ==============
868 */
FS_CloseFile(fileHandle_t fileNum)869 void FS_CloseFile (fileHandle_t fileNum)
870 {
871 fsHandleIndex_t *handle;
872
873 // Get local handle
874 handle = FS_GetHandle (fileNum);
875 if (!handle->inUse)
876 return;
877
878 // Close file/zip
879 if (handle->regFile)
880 fclose (handle->regFile);
881 else if (handle->pkzFile) {
882 unzCloseCurrentFile (handle->pkzFile);
883 unzClose (handle->pkzFile);
884 }
885 else
886 assert (0);
887
888 // Clear handle
889 handle->inUse = qFalse;
890 handle->name[0] = '\0';
891 handle->pkzFile = NULL;
892 handle->regFile = NULL;
893 }
894
895 // ==========================================================================
896
897 /*
898 ============
899 FS_LoadFile
900
901 Filename are reletive to the egl search path.
902 A NULL buffer will just return the file length without loading.
903 -1 is returned if it wasn't found, 0 is returned if it's a blank file. In both cases a buffer is set to NULL.
904 ============
905 */
FS_LoadFile(char * path,void ** buffer,char * terminate)906 int FS_LoadFile (char *path, void **buffer, char *terminate)
907 {
908 byte *buf;
909 int fileLen;
910 fileHandle_t fileNum;
911 uint32 termLen;
912
913 // Look for it in the filesystem or pack files
914 fileLen = FS_OpenFile (path, &fileNum, FS_MODE_READ_BINARY);
915 if (!fileNum || fileLen <= 0) {
916 if (buffer)
917 *buffer = NULL;
918 if (fileNum)
919 FS_CloseFile (fileNum);
920 if (fileLen >= 0)
921 return 0;
922 return -1;
923 }
924
925 // Just needed to get the length
926 if (!buffer) {
927 FS_CloseFile (fileNum);
928 return fileLen;
929 }
930
931 // Allocate a local buffer
932 // If we're terminating, pad by one byte. Mem_PoolAlloc below will zero-fill...
933 if (terminate)
934 termLen = strlen (terminate);
935 else
936 termLen = 0;
937 buf = Mem_PoolAlloc (fileLen+termLen, com_fileSysPool, 0);
938 *buffer = buf;
939
940 // Copy the file data to a local buffer
941 FS_Read (buf, fileLen, fileNum);
942 FS_CloseFile (fileNum);
943
944 // Terminate if desired
945 if (termLen)
946 strncpy ((char *)buf+fileLen, terminate, termLen);
947 return fileLen+termLen;
948 }
949
950
951 /*
952 =============
953 _FS_FreeFile
954 =============
955 */
_FS_FreeFile(void * buffer,const char * fileName,const int fileLine)956 void _FS_FreeFile (void *buffer, const char *fileName, const int fileLine)
957 {
958 if (buffer)
959 _Mem_Free (buffer, fileName, fileLine);
960 }
961
962 // ==========================================================================
963
964 /*
965 ============
966 FS_FileExists
967
968 Filename are reletive to the egl search path.
969 Just like calling FS_LoadFile with a NULL buffer.
970 ============
971 */
FS_FileExists(char * path)972 int FS_FileExists (char *path)
973 {
974 int fileLen;
975 fileHandle_t fileNum;
976
977 // Look for it in the filesystem or pack files
978 fileLen = FS_OpenFile (path, &fileNum, FS_MODE_READ_BINARY);
979 if (!fileNum || fileLen <= 0)
980 return -1;
981
982 // Just needed to get the length
983 FS_CloseFile (fileNum);
984 return fileLen;
985 }
986
987 /*
988 =============================================================================
989
990 PACKAGE LOADING
991
992 =============================================================================
993 */
994
995 /*
996 =================
997 FS_LoadPAK
998
999 Takes an explicit (not game tree related) path to a pak file.
1000
1001 Loads the header and directory, adding the files at the beginning
1002 of the list so they override previous pack files.
1003 =================
1004 */
FS_LoadPAK(char * fileName,qBool complain)1005 mPack_t *FS_LoadPAK (char *fileName, qBool complain)
1006 {
1007 dPackHeader_t header;
1008 mPackFile_t *outPackFile;
1009 mPack_t *outPack;
1010 FILE *handle;
1011 dPackFile_t info[PAK_MAX_FILES];
1012 int i, numFiles;
1013 uint32 hashValue;
1014
1015 // Open
1016 handle = fopen (fileName, "rb");
1017 if (!handle) {
1018 if (complain)
1019 Com_Printf (PRNT_ERROR, "FS_LoadPAK: couldn't open \"%s\"\n", fileName);
1020 return NULL;
1021 }
1022
1023 // Read header
1024 fread (&header, 1, sizeof (header), handle);
1025 if (LittleLong (header.ident) != PAK_HEADER) {
1026 fclose (handle);
1027 Com_Error (ERR_FATAL, "FS_LoadPAK: \"%s\" is not a packfile", fileName);
1028 }
1029
1030 header.dirOfs = LittleLong (header.dirOfs);
1031 header.dirLen = LittleLong (header.dirLen);
1032
1033 // Sanity checks
1034 numFiles = header.dirLen / sizeof (dPackFile_t);
1035 if (numFiles > PAK_MAX_FILES) {
1036 fclose (handle);
1037 Com_Error (ERR_FATAL, "FS_LoadPAK: \"%s\" has too many files (%i > %i)", fileName, numFiles, PAK_MAX_FILES);
1038 }
1039 if (numFiles <= 0) {
1040 fclose (handle);
1041 Com_Error (ERR_FATAL, "FS_LoadPAK: \"%s\" is empty", fileName);
1042 }
1043
1044 // Read past header
1045 fseek (handle, header.dirOfs, SEEK_SET);
1046 fread (info, 1, header.dirLen, handle);
1047
1048 // Create pak
1049 outPack = Mem_PoolAlloc (sizeof (mPack_t), com_fileSysPool, 0);
1050 outPackFile = Mem_PoolAlloc (sizeof (mPackFile_t) * numFiles, com_fileSysPool, 0);
1051
1052 Q_strncpyz (outPack->name, fileName, sizeof (outPack->name));
1053 outPack->pak = handle;
1054 outPack->numFiles = numFiles;
1055 outPack->files = outPackFile;
1056
1057 // Parse the directory
1058 for (i=0 ; i<numFiles ; i++) {
1059 Q_strncpyz (outPackFile->fileName, info[i].name, sizeof (outPackFile->fileName));
1060 outPackFile->filePos = LittleLong (info[i].filePos);
1061 outPackFile->fileLen = LittleLong (info[i].fileLen);
1062
1063 // Link it into the hash tree
1064 hashValue = Com_HashFileName (outPackFile->fileName, FS_MAX_HASHSIZE);
1065
1066 outPackFile->hashNext = outPack->fileHashTree[hashValue];
1067 outPack->fileHashTree[hashValue] = outPackFile;
1068
1069 // Next
1070 outPackFile++;
1071 }
1072
1073 Com_Printf (0, "FS_LoadPAK: loaded \"%s\"\n", fileName);
1074 return outPack;
1075 }
1076
1077
1078 /*
1079 =================
1080 FS_LoadPKZ
1081 =================
1082 */
FS_LoadPKZ(char * fileName,qBool complain)1083 mPack_t *FS_LoadPKZ (char *fileName, qBool complain)
1084 {
1085 mPackFile_t *outPkzFile;
1086 mPack_t *outPkz;
1087 unzFile *handle;
1088 unz_global_info global;
1089 unz_file_info info;
1090 char name[MAX_QPATH];
1091 int status;
1092 int numFiles;
1093 uint32 hashValue;
1094
1095 // Open
1096 handle = unzOpen (fileName);
1097 if (!handle) {
1098 if (complain)
1099 Com_Printf (PRNT_ERROR, "FS_LoadPKZ: couldn't open \"%s\"\n", fileName);
1100 return NULL;
1101 }
1102
1103 // Get global info
1104 if (unzGetGlobalInfo (handle, &global) != UNZ_OK) {
1105 unzClose (handle);
1106 Com_Error (ERR_FATAL, "FS_LoadPKZ: \"%s\" is not a packfile", fileName);
1107 }
1108
1109 // Sanity checks
1110 numFiles = global.number_entry;
1111 if (numFiles > PKZ_MAX_FILES) {
1112 unzClose (handle);
1113 Com_Error (ERR_FATAL, "FS_LoadPKZ: \"%s\" has too many files (%i > %i)", fileName, numFiles, PKZ_MAX_FILES);
1114 }
1115 if (numFiles <= 0) {
1116 unzClose (handle);
1117 Com_Error (ERR_FATAL, "FS_LoadPKZ: \"%s\" is empty", fileName);
1118 }
1119
1120 // Create pak
1121 outPkz = Mem_PoolAlloc (sizeof (mPack_t), com_fileSysPool, 0);
1122 outPkzFile = Mem_PoolAlloc (sizeof (mPackFile_t) * numFiles, com_fileSysPool, 0);
1123
1124 Q_strncpyz (outPkz->name, fileName, sizeof (outPkz->name));
1125 outPkz->pkz = handle;
1126 outPkz->numFiles = numFiles;
1127 outPkz->files = outPkzFile;
1128
1129 status = unzGoToFirstFile (handle);
1130
1131 while (status == UNZ_OK) {
1132 unzGetCurrentFileInfo (handle, &info, name, MAX_QPATH, NULL, 0, NULL, 0);
1133
1134 Q_strncpyz (outPkzFile->fileName, name, sizeof (outPkzFile->fileName));
1135 outPkzFile->filePos = unzGetOffset (handle);
1136 outPkzFile->fileLen = info.uncompressed_size;
1137
1138 // Link it into the hash tree
1139 hashValue = Com_HashFileName (outPkzFile->fileName, FS_MAX_HASHSIZE);
1140
1141 outPkzFile->hashNext = outPkz->fileHashTree[hashValue];
1142 outPkz->fileHashTree[hashValue] = outPkzFile;
1143
1144 // Next
1145 outPkzFile++;
1146
1147 status = unzGoToNextFile (handle);
1148 }
1149
1150 Com_Printf (0, "FS_LoadPKZ: loaded \"%s\"\n", fileName);
1151 return outPkz;
1152 }
1153
1154
1155 /*
1156 ================
1157 FS_AddGameDirectory
1158
1159 Sets fs_gameDir, adds the directory to the head of the path, and loads *.pak/*.pkz
1160 ================
1161 */
FS_AddGameDirectory(char * dir,char * gamePath)1162 static void FS_AddGameDirectory (char *dir, char *gamePath)
1163 {
1164 char searchName[MAX_OSPATH];
1165 char *packFiles[FS_MAX_PAKS];
1166 int numPacks, i;
1167 fsPath_t *search;
1168 mPack_t *pak;
1169 mPack_t *pkz;
1170
1171 if (fs_developer->intVal)
1172 Com_Printf (0, "FS_AddGameDirectory: adding \"%s\"\n", dir);
1173
1174 // Set as game directory
1175 Q_strncpyz (fs_gameDir, dir, sizeof (fs_gameDir));
1176
1177 // Add the directory to the search path
1178 search = Mem_PoolAlloc (sizeof (fsPath_t), com_fileSysPool, 0);
1179 Q_strncpyz (search->pathName, dir, sizeof (search->pathName));
1180 Q_strncpyz (search->gamePath, gamePath, sizeof (search->gamePath));
1181 search->next = fs_searchPaths;
1182 fs_searchPaths = search;
1183
1184 // Add any pak files in the format pak0.pak pak1.pak, ...
1185 for (i=0 ; i<10 ; i++) {
1186 Q_snprintfz (searchName, sizeof (searchName), "%s/pak%i.pak", dir, i);
1187 pak = FS_LoadPAK (searchName, qFalse);
1188 if (!pak)
1189 continue;
1190 search = Mem_PoolAlloc (sizeof (fsPath_t), com_fileSysPool, 0);
1191 search->package = pak;
1192 search->next = fs_searchPaths;
1193 fs_searchPaths = search;
1194 }
1195
1196 // Add the rest of the *.pak files
1197 if (!fs_defaultPaks->intVal) {
1198 numPacks = Sys_FindFiles (dir, "*/*.pak", packFiles, FS_MAX_PAKS, 0, qFalse, qTrue, qFalse);
1199
1200 for (i=0 ; i<numPacks ; i++) {
1201 if (strstr (packFiles[i], "/pak0.pak") || strstr (packFiles[i], "/pak1.pak")
1202 || strstr (packFiles[i], "/pak2.pak") || strstr (packFiles[i], "/pak3.pak")
1203 || strstr (packFiles[i], "/pak4.pak") || strstr (packFiles[i], "/pak5.pak")
1204 || strstr (packFiles[i], "/pak6.pak") || strstr (packFiles[i], "/pak7.pak")
1205 || strstr (packFiles[i], "/pak8.pak") || strstr (packFiles[i], "/pak9.pak"))
1206 continue; // FIXME :|
1207
1208 pak = FS_LoadPAK (packFiles[i], qTrue);
1209 if (!pak)
1210 continue;
1211 search = Mem_PoolAlloc (sizeof (fsPath_t), com_fileSysPool, 0);
1212 Q_strncpyz (search->pathName, dir, sizeof (search->pathName));
1213 Q_strncpyz (search->gamePath, gamePath, sizeof (search->gamePath));
1214 search->package = pak;
1215 search->next = fs_searchPaths;
1216 fs_searchPaths = search;
1217 }
1218
1219 FS_FreeFileList (packFiles, numPacks);
1220 }
1221
1222 // Load *.pkz files
1223 numPacks = Sys_FindFiles (dir, "*/*.pkz", packFiles, FS_MAX_PAKS, 0, qFalse, qTrue, qFalse);
1224
1225 for (i=0 ; i<numPacks ; i++) {
1226 pkz = FS_LoadPKZ (packFiles[i], qTrue);
1227 if (!pkz)
1228 continue;
1229 search = Mem_PoolAlloc (sizeof (fsPath_t), com_fileSysPool, 0);
1230 Q_strncpyz (search->pathName, dir, sizeof (search->pathName));
1231 Q_strncpyz (search->gamePath, gamePath, sizeof (search->gamePath));
1232 search->package = pkz;
1233 search->next = fs_searchPaths;
1234 fs_searchPaths = search;
1235 }
1236
1237 FS_FreeFileList (packFiles, numPacks);
1238 }
1239
1240 /*
1241 =============================================================================
1242
1243 GAME PATH
1244
1245 =============================================================================
1246 */
1247
1248 /*
1249 ============
1250 FS_Gamedir
1251 ============
1252 */
FS_Gamedir(void)1253 char *FS_Gamedir (void)
1254 {
1255 if (*fs_gameDir)
1256 return fs_gameDir;
1257 else
1258 return BASE_MODDIRNAME;
1259 }
1260
1261 #ifdef HOMEDIR
1262 /*
1263 * FS_AddHomeAsGameDirectory
1264 * Add a game directory in "~/.egl". It can be used to set the last path, so
1265 * it will be used for writting.
1266 */
FS_AddHomeAsGameDirectory(char * dir)1267 void FS_AddHomeAsGameDirectory(char *dir)
1268 {
1269 char gdir[MAX_OSPATH]; /* Game directory. */
1270 char *homedir; /* Home directory. */
1271
1272 if ((homedir = getenv("HOME")) != NULL) {
1273 Q_snprintfz(gdir, sizeof(gdir), "%s/.egl/%s", homedir, dir);
1274 FS_AddGameDirectory(gdir, dir);
1275 }
1276 }
1277 #endif
1278
1279 /*
1280 ================
1281 FS_SetGamedir
1282
1283 Sets the gamedir and path to a different directory.
1284 ================
1285 */
FS_SetGamedir(char * dir,qBool firstTime)1286 void FS_SetGamedir (char *dir, qBool firstTime)
1287 {
1288 fsPath_t *next;
1289 mPack_t *package;
1290 uint32 initTime;
1291 int i;
1292
1293 // Make sure it's not a path
1294 if (strstr (dir, "..") || strchr (dir, '/') || strchr (dir, '\\') || strchr (dir, ':')) {
1295 Com_Printf (PRNT_WARNING, "FS_SetGamedir: Gamedir should be a single directory name, not a path\n");
1296 return;
1297 }
1298
1299 // Free old inverted paths
1300 if (fs_invSearchPaths)
1301 Mem_Free (fs_invSearchPaths);
1302
1303 // Free up any current game dir info
1304 for ( ; fs_searchPaths != fs_baseSearchPath ; fs_searchPaths=next) {
1305 next = fs_searchPaths->next;
1306
1307 if (fs_searchPaths->package) {
1308 package = fs_searchPaths->package;
1309
1310 if (package->pak)
1311 fclose (package->pak);
1312 else if (package->pkz)
1313 unzClose (package->pkz);
1314
1315 Mem_Free (package->files);
1316 Mem_Free (package);
1317 }
1318
1319 Mem_Free (fs_searchPaths);
1320 }
1321
1322 // Load packages
1323 initTime = Sys_UMilliseconds ();
1324 Com_Printf (0, "\n------------- Changing Game ------------\n");
1325
1326 Q_snprintfz (fs_gameDir, sizeof (fs_gameDir), "%s/%s", fs_basedir->string, dir);
1327
1328 if (!strcmp (dir, BASE_MODDIRNAME) || *dir == 0) {
1329 Cvar_VariableSet (fs_gamedircvar, "", qTrue);
1330 Cvar_VariableSet (fs_game, "", qTrue);
1331 }
1332 else {
1333 Cvar_VariableSet (fs_gamedircvar, dir, qTrue);
1334 #ifdef __FreeBSD__
1335 FS_AddGameDirectory (Q_VarArgs ("%s/%s", DATADIR, dir), dir);
1336 FS_AddGameDirectory (Q_VarArgs ("%s/%s", LIBDIR, dir), dir);
1337 #endif
1338 if (fs_cddir->string[0])
1339 FS_AddGameDirectory (Q_VarArgs ("%s/%s", fs_cddir->string, dir), dir);
1340
1341 FS_AddGameDirectory (Q_VarArgs ("%s/%s", fs_basedir->string, dir), dir);
1342 #ifdef HOMEDIR
1343 FS_AddHomeAsGameDirectory(dir);
1344 #endif
1345 }
1346
1347 // Store a copy of the search paths inverted for FS_FindFiles
1348 for (fs_numInvSearchPaths=0, next=fs_searchPaths ; next ; next=next->next, fs_numInvSearchPaths++);
1349 fs_invSearchPaths = Mem_PoolAlloc (sizeof (fsPath_t) * fs_numInvSearchPaths, com_fileSysPool, 0);
1350 for (i=fs_numInvSearchPaths-1, next=fs_searchPaths ; i>=0 ; next=next->next, i--)
1351 fs_invSearchPaths[i] = next;
1352
1353 if (!firstTime) {
1354 Com_Printf (0, "----------------------------------------\n");
1355 Com_Printf (0, "init time: %ums\n", Sys_UMilliseconds()-initTime);
1356 Com_Printf (0, "----------------------------------------\n");
1357
1358 #ifndef DEDICATED_ONLY
1359 // Forced to reload to flush old data
1360 if (!dedicated->intVal) {
1361 Cbuf_AddText ("exec default.cfg\n");
1362 Cbuf_AddText ("exec config.cfg\n");
1363 Cbuf_AddText ("exec eglcfg.cfg\n");
1364 FS_ExecAutoexec ();
1365 Cbuf_Execute ();
1366 Cbuf_AddText ("vid_restart\nsnd_restart\n");
1367 Cbuf_Execute ();
1368 }
1369 #endif
1370 }
1371 }
1372
1373
1374 /*
1375 =============
1376 FS_ExecAutoexec
1377 =============
1378 */
FS_ExecAutoexec(void)1379 void FS_ExecAutoexec (void)
1380 {
1381 char *dir;
1382 char name[MAX_QPATH];
1383
1384 dir = Cvar_GetStringValue ("gamedir");
1385 if (*dir)
1386 Q_snprintfz (name, sizeof (name), "%s/%s/autoexec.cfg", fs_basedir->string, dir);
1387 else
1388 Q_snprintfz (name, sizeof (name), "%s/%s/autoexec.cfg", fs_basedir->string, BASE_MODDIRNAME);
1389
1390 if (Sys_FindFirst (name, 0, (SFF_SUBDIR|SFF_HIDDEN|SFF_SYSTEM)))
1391 Cbuf_AddText ("exec autoexec.cfg\n");
1392
1393 Sys_FindClose ();
1394 }
1395
1396 /*
1397 =============================================================================
1398
1399 FILE SEARCHING
1400
1401 =============================================================================
1402 */
1403
1404 /*
1405 ================
1406 FS_FindFiles
1407 ================
1408 */
FS_FindFiles(char * path,char * filter,char * extension,char ** fileList,int maxFiles,qBool addGameDir,qBool recurse)1409 int FS_FindFiles (char *path, char *filter, char *extension, char **fileList, int maxFiles, qBool addGameDir, qBool recurse)
1410 {
1411 fsPath_t *search;
1412 mPackFile_t *packFile;
1413 mPack_t *pack;
1414 int fileCount;
1415 char *name;
1416 char dir[MAX_OSPATH];
1417 char ext[MAX_QEXT];
1418 char *dirFiles[FS_MAX_FINDFILES];
1419 int dirCount, i, j, k;
1420
1421 // Sanity check
1422 if (maxFiles > FS_MAX_FINDFILES) {
1423 Com_Printf (PRNT_ERROR, "FS_FindFiles: maxFiles(%i) > %i, forcing %i...\n", maxFiles, FS_MAX_FINDFILES, FS_MAX_FINDFILES);
1424 maxFiles = FS_MAX_FINDFILES;
1425 }
1426
1427 // Search through the path, one element at a time
1428 fileCount = 0;
1429 for (k=0 ; k<fs_numInvSearchPaths ; k++) {
1430 search = fs_invSearchPaths[k];
1431
1432 if (search->package) {
1433 // Pack file
1434 pack = search->package;
1435 for (i=0, packFile=pack->files ; i<pack->numFiles ; i++, packFile++) {
1436 // Match path
1437 if (!recurse) {
1438 Com_FilePath (packFile->fileName, dir, sizeof (dir));
1439 if (Q_stricmp (path, dir))
1440 continue;
1441 }
1442 // Check path
1443 else if (!strstr (packFile->fileName, path))
1444 continue;
1445
1446 // Match extension
1447 if (extension) {
1448 Com_FileExtension (packFile->fileName, ext, sizeof (ext));
1449
1450 // Filter or compare
1451 if (strchr (extension, '*')) {
1452 if (!Q_WildcardMatch (extension, ext, 1))
1453 continue;
1454 }
1455 else {
1456 if (Q_stricmp (extension, ext))
1457 continue;
1458 }
1459 }
1460
1461 // Match filter
1462 if (filter) {
1463 if (!Q_WildcardMatch (filter, packFile->fileName, 1))
1464 continue;
1465 }
1466
1467 // Found something
1468 name = packFile->fileName;
1469 if (fileCount < maxFiles) {
1470 // Ignore duplicates
1471 for (j=0 ; j<fileCount ; j++) {
1472 if (!Q_stricmp (fileList[j], name))
1473 break;
1474 }
1475
1476 if (j == fileCount) {
1477 if (addGameDir)
1478 fileList[fileCount++] = Mem_PoolStrDup (Q_VarArgs ("%s/%s", search->gamePath, name), com_fileSysPool, 0);
1479 else
1480 fileList[fileCount++] = Mem_PoolStrDup (name, com_fileSysPool, 0);
1481 }
1482 }
1483 }
1484 }
1485 else {
1486 // Directory tree
1487 Q_snprintfz (dir, sizeof (dir), "%s/%s", search->pathName, path);
1488
1489 if (extension) {
1490 Q_snprintfz (ext, sizeof (ext), "*.%s", extension);
1491 dirCount = Sys_FindFiles (dir, ext, dirFiles, FS_MAX_FINDFILES, 0, recurse, qTrue, qFalse);
1492 }
1493 else {
1494 dirCount = Sys_FindFiles (dir, "*", dirFiles, FS_MAX_FINDFILES, 0, recurse, qTrue, qTrue);
1495 }
1496
1497 for (i=0 ; i<dirCount ; i++) {
1498 // Match filter
1499 if (filter) {
1500 if (!Q_WildcardMatch (filter, dirFiles[i]+strlen(search->pathName)+1, 1)) {
1501 Mem_Free (dirFiles[i]);
1502 continue;
1503 }
1504 }
1505
1506 // Found something
1507 name = dirFiles[i] + strlen (search->pathName) + 1;
1508
1509 if (fileCount < maxFiles) {
1510 // Ignore duplicates
1511 for (j=0 ; j<fileCount ; j++) {
1512 if (!Q_stricmp (fileList[j], name))
1513 break;
1514 }
1515
1516 if (j == fileCount) {
1517 if (addGameDir)
1518 fileList[fileCount++] = Mem_PoolStrDup (Q_VarArgs ("%s/%s", search->gamePath, name), com_fileSysPool, 0);
1519 else
1520 fileList[fileCount++] = Mem_PoolStrDup (name, com_fileSysPool, 0);
1521 }
1522 }
1523
1524 Mem_Free (dirFiles[i]);
1525 }
1526 }
1527 }
1528
1529 return fileCount;
1530 }
1531
1532
1533 /*
1534 =============
1535 _FS_FreeFileList
1536 =============
1537 */
_FS_FreeFileList(char ** list,int num,const char * fileName,const int fileLine)1538 void _FS_FreeFileList (char **list, int num, const char *fileName, const int fileLine)
1539 {
1540 int i;
1541
1542 for (i=0 ; i<num ; i++) {
1543 if (!list[i])
1544 continue;
1545
1546 _Mem_Free (list[i], fileName, fileLine);
1547 list[i] = NULL;
1548 }
1549 }
1550
1551
1552 /*
1553 ================
1554 FS_NextPath
1555
1556 Allows enumerating all of the directories in the search path
1557 ================
1558 */
FS_NextPath(char * prevPath)1559 char *FS_NextPath (char *prevPath)
1560 {
1561 fsPath_t *s;
1562 char *prev;
1563
1564 if (!prevPath)
1565 return fs_gameDir;
1566
1567 prev = fs_gameDir;
1568 for (s=fs_searchPaths ; s ; s=s->next) {
1569 if (s->package)
1570 continue;
1571 if (prevPath == prev)
1572 return s->pathName;
1573 prev = s->pathName;
1574 }
1575
1576 return NULL;
1577 }
1578
1579 /*
1580 =============================================================================
1581
1582 CONSOLE FUNCTIONS
1583
1584 =============================================================================
1585 */
1586
1587 /*
1588 ================
1589 FS_Link_f
1590
1591 Creates a fsLink_t
1592 ================
1593 */
FS_Link_f(void)1594 static void FS_Link_f (void)
1595 {
1596 fsLink_t *l, **prev;
1597
1598 if (Cmd_Argc () != 3) {
1599 Com_Printf (0, "USAGE: link <from> <to>\n");
1600 return;
1601 }
1602
1603 // See if the link already exists
1604 prev = &fs_fileLinks;
1605 for (l=fs_fileLinks ; l ; l=l->next) {
1606 if (!strcmp (l->from, Cmd_Argv (1))) {
1607 Mem_Free (l->to);
1608 if (!strlen (Cmd_Argv (2))) {
1609 // Delete it
1610 *prev = l->next;
1611 Mem_Free (l->from);
1612 Mem_Free (l);
1613 return;
1614 }
1615 l->to = Mem_PoolStrDup (Cmd_Argv (2), com_fileSysPool, 0);
1616 return;
1617 }
1618 prev = &l->next;
1619 }
1620
1621 // Create a new link
1622 l = Mem_PoolAlloc (sizeof (*l), com_fileSysPool, 0);
1623 l->from = Mem_PoolStrDup (Cmd_Argv (1), com_fileSysPool, 0);
1624 l->fromLength = strlen (l->from);
1625 l->to = Mem_PoolStrDup (Cmd_Argv (2), com_fileSysPool, 0);
1626 l->next = fs_fileLinks;
1627 fs_fileLinks = l;
1628 }
1629
1630
1631 /*
1632 ============
1633 FS_ListHandles_f
1634 ============
1635 */
FS_ListHandles_f(void)1636 static void FS_ListHandles_f (void)
1637 {
1638 fsHandleIndex_t *index;
1639 int i;
1640
1641 Com_Printf (0, " # mode name\n");
1642 Com_Printf (0, "--- ---- ----------------\n");
1643 for (i=0, index=&fs_fileIndices[0] ; i<FS_MAX_FILEINDICES ; index++, i++) {
1644 if (!index->inUse)
1645 continue;
1646
1647 Com_Printf (0, "%3i ", i+1);
1648 switch (index->openMode) {
1649 case FS_MODE_READ_BINARY: Com_Printf (0, "RB "); break;
1650 case FS_MODE_WRITE_BINARY: Com_Printf (0, "WB "); break;
1651 case FS_MODE_APPEND_BINARY: Com_Printf (0, "AB "); break;
1652 case FS_MODE_WRITE_TEXT: Com_Printf (0, "WT "); break;
1653 case FS_MODE_APPEND_TEXT: Com_Printf (0, "AT "); break;
1654 }
1655 Com_Printf (0, "%s\n", index->name);
1656 }
1657 }
1658
1659
1660 /*
1661 ============
1662 FS_Path_f
1663 ============
1664 */
FS_Path_f(void)1665 static void FS_Path_f (void)
1666 {
1667 fsPath_t *s;
1668 fsLink_t *l;
1669
1670 Com_Printf (0, "Current search path:\n");
1671 for (s=fs_searchPaths ; s ; s=s->next) {
1672 if (s == fs_baseSearchPath)
1673 Com_Printf (0, "----------\n");
1674
1675 if (s->package)
1676 Com_Printf (0, "%s (%i files)\n", s->package->name, s->package->numFiles);
1677 else
1678 Com_Printf (0, "%s\n", s->pathName);
1679 }
1680
1681 Com_Printf (0, "\nLinks:\n");
1682 for (l=fs_fileLinks ; l ; l=l->next)
1683 Com_Printf (0, "%s : %s\n", l->from, l->to);
1684 }
1685
1686 /*
1687 =============================================================================
1688
1689 INIT / SHUTDOWN
1690
1691 =============================================================================
1692 */
1693
1694 /*
1695 ================
1696 FS_Init
1697 ================
1698 */
FS_Init(void)1699 void FS_Init (void)
1700 {
1701 uint32 initTime;
1702 fsPath_t *next;
1703 int i;
1704
1705 initTime = Sys_UMilliseconds ();
1706 Com_Printf (0, "\n------- Filesystem Initialization ------\n");
1707
1708 // Register commands/cvars
1709 Cmd_AddCommand ("link", FS_Link_f, "");
1710 Cmd_AddCommand ("listHandles", FS_ListHandles_f, "Lists active files");
1711 Cmd_AddCommand ("path", FS_Path_f, "");
1712
1713 fs_basedir = Cvar_Register ("basedir", ".", CVAR_READONLY);
1714 fs_cddir = Cvar_Register ("cddir", "", CVAR_READONLY);
1715 fs_game = Cvar_Register ("game", "", CVAR_LATCH_SERVER|CVAR_SERVERINFO|CVAR_RESET_GAMEDIR);
1716 fs_gamedircvar = Cvar_Register ("gamedir", "", CVAR_SERVERINFO|CVAR_READONLY);
1717 fs_defaultPaks = Cvar_Register ("fs_defaultPaks", "1", CVAR_ARCHIVE);
1718
1719 // Load pak files
1720 #ifdef __FreeBSD__
1721 FS_AddGameDirectory (Q_VarArgs ("%s/"BASE_MODDIRNAME, DATADIR), BASE_MODDIRNAME);
1722 FS_AddGameDirectory (Q_VarArgs ("%s/"BASE_MODDIRNAME, LIBDIR), BASE_MODDIRNAME);
1723 #endif
1724 if (fs_cddir->string[0])
1725 FS_AddGameDirectory (Q_VarArgs ("%s/"BASE_MODDIRNAME, fs_cddir->string), BASE_MODDIRNAME);
1726
1727 FS_AddGameDirectory (Q_VarArgs ("%s/"BASE_MODDIRNAME, fs_basedir->string), BASE_MODDIRNAME);
1728 #ifdef HOMEDIR
1729 FS_AddHomeAsGameDirectory(BASE_MODDIRNAME);
1730 #endif
1731
1732 // Any set gamedirs will be freed up to here
1733 fs_baseSearchPath = fs_searchPaths;
1734
1735 // Load the game directory
1736 if (fs_game->string[0]) {
1737 FS_SetGamedir (fs_game->string, qTrue);
1738 #ifdef HOMEDIR
1739 /* Create the writable directory if doesn't exist ("~/.egl"). */
1740 FS_CreatePath(fs_gameDir);
1741 Sys_Mkdir(fs_gameDir);
1742 Com_Printf(0, "Using '%s' for writing.\n", fs_gameDir);
1743 #endif
1744 }
1745 else {
1746 // Store a copy of the search paths inverted for FS_FindFiles
1747 for (fs_numInvSearchPaths=0, next=fs_searchPaths ; next ; next=next->next, fs_numInvSearchPaths++);
1748 fs_invSearchPaths = Mem_PoolAlloc (sizeof (fsPath_t) * fs_numInvSearchPaths, com_fileSysPool, 0);
1749 for (i=fs_numInvSearchPaths-1, next=fs_searchPaths ; i>=0 ; next=next->next, i--)
1750 fs_invSearchPaths[i] = next;
1751 }
1752
1753 Com_Printf (0, "----------------------------------------\n");
1754
1755 // Check memory integrity
1756 Mem_CheckPoolIntegrity (com_fileSysPool);
1757
1758 Com_Printf (0, "init time: %ums\n", Sys_UMilliseconds()-initTime);
1759 Com_Printf (0, "----------------------------------------\n");
1760 }
1761