UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
files.cpp
Go to the documentation of this file.
1 
11 /*
12 Copyright (C) 1997-2001 Id Software, Inc.
13 
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License
16 as published by the Free Software Foundation; either version 2
17 of the License, or (at your option) any later version.
18 
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22 
23 See the GNU General Public License for more details.
24 
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 
29 */
30 
31 #include "common.h"
32 #include "qfiles.h"
33 #include "unzip.h"
34 #include "../ports/system.h"
35 #include "../shared/defines.h"
36 #include "../shared/parse.h"
37 #ifndef _MSC_VER
38 #include <unistd.h>
39 #endif
40 
42 static int fs_openedFiles;
45 #define MODS_DIR "mods"
46 
47 void FS_CreateOpenPipeFile (const char* filename, qFILE* f)
48 {
49  if (fs_searchpaths == nullptr) {
50  Sys_Error("Filesystem call made without initialization");
51  }
52 
53  OBJZERO(*f);
54 
55  Q_strncpyz(f->name, filename, sizeof(f->name));
56  Sys_Mkfifo(va("%s/%s", FS_Gamedir(), filename), f);
57 
58  if (f->f != nullptr) {
59  Com_Printf("created pipe %s\n", filename);
61  }
62 }
63 
68 const char* FS_Gamedir (void)
69 {
70  for (const searchpath_t* search = fs_searchpaths; search; search = search->next) {
71  if (search->write)
72  return search->filename;
73  }
74 
75  return nullptr;
76 }
77 
83 void FS_NormPath (char* path)
84 {
85  Sys_NormPath(path);
86 }
87 
92 {
93  if (f->f) {
94  const int pos = ftell(f->f);
95 
96  fseek(f->f, 0, SEEK_END);
97  const int end = ftell(f->f);
98  fseek(f->f, pos, SEEK_SET);
99 
100  return end;
101  } else if (f->z) {
102  unz_file_info info;
103  if (unzGetCurrentFileInfo(f->z, &info, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK)
104  Sys_Error("Couldn't get size of %s", f->name);
105  return info.uncompressed_size;
106  }
107 
108  return -1;
109 }
110 
117 void FS_CreatePath (const char* path)
118 {
119  char pathCopy[MAX_OSPATH];
120 
121  Q_strncpyz(pathCopy, path, sizeof(pathCopy));
122 
123  for (char* ofs = pathCopy + 1; *ofs; ofs++) {
124  /* create the directory */
125  if (*ofs == '/') {
126  *ofs = 0;
127  Sys_Mkdir(pathCopy);
128  *ofs = '/';
129  }
130  }
131 }
132 
138 {
139  if (f->f) {
140  fclose(f->f);
141  fs_openedFiles--;
142  } else if (f->z) {
144  fs_openedFiles--;
145  } else {
146  return;
147  }
148  assert(fs_openedFiles >= 0);
149 
150  f->f = nullptr;
151  f->z = nullptr;
152 }
153 
162 int FS_OpenFile (const char* filename, qFILE* file, filemode_t mode)
163 {
164  char netpath[MAX_OSPATH];
165 
166  file->z = file->f = nullptr;
167 
168  /* open for write or append in gamedir and return */
169  if (mode == FILE_WRITE || mode == FILE_APPEND) {
170  Com_sprintf(netpath, sizeof(netpath), "%s/%s", FS_Gamedir(), filename);
171  FS_CreatePath(netpath);
172 
173  file->f = Sys_Fopen(netpath, (mode == FILE_WRITE ? "wb" : "ab"));
174  if (file->f) {
175  fs_openedFiles++;
176  return 0;
177  }
178 
179  return -1;
180  }
181 
182  Q_strncpyz(file->name, filename, sizeof(file->name));
183 
184  /* check for links first */
185  for (const filelink_t* link = fs_links; link; link = link->next) {
186  if (!strncmp(filename, link->from, link->fromlength)) {
187  Com_sprintf(netpath, sizeof(netpath), "%s%s", link->to, filename + link->fromlength);
188  const int length = FS_OpenFile(netpath, file, mode);
189  Q_strncpyz(file->name, filename, sizeof(file->name));
190  if (length == -1)
191  Com_Printf("linked file could not be opened: %s\n", netpath);
192  return length;
193  }
194  }
195 
196  /* search through the path, one element at a time */
197  for (const searchpath_t* search = fs_searchpaths; search; search = search->next) {
198  /* is the element a pak file? */
199  if (search->pack) {
200  /* look through all the pak file elements */
201  const pack_t* pak = search->pack;
202  for (int i = 0; i < pak->numfiles; i++) {
203  /* found it! */
204  if (!Q_strcasecmp(pak->files[i].name, filename)) {
205  /* open a new file on the pakfile */
206  if (unzLocateFile(pak->handle.z, filename, 2) == UNZ_OK) { /* found it! */
207  if (unzOpenCurrentFile(pak->handle.z) == UNZ_OK) {
208  unz_file_info info;
209  if (unzGetCurrentFileInfo(pak->handle.z, &info, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK)
210  Sys_Error("Couldn't get size of %s in %s", filename, pak->filename);
212  file->z = pak->handle.z;
213  fs_openedFiles++;
214  return info.uncompressed_size;
215  }
216  }
217  return pak->files[i].filelen;
218  }
219  }
220  } else {
221  /* check a file in the directory tree */
222  Com_sprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename);
223 
224  file->f = Sys_Fopen(netpath, "rb");
225  if (!file->f)
226  continue;
227 
228  fs_openedFiles++;
229  return FS_FileLength(file);
230  }
231  }
232 
233  file->f = nullptr;
234  file->z = nullptr;
235  return -1;
236 }
237 
238 #define PK3_SEEK_BUFFER_SIZE 65536
239 
246 int FS_Seek (qFILE* f, long offset, int origin)
247 {
248  if (f->z) {
249  byte buffer[PK3_SEEK_BUFFER_SIZE];
250  int remainder = offset;
251 
252  if (offset < 0 || origin == FS_SEEK_END) {
253  Sys_Error("Negative offsets and FS_SEEK_END not implemented "
254  "for FS_Seek on pk3 file contents\n");
255  }
256 
257  switch (origin) {
258  case FS_SEEK_SET:
259  unzSetCurrentFileInfoPosition(f->z, (unsigned long)f->filepos);
260  unzOpenCurrentFile(f->z);
261  /* fall through */
262  case FS_SEEK_CUR:
263  while (remainder > PK3_SEEK_BUFFER_SIZE) {
264  FS_Read(buffer, PK3_SEEK_BUFFER_SIZE, f);
265  remainder -= PK3_SEEK_BUFFER_SIZE;
266  }
267  FS_Read(buffer, remainder, f);
268  return offset;
269 
270  default:
271  Sys_Error("Bad origin in FS_Seek");
272  }
273  } else if (f->f) {
274  int _origin;
275  switch (origin) {
276  case FS_SEEK_CUR:
277  _origin = SEEK_CUR;
278  break;
279  case FS_SEEK_END:
280  _origin = SEEK_END;
281  break;
282  case FS_SEEK_SET:
283  _origin = SEEK_SET;
284  break;
285  default:
286  Sys_Error("Bad origin in FS_Seek");
287  }
288  return fseek(f->f, offset, _origin);
289  } else
290  Sys_Error("FS_Seek: no file opened");
291 }
292 
298 int FS_CheckFile (const char* fmt, ...)
299 {
300  va_list ap;
301  char filename[MAX_QPATH];
302 
303  va_start(ap, fmt);
304  Q_vsnprintf(filename, sizeof(filename), fmt, ap);
305  va_end(ap);
306 
307  ScopedFile file;
308  return FS_OpenFile(filename, &file, FILE_READ);
309 }
310 
311 #define MAX_READ 0x10000 /* read in blocks of 64k */
312 
327 int FS_Read2 (void* buffer, int len, qFILE* f, bool failOnEmptyRead)
328 {
329  byte* buf = (byte*) buffer;
330 
331  if (f->z) {
332  const int read = unzReadCurrentFile(f->z, buf, len);
333  if (read == -1)
334  Sys_Error("FS_Read (zipfile): -1 bytes read");
335 
336  return read;
337  }
338 
339  int remaining = len;
340  int tries = 0;
341  while (remaining) {
342  int block = remaining;
343  if (block > MAX_READ)
344  block = MAX_READ;
345  const int read = fread(buf, 1, block, f->f);
346 
347  /* end of file reached */
348  if (read != block && feof(f->f))
349  return (len - remaining + read);
350 
351  if (read == 0) {
352  /* we might have been trying to read from a CD */
353  if (!tries)
354  tries = 1;
355  else if (failOnEmptyRead)
356  Sys_Error("FS_Read: 0 bytes read");
357  else
358  return len - remaining;
359  }
360 
361  if (read == -1)
362  Sys_Error("FS_Read: -1 bytes read");
363 
364  /* do some progress bar thing here... */
365  remaining -= read;
366  buf += read;
367  }
368  return len;
369 }
370 
371 int FS_Read (void* buffer, int len, qFILE* f)
372 {
373  return FS_Read2(buffer, len, f, true);
374 }
375 
384 int FS_LoadFile (const char* path, byte** buffer)
385 {
386  ScopedFile h;
387 
388  /* look for it in the filesystem or pack files */
389  const int len = FS_OpenFile(path, &h, FILE_READ);
390  if (!h) {
391  if (buffer)
392  *buffer = nullptr;
393  return -1;
394  }
395 
396  if (!buffer) {
397  return len;
398  }
399 
400  byte* const buf = Mem_PoolAllocTypeN(byte, len + 1, com_fileSysPool);
401  if (!buf)
402  return -1;
403  *buffer = buf;
404 
405  FS_Read(buf, len, &h);
406  buf[len] = 0;
407 
408  return len;
409 }
410 
411 void FS_FreeFile (void* buffer)
412 {
413  _Mem_Free(buffer, "FS_FreeFile", 0);
414 }
415 
422 static pack_t* FS_LoadPackFile (const char* packfile)
423 {
424  const char* extension = Com_GetExtension(packfile);
425 
426  if (Q_streq(extension, "pk3") || Q_streq(extension, "zip")) {
427  int i;
428  unz_file_info file_info;
430  unzFile uf = unzOpen(packfile);
431  unsigned int err = unzGetGlobalInfo(uf, &gi);
432  char filenameInZip[MAX_QPATH];
433 
434  if (err != UNZ_OK) {
435  Com_Printf("Could not load '%s'\n", packfile);
436  return nullptr;
437  }
438 
439  unzGoToFirstFile(uf);
440  for (i = 0; i < gi.number_entry; i++) {
441  err = unzGetCurrentFileInfo(uf, &file_info, filenameInZip, sizeof(filenameInZip), nullptr, 0, nullptr, 0);
442  if (err != UNZ_OK) {
443  break;
444  }
445  unzGoToNextFile(uf);
446  }
447 
449  Q_strncpyz(pack->filename, packfile, sizeof(pack->filename));
450  pack->handle.z = uf;
451  pack->handle.f = nullptr;
452  pack->numfiles = gi.number_entry;
453  unzGoToFirstFile(uf);
454 
455  /* Allocate space for array of packfile structures (filename, offset, length) */
457 
458  for (i = 0; i < gi.number_entry; i++) {
459  err = unzGetCurrentFileInfo(uf, &file_info, filenameInZip, sizeof(filenameInZip), nullptr, 0, nullptr, 0);
460  if (err != UNZ_OK)
461  break;
462  Q_strlwr(filenameInZip);
463 
464  unzGetCurrentFileInfoPosition(uf, &newfiles[i].filepos);
465  Q_strncpyz(newfiles[i].name, filenameInZip, sizeof(newfiles[i].name));
466  newfiles[i].filelen = file_info.compressed_size;
467  unzGoToNextFile(uf);
468  }
469  pack->files = newfiles;
470 
471  /* Sort our list alphabetically - also rearrange the unsigned long values */
472  qsort((void*)pack->files, i, sizeof(*newfiles), Q_StringSort);
473 
474  Com_Printf("Added packfile %s (%li files)\n", packfile, gi.number_entry);
475  return pack;
476  } else {
477  /* Unrecognized file type! */
478  Com_Printf("Pack file type %s unrecognized\n", extension);
479  return nullptr;
480  }
481 }
482 
483 #define MAX_PACKFILES 1024
484 
485 static char const* const pakFileExt[] = {
486  "pk3", "zip", nullptr
487 };
488 
495 void FS_AddGameDirectory (const char* dir, bool write)
496 {
497  int ndirs = 0;
498  char pakfile_list[MAX_PACKFILES][MAX_OSPATH];
499  int pakfile_count = 0;
500  char pattern[MAX_OSPATH];
501 
502  for (searchpath_t* search = fs_searchpaths; search; search = search->next) {
503  if (Q_streq(search->filename, dir))
504  return;
505  if (write && search->write) {
506  Com_Printf("change writing directory to %s\n", dir);
507  search->write = false;
508  }
509  }
510 
511  Com_Printf("Adding game dir: %s\n", dir);
512 
513  for (char const* const* extList = pakFileExt; *extList; ++extList) {
514  Com_sprintf(pattern, sizeof(pattern), "%s/*.%s", dir, *extList);
515  char** dirnames = FS_ListFiles(pattern, &ndirs, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
516  if (dirnames != nullptr) {
517  for (int i = 0; i < ndirs - 1; i++) {
518  if (strrchr(dirnames[i], '/')) {
519  Q_strncpyz(pakfile_list[pakfile_count], dirnames[i], sizeof(pakfile_list[pakfile_count]));
520  pakfile_count++;
521  if (pakfile_count >= MAX_PACKFILES) {
522  Com_Printf("Warning: Max allowed pakfiles reached (%i) - skipping the rest\n", MAX_PACKFILES);
523  break;
524  }
525  }
526  Mem_Free(dirnames[i]);
527  }
528  Mem_Free(dirnames);
529  }
530  }
531 
532  /* Sort our list alphabetically */
533  qsort((void*)pakfile_list, pakfile_count, MAX_OSPATH, Q_StringSort);
534 
535  for (int i = 0; i < pakfile_count; i++) {
536  pack_t* pak = FS_LoadPackFile(pakfile_list[i]);
537  if (!pak)
538  continue;
539 
541  search->pack = pak;
542  search->next = fs_searchpaths;
543  search->write = false;
544  fs_searchpaths = search;
545  }
546 
547  /* add the directory to the search path */
549  Q_strncpyz(search->filename, dir, sizeof(search->filename));
550  search->next = fs_searchpaths;
551  search->write = write;
552  fs_searchpaths = search;
553 }
554 
562 char** FS_ListFiles (const char* findname, int* numfiles, unsigned musthave, unsigned canthave)
563 {
564  int nfiles = 0;
565 
566  *numfiles = 0;
567 
568  char* s = Sys_FindFirst(findname, musthave, canthave);
569  while (s) {
570  if (s[strlen(s) - 1] != '.')
571  nfiles++;
572  s = Sys_FindNext(musthave, canthave);
573  }
574  Sys_FindClose();
575 
576  if (!nfiles)
577  return nullptr;
578 
579  nfiles++; /* add space for a guard */
580  *numfiles = nfiles;
581 
582  char** const list = Mem_PoolAllocTypeN(char*, nfiles, com_fileSysPool);
583  char tempList[MAX_FILES][MAX_OSPATH];
584  OBJZERO(tempList);
585 
586  s = Sys_FindFirst(findname, musthave, canthave);
587  nfiles = 0;
588  while (s) {
589  if (s[strlen(s) - 1] != '.') {
590  Q_strncpyz(tempList[nfiles], s, sizeof(tempList[nfiles]));
591 #ifdef _WIN32
592  Q_strlwr(tempList[nfiles]);
593 #endif
594  nfiles++;
595  if (nfiles >= MAX_FILES)
596  break;
597  }
598  s = Sys_FindNext(musthave, canthave);
599  }
600  Sys_FindClose();
601 
602  qsort(tempList, nfiles, MAX_OSPATH, Q_StringSort);
603  for (int i = 0; i < nfiles; i++) {
604  list[i] = Mem_PoolStrDup(tempList[i], com_fileSysPool, 0);
605  }
606 
607  return list;
608 }
609 
614 const char* FS_NextPath (const char* prevpath)
615 {
616  if (!prevpath)
617  return FS_Gamedir();
618 
619  char* prev = nullptr;
620  for (searchpath_t* s = fs_searchpaths; s; s = s->next) {
621  if (s->pack)
622  continue;
623  if (prev && Q_streq(prevpath, prev))
624  return s->filename;
625  prev = s->filename;
626  }
627 
628  return nullptr;
629 }
630 
631 static bool FS_GetHomeDirectory (char* gdir, size_t length)
632 {
633  const char* homedir = Sys_GetHomeDirectory();
634 
635  if (homedir) {
636 #ifdef _WIN32
637  Com_sprintf(gdir, length, "%s/" UFO_VERSION, homedir);
638 #elif defined (__APPLE__) || defined (MACOSX)
639  Com_sprintf(gdir, length, "%s/Documents/UFOAI-" UFO_VERSION, homedir);
640 #else
641  Com_sprintf(gdir, length, "%s/.ufoai/" UFO_VERSION, homedir);
642 #endif
643  return true;
644  }
645  Com_Printf("could not find the home directory\n");
646  return false;
647 }
648 
655 void FS_AddHomeAsGameDirectory (const char* dir, bool write)
656 {
657  char gdir[MAX_OSPATH];
658 
659  if (FS_GetHomeDirectory(gdir, sizeof(gdir))) {
660  Q_strcat(gdir, sizeof(gdir), "/%s", dir);
661  FS_CreatePath(va("%s/", gdir));
662  FS_AddGameDirectory(gdir, write);
663  }
664 }
665 
670 {
671  char gdir[MAX_OSPATH];
672  const char* homedir;
673 
674  if (FS_GetHomeDirectory(gdir, sizeof(gdir))) {
675  char const* const append = "/" MODS_DIR;
676  Q_strcat(gdir, sizeof(gdir), append);
677  homedir = gdir;
678  } else {
679  homedir = nullptr;
680  }
681 
682  char const* searchpaths[] = {
683 #ifdef PKGDATADIR
684  PKGDATADIR "/" MODS_DIR,
685 #endif
686  "./" MODS_DIR,
687  homedir,
688  nullptr
689  };
690 
692  int numberMods = 1;
693  /* it is likely that we have duplicate names now, which we will cleanup below */
694  for (const char** path = searchpaths; *path; path++) {
695  const char* pattern = *path;
696  int ndirs = 0;
697  char** dirnames = FS_ListFiles(va("%s/*", pattern), &ndirs, SFF_SUBDIR, SFF_HIDDEN | SFF_SYSTEM);
698  if (dirnames != nullptr) {
699  for (int i = 0; i < ndirs - 1; i++) {
700  LIST_AddString(mods, dirnames[i] + (strlen(pattern) + 1));
701  numberMods++;
702  Mem_Free(dirnames[i]);
703  }
704  Mem_Free(dirnames);
705  }
706  }
707 
708  return numberMods;
709 }
710 
711 #ifdef COMPILE_UFO
712 
713 static void FS_Mod_f (void)
714 {
715  if (Cmd_Argc() == 1) {
716  linkedList_t* list = nullptr;
717  FS_GetModList(&list);
718  LIST_Foreach(list, const char, mod) {
719  Com_Printf("mod: %s\n", mod);
720  }
721  LIST_Delete(&list);
722  } else {
724  }
725 }
729 void FS_ExecAutoexec (void)
730 {
731  /* search through all the paths for an autoexec.cfg file */
732  for (searchpath_t* s = fs_searchpaths; s != nullptr; s = s->next) {
733  char name[MAX_QPATH];
734  snprintf(name, sizeof(name), "%s/autoexec.cfg", s->filename);
735 
736  if (Sys_FindFirst(name, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM)) {
737  Cbuf_AddText("exec autoexec.cfg\n");
738  Sys_FindClose();
739  break;
740  }
741 
742  Sys_FindClose();
743  }
744 
745  Cbuf_Execute(); /* execute it */
746 }
747 
751 static void FS_Link_f (void)
752 {
753  if (Cmd_Argc() != 3) {
754  Com_Printf("Usage: %s <from> <to>\n", Cmd_Argv(0));
755  return;
756  }
757 
758  /* see if the link already exists */
759  filelink_t** prev = &fs_links;
760  for (filelink_t* l = fs_links; l; l = l->next) {
761  if (Q_streq(l->from, Cmd_Argv(1))) {
762  Mem_Free(l->to);
763  if (!strlen(Cmd_Argv(2))) { /* delete it */
764  *prev = l->next;
765  Mem_Free(l->from);
766  Mem_Free(l);
767  return;
768  }
769  l->to = Mem_PoolStrDup(Cmd_Argv(2), com_fileSysPool, 0);
770  return;
771  }
772  prev = &l->next;
773  }
774 
775  /* create a new link */
777  l->next = fs_links;
778  fs_links = l;
780  l->fromlength = strlen(l->from);
782 }
783 
789 static void FS_Dir_f (void)
790 {
791  char const* wildcard = Cmd_Argc() != 1 ? Cmd_Argv(1) : "*.*";
792  const char* path = nullptr;
793  char findname[1024];
794  int ndirs;
795 
796  while ((path = FS_NextPath(path)) != nullptr) {
797  Com_sprintf(findname, sizeof(findname), "%s/%s", path, wildcard);
798  FS_NormPath(findname);
799 
800  Com_Printf("Directory of %s\n", findname);
801  Com_Printf("----\n");
802 
803  char** dirnames = FS_ListFiles(findname, &ndirs, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
804  if (dirnames != nullptr) {
805  for (int i = 0; i < ndirs - 1; i++) {
806  char const* const slash = strrchr(dirnames[i], '/');
807  Com_Printf("%s\n", slash ? slash + 1 : dirnames[i]);
808 
809  Mem_Free(dirnames[i]);
810  }
811  Mem_Free(dirnames);
812  }
813  Com_Printf("\n");
814  }
815 }
816 
817 static void FS_List_f (void)
818 {
819  char const* wildcard = Cmd_Argc() == 2 ? Cmd_Argv(1) : "*.*";
820  const char* filename;
821 
822  Com_Printf("Show files for '%s'\n", wildcard);
823  FS_BuildFileList(wildcard);
824  while ((filename = FS_NextFileFromFileList(wildcard)) != nullptr)
825  Com_Printf("%s\n", filename);
826  FS_NextFileFromFileList(nullptr);
827 }
828 
832 static void FS_Info_f (void)
833 {
834  Com_Printf("Filesystem information\n");
835  Com_Printf("...write dir: '%s'\n", FS_Gamedir());
836 
837  for (searchpath_t* search = fs_searchpaths; search; search = search->next) {
838  if (search->pack == nullptr)
839  Com_Printf("...path: '%s'\n", search->filename);
840  else
841  Com_Printf("...pakfile: '%s' (%i files)\n", search->pack->filename, search->pack->numfiles);
842  }
843 
844  for (filelink_t* l = fs_links; l; l = l->next)
845  Com_Printf("...link: %s : %s\n", l->from, l->to);
846 }
847 
848 static void FS_RestartFilesystem_f (void)
849 {
850  if (Cmd_Argc() == 2)
852  else
853  FS_RestartFilesystem(nullptr);
854 }
855 
861 static const cmdList_t fs_commands[] = {
862  {"fs_restart", FS_RestartFilesystem_f, "Reloads the file subsystem"},
863  {"link", FS_Link_f, "Create file links"},
864  {"dir", FS_Dir_f, "Show the filesystem contents per game dir - also supports wildcarding"},
865  {"ls", FS_List_f, "Show the filesystem contents"},
866  {"fs_info", FS_Info_f, "Show information about the virtual filesystem"},
867  {"fs_mod", FS_Mod_f, "Show or activate mods"},
868 
869  {nullptr, nullptr, nullptr}
870 };
871 
872 static void FS_RemoveCommands (void)
873 {
874  Cmd_TableCheck();
875  Cmd_TableRemoveList(fs_commands);
876 }
877 
878 static void FS_InitCommandsAndCvars (void)
879 {
880  Cmd_TableAddList(fs_commands);
881 }
882 #endif
883 
888 void FS_InitFilesystem (bool writeToHomeDir)
889 {
890  Com_Printf("\n---- filesystem initialization -----\n");
891 
892 #ifdef PKGDATADIR
893  /* add the system search path */
895 #endif
896 
897  FS_AddGameDirectory("./" BASEDIRNAME, !writeToHomeDir);
898  FS_AddHomeAsGameDirectory(BASEDIRNAME, writeToHomeDir);
899 #ifdef COMPILE_UFO
900  const char* fsGameDir = Cvar_GetString("fs_gamedir");
901  if (Q_strvalid(fsGameDir)) {
902  char path[MAX_QPATH];
903  Com_sprintf(path, sizeof(path), "./%s", fsGameDir);
904  if (!FS_FileExists("%s", path)) {
905  Com_sprintf(path, sizeof(path), "./" MODS_DIR "/%s", fsGameDir);
906  }
907  FS_AddGameDirectory(path, !writeToHomeDir);
908  FS_AddHomeAsGameDirectory(fsGameDir, writeToHomeDir);
909  }
910 #endif
911 
912 #ifdef COMPILE_UFO
913  FS_InitCommandsAndCvars();
914  Cbuf_AddText("exec filesystem.cfg\n");
915 #endif
916 
917  Com_Printf("using %s for writing\n", FS_Gamedir());
918 }
919 
926 typedef struct listBlock_s {
927  char path[MAX_QPATH];
929  struct listBlock_s* next;
930 } listBlock_t;
931 
932 static listBlock_t* fs_blocklist = nullptr;
933 
939 static void _AddToListBlock (linkedList_t** fl, const char* name, bool stripPath)
940 {
941  const char* f;
942 
943  /* strip path */
944  if (stripPath)
945  f = Com_SkipPath(name);
946  else
947  f = name;
948 
949  if (LIST_ContainsString(*fl, f))
950  return;
951 
952  /* add the new file */
953  LIST_AddStringSorted(fl, f);
954 }
955 
960 int FS_BuildFileList (const char* fileList)
961 {
962  char files[MAX_QPATH];
963 
964  /* bring it into normal form */
965  Q_strncpyz(files, fileList, sizeof(files));
966  FS_NormPath(files);
967 
968  /* check the blocklist for older searches
969  * and do a new one after deleting them */
970  for (listBlock_t** anchor = &fs_blocklist; *anchor;) {
971  listBlock_t* const block = *anchor;
972  if (Q_streq(block->path, files)) {
973  *anchor = block->next;
974 
975  LIST_Delete(&block->files);
976  Mem_Free(block);
977  } else {
978  anchor = &block->next;
979  }
980  }
981 
982  /* allocate a new block and link it into the list */
984  block->next = fs_blocklist;
985  fs_blocklist = block;
986 
987  /* store the search string */
988  Q_strncpyz(block->path, files, sizeof(block->path));
989 
990  /* search for the files */
991  LIST_Delete(&block->files);
992 
993  /* search through the path, one element at a time */
994  char findname[1024];
995  for (searchpath_t* search = fs_searchpaths; search; search = search->next) {
996  /* is the element a pak file? */
997  if (search->pack) {
998  const char* ext = strrchr(files, '.');
999  const pack_t* pak = search->pack;
1000  size_t l = strlen(files);
1001  if (!ext)
1002  break;
1003  Q_strncpyz(findname, files, sizeof(findname));
1004  FS_NormPath(findname);
1005  l -= (strlen(ext) + 1);
1006  findname[l] = '\0';
1007 
1008  /* look through all the pak file elements */
1009  for (int i = 0; i < pak->numfiles; i++) {
1010  /* found it! */
1011  const char* fileNameEntry = pak->files[i].name;
1012  bool matchAlsoInSubDirs = (findname[0] == '*' || !strncmp(fileNameEntry, findname, l))
1013  && (ext[0] == '*' || strstr(fileNameEntry, ext));
1014  if (matchAlsoInSubDirs) {
1015  bool add = false;
1016  if (strstr(findname, "**"))
1017  add = true;
1018  else {
1019  char pathName[MAX_QPATH];
1020  char pathNameEntry[MAX_QPATH];
1021  Com_FilePath(findname, pathName, sizeof(pathName));
1022  Com_FilePath(fileNameEntry, pathNameEntry, sizeof(pathNameEntry));
1023  if (Q_streq(pathNameEntry, pathName))
1024  add = true;
1025  }
1026 
1027  if (add)
1028  _AddToListBlock(&block->files, pak->files[i].name, true);
1029  }
1030  }
1031  } else if (strstr(files, "**")) {
1032  linkedList_t* list = nullptr;
1033  const char* wildcard = strstr(files, "**");
1034  const size_t l = strlen(files) - strlen(wildcard);
1035 
1036  Q_strncpyz(findname, files, sizeof(findname));
1037  FS_NormPath(findname);
1038  findname[l] = '\0';
1039  if (l > 0 && findname[l - 1] == '/')
1040  findname[l - 1] = '\0';
1041 
1042  Sys_ListFilteredFiles(search->filename, findname, &findname[l + 1], &list);
1043 
1044  LIST_Foreach(list, const char, name) {
1045  _AddToListBlock(&block->files, name, false);
1046  }
1047 
1048  LIST_Delete(&list);
1049  } else {
1050  int nfiles = 0;
1051 
1052  Com_sprintf(findname, sizeof(findname), "%s/%s", search->filename, files);
1053  FS_NormPath(findname);
1054 
1055  char** filenames = FS_ListFiles(findname, &nfiles, 0, SFF_HIDDEN | SFF_SYSTEM);
1056  if (filenames != nullptr) {
1057  for (int i = 0; i < nfiles - 1; i++) {
1058  _AddToListBlock(&block->files, filenames[i], true);
1059  Mem_Free(filenames[i]);
1060  }
1061  Mem_Free(filenames);
1062  }
1063  }
1064  }
1065 
1066  return LIST_Count(block->files);
1067 }
1068 
1079 const char* FS_NextFileFromFileList (const char* files)
1080 {
1081  static linkedList_t* listEntry = nullptr;
1082  static listBlock_t* _block = nullptr;
1083 
1084  /* restart the list? */
1085  if (files == nullptr) {
1086  _block = nullptr;
1087  return nullptr;
1088  }
1089 
1090  listBlock_t* block;
1091  for (block = fs_blocklist; block; block = block->next) {
1092  if (!strncmp(files, block->path, MAX_QPATH))
1093  break;
1094  }
1095 
1096  if (!block) {
1097  FS_BuildFileList(files);
1098  for (block = fs_blocklist; block; block = block->next) {
1099  if (!strncmp(files, block->path, MAX_QPATH))
1100  break;
1101  }
1102  if (!block) {
1103  /* still no filelist */
1104  Com_Printf("FS_NextFileFromFileList: Could not create filelist for %s\n", files);
1105  return nullptr;
1106  }
1107  }
1108 
1109  /* everytime we switch between different blocks we get the
1110  * first file again when we switch back */
1111  if (_block != block) {
1112  _block = block;
1113  listEntry = block->files;
1114  }
1115 
1116  const char* file = nullptr;
1117  if (listEntry) {
1118  file = (const char*)listEntry->data;
1119  listEntry = listEntry->next;
1120  }
1121 
1122  /* finished */
1123  return file;
1124 }
1125 
1135 const char* FS_GetFileData (const char* files)
1136 {
1137  static linkedList_t* fileList = nullptr;
1138  static byte* buffer = nullptr;
1139 
1140  /* free the old file */
1141  if (buffer) {
1142  FS_FreeFile(buffer);
1143  buffer = nullptr;
1144  }
1145 
1146  if (!files) {
1147  fileList = nullptr;
1148  return nullptr;
1149  }
1150 
1151  listBlock_t* block;
1152  for (block = fs_blocklist; block; block = block->next) {
1153  if (Q_streq(files, block->path))
1154  break;
1155  }
1156 
1157  if (!block) {
1158  /* didn't find any valid file list */
1159  fileList = nullptr;
1160  FS_BuildFileList(files);
1161  for (block = fs_blocklist; block; block = block->next) {
1162  if (Q_streq(files, block->path))
1163  break;
1164  }
1165  if (!block) {
1166  /* still no filelist */
1167  Com_Printf("FS_GetFileData: Could not create filelist for %s\n", files);
1168  return nullptr;
1169  }
1170  }
1171 
1172  if (!fileList)
1173  /* start the list */
1174  fileList = block->files;
1175  else
1176  /* search a new file */
1177  fileList = fileList->next;
1178 
1179  if (fileList) {
1180  char filename[MAX_QPATH];
1181 
1182  /* load a new file */
1183  Q_strncpyz(filename, block->path, sizeof(filename));
1184  strcpy(strrchr(filename, '/') + 1, (const char*)fileList->data);
1185 
1186  FS_LoadFile(filename, &buffer);
1187  return (const char*)buffer;
1188  }
1189 
1190  /* finished */
1191  return nullptr;
1192 }
1193 
1194 char* FS_NextScriptHeader (const char* files, const char** name, const char** text)
1195 {
1196  static char lastList[MAX_QPATH];
1197  static listBlock_t* lBlock;
1198  static linkedList_t* lFile;
1199  static byte* lBuffer;
1200  static char headerType[MAX_VAR];
1201  static char headerName[512];
1202 
1203  if (!text) {
1204  *lastList = 0;
1205 
1206  /* free the old file */
1207  if (lBuffer) {
1208  FS_FreeFile(lBuffer);
1209  lBuffer = nullptr;
1210  }
1211 
1212  return nullptr;
1213  }
1214 
1215  if (!Q_streq(files, lastList)) {
1216  /* search for file lists */
1217  Q_strncpyz(lastList, files, sizeof(lastList));
1218 
1219  listBlock_t* block;
1220  for (block = fs_blocklist; block; block = block->next) {
1221  if (Q_streq(files, block->path))
1222  break;
1223  }
1224 
1225  if (!block)
1226  /* didn't find any valid file list */
1227  return nullptr;
1228 
1229  lBlock = block;
1230  lFile = block->files;
1231  }
1232 
1233  while (lBlock) {
1234  if (lBuffer) {
1235  /* continue reading the current file */
1236  if (*text) {
1237  const char* token = Com_Parse(text);
1238  if (*token == '{') {
1239  Com_SkipBlock(text);
1240  continue;
1241  }
1242 
1243  Q_strncpyz(headerType, token, sizeof(headerType));
1244  if (*text) {
1245  token = Com_Parse(text);
1246  Q_strncpyz(headerName, token, sizeof(headerName));
1247  *name = headerName;
1248  return headerType;
1249  }
1250  }
1251 
1252  /* search a new file */
1253  lFile = lFile->next;
1254 
1255  while (!lFile && lBlock) {
1256  /* it was the last file in the block, continue to next block */
1257  for (lBlock = lBlock->next; lBlock; lBlock = lBlock->next) {
1258  if (Q_streq(files, lBlock->path)) {
1259  lFile = lBlock->files;
1260  break;
1261  }
1262  }
1263  }
1264  }
1265 
1266  if (lFile) {
1267  char filename[MAX_QPATH];
1268 
1269  /* free the old file */
1270  if (lBuffer) {
1271  FS_FreeFile(lBuffer);
1272  lBuffer = nullptr;
1273  }
1274 
1275  /* load a new file */
1276  Q_strncpyz(filename, lBlock->path, sizeof(filename));
1277  strcpy(strrchr(filename, '/') + 1, (const char*)lFile->data);
1278 
1279  FS_LoadFile(filename, &lBuffer);
1280  /* skip a file that couldn't get loaded */
1281  if (!lBuffer) {
1282  lFile = lFile->next;
1283  continue;
1284  }
1285  *text = (char*)lBuffer;
1286 
1287  /* test if the contents of this file starts with the string "--!usr/bin/lua", if yes, the
1288  file is a lua script file and should be returned immediately with type set to "lua" */
1289  if (Q_strneq (*text, "--!usr/bin/lua", 14)) {
1290  /* copy filename to header */
1291  Q_strncpyz(headerName, (const char*)lFile->data, sizeof(headerName));
1292  *name = headerName;
1293  static char luaType[] = "lua";
1294  return luaType;
1295  }
1296  } else if (!lBuffer)
1297  break;
1298  }
1299 
1300  /* free the old file */
1301  if (lBuffer) {
1302  FS_FreeFile(lBuffer);
1303  lBuffer = nullptr;
1304  }
1305 
1306  /* finished */
1307  return nullptr;
1308 }
1309 
1310 /* global vars for maplisting */
1313 static bool fs_mapsInstalledInit = false;
1314 
1318 static int FS_MapDefSort (const void* map1, const void* map2)
1319 {
1320  const char* mapStr1 = *(const char* const*)map1;
1321  const char* mapStr2 = *(const char* const*)map2;
1322 
1323  /* skip special map chars for rma and base attack */
1324  if (mapStr1[0] == '+')
1325  mapStr1++;
1326  if (mapStr2[0] == '+')
1327  mapStr2++;
1328 
1329  return Q_StringSort(mapStr1, mapStr2);
1330 }
1331 
1342 static int CheckBSPFile (const char* filename)
1343 {
1344  /* load the file */
1345  char name[MAX_QPATH];
1346  Com_sprintf(name, sizeof(name), "maps/%s.bsp", filename);
1347 
1348  ScopedFile file;
1349  FS_OpenFile(name, &file, FILE_READ);
1350  if (!file)
1351  return 1;
1352 
1353  int header[2];
1354  FS_Read(header, sizeof(header), &file);
1355 
1356  for (int i = 0; i < 2; i++)
1357  header[i] = LittleLong(header[i]);
1358 
1359  if (header[0] != IDBSPHEADER)
1360  return 2;
1361  if (header[1] != BSPVERSION)
1362  return 3;
1363 
1364  /* valid BSP-File */
1365  return 0;
1366 }
1367 
1373 void FS_GetMaps (bool reset)
1374 {
1375  char filename[MAX_QPATH];
1376  const char* baseMapName = nullptr;
1377  int ndirs;
1378 
1379  /* force a reread */
1380  if (!reset && fs_mapsInstalledInit)
1381  return;
1382  else if (fs_mapsInstalledInit) {
1383  for (int i = 0; i <= fs_numInstalledMaps; i++)
1384  Mem_Free(fs_maps[i]);
1385  }
1386 
1387  fs_numInstalledMaps = -1;
1388 
1389  /* search through the path, one element at a time */
1390  char findname[MAX_OSPATH];
1391  for (searchpath_t* search = fs_searchpaths; search; search = search->next) {
1392  /* is the element a pak file? */
1393  if (search->pack) {
1394  /* look through all the pak file elements */
1395  pack_t* pak = search->pack;
1396  for (int i = 0; i < pak->numfiles; i++) {
1397  /* found it! */
1398  baseMapName = strchr(pak->files[i].name, '/');
1399  if (baseMapName) {
1401  baseMapName = strchr(baseMapName + 1, '/');
1402  /* ugly hack - only show the maps in base/maps - not in base/maps/b and so on */
1403  if (baseMapName)
1404  continue;
1405  } else
1406  continue;
1407 
1408  if (strstr(pak->files[i].name, ".bsp") || strstr(pak->files[i].name, ".ump") ) {
1409  if (fs_numInstalledMaps + 1 >= MAX_MAPS) {
1410  Com_Printf("FS_GetMaps: Max maps limit hit\n");
1411  break;
1412  }
1413  fs_maps[fs_numInstalledMaps + 1] = Mem_PoolAllocTypeN(char, MAX_QPATH, com_fileSysPool);
1414  if (fs_maps[fs_numInstalledMaps + 1] == nullptr) {
1415  Com_Printf("Could not allocate memory in FS_GetMaps\n");
1416  continue;
1417  }
1418  Q_strncpyz(findname, pak->files[i].name, sizeof(findname));
1419  FS_NormPath(findname);
1420  baseMapName = Com_SkipPath(findname);
1421  Com_StripExtension(baseMapName, filename, sizeof(filename));
1422  fs_numInstalledMaps++;
1423  if (strstr(findname, ".ump"))
1424  Com_sprintf(fs_maps[fs_numInstalledMaps], MAX_QPATH, "+%s", filename);
1425  else
1426  Q_strncpyz(fs_maps[fs_numInstalledMaps], filename, MAX_QPATH);
1427  }
1428  }
1429  } else {
1430  Com_sprintf(findname, sizeof(findname), "%s/maps/*.bsp", search->filename);
1431  FS_NormPath(findname);
1432 
1433  char** dirnames = FS_ListFiles(findname, &ndirs, 0, SFF_HIDDEN | SFF_SYSTEM);
1434  if (dirnames != nullptr) {
1435  for (int i = 0; i < ndirs - 1; i++) {
1436  baseMapName = Com_SkipPath(dirnames[i]);
1437  Com_StripExtension(baseMapName, filename, sizeof(filename));
1438  const int status = CheckBSPFile(filename);
1439  if (!status) {
1440  if (fs_numInstalledMaps + 1 >= MAX_MAPS) {
1441  Com_Printf("FS_GetMaps: Max maps limit hit\n");
1442  break;
1443  }
1444  fs_maps[fs_numInstalledMaps + 1] = Mem_PoolAllocTypeN(char, MAX_QPATH, com_fileSysPool);
1445  if (fs_maps[fs_numInstalledMaps + 1] == nullptr) {
1446  Com_Printf("Could not allocate memory in FS_GetMaps\n");
1447  Mem_Free(dirnames[i]);
1448  continue;
1449  }
1450  fs_numInstalledMaps++;
1451  Q_strncpyz(fs_maps[fs_numInstalledMaps], filename, MAX_QPATH);
1452  } else
1453  Com_Printf("invalid mapstatus: %i (%s)\n", status, dirnames[i]);
1454  Mem_Free(dirnames[i]);
1455  }
1456  Mem_Free(dirnames);
1457  }
1458  /* +RMA to maplisting */
1459  Com_sprintf(findname, sizeof(findname), "%s/maps/*.ump", search->filename);
1460  FS_NormPath(findname);
1461 
1462  dirnames = FS_ListFiles(findname, &ndirs, 0, SFF_HIDDEN | SFF_SYSTEM);
1463  if (dirnames != nullptr) {
1464  for (int i = 0; i < ndirs - 1; i++) {
1465  baseMapName = Com_SkipPath(dirnames[i]);
1466  Com_StripExtension(baseMapName, filename, sizeof(filename));
1467  if (fs_numInstalledMaps + 1 >= MAX_MAPS) {
1468  Com_Printf("FS_GetMaps: Max maps limit hit\n");
1469  break;
1470  }
1471  fs_maps[fs_numInstalledMaps + 1] = Mem_PoolAllocTypeN(char, MAX_QPATH, com_fileSysPool);
1472  if (fs_maps[fs_numInstalledMaps + 1] == nullptr) {
1473  Com_Printf("Could not allocate memory in FS_GetMaps\n");
1474  Mem_Free(dirnames[i]);
1475  continue;
1476  }
1477  fs_numInstalledMaps++;
1478  Com_sprintf(fs_maps[fs_numInstalledMaps], MAX_QPATH, "+%s", filename);
1479  Mem_Free(dirnames[i]);
1480  }
1481  Mem_Free(dirnames);
1482  }
1483  }
1484  }
1485 
1486  fs_mapsInstalledInit = true;
1487 
1488  qsort(fs_maps, fs_numInstalledMaps + 1, sizeof(char*), FS_MapDefSort);
1489 }
1490 
1495 int FS_Printf (qFILE* f, const char* msg, ...)
1496 {
1497  va_list ap;
1498  char buf[1024];
1499 
1500  va_start(ap, msg);
1501  Q_vsnprintf(buf, sizeof(buf), msg, ap);
1502  const int len = fprintf(f->f, "%s", buf);
1503  va_end(ap);
1504 
1505  return len;
1506 }
1507 
1511 int FS_Write (const void* buffer, int len, qFILE* f)
1512 {
1513  if (!f->f)
1514  return 0;
1515 
1516  const byte* buf = (const byte*) buffer;
1517 
1518  int remaining = len;
1519  int tries = 0;
1520  while (remaining) {
1521  const int block = remaining;
1522  const int written = fwrite(buf, 1, block, f->f);
1523  if (written == 0) {
1524  if (!tries) {
1525  tries = 1;
1526  } else {
1527  Com_Printf("FS_Write: 0 bytes written\n");
1528  return 0;
1529  }
1530  }
1531 
1532  if (written == -1) {
1533  Com_Printf("FS_Write: -1 bytes written\n");
1534  return 0;
1535  }
1536 
1537  remaining -= written;
1538  buf += written;
1539  }
1540  return len;
1541 }
1542 
1543 
1544 int FS_WriteFile (const void* buffer, size_t len, const char* filename)
1545 {
1546  ScopedFile f;
1547  FS_OpenFile(filename, &f, FILE_WRITE);
1548  if (!f)
1549  return 0;
1550 
1551  const int c = FS_Write(buffer, len, &f);
1552  const int lencheck = FS_FileLength(&f);
1553 
1554  /* if file write failed (file is incomplete) then delete it */
1555  if (c != len || lencheck != len) {
1556  Com_Printf("FS_WriteFile: failed to finish writing '%s'\n", filename);
1557  if (Sys_Remove(va("%s/%s", FS_Gamedir(), filename)))
1558  Com_Printf("FS_WriteFile: could not remove file: %s\n", filename);
1559  return 0;
1560  }
1561 
1562  return c;
1563 }
1564 
1568 const char* FS_GetCwd (void)
1569 {
1570  static char buf[MAX_OSPATH];
1571  Q_strncpyz(buf, Sys_Cwd(), sizeof(buf));
1572  FS_NormPath(buf);
1573  return buf;
1574 }
1575 
1581 bool FS_FileExists (const char* filename, ...)
1582 {
1583  char path[MAX_OSPATH];
1584  va_list ap;
1585 
1586  va_start(ap, filename);
1587  Q_vsnprintf(path, sizeof(path), filename, ap);
1588  va_end(ap);
1589 
1590 #ifdef _WIN32
1591  return (Sys_Access(path, 00) == 0);
1592 #else
1593  return (Sys_Access(path, R_OK) == 0);
1594 #endif
1595 }
1596 
1602 void FS_Shutdown (void)
1603 {
1604  if (fs_openedFiles != 0) {
1605  Com_Printf("There are still %i opened files\n", fs_openedFiles);
1606  }
1607 
1608  /* free everything */
1609  searchpath_t* next;
1610  for (searchpath_t* p = fs_searchpaths; p; p = next) {
1611  next = p->next;
1612 
1613  if (p->pack) {
1614  unzClose(p->pack->handle.z);
1615  Mem_Free(p->pack->files);
1616  Mem_Free(p->pack);
1617  }
1618  Mem_Free(p);
1619  }
1620 
1621  /* any FS_ calls will now be an error until reinitialized */
1622  fs_searchpaths = nullptr;
1623  fs_links = nullptr;
1624  fs_mapsInstalledInit = false;
1625  fs_numInstalledMaps = -1;
1626  fs_blocklist = nullptr;
1627 
1628 #ifdef COMPILE_UFO
1629  FS_RemoveCommands();
1630 #endif
1631 
1633 }
1634 
1641 void FS_RestartFilesystem (const char* gamedir)
1642 {
1643  if (gamedir != nullptr)
1644  Com_Printf("restarting with gamedir set to %s\n", gamedir);
1645  throw comRestart_t(gamedir);
1646 }
1647 
1652 void FS_CopyFile (const char* fromOSPath, const char* toOSPath)
1653 {
1654  if (!fs_searchpaths)
1655  Sys_Error("Filesystem call made without initialization");
1656 
1657  Com_Printf("FS_CopyFile: copy %s to %s\n", fromOSPath, toOSPath);
1658 
1659  FILE* f = Sys_Fopen(fromOSPath, "rb");
1660  if (!f)
1661  return;
1662 
1663  fseek(f, 0, SEEK_END);
1664  const int len = ftell(f);
1665  fseek(f, 0, SEEK_SET);
1666 
1668  if (fread(buf, 1, len, f) != len)
1669  Sys_Error("Short read in FS_CopyFile");
1670  fclose(f);
1671 
1672  FS_CreatePath(toOSPath);
1673 
1674  f = Sys_Fopen(toOSPath, "wb");
1675  if (!f) {
1676  Mem_Free(buf);
1677  return;
1678  }
1679 
1680  if (fwrite(buf, 1, len, f) != len)
1681  Sys_Error("Short write in FS_CopyFile");
1682 
1683  fclose(f);
1684  Mem_Free(buf);
1685 }
1686 
1690 void FS_RemoveFile (const char* osPath)
1691 {
1692  if (!fs_searchpaths)
1693  Sys_Error("Filesystem call made without initialization");
1694 
1695  Com_Printf("FS_RemoveFile: remove %s\n", osPath);
1696  Sys_Remove(osPath);
1697 }
1698 
1707 bool FS_RenameFile (const char* from, const char* to, bool relative)
1708 {
1709  char from_buf[MAX_OSPATH];
1710  char to_buf[MAX_OSPATH];
1711 
1712  if (!fs_searchpaths)
1713  Sys_Error("Filesystem call made without initialization");
1714 
1715  if (relative) {
1716  Com_sprintf(from_buf, sizeof(from_buf), "%s/%s", FS_Gamedir(), from);
1717  Com_sprintf(to_buf, sizeof(to_buf), "%s/%s", FS_Gamedir(), to);
1718  from = from_buf;
1719  to = to_buf;
1720  }
1721 
1722  return Sys_Rename(from, to) == 0;
1723 }
#define BASEDIRNAME
Definition: filesys.h:34
char * Sys_GetHomeDirectory(void)
Returns the home environment variable (which hold the path of the user's homedir) ...
Definition: unix_files.cpp:45
const char * Cmd_Argv(int arg)
Returns a given argument.
Definition: cmd.cpp:516
char filename[MAX_OSPATH]
Definition: filesys.h:95
const char * FS_NextFileFromFileList(const char *files)
Returns the next file that is found in the virtual filesystem identified by the given file pattern...
Definition: files.cpp:1079
int ZEXPORT unzGetCurrentFileInfoPosition(unzFile file, unsigned long *pos)
Definition: unzip.cpp:1450
void FS_AddGameDirectory(const char *dir, bool write)
Adds the directory to the head of the search path.
Definition: files.cpp:495
int Q_vsnprintf(char *str, size_t size, const char *format, va_list ap)
Safe (null terminating) vsnprintf implementation.
Definition: shared.cpp:535
void Sys_Error(const char *error,...)
Definition: g_main.cpp:421
void Sys_Mkdir(const char *path)
Definition: unix_files.cpp:208
int FS_CheckFile(const char *fmt,...)
Just returns the filelength and -1 if the file wasn't found.
Definition: files.cpp:298
bool write
Definition: filesys.h:104
static pack_t * FS_LoadPackFile(const char *packfile)
Takes an explicit (not game tree related) path to a pak file. Adding the files at the beginning of th...
Definition: files.cpp:422
void FS_ExecAutoexec(void)
const char * Com_SkipPath(const char *pathname)
Returns just the filename from a given path.
Definition: shared.cpp:37
static char const *const pakFileExt[]
Definition: files.cpp:485
int FS_OpenFile(const char *filename, qFILE *file, filemode_t mode)
Finds and opens the file in the search path.
Definition: files.cpp:162
const char * Com_GetExtension(const char *path)
Definition: shared.cpp:282
struct listBlock_s * next
Definition: files.cpp:929
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition: shared.cpp:410
void LIST_AddStringSorted(linkedList_t **listDest, const char *data)
Definition: list.cpp:107
#define MAX_FILES
Definition: filesys.h:47
voidp unzFile
Definition: unzip.h:70
char name[MAX_QPATH]
Definition: filesys.h:89
int FS_Seek(qFILE *f, long offset, int origin)
Sets the file position of the given file.
Definition: files.cpp:246
#define UNZ_OK
Definition: unzip.h:74
void * data
Definition: list.h:31
static int CheckBSPFile(const char *filename)
Checks for valid BSP-file.
Definition: files.cpp:1342
voidpf uLong int origin
Definition: ioapi.h:45
#define PK3_SEEK_BUFFER_SIZE
Definition: files.cpp:238
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
void Com_StripExtension(const char *in, char *out, const size_t size)
Removes the file extension from a filename.
Definition: shared.cpp:259
bool FS_FileExists(const char *filename,...)
Checks whether a file exists (not in virtual filesystem)
Definition: files.cpp:1581
struct searchpath_s * next
Definition: filesys.h:106
const char * filename
Definition: ioapi.h:41
int Sys_Rename(const char *oldname, const char *newname)
Definition: unix_files.cpp:250
int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info)
Definition: unzip.cpp:475
void Cbuf_AddText(const char *format,...)
Adds command text at the end of the buffer.
Definition: cmd.cpp:126
int ZEXPORT unzLocateFile(unzFile file, const char *szFileName, int iCaseSensitivity)
Definition: unzip.cpp:731
#define FILE
Definition: test_webapi.cpp:30
void FS_GetMaps(bool reset)
File the fs_maps array with valid maps.
Definition: files.cpp:1373
void Com_FilePath(const char *in, char *out, size_t size)
Returns the path up to, but not including the last /.
Definition: shared.cpp:319
int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)
Definition: unzip.cpp:669
void FS_RestartFilesystem(const char *gamedir)
Restart the filesystem (reload all pk3 files)
Definition: files.cpp:1641
void FS_RemoveFile(const char *osPath)
Definition: files.cpp:1690
#define MAX_MAPS
Definition: filesys.h:37
int FS_LoadFile(const char *path, byte **buffer)
Filenames are relative to the quake search path.
Definition: files.cpp:384
#define SFF_SYSTEM
Definition: filesys.h:127
#define MAX_OSPATH
Definition: filesys.h:44
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
Definition: filesys.h:94
static void _AddToListBlock(linkedList_t **fl, const char *name, bool stripPath)
Add one name to the filelist.
Definition: files.cpp:939
int FS_BuildFileList(const char *fileList)
Build a filelist.
Definition: files.cpp:960
#define PKGDATADIR
Definition: config_android.h:7
void LIST_Delete(linkedList_t **list)
Definition: list.cpp:195
uLong uncompressed_size
Definition: unzip.h:114
char path[MAX_QPATH]
Definition: files.cpp:927
int ZEXPORT unzGoToNextFile(unzFile file)
Definition: unzip.cpp:701
#define SFF_SUBDIR
Definition: filesys.h:126
void FS_CopyFile(const char *fromOSPath, const char *toOSPath)
Copy a fully specified file from one place to another.
Definition: files.cpp:1652
voidpf void * buf
Definition: ioapi.h:42
void Cmd_TableRemoveList(const cmdList_t *cmdList)
Definition: cmd.cpp:859
static char findname[MAX_OSPATH]
Definition: win_shared.cpp:167
int LIST_Count(const linkedList_t *list)
Definition: list.cpp:344
#define Q_strvalid(string)
Definition: shared.h:141
#define SFF_HIDDEN
Definition: filesys.h:124
static bool FS_GetHomeDirectory(char *gdir, size_t length)
Definition: files.cpp:631
char * Sys_FindFirst(const char *path, unsigned musthave, unsigned canthave)
Opens the directory and returns the first file that matches our searchrules.
Definition: unix_files.cpp:87
packfile_t * files
Definition: filesys.h:98
const char * FS_Gamedir(void)
Called to find where to write a file (savegames, etc)
Definition: files.cpp:68
int numfiles
Definition: filesys.h:97
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
struct listBlock_s listBlock_t
void FS_NormPath(char *path)
Convert operating systems path separators to ufo virtual filesystem separators (/) ...
Definition: files.cpp:83
int ZEXPORT unzGoToFirstFile(unzFile file)
Definition: unzip.cpp:681
void _Mem_Free(void *ptr, const char *fileName, const int fileLine)
Definition: mem.cpp:204
#define UFO_VERSION
Definition: common.h:36
#define SEEK_END
Definition: ioapi.cpp:26
void LIST_AddString(linkedList_t **listDest, const char *data)
Adds an string to a new or to an already existing linked list. The string is copied here...
Definition: list.cpp:139
game_import_t gi
Definition: g_main.cpp:39
#define OBJZERO(obj)
Definition: shared.h:178
#define MAX_VAR
Definition: shared.h:36
unzFile ZEXPORT unzOpen(const char *path)
Definition: unzip.cpp:446
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
int ZEXPORT unzCloseCurrentFile(unzFile file)
Definition: unzip.cpp:1333
int Cmd_Argc(void)
Return the number of arguments of the current command. "command parameter" will result in a argc of 2...
Definition: cmd.cpp:505
int ZEXPORT unzSetCurrentFileInfoPosition(unzFile file, unsigned long pos)
Definition: unzip.cpp:1432
int Sys_Remove(const char *filename)
Definition: unix_files.cpp:245
#define MAX_PACKFILES
Definition: files.cpp:483
int FS_GetModList(linkedList_t **mods)
Searches and builds a list of mod directories.
Definition: files.cpp:669
void FS_AddHomeAsGameDirectory(const char *dir, bool write)
Definition: files.cpp:655
qFILE handle
Definition: filesys.h:96
void FS_InitFilesystem(bool writeToHomeDir)
Definition: files.cpp:888
void Sys_FindClose(void)
Closes the find handle.
Definition: unix_files.cpp:142
filemode_t
Definition: filesys.h:110
#define MODS_DIR
Definition: files.cpp:45
int FS_WriteFile(const void *buffer, size_t len, const char *filename)
Definition: files.cpp:1544
pack_t * pack
Definition: filesys.h:103
Definition: cmd.h:86
int FS_Printf(qFILE *f, const char *msg,...)
Can print chunks for 1024 chars into a file.
Definition: files.cpp:1495
const char * FS_GetCwd(void)
Return current working dir.
Definition: files.cpp:1568
static int FS_MapDefSort(const void *map1, const void *map2)
Definition: files.cpp:1318
#define Q_strcasecmp(a, b)
Definition: shared.h:131
int fs_numInstalledMaps
Definition: files.cpp:1312
const linkedList_t * LIST_ContainsString(const linkedList_t *list, const char *string)
Searches for the first occurrence of a given string.
Definition: list.cpp:73
unsigned long filepos
Definition: filesys.h:58
Header for various formats like pak, and model formats as well as bsp format.
#define Mem_FreePool(pool)
Definition: mem.h:37
char * Q_strlwr(char *str)
Converts a string to lowercase.
Definition: shared.cpp:438
static bool fs_mapsInstalledInit
Definition: files.cpp:1313
linkedList_t * files
Definition: files.cpp:928
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
int FS_FileLength(qFILE *f)
Returns the size of a given file or -1 if no file is opened.
Definition: files.cpp:91
#define Mem_PoolAllocTypeN(type, n, pool)
Definition: mem.h:42
char * fs_maps[MAX_MAPS]
Definition: files.cpp:1311
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition: parse.cpp:107
void Sys_Mkfifo(const char *ospath, struct qFILE_s *f)
Definition: unix_files.cpp:217
int FS_Read(void *buffer, int len, qFILE *f)
Definition: files.cpp:371
#define Q_strneq(a, b, n)
Definition: shared.h:137
bool FS_RenameFile(const char *from, const char *to, bool relative)
Renames a file.
Definition: files.cpp:1707
#define MAX_QPATH
Definition: filesys.h:40
QGL_EXTERN GLint i
Definition: r_gl.h:113
QGL_EXTERN GLuint GLchar GLuint * len
Definition: r_gl.h:99
char * Sys_FindNext(unsigned musthave, unsigned canthave)
Returns the next file of the already opened directory (Sys_FindFirst) that matches our search mask...
Definition: unix_files.cpp:125
const char * FS_GetFileData(const char *files)
Returns the buffer of a file.
Definition: files.cpp:1135
char * FS_NextScriptHeader(const char *files, const char **name, const char **text)
Definition: files.cpp:1194
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
int ZEXPORT unzOpenCurrentFile(unzFile file)
Definition: unzip.cpp:1064
void Cmd_TableCheck(void)
Check both the functiontable and the associated hashtable for invalid entries.
Definition: cmd.cpp:826
uLong compressed_size
Definition: unzip.h:113
char filename[MAX_OSPATH]
Definition: filesys.h:102
#define Mem_Free(ptr)
Definition: mem.h:35
const char int mode
Definition: ioapi.h:41
uLong number_entry
Definition: unzip.h:98
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition: shared.cpp:475
#define IDBSPHEADER
Definition: qfiles.h:251
int FS_Read2(void *buffer, int len, qFILE *f, bool failOnEmptyRead)
Read a file into a given buffer in memory.
Definition: files.cpp:327
static int fs_openedFiles
Definition: files.cpp:42
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
int ZEXPORT unzClose(unzFile file)
Definition: unzip.cpp:456
void FS_CreateOpenPipeFile(const char *filename, qFILE *f)
Definition: files.cpp:47
linkedList_t * next
Definition: list.h:32
definitions common between client and server, but not game lib
FILE * Sys_Fopen(const char *filename, const char *mode)
Definition: unix_files.cpp:240
int Sys_Access(const char *filename, int mode)
Definition: unix_files.cpp:255
static searchpath_t * fs_searchpaths
Definition: files.cpp:44
memPool_t * com_fileSysPool
Definition: common.cpp:72
int Q_StringSort(const void *string1, const void *string2)
Compare two strings.
Definition: shared.cpp:385
#define SEEK_CUR
Definition: ioapi.cpp:22
#define MAX_READ
Definition: files.cpp:311
const char * Cvar_GetString(const char *varName)
Returns the value of cvar as string.
Definition: cvar.cpp:210
#define Q_streq(a, b)
Definition: shared.h:136
#define Mem_PoolStrDup(in, pool, tagNum)
Definition: mem.h:50
#define BSPVERSION
Definition: qfiles.h:253
static filelink_t * fs_links
Definition: files.cpp:43
#define SEEK_SET
Definition: ioapi.cpp:30
void FS_CloseFile(qFILE *f)
Closes a file handle.
Definition: files.cpp:137
#define Mem_PoolAllocType(type, pool)
Definition: mem.h:43
voidpf uLong offset
Definition: ioapi.h:45
char * Sys_Cwd(void)
Get current working dir.
Definition: unix_files.cpp:197
void Sys_NormPath(char *path)
Normalize path (remove all \ )
Definition: unix_files.cpp:50
void Cbuf_Execute(void)
Pulls off terminated lines of text from the command buffer and sends them through Cmd_ExecuteString...
Definition: cmd.cpp:214
char name[MAX_OSPATH]
Definition: filesys.h:57
void * z
Definition: filesys.h:55
unsigned long filelen
Definition: filesys.h:91
uint8_t byte
Definition: ufotypes.h:34
void Sys_ListFilteredFiles(const char *basedir, const char *subdirs, const char *filter, linkedList_t **list)
Definition: unix_files.cpp:151
void FS_CreatePath(const char *path)
Creates any directories needed to store the given filename.
Definition: files.cpp:117
static listBlock_t * fs_blocklist
Definition: files.cpp:932
void FS_Shutdown(void)
Cleanup function.
Definition: files.cpp:1602
char ** FS_ListFiles(const char *findname, int *numfiles, unsigned musthave, unsigned canthave)
Builds a qsorted filelist.
Definition: files.cpp:562
void Com_SkipBlock(const char **text)
Skips a block of {} in our script files.
Definition: parse.cpp:253
void Cmd_TableAddList(const cmdList_t *cmdList)
Definition: cmd.cpp:853
void FS_FreeFile(void *buffer)
Definition: files.cpp:411
#define LittleLong(X)
Definition: byte.h:37
int FS_Write(const void *buffer, int len, qFILE *f)
Properly handles partial writes.
Definition: files.cpp:1511
const char * FS_NextPath(const char *prevpath)
Allows enumerating all of the directories in the search path.
Definition: files.cpp:614
int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, unsigned len)
Definition: unzip.cpp:1089
FILE * f
Definition: filesys.h:56