1 /*
2 This program is free software; you can redistribute it and/or
3 modify it under the terms of the GNU General Public License
4 as published by the Free Software Foundation; either version 2
5 of the License, or (at your option) any later version.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
11 See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16
17
18 */
19
20 #include "qwsvdef.h"
21
22
23 /*
24 All of Quake's data access is through a hierchal file system, but the contents
25 of the file system can be transparently merged from several sources.
26
27 The "base directory" is the path to the directory holding the quake.exe and
28 all game directories. The sys_* files pass this to host_init in
29 quakeparms_t->basedir. This can be overridden with the "-basedir" command
30 line parm to allow code debugging in a different directory. The base
31 directory is only used during filesystem initialization.
32
33 The "game directory" is the first tree on the search path and directory that
34 all generated files (savegames, screenshots, demos, config files) will be
35 saved to. This can be overridden with the "-game" command line parameter.
36 The game directory can never be changed while quake is executing. This is a
37 precaution against having a malicious server instruct clients to write files
38 over areas they shouldn't.
39 */
40
41 typedef enum
42 {
43 FS_LOAD_NONE = 1,
44 FS_LOAD_FILE_PAK = 2,
45 FS_LOAD_FILE_ALL = FS_LOAD_FILE_PAK
46 } FS_Load_File_Types;
47
48 /*
49 =============================================================================
50
51 VARIABLES
52
53 =============================================================================
54 */
55
56 // WARNING: if you add some FS related global variable
57 // then made appropriate change to FS_ShutDown() too, if required.
58
59 static char fs_basedir[MAX_OSPATH]; // c:/quake
60
61 char fs_gamedir[MAX_OSPATH]; // c:/quake/qw
62 static char fs_gamedirfile[MAX_QPATH]; // qw tf ctf and etc. In other words single dir name without path
63
64 static searchpath_t *fs_searchpaths = NULL;
65 static searchpath_t *fs_base_searchpaths = NULL; // without gamedirs
66
67 hashtable_t *filesystemhash = NULL;
68 qbool filesystemchanged = true;
69 int fs_hash_dups = 0;
70 int fs_hash_files = 0;
71
72 cvar_t fs_cache = {"fs_cache", "1"};
73
74 /*
75 =============================================================================
76
77 FORWARD DEFINITION
78
79 =============================================================================
80 */
81
82 static searchpath_t *FS_AddPathHandle(char *probablepath, searchpathfuncs_t *funcs, void *handle, qbool copyprotect, qbool istemporary, FS_Load_File_Types loadstuff);
83
84 /*
85 =============================================================================
86
87 COMMANDS
88
89 =============================================================================
90 */
91
92 /*
93 ============
94 FS_Path_f
95
96 ============
97 */
FS_Path_f(void)98 static void FS_Path_f (void)
99 {
100 searchpath_t *search;
101
102 Con_Printf ("Current search path:\n");
103
104 for (search = fs_searchpaths; search ; search = search->next)
105 {
106 if (search == fs_base_searchpaths)
107 Con_Printf ("----------\n");
108
109 search->funcs->PrintPath(search->handle);
110 }
111 }
112
113 /*
114 =============================================================================
115
116 FUNCTIONS
117
118 =============================================================================
119 */
120
121 /*
122 ================
123 FS_GetCleanPath
124
125 ================
126 */
FS_GetCleanPath(const char * pattern,char * outbuf,int outlen)127 static const char *FS_GetCleanPath(const char *pattern, char *outbuf, int outlen)
128 {
129 char *s;
130
131 if (strchr(pattern, '\\'))
132 {
133 strlcpy(outbuf, pattern, outlen);
134 pattern = outbuf;
135
136 Con_Printf("Warning: \\ characters in filename %s\n", pattern);
137
138 for (s = (char*)pattern; (s = strchr(s, '\\')); s++)
139 *s = '/';
140 }
141
142 if (*pattern == '/' || strstr(pattern, "..") || strstr(pattern, ":"))
143 Con_Printf("Error: absolute path in filename %s\n", pattern);
144 else
145 return pattern;
146
147 return NULL;
148 }
149
150 /*
151 ============
152 FS_FlushFSHash
153
154 Flush FS hash and mark FS as changed,
155 so FS_FLocateFile() will be forced to call FS_RebuildFSHash().
156 ============
157 */
FS_FlushFSHash(void)158 void FS_FlushFSHash(void)
159 {
160 if (filesystemhash)
161 {
162 Hash_Flush(filesystemhash);
163 }
164
165 filesystemchanged = true;
166 }
167
168 /*
169 ============
170 FS_RebuildFSHash
171
172 Rebuild FS hash.
173 ============
174 */
FS_RebuildFSHash(void)175 static void FS_RebuildFSHash(void)
176 {
177 searchpath_t *search;
178 if (!filesystemhash)
179 {
180 filesystemhash = Hash_InitTable(1024);
181 }
182 else
183 {
184 FS_FlushFSHash();
185 }
186
187 fs_hash_dups = 0;
188 fs_hash_files = 0;
189
190 for (search = fs_searchpaths ; search ; search = search->next)
191 {
192 search->funcs->BuildHash(search->handle);
193 }
194
195 filesystemchanged = false;
196
197 Con_DPrintf("FS_RebuildFSHash: %i unique files, %i duplicates\n", fs_hash_files, fs_hash_dups);
198 }
199
200 /*
201 ============
202 FS_FLocateFile
203
204 Finds the file in the search path.
205 Look FSLF_ReturnType_e definition so you know that it returns.
206 ============
207 */
FS_FLocateFile(const char * filename,FSLF_ReturnType_e returntype,flocation_t * loc)208 int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation_t *loc)
209 {
210 int depth = 0;
211 int len;
212 searchpath_t *search;
213 char cleanpath[MAX_OSPATH];
214 void *pf = NULL;
215
216 filename = FS_GetCleanPath(filename, cleanpath, sizeof(cleanpath));
217 if (!filename)
218 goto fail;
219
220 if (fs_cache.value)
221 {
222 if (filesystemchanged)
223 FS_RebuildFSHash();
224 pf = Hash_GetInsensitive(filesystemhash, filename);
225 if (!pf)
226 goto fail;
227 }
228
229 //
230 // search through the path, one element at a time.
231 //
232 for (search = fs_searchpaths ; search ; search = search->next)
233 {
234 if (search->funcs->FindFile(search->handle, loc, filename, pf))
235 {
236 if (loc)
237 {
238 loc->search = search;
239 len = loc->len;
240 }
241 else
242 {
243 len = 0;
244 }
245
246 goto out;
247 }
248
249 depth += (search->funcs == &osfilefuncs || returntype == FSLFRT_DEPTH_ANYPATH);
250 }
251
252 fail:
253 if (loc)
254 loc->search = NULL;
255 depth = 0x7fffffff; // NOTE: weird, we return it on fail in some cases, may cause mistakes by user.
256 len = -1;
257
258 out:
259
260 /* Debug printing removed
261 * if (len>=0)
262 {
263 if (loc)
264 Con_Printf("Found %s:%i\n", loc->rawname, loc->len);
265 else
266 Con_Printf("Found %s\n", filename);
267 }
268 else
269 Con_Printf("Failed\n");
270 */
271
272 if (returntype == FSLFRT_IFFOUND)
273 return len != -1;
274 else if (returntype == FSLFRT_LENGTH)
275 return len;
276 else
277 return depth;
278 }
279
280 // internal struct, no point to expose it outside.
281 typedef struct
282 {
283 searchpathfuncs_t *funcs;
284 searchpath_t *parentpath;
285 char *parentdesc;
286 } wildpaks_t;
287
288 /*
289 ================
290 FS_AddWildDataFiles
291
292 Add pack files masked by wildcards, e.g. '*.pak' .
293 That allow add not only packs named like pak0.pak pak1.pak but with random names too.
294 ================
295 */
FS_AddWildDataFiles(char * descriptor,int size,void * vparam)296 static int FS_AddWildDataFiles (char *descriptor, int size, void *vparam)
297 {
298 wildpaks_t *param = vparam;
299 vfsfile_t *vfs;
300 searchpathfuncs_t *funcs = param->funcs;
301 searchpath_t *search;
302 void *pak;
303 char pakfile[MAX_OSPATH];
304 flocation_t loc = {0};
305
306 snprintf (pakfile, sizeof (pakfile), "%s%s", param->parentdesc, descriptor);
307
308 for (search = fs_searchpaths; search; search = search->next)
309 {
310 if (search->funcs != funcs)
311 continue;
312 if (!strcasecmp((char*)search->handle, pakfile)) //assumption: first member of structure is a char array
313 return true; //already loaded (base paths?)
314 }
315
316 search = param->parentpath;
317
318 if (!search->funcs->FindFile(search->handle, &loc, descriptor, NULL))
319 return true; //not found..
320
321 vfs = search->funcs->OpenVFS(search->handle, &loc, "rb");
322 if (!vfs)
323 return true;
324 pak = funcs->OpenNew (vfs, pakfile);
325 if (!pak)
326 {
327 VFS_CLOSE(vfs);
328 return true;
329 }
330
331 snprintf (pakfile, sizeof (pakfile), "%s%s/", param->parentdesc, descriptor);
332 FS_AddPathHandle(pakfile, funcs, pak, true, false, FS_LOAD_FILE_ALL);
333
334 return true;
335 }
336
337 /*
338 ================
339 FS_AddDataFiles
340
341 Load pack files.
342 ================
343 */
FS_AddDataFiles(char * pathto,searchpath_t * parent,char * extension,searchpathfuncs_t * funcs)344 static void FS_AddDataFiles(char *pathto, searchpath_t *parent, char *extension, searchpathfuncs_t *funcs)
345 {
346 int i;
347 char pakfile[MAX_OSPATH];
348 wildpaks_t wp = {0};
349
350 // first load all the numbered pak files.
351 for (i = 0; ; i++)
352 {
353 void *handle;
354 vfsfile_t *vfs;
355 flocation_t loc = {0};
356
357 snprintf (pakfile, sizeof(pakfile), "pak%i.%s", i, extension);
358 if (!parent->funcs->FindFile(parent->handle, &loc, pakfile, NULL))
359 break; //not found..
360 snprintf (pakfile, sizeof(pakfile), "%spak%i.%s", pathto, i, extension);
361 vfs = parent->funcs->OpenVFS(parent->handle, &loc, "rb");
362 if (!vfs)
363 break;
364 handle = funcs->OpenNew (vfs, pakfile);
365 if (!handle)
366 {
367 VFS_CLOSE(vfs);
368 break;
369 }
370 snprintf (pakfile, sizeof(pakfile), "%spak%i.%s/", pathto, i, extension);
371 FS_AddPathHandle(pakfile, funcs, handle, true, false, FS_LOAD_FILE_ALL);
372 }
373
374 // now load the random ones.
375 snprintf (pakfile, sizeof (pakfile), "*.%s", extension);
376 wp.funcs = funcs;
377 wp.parentdesc = pathto;
378 wp.parentpath = parent;
379 parent->funcs->EnumerateFiles(parent->handle, pakfile, FS_AddWildDataFiles, &wp);
380 }
381
382 /*
383 ================
384 FS_AddPathHandle
385
386 Adds searchpath and load pack files in it if requested.
387 ================
388 */
FS_AddPathHandle(char * probablepath,searchpathfuncs_t * funcs,void * handle,qbool copyprotect,qbool istemporary,FS_Load_File_Types loadstuff)389 static searchpath_t *FS_AddPathHandle(char *probablepath, searchpathfuncs_t *funcs, void *handle, qbool copyprotect, qbool istemporary, FS_Load_File_Types loadstuff)
390 {
391 searchpath_t *search;
392
393 // allocate new search path and init it.
394 search = (searchpath_t*)Q_malloc (sizeof(searchpath_t));
395 search->copyprotected = copyprotect;
396 search->istemporary = istemporary;
397 search->handle = handle;
398 search->funcs = funcs;
399
400 // link seach path in.
401 search->next = fs_searchpaths;
402 fs_searchpaths = search;
403
404 // mark file system is changed.
405 filesystemchanged = true;
406
407 // add any data files too.
408 if (loadstuff & FS_LOAD_FILE_PAK)
409 FS_AddDataFiles(probablepath, search, "pak", &packfilefuncs);//q1/hl/h2/q2
410
411 return search;
412 }
413
414 /*
415 ================
416 FS_AddGameDirectory
417
418 Sets fs_gamedir, adds the directory to the head of the path,
419 then loads and adds pak0.pak pak1.pak ...
420 ================
421 */
FS_AddGameDirectory(char * dir,FS_Load_File_Types loadstuff)422 static void FS_AddGameDirectory (char *dir, FS_Load_File_Types loadstuff)
423 {
424 char *p;
425 searchpath_t *search;
426
427 if ((p = strrchr(dir, '/')))
428 strlcpy (fs_gamedirfile, ++p, sizeof (fs_gamedirfile));
429 else
430 strlcpy (fs_gamedirfile, dir, sizeof (fs_gamedirfile));
431
432 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
433
434 for (search = fs_searchpaths; search; search = search->next)
435 {
436 if (search->funcs != &osfilefuncs)
437 continue; // ignore packs and such.
438
439 if (!strcasecmp(search->handle, fs_gamedir))
440 return; //already loaded (base paths?)
441 }
442
443 // add the directory to the search path
444 FS_AddPathHandle (va("%s/", dir), &osfilefuncs, Q_strdup(dir), false, false, loadstuff);
445 }
446
447 /*
448 ============
449 FS_SetGamedir
450
451 Sets the gamedir and path to a different directory.
452 That is basically handy wrapper around FS_AddGameDirectory() for "gamedir" command.
453 ============
454 */
FS_SetGamedir(char * dir,qbool force)455 void FS_SetGamedir (char *dir, qbool force)
456 {
457 if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\") || strstr(dir, ":"))
458 {
459 Con_Printf ("Gamedir should be a single filename, not a path\n");
460 return;
461 }
462
463 if (!force && !strcmp(fs_gamedirfile, dir))
464 return; // Still the same, unless we forced.
465
466 // FIXME: do we need it? since it will be set in FS_AddGameDirectory().
467 strlcpy (fs_gamedirfile, dir, sizeof(fs_gamedirfile));
468
469 // Free up any current game dir info.
470 FS_FlushFSHash();
471
472 // free up any current game dir info
473 while (fs_searchpaths != fs_base_searchpaths)
474 {
475 searchpath_t *next;
476
477 fs_searchpaths->funcs->ClosePath(fs_searchpaths->handle);
478 next = fs_searchpaths->next;
479 Q_free (fs_searchpaths);
480 fs_searchpaths = next;
481 }
482
483 // mark file system is changed.
484 filesystemchanged = true;
485
486 // Flush all data, so it will be forced to reload.
487 // well, mvdsv does not use cache anyway.
488 // Cache_Flush ();
489
490 // FIXME: do we need it? since it will be set in FS_AddGameDirectory().
491 snprintf (fs_gamedir, sizeof (fs_gamedir), "%s/%s", fs_basedir, dir);
492
493 FS_AddGameDirectory(va("%s/%s", fs_basedir, dir), FS_LOAD_FILE_ALL);
494 }
495
496
497 /*
498 ================
499 FS_InitModule
500
501 Add commands and cvars.
502 ================
503 */
FS_InitModule(void)504 void FS_InitModule(void)
505 {
506 Cmd_AddCommand ("path", FS_Path_f);
507 Cvar_Register(&fs_cache);
508 }
509
510 /*
511 ================
512 FS_Init
513
514 ================
515 */
FS_InitEx(void)516 void FS_InitEx(void)
517 {
518 int i;
519 char *s;
520
521 FS_ShutDown();
522
523 if ((i = COM_CheckParm ("-basedir")) && i < COM_Argc() - 1)
524 {
525 // -basedir <path>
526 // Overrides the system supplied base directory (under id1)
527 strlcpy (fs_basedir, COM_Argv(i + 1), sizeof(fs_basedir));
528 }
529 else
530 {
531 #if 0 // FIXME: made fs_basedir equal to cwd
532 Sys_getcwd(fs_basedir, sizeof(fs_basedir) - 1);
533 #else
534 strlcpy (fs_basedir, ".", sizeof(fs_basedir));
535 #endif
536 }
537
538 // replace backslahes with slashes.
539 for (s = fs_basedir; (s = strchr(s, '\\')); s++)
540 *s = '/';
541
542 // remove terminating slash if any.
543 i = (int)strlen(fs_basedir) - 1;
544 if (i >= 0 && fs_basedir[i] == '/')
545 fs_basedir[i] = 0;
546
547 // start up with id1 by default
548 FS_AddGameDirectory(va("%s/%s", fs_basedir, "id1"), FS_LOAD_FILE_ALL);
549 FS_AddGameDirectory(va("%s/%s", fs_basedir, "qw"), FS_LOAD_FILE_ALL);
550
551 // any set gamedirs will be freed up to here
552 fs_base_searchpaths = fs_searchpaths;
553
554 // the user might want to override default game directory
555 if (!(i = COM_CheckParm ("-game")))
556 i = COM_CheckParm ("+gamedir");
557 if (i && i < COM_Argc() - 1)
558 {
559 FS_SetGamedir (COM_Argv(i + 1), true);
560 // FIXME: move in FS_SetGamedir() instead!!!
561 Info_SetValueForStarKey (svs.info, "*gamedir", COM_Argv(i + 1), MAX_SERVERINFO_STRING);
562 }
563 }
564
565 /*
566 ================
567 FS_Init
568
569 ================
570 */
FS_Init(void)571 void FS_Init( void )
572 {
573 FS_InitModule(); // init commands and variables.
574 FS_InitEx();
575 }
576
577 /*
578 ================
579 FS_ShutDown
580
581 ================
582 */
FS_ShutDown(void)583 void FS_ShutDown( void )
584 {
585 // free data
586 while (fs_searchpaths)
587 {
588 searchpath_t *next = fs_searchpaths->next;
589 Q_free (fs_searchpaths); // FIXME: close handles and such!!!
590 fs_searchpaths = next;
591 }
592
593 // flush all data, so it will be forced to reload
594 // well, mvdsv does not use cache anyway.
595 // Cache_Flush ();
596
597 // reset globals
598
599 fs_base_searchpaths = fs_searchpaths = NULL;
600
601 fs_gamedir[0] = 0;
602 fs_gamedirfile[0] = 0;
603
604 fs_basedir[0] = 0;
605
606 // FIXME:
607 //hashtable_t *filesystemhash = NULL;
608 //qbool filesystemchanged = true;
609 //int fs_hash_dups = 0;
610 //int fs_hash_files = 0;
611 }
612
613 /*
614 ================
615 FS_OpenVFS
616
617 This should be how all files are opened.
618 ================
619 */
FS_OpenVFS(const char * filename,char * mode,relativeto_t relativeto)620 vfsfile_t *FS_OpenVFS(const char *filename, char *mode, relativeto_t relativeto)
621 {
622 flocation_t loc = {0};
623 vfsfile_t *vfs = NULL;
624 char cleanname[MAX_OSPATH];
625 char fullname[MAX_OSPATH];
626
627 //blanket-bans
628 filename = FS_GetCleanPath(filename, cleanname, sizeof(cleanname));
629 if (!filename)
630 return NULL;
631
632 if (strcmp(mode, "rb"))
633 if (strcmp(mode, "wb"))
634 if (strcmp(mode, "ab"))
635 return NULL; //urm, unable to write/append
636
637 /* General opening of files */
638 switch (relativeto)
639 {
640 case FS_NONE_OS: //OS access only, no paks, open file as is
641 snprintf(fullname, sizeof(fullname), "%s", filename);
642 return VFSOS_Open(fullname, mode);
643
644 case FS_GAME_OS: //OS access only, no paks
645 snprintf(fullname, sizeof(fullname), "%s/%s/%s", fs_basedir, fs_gamedirfile, filename);
646 if (strchr(mode, 'w') || strchr(mode, 'a'))
647 FS_CreatePath(fullname); // FIXME: It should be moved to VFSOS_Open() itself?
648 return VFSOS_Open(fullname, mode);
649
650 case FS_GAME:
651 // snprintf(fullname, sizeof(fullname), "%s/%s/%s", fs_basedir, fs_gamedirfile, filename);
652
653 // That an error attempt to write with FS_GAME, since file can be in pack file. redirect.
654 if (strchr(mode, 'w') || strchr(mode, 'a'))
655 return FS_OpenVFS(filename, mode, FS_GAME_OS);
656
657 // Search on path, try to open if found.
658 if (FS_FLocateFile(filename, FSLFRT_IFFOUND, &loc))
659 return loc.search->funcs->OpenVFS(loc.search->handle, &loc, mode);
660
661 return NULL;
662
663 case FS_BASE_OS: //OS access only, no paks
664 snprintf(fullname, sizeof(fullname), "%s/%s", fs_basedir, filename);
665 return VFSOS_Open(fullname, mode);
666
667 case FS_ANY:
668 // That an error attempt to write with FS_ANY, since file can be in pack file.
669 vfs = FS_OpenVFS(filename, mode, FS_GAME);
670 if (vfs)
671 return vfs;
672
673 vfs = FS_OpenVFS(filename, mode, FS_NONE_OS);
674 if (vfs)
675 return vfs;
676
677 return NULL;
678
679 default:
680 Sys_Error("FS_OpenVFS: Bad relative path (%i)", relativeto);
681 break;
682 }
683
684 return NULL;
685 }
686
687 //=======================================================================
688
VFS_CHECKCALL(struct vfsfile_s * vf,void * fld,char * emsg)689 void VFS_CHECKCALL (struct vfsfile_s *vf, void *fld, char *emsg)
690 {
691 if (!fld)
692 Sys_Error("%s", emsg);
693 }
694
VFS_CLOSE(struct vfsfile_s * vf)695 void VFS_CLOSE (struct vfsfile_s *vf)
696 {
697 assert(vf);
698 VFS_CHECKCALL(vf, vf->Close, "VFS_CLOSE");
699 vf->Close(vf);
700 }
701
VFS_TELL(struct vfsfile_s * vf)702 unsigned long VFS_TELL (struct vfsfile_s *vf)
703 {
704 assert(vf);
705 VFS_CHECKCALL(vf, vf->Tell, "VFS_TELL");
706 return vf->Tell(vf);
707 }
708
VFS_GETLEN(struct vfsfile_s * vf)709 unsigned long VFS_GETLEN (struct vfsfile_s *vf)
710 {
711 assert(vf);
712 VFS_CHECKCALL(vf, vf->GetLen, "VFS_GETLEN");
713 return vf->GetLen(vf);
714 }
715
716 /**
717 * VFS_SEEK() reposition a stream
718 * If whence is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is
719 * relative to the start of the file, the current position indicator, or
720 * end-of-file, respectively.
721 * Return Value
722 * Upon successful completion, VFS_SEEK(), returns 0.
723 * Otherwise, -1 is returned
724 */
VFS_SEEK(struct vfsfile_s * vf,unsigned long pos,int whence)725 int VFS_SEEK (struct vfsfile_s *vf, unsigned long pos, int whence)
726 {
727 assert(vf);
728 VFS_CHECKCALL(vf, vf->Seek, "VFS_SEEK");
729 return vf->Seek(vf, pos, whence);
730 }
731
VFS_READ(struct vfsfile_s * vf,void * buffer,int bytestoread,vfserrno_t * err)732 int VFS_READ (struct vfsfile_s *vf, void *buffer, int bytestoread, vfserrno_t *err)
733 {
734 assert(vf);
735 VFS_CHECKCALL(vf, vf->ReadBytes, "VFS_READ");
736 return vf->ReadBytes(vf, buffer, bytestoread, err);
737 }
738
VFS_WRITE(struct vfsfile_s * vf,const void * buffer,int bytestowrite)739 int VFS_WRITE (struct vfsfile_s *vf, const void *buffer, int bytestowrite)
740 {
741 assert(vf);
742 VFS_CHECKCALL(vf, vf->WriteBytes, "VFS_WRITE");
743 return vf->WriteBytes(vf, buffer, bytestowrite);
744 }
745
VFS_FLUSH(struct vfsfile_s * vf)746 void VFS_FLUSH (struct vfsfile_s *vf)
747 {
748 assert(vf);
749 if(vf->Flush)
750 vf->Flush(vf);
751 }
752
753 // return null terminated string
VFS_GETS(struct vfsfile_s * vf,char * buffer,int buflen)754 char *VFS_GETS(struct vfsfile_s *vf, char *buffer, int buflen)
755 {
756 char in;
757 char *out = buffer;
758 int len = buflen-1;
759
760 assert(vf);
761 VFS_CHECKCALL(vf, vf->ReadBytes, "VFS_GETS");
762
763 // if (len == 0)
764 // return NULL;
765
766 // FIXME: I am not sure how to handle this better
767 if (len <= 0)
768 Sys_Error("VFS_GETS: len <= 0");
769
770 while (len > 0)
771 {
772 if (!VFS_READ(vf, &in, 1, NULL))
773 {
774 if (len == buflen-1)
775 return NULL;
776 *out = '\0';
777 return buffer;
778 }
779 if (in == '\n')
780 break;
781 *out++ = in;
782 len--;
783 }
784 *out = '\0';
785
786 return buffer;
787 }
788
VFS_COPYPROTECTED(struct vfsfile_s * vf)789 qbool VFS_COPYPROTECTED(struct vfsfile_s *vf)
790 {
791 assert(vf);
792 return vf->copyprotected;
793 }
794
795 /*
796 =============================================================================
797
798 LEGACY FUNCTIONS
799
800 =============================================================================
801 */
802
803 /*
804 ================
805 FS_FileLength
806
807 ================
808 */
FS_FileLength(FILE * f)809 long FS_FileLength (FILE *f)
810 {
811 long pos, end;
812
813 pos = ftell (f);
814 fseek (f, 0, SEEK_END);
815 end = ftell (f);
816 fseek (f, pos, SEEK_SET);
817
818 return end;
819 }
820
821 /*
822 ============
823 FS_FileBase
824
825 ============
826 */
FS_FileBase(char * in,char * out)827 void FS_FileBase (char *in, char *out)
828 {
829 char *begin, *end;
830 int len;
831
832 if (!(end = strrchr (in, '.')))
833 end = in + strlen (in);
834
835 if (!(begin = strchr (in, '/')))
836 begin = in;
837 else
838 begin++;
839
840 len = end - begin + 1;
841 if (len < 1)
842 strlcpy (out, "?model?", 8);
843 else
844 strlcpy (out, begin, min (len, MAX_OSPATH));
845 }
846
847 /*
848 ============
849 FS_WriteFile
850
851 The filename will be prefixed by the current game directory
852 ============
853 */
FS_WriteFile(char * filename,void * data,int len)854 void FS_WriteFile (char *filename, void *data, int len)
855 {
856 FILE *f;
857 char name[MAX_OSPATH];
858
859 snprintf (name, MAX_OSPATH, "%s/%s", fs_gamedir, filename);
860
861 f = fopen (name, "wb");
862 if (!f)
863 {
864 Sys_mkdir (fs_gamedir);
865 f = fopen (name, "wb");
866 if (!f)
867 Sys_Error ("Error opening %s", filename);
868 }
869
870 Sys_Printf ("FS_WriteFile: %s\n", name);
871 fwrite (data, 1, len, f);
872 fclose (f);
873 }
874
875
876 /*
877 ============
878 FS_CreatePath
879
880 Only used for CopyFile and download
881 ============
882 */
FS_CreatePath(char * path)883 void FS_CreatePath(char *path)
884 {
885 char *s, save;
886
887 if (!*path)
888 return;
889
890 for (s = path + 1; *s; s++)
891 {
892 #ifdef _WIN32
893 if (*s == '/' || *s == '\\')
894 {
895 #else
896 if (*s == '/')
897 {
898 #endif
899 save = *s;
900 *s = 0;
901 Sys_mkdir(path);
902 *s = save;
903 }
904 }
905 }
906
907 /*
908 ============
909 FS_LoadFile
910
911 Filename are relative to the quake directory.
912 Always appends a 0 byte to the loaded data.
913 ============
914 */
915 static byte *FS_LoadFile (char *path, void *allocator, int *file_length)
916 {
917 vfsfile_t *f = NULL;
918 vfserrno_t err;
919 flocation_t loc = {0};
920 byte *buf;
921 int len;
922
923 // Look for it in the filesystem or pack files.
924 FS_FLocateFile(path, FSLFRT_LENGTH, &loc);
925 if (loc.search)
926 {
927 f = loc.search->funcs->OpenVFS(loc.search->handle, &loc, "rb");
928 }
929
930 if (!f)
931 return NULL;
932
933 // FIXME: should we use loc.len instead?
934 len = VFS_GETLEN(f);
935 if (file_length)
936 *file_length = len;
937
938 if (allocator == Hunk_AllocName)
939 {
940 char base[32];
941 // Extract the filename base name for hunk tag.
942 FS_FileBase (path, base);
943 buf = (byte *) Hunk_AllocName (len + 1, base);
944 }
945 else if (allocator == Hunk_TempAlloc)
946 {
947 buf = (byte *) Hunk_TempAlloc (len + 1);
948 }
949 #if 0
950 else if (allocator == Q_malloc)
951 {
952 buf = Q_malloc (len + 1);
953 }
954 #endif
955 else
956 {
957 Sys_Error ("FS_LoadFile: bad usehunk\n");
958 return NULL;
959 }
960
961 if (!buf)
962 {
963 Sys_Error ("FS_LoadFile: not enough space for %s\n", path);
964 return NULL;
965 }
966
967 buf[len] = 0;
968
969 VFS_READ(f, buf, len, &err);
970 VFS_CLOSE(f);
971
972 return buf;
973 }
974
975 byte *FS_LoadHunkFile (char *path, int *len)
976 {
977 return FS_LoadFile (path, Hunk_AllocName, len);
978 }
979
980 byte *FS_LoadTempFile (char *path, int *len)
981 {
982 return FS_LoadFile (path, Hunk_TempAlloc, len);
983 }
984
985 /*
986 ============
987 FS_NextPath
988
989 Iterate along searchpaths (no packs).
990 ============
991 */
992 char *FS_NextPath (char *prevpath)
993 {
994 searchpath_t *s;
995 char *prev;
996
997 if (!prevpath)
998 return fs_gamedir;
999
1000 prev = fs_gamedir;
1001 for (s = fs_searchpaths; s ; s = s->next)
1002 {
1003 if (s->funcs != &osfilefuncs)
1004 continue;
1005
1006 if (prevpath == prev)
1007 return s->handle;
1008 prev = s->handle;
1009 }
1010
1011 return NULL;
1012 }
1013
1014 /*
1015 ===========
1016 FS_UnsafeFilename
1017
1018 Returns true if user-specified path is unsafe
1019 ===========
1020 */
1021 qbool FS_UnsafeFilename(const char* fileName)
1022 {
1023 return !fileName ||
1024 !*fileName || // invalid name.
1025 fileName[1] == ':' || // dos filename absolute path specified - reject.
1026 *fileName == '\\' ||
1027 *fileName == '/' || // absolute path was given - reject.
1028 strstr(fileName, "..");
1029 }
1030
1031