|
|
1.1 root 1:
2: #include "qcommon.h"
3:
4: // define this to dissalow any data but the demo pak file
5: //#define NO_ADDONS
6:
7: // if a packfile directory differs from this, it is assumed to be hacked
8: // Full version
9: #define PAK0_CHECKSUM 0x40e614e0
10: // Demo
11: //#define PAK0_CHECKSUM 0xb2c6d7ea
12: // OEM
13: //#define PAK0_CHECKSUM 0x78e135c
14:
15: /*
16: =============================================================================
17:
18: QUAKE FILESYSTEM
19:
20: =============================================================================
21: */
22:
23:
24: //
25: // in memory
26: //
27:
28: typedef struct
29: {
30: char name[MAX_QPATH];
31: int filepos, filelen;
32: } packfile_t;
33:
34: typedef struct pack_s
35: {
36: char filename[MAX_OSPATH];
37: FILE *handle;
38: int numfiles;
39: packfile_t *files;
40: } pack_t;
41:
42: char fs_gamedir[MAX_OSPATH];
43: cvar_t *fs_basedir;
44: cvar_t *fs_cddir;
45: cvar_t *fs_gamedirvar;
46:
47: typedef struct filelink_s
48: {
49: struct filelink_s *next;
50: char *from;
51: int fromlength;
52: char *to;
53: } filelink_t;
54:
55: filelink_t *fs_links;
56:
57: typedef struct searchpath_s
58: {
59: char filename[MAX_OSPATH];
60: pack_t *pack; // only one of filename / pack will be used
61: struct searchpath_s *next;
62: } searchpath_t;
63:
64: searchpath_t *fs_searchpaths;
65: searchpath_t *fs_base_searchpaths; // without gamedirs
66:
67:
68: /*
69:
70: All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources.
71:
72: The "base directory" is the path to the directory holding the quake.exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is
73: only used during filesystem initialization.
74:
75: The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't.
76:
77: */
78:
79:
80: /*
81: ================
82: FS_filelength
83: ================
84: */
85: int FS_filelength (FILE *f)
86: {
87: int pos;
88: int end;
89:
90: pos = ftell (f);
91: fseek (f, 0, SEEK_END);
92: end = ftell (f);
93: fseek (f, pos, SEEK_SET);
94:
95: return end;
96: }
97:
98:
99: /*
100: ============
101: FS_CreatePath
102:
103: Creates any directories needed to store the given filename
104: ============
105: */
106: void FS_CreatePath (char *path)
107: {
108: char *ofs;
109:
110: for (ofs = path+1 ; *ofs ; ofs++)
111: {
112: if (*ofs == '/')
113: { // create the directory
114: *ofs = 0;
115: Sys_Mkdir (path);
116: *ofs = '/';
117: }
118: }
119: }
120:
121:
122: /*
123: ==============
124: FS_FCloseFile
125:
126: For some reason, other dll's can't just cal fclose()
127: on files returned by FS_FOpenFile...
128: ==============
129: */
130: void FS_FCloseFile (FILE *f)
131: {
132: fclose (f);
133: }
134:
1.1.1.2 root 135:
136: // RAFAEL
137: /*
138: Developer_searchpath
139: */
140: int Developer_searchpath (int who)
141: {
142:
143: int ch;
1.1.1.3 ! root 144: // PMM - warning removal
! 145: // char *start;
1.1.1.2 root 146: searchpath_t *search;
147:
148: if (who == 1) // xatrix
149: ch = 'x';
1.1.1.3 ! root 150: else if (who == 2)
! 151: ch = 'r';
1.1.1.2 root 152:
153: for (search = fs_searchpaths ; search ; search = search->next)
154: {
1.1.1.3 ! root 155: if (strstr (search->filename, "xatrix"))
! 156: return 1;
! 157:
! 158: if (strstr (search->filename, "rogue"))
! 159: return 2;
! 160: /*
1.1.1.2 root 161: start = strchr (search->filename, ch);
162:
163: if (start == NULL)
164: continue;
165:
166: if (strcmp (start ,"xatrix") == 0)
167: return (1);
1.1.1.3 ! root 168: */
1.1.1.2 root 169: }
170: return (0);
171:
172: }
173:
174:
1.1 root 175: /*
176: ===========
177: FS_FOpenFile
178:
179: Finds the file in the search path.
180: returns filesize and an open FILE *
181: Used for streaming data out of either a pak file or
182: a seperate file.
183: ===========
184: */
1.1.1.2 root 185: int file_from_pak = 0;
1.1 root 186: #ifndef NO_ADDONS
187: int FS_FOpenFile (char *filename, FILE **file)
188: {
189: searchpath_t *search;
190: char netpath[MAX_OSPATH];
191: pack_t *pak;
192: int i;
193: filelink_t *link;
194:
1.1.1.2 root 195: file_from_pak = 0;
196:
1.1 root 197: // check for links first
198: for (link = fs_links ; link ; link=link->next)
199: {
200: if (!strncmp (filename, link->from, link->fromlength))
201: {
202: Com_sprintf (netpath, sizeof(netpath), "%s%s",link->to, filename+link->fromlength);
203: *file = fopen (netpath, "rb");
204: if (*file)
205: {
206: Com_DPrintf ("link file: %s\n",netpath);
207: return FS_filelength (*file);
208: }
209: return -1;
210: }
211: }
212:
213: //
214: // search through the path, one element at a time
215: //
216: for (search = fs_searchpaths ; search ; search = search->next)
217: {
218: // is the element a pak file?
219: if (search->pack)
220: {
221: // look through all the pak file elements
222: pak = search->pack;
223: for (i=0 ; i<pak->numfiles ; i++)
224: if (!Q_strcasecmp (pak->files[i].name, filename))
225: { // found it!
1.1.1.2 root 226: file_from_pak = 1;
1.1 root 227: Com_DPrintf ("PackFile: %s : %s\n",pak->filename, filename);
228: // open a new file on the pakfile
229: *file = fopen (pak->filename, "rb");
230: if (!*file)
231: Com_Error (ERR_FATAL, "Couldn't reopen %s", pak->filename);
232: fseek (*file, pak->files[i].filepos, SEEK_SET);
233: return pak->files[i].filelen;
234: }
235: }
236: else
237: {
238: // check a file in the directory tree
239:
240: Com_sprintf (netpath, sizeof(netpath), "%s/%s",search->filename, filename);
241:
242: *file = fopen (netpath, "rb");
243: if (!*file)
244: continue;
245:
246: Com_DPrintf ("FindFile: %s\n",netpath);
247:
248: return FS_filelength (*file);
249: }
250:
251: }
252:
253: Com_DPrintf ("FindFile: can't find %s\n", filename);
254:
255: *file = NULL;
256: return -1;
257: }
258:
259: #else
260:
261: // this is just for demos to prevent add on hacking
262:
263: int FS_FOpenFile (char *filename, FILE **file)
264: {
265: searchpath_t *search;
266: char netpath[MAX_OSPATH];
267: pack_t *pak;
268: int i;
269:
1.1.1.2 root 270: file_from_pak = 0;
271:
1.1 root 272: // get config from directory, everything else from pak
273: if (!strcmp(filename, "config.cfg") || !strncmp(filename, "players/", 8))
274: {
275: Com_sprintf (netpath, sizeof(netpath), "%s/%s",FS_Gamedir(), filename);
276:
277: *file = fopen (netpath, "rb");
278: if (!*file)
279: return -1;
280:
281: Com_DPrintf ("FindFile: %s\n",netpath);
282:
283: return FS_filelength (*file);
284: }
285:
286: for (search = fs_searchpaths ; search ; search = search->next)
287: if (search->pack)
288: break;
289: if (!search)
290: {
291: *file = NULL;
292: return -1;
293: }
294:
295: pak = search->pack;
296: for (i=0 ; i<pak->numfiles ; i++)
297: if (!Q_strcasecmp (pak->files[i].name, filename))
298: { // found it!
1.1.1.2 root 299: file_from_pak = 1;
1.1 root 300: Com_DPrintf ("PackFile: %s : %s\n",pak->filename, filename);
301: // open a new file on the pakfile
302: *file = fopen (pak->filename, "rb");
303: if (!*file)
304: Com_Error (ERR_FATAL, "Couldn't reopen %s", pak->filename);
305: fseek (*file, pak->files[i].filepos, SEEK_SET);
306: return pak->files[i].filelen;
307: }
308:
309: Com_DPrintf ("FindFile: can't find %s\n", filename);
310:
311: *file = NULL;
312: return -1;
313: }
314:
315: #endif
316:
317:
318: /*
319: =================
320: FS_ReadFile
321:
322: Properly handles partial reads
323: =================
324: */
325: void CDAudio_Stop(void);
326: #define MAX_READ 0x10000 // read in blocks of 64k
327: void FS_Read (void *buffer, int len, FILE *f)
328: {
329: int block, remaining;
330: int read;
331: byte *buf;
332: int tries;
333:
334: buf = (byte *)buffer;
335:
336: // read in chunks for progress bar
337: remaining = len;
338: tries = 0;
339: while (remaining)
340: {
341: block = remaining;
342: if (block > MAX_READ)
343: block = MAX_READ;
344: read = fread (buf, 1, block, f);
345: if (read == 0)
346: {
347: // we might have been trying to read from a CD
348: if (!tries)
349: {
350: tries = 1;
351: CDAudio_Stop();
352: }
353: else
354: Com_Error (ERR_FATAL, "FS_Read: 0 bytes read");
355: }
356:
357: if (read == -1)
358: Com_Error (ERR_FATAL, "FS_Read: -1 bytes read");
359:
360: // do some progress bar thing here...
361:
362: remaining -= read;
363: buf += read;
364: }
365: }
366:
367: /*
368: ============
369: FS_LoadFile
370:
371: Filename are reletive to the quake search path
372: a null buffer will just return the file length without loading
373: ============
374: */
375: int FS_LoadFile (char *path, void **buffer)
376: {
377: FILE *h;
378: byte *buf;
379: int len;
380:
381: buf = NULL; // quiet compiler warning
382:
383: // look for it in the filesystem or pack files
384: len = FS_FOpenFile (path, &h);
385: if (!h)
386: {
387: if (buffer)
388: *buffer = NULL;
389: return -1;
390: }
391:
392: if (!buffer)
393: {
394: fclose (h);
395: return len;
396: }
397:
398: buf = Z_Malloc(len);
399: *buffer = buf;
400:
401: FS_Read (buf, len, h);
402:
403: fclose (h);
404:
405: return len;
406: }
407:
408:
409: /*
410: =============
411: FS_FreeFile
412: =============
413: */
414: void FS_FreeFile (void *buffer)
415: {
416: Z_Free (buffer);
417: }
418:
419: /*
420: =================
421: FS_LoadPackFile
422:
423: Takes an explicit (not game tree related) path to a pak file.
424:
425: Loads the header and directory, adding the files at the beginning
426: of the list so they override previous pack files.
427: =================
428: */
429: pack_t *FS_LoadPackFile (char *packfile)
430: {
431: dpackheader_t header;
432: int i;
433: packfile_t *newfiles;
434: int numpackfiles;
435: pack_t *pack;
436: FILE *packhandle;
437: dpackfile_t info[MAX_FILES_IN_PACK];
438: unsigned checksum;
439:
440: packhandle = fopen(packfile, "rb");
441: if (!packhandle)
442: return NULL;
443:
444: fread (&header, 1, sizeof(header), packhandle);
445: if (LittleLong(header.ident) != IDPAKHEADER)
446: Com_Error (ERR_FATAL, "%s is not a packfile", packfile);
447: header.dirofs = LittleLong (header.dirofs);
448: header.dirlen = LittleLong (header.dirlen);
449:
450: numpackfiles = header.dirlen / sizeof(dpackfile_t);
451:
452: if (numpackfiles > MAX_FILES_IN_PACK)
453: Com_Error (ERR_FATAL, "%s has %i files", packfile, numpackfiles);
454:
455: newfiles = Z_Malloc (numpackfiles * sizeof(packfile_t));
456:
457: fseek (packhandle, header.dirofs, SEEK_SET);
458: fread (info, 1, header.dirlen, packhandle);
459:
460: // crc the directory to check for modifications
461: checksum = Com_BlockChecksum ((void *)info, header.dirlen);
462:
463: #ifdef NO_ADDONS
464: if (checksum != PAK0_CHECKSUM)
465: return NULL;
466: #endif
467: // parse the directory
468: for (i=0 ; i<numpackfiles ; i++)
469: {
470: strcpy (newfiles[i].name, info[i].name);
471: newfiles[i].filepos = LittleLong(info[i].filepos);
472: newfiles[i].filelen = LittleLong(info[i].filelen);
473: }
474:
475: pack = Z_Malloc (sizeof (pack_t));
476: strcpy (pack->filename, packfile);
477: pack->handle = packhandle;
478: pack->numfiles = numpackfiles;
479: pack->files = newfiles;
480:
481: Com_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
482: return pack;
483: }
484:
485:
486: /*
487: ================
488: FS_AddGameDirectory
489:
490: Sets fs_gamedir, adds the directory to the head of the path,
491: then loads and adds pak1.pak pak2.pak ...
492: ================
493: */
494: void FS_AddGameDirectory (char *dir)
495: {
496: int i;
497: searchpath_t *search;
498: pack_t *pak;
499: char pakfile[MAX_OSPATH];
500:
501: strcpy (fs_gamedir, dir);
502:
503: //
504: // add the directory to the search path
505: //
506: search = Z_Malloc (sizeof(searchpath_t));
507: strcpy (search->filename, dir);
508: search->next = fs_searchpaths;
509: fs_searchpaths = search;
510:
511: //
512: // add any pak files in the format pak0.pak pak1.pak, ...
513: //
514: for (i=0; i<10; i++)
515: {
516: Com_sprintf (pakfile, sizeof(pakfile), "%s/pak%i.pak", dir, i);
517: pak = FS_LoadPackFile (pakfile);
518: if (!pak)
519: continue;
520: search = Z_Malloc (sizeof(searchpath_t));
521: search->pack = pak;
522: search->next = fs_searchpaths;
523: fs_searchpaths = search;
524: }
525:
526:
527: }
528:
529: /*
530: ============
531: FS_Gamedir
532:
533: Called to find where to write a file (demos, savegames, etc)
534: ============
535: */
536: char *FS_Gamedir (void)
537: {
538: return fs_gamedir;
539: }
540:
541: /*
542: =============
543: FS_ExecAutoexec
544: =============
545: */
546: void FS_ExecAutoexec (void)
547: {
548: char *dir;
549: char name [MAX_QPATH];
550:
551: dir = Cvar_VariableString("gamedir");
552: if (*dir)
553: Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, dir);
554: else
555: Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, BASEDIRNAME);
556: if (Sys_FindFirst(name, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM))
557: Cbuf_AddText ("exec autoexec.cfg\n");
558: Sys_FindClose();
559: }
560:
561:
562: /*
563: ================
564: FS_SetGamedir
565:
566: Sets the gamedir and path to a different directory.
567: ================
568: */
569: void FS_SetGamedir (char *dir)
570: {
571: searchpath_t *next;
572:
573: if (strstr(dir, "..") || strstr(dir, "/")
574: || strstr(dir, "\\") || strstr(dir, ":") )
575: {
576: Com_Printf ("Gamedir should be a single filename, not a path\n");
577: return;
578: }
579:
580: //
581: // free up any current game dir info
582: //
583: while (fs_searchpaths != fs_base_searchpaths)
584: {
585: if (fs_searchpaths->pack)
586: {
587: fclose (fs_searchpaths->pack->handle);
588: Z_Free (fs_searchpaths->pack->files);
589: Z_Free (fs_searchpaths->pack);
590: }
591: next = fs_searchpaths->next;
592: Z_Free (fs_searchpaths);
593: fs_searchpaths = next;
594: }
595:
596: //
597: // flush all data, so it will be forced to reload
598: //
599: if (dedicated && !dedicated->value)
600: Cbuf_AddText ("vid_restart\nsnd_restart\n");
601:
602: Com_sprintf (fs_gamedir, sizeof(fs_gamedir), "%s/%s", fs_basedir->string, dir);
603:
604: if (!strcmp(dir,BASEDIRNAME) || (*dir == 0))
605: {
606: Cvar_FullSet ("gamedir", "", CVAR_SERVERINFO|CVAR_NOSET);
607: Cvar_FullSet ("game", "", CVAR_LATCH|CVAR_SERVERINFO);
608: }
609: else
610: {
611: Cvar_FullSet ("gamedir", dir, CVAR_SERVERINFO|CVAR_NOSET);
612: if (fs_cddir->string[0])
613: FS_AddGameDirectory (va("%s/%s", fs_cddir->string, dir) );
614: FS_AddGameDirectory (va("%s/%s", fs_basedir->string, dir) );
615: }
616: }
617:
618:
619: /*
620: ================
621: FS_Link_f
622:
623: Creates a filelink_t
624: ================
625: */
626: void FS_Link_f (void)
627: {
628: filelink_t *l, **prev;
629:
630: if (Cmd_Argc() != 3)
631: {
632: Com_Printf ("USAGE: link <from> <to>\n");
633: return;
634: }
635:
636: // see if the link already exists
637: prev = &fs_links;
638: for (l=fs_links ; l ; l=l->next)
639: {
640: if (!strcmp (l->from, Cmd_Argv(1)))
641: {
642: Z_Free (l->to);
643: if (!strlen(Cmd_Argv(2)))
644: { // delete it
645: *prev = l->next;
646: Z_Free (l->from);
647: Z_Free (l);
648: return;
649: }
650: l->to = CopyString (Cmd_Argv(2));
651: return;
652: }
653: prev = &l->next;
654: }
655:
656: // create a new link
657: l = Z_Malloc(sizeof(*l));
658: l->next = fs_links;
659: fs_links = l;
660: l->from = CopyString(Cmd_Argv(1));
661: l->fromlength = strlen(l->from);
662: l->to = CopyString(Cmd_Argv(2));
663: }
664:
665: /*
666: ** FS_ListFiles
667: */
668: char **FS_ListFiles( char *findname, int *numfiles, unsigned musthave, unsigned canthave )
669: {
670: char *s;
671: int nfiles = 0;
672: char **list = 0;
673:
674: s = Sys_FindFirst( findname, musthave, canthave );
675: while ( s )
676: {
677: if ( s[strlen(s)-1] != '.' )
678: nfiles++;
679: s = Sys_FindNext( musthave, canthave );
680: }
681: Sys_FindClose ();
682:
683: if ( !nfiles )
684: return NULL;
685:
686: nfiles++; // add space for a guard
687: *numfiles = nfiles;
688:
689: list = malloc( sizeof( char * ) * nfiles );
690: memset( list, 0, sizeof( char * ) * nfiles );
691:
692: s = Sys_FindFirst( findname, musthave, canthave );
693: nfiles = 0;
694: while ( s )
695: {
696: if ( s[strlen(s)-1] != '.' )
697: {
698: list[nfiles] = strdup( s );
699: #ifdef _WIN32
700: strlwr( list[nfiles] );
701: #endif
702: nfiles++;
703: }
704: s = Sys_FindNext( musthave, canthave );
705: }
706: Sys_FindClose ();
707:
708: return list;
709: }
710:
711: /*
712: ** FS_Dir_f
713: */
714: void FS_Dir_f( void )
715: {
716: char *path = NULL;
717: char findname[1024];
718: char wildcard[1024] = "*.*";
719: char **dirnames;
720: int ndirs;
721:
722: if ( Cmd_Argc() != 1 )
723: {
724: strcpy( wildcard, Cmd_Argv( 1 ) );
725: }
726:
727: while ( ( path = FS_NextPath( path ) ) != NULL )
728: {
729: char *tmp = findname;
730:
731: Com_sprintf( findname, sizeof(findname), "%s/%s", path, wildcard );
732:
733: while ( *tmp != 0 )
734: {
735: if ( *tmp == '\\' )
736: *tmp = '/';
737: tmp++;
738: }
739: Com_Printf( "Directory of %s\n", findname );
740: Com_Printf( "----\n" );
741:
742: if ( ( dirnames = FS_ListFiles( findname, &ndirs, 0, 0 ) ) != 0 )
743: {
744: int i;
745:
746: for ( i = 0; i < ndirs-1; i++ )
747: {
748: if ( strrchr( dirnames[i], '/' ) )
749: Com_Printf( "%s\n", strrchr( dirnames[i], '/' ) + 1 );
750: else
751: Com_Printf( "%s\n", dirnames[i] );
752:
753: free( dirnames[i] );
754: }
755: free( dirnames );
756: }
757: Com_Printf( "\n" );
758: };
759: }
760:
761: /*
762: ============
763: FS_Path_f
764:
765: ============
766: */
767: void FS_Path_f (void)
768: {
769: searchpath_t *s;
770: filelink_t *l;
771:
772: Com_Printf ("Current search path:\n");
773: for (s=fs_searchpaths ; s ; s=s->next)
774: {
775: if (s == fs_base_searchpaths)
776: Com_Printf ("----------\n");
777: if (s->pack)
778: Com_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
779: else
780: Com_Printf ("%s\n", s->filename);
781: }
782:
783: Com_Printf ("\nLinks:\n");
784: for (l=fs_links ; l ; l=l->next)
785: Com_Printf ("%s : %s\n", l->from, l->to);
786: }
787:
788: /*
789: ================
790: FS_NextPath
791:
792: Allows enumerating all of the directories in the search path
793: ================
794: */
795: char *FS_NextPath (char *prevpath)
796: {
797: searchpath_t *s;
798: char *prev;
799:
800: if (!prevpath)
801: return fs_gamedir;
802:
803: prev = fs_gamedir;
804: for (s=fs_searchpaths ; s ; s=s->next)
805: {
806: if (s->pack)
807: continue;
808: if (prevpath == prev)
809: return s->filename;
810: prev = s->filename;
811: }
812:
813: return NULL;
814: }
815:
816:
817: /*
818: ================
819: FS_InitFilesystem
820: ================
821: */
822: void FS_InitFilesystem (void)
823: {
824: Cmd_AddCommand ("path", FS_Path_f);
825: Cmd_AddCommand ("link", FS_Link_f);
826: Cmd_AddCommand ("dir", FS_Dir_f );
827:
828: //
829: // basedir <path>
830: // allows the game to run from outside the data tree
831: //
832: fs_basedir = Cvar_Get ("basedir", ".", CVAR_NOSET);
833:
834: //
835: // cddir <path>
836: // Logically concatenates the cddir after the basedir for
837: // allows the game to run from outside the data tree
838: //
839: fs_cddir = Cvar_Get ("cddir", "", CVAR_NOSET);
840: if (fs_cddir->string[0])
841: FS_AddGameDirectory (va("%s/"BASEDIRNAME, fs_cddir->string) );
842:
843: //
844: // start up with baseq2 by default
845: //
846: FS_AddGameDirectory (va("%s/"BASEDIRNAME, fs_basedir->string) );
847:
848: // any set gamedirs will be freed up to here
849: fs_base_searchpaths = fs_searchpaths;
850:
851: // check for game override
852: fs_gamedirvar = Cvar_Get ("game", "", CVAR_LATCH|CVAR_SERVERINFO);
853: if (fs_gamedirvar->string[0])
854: FS_SetGamedir (fs_gamedirvar->string);
855: }
856:
857:
858:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.