00001
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include "common.h"
00032 #include "../ports/system.h"
00033 #include "../shared/defines.h"
00034 #include "../shared/typedefs.h"
00035 #include "../shared/parse.h"
00036 #include <unistd.h>
00037
00039 static int fs_openedFiles;
00040
00041 static filelink_t *fs_links;
00042
00043 static searchpath_t *fs_searchpaths;
00044
00049 const char *FS_Gamedir (void)
00050 {
00051 searchpath_t *search;
00052
00053 for (search = fs_searchpaths; search; search = search->next) {
00054 if (search->pack == NULL)
00055 return search->filename;
00056 }
00057
00058 return NULL;
00059 }
00060
00066 void FS_NormPath (char *path)
00067 {
00068 Sys_NormPath(path);
00069 }
00070
00075 int FS_FileLength (qFILE * f)
00076 {
00077 int pos;
00078 int end;
00079
00080 if (!f->f)
00081 return 0;
00082
00083 pos = ftell(f->f);
00084 fseek(f->f, 0, SEEK_END);
00085 end = ftell(f->f);
00086 fseek(f->f, pos, SEEK_SET);
00087
00088 return end;
00089 }
00090
00097 void FS_CreatePath (const char *path)
00098 {
00099 char pathCopy[MAX_OSPATH];
00100 char *ofs;
00101
00102 Q_strncpyz(pathCopy, path, sizeof(pathCopy));
00103
00104 for (ofs = pathCopy + 1; *ofs; ofs++) {
00105
00106 if (*ofs == '/') {
00107 *ofs = 0;
00108 Sys_Mkdir(pathCopy);
00109 *ofs = '/';
00110 }
00111 }
00112 }
00113
00118 void FS_CloseFile (qFILE * f)
00119 {
00120 if (f->f) {
00121 fclose(f->f);
00122 fs_openedFiles--;
00123 } else if (f->z) {
00124 unzCloseCurrentFile(f->z);
00125 fs_openedFiles--;
00126 }
00127 assert(fs_openedFiles >= 0);
00128
00129 f->f = f->z = NULL;
00130 }
00131
00141 static int FS_OpenFileSingle (const char *filename, qFILE * file, filemode_t mode)
00142 {
00143 searchpath_t *search;
00144 char netpath[MAX_OSPATH];
00145 int i;
00146 filelink_t *link;
00147
00148 file->z = file->f = NULL;
00149
00150
00151 if (mode == FILE_WRITE || mode == FILE_APPEND) {
00152 Com_sprintf(netpath, sizeof(netpath), "%s/%s", FS_Gamedir(), filename);
00153 FS_CreatePath(netpath);
00154
00155 file->f = fopen(netpath, (mode == FILE_WRITE ? "wb" : "ab"));
00156 if (file->f) {
00157 fs_openedFiles++;
00158 return 0;
00159 }
00160
00161 return -1;
00162 }
00163
00164 Q_strncpyz(file->name, filename, sizeof(file->name));
00165
00166
00167 for (link = fs_links; link; link = link->next)
00168 if (!strncmp(filename, link->from, link->fromlength)) {
00169 Com_sprintf(netpath, sizeof(netpath), "%s%s", link->to, filename + link->fromlength);
00170 file->f = fopen(netpath, "rb");
00171 if (file->f) {
00172 fs_openedFiles++;
00173 return FS_FileLength(file);
00174 }
00175 Com_Printf("linked file could not be opened: %s\n", netpath);
00176 return -1;
00177 }
00178
00179
00180 for (search = fs_searchpaths; search; search = search->next) {
00181
00182 if (search->pack) {
00183
00184 pack_t *pak = search->pack;
00185 for (i = 0; i < pak->numfiles; i++)
00186
00187 if (!Q_strcasecmp(pak->files[i].name, filename)) {
00188
00189 if (unzLocateFile(pak->handle.z, filename, 2) == UNZ_OK) {
00190 if (unzOpenCurrentFile(pak->handle.z) == UNZ_OK) {
00191 unz_file_info info;
00192 if (unzGetCurrentFileInfo(pak->handle.z, &info, NULL, 0, NULL, 0, NULL, 0) != UNZ_OK)
00193 Sys_Error("Couldn't get size of %s in %s", filename, pak->filename);
00194 unzGetCurrentFileInfoPosition(pak->handle.z, &file->filepos);
00195 file->z = pak->handle.z;
00196 fs_openedFiles++;
00197 return info.uncompressed_size;
00198 }
00199 }
00200 return pak->files[i].filelen;
00201 }
00202 } else {
00203
00204 Com_sprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename);
00205
00206 file->f = fopen(netpath, "rb");
00207 if (!file->f)
00208 continue;
00209
00210 fs_openedFiles++;
00211 return FS_FileLength(file);
00212 }
00213 }
00214
00215 file->f = NULL;
00216 file->z = NULL;
00217 return -1;
00218 }
00219
00220 #define PK3_SEEK_BUFFER_SIZE 65536
00221
00228 int FS_Seek (qFILE * f, long offset, int origin)
00229 {
00230 if (f->z) {
00231 byte buffer[PK3_SEEK_BUFFER_SIZE];
00232 int remainder = offset;
00233
00234 if (offset < 0 || origin == FS_SEEK_END) {
00235 Sys_Error("Negative offsets and FS_SEEK_END not implemented "
00236 "for FS_Seek on pk3 file contents\n");
00237 }
00238
00239 switch (origin) {
00240 case FS_SEEK_SET:
00241 unzSetCurrentFileInfoPosition(f->z, (unsigned long)f->filepos);
00242 unzOpenCurrentFile(f->z);
00243
00244 case FS_SEEK_CUR:
00245 while (remainder > PK3_SEEK_BUFFER_SIZE) {
00246 FS_Read(buffer, PK3_SEEK_BUFFER_SIZE, f);
00247 remainder -= PK3_SEEK_BUFFER_SIZE;
00248 }
00249 FS_Read(buffer, remainder, f);
00250 return offset;
00251
00252 default:
00253 Sys_Error("Bad origin in FS_Seek");
00254 }
00255 } else if (f->f) {
00256 int _origin;
00257 switch (origin) {
00258 case FS_SEEK_CUR:
00259 _origin = SEEK_CUR;
00260 break;
00261 case FS_SEEK_END:
00262 _origin = SEEK_END;
00263 break;
00264 case FS_SEEK_SET:
00265 _origin = SEEK_SET;
00266 break;
00267 default:
00268 Sys_Error("Bad origin in FS_Seek\n");
00269 }
00270 return fseek(f->f, offset, _origin);
00271 } else
00272 Sys_Error("FS_Seek: no file opened\n");
00273 }
00274
00280 int FS_OpenFile (const char *filename, qFILE * file, filemode_t mode)
00281 {
00282
00283 return FS_OpenFileSingle(filename, file, mode);
00284 }
00285
00291 int FS_CheckFile (const char *fmt, ...)
00292 {
00293 int result;
00294 qFILE file;
00295 va_list ap;
00296 char filename[MAX_QPATH];
00297
00298 va_start(ap, fmt);
00299 Q_vsnprintf(filename, sizeof(filename), fmt, ap);
00300 va_end(ap);
00301
00302 result = FS_OpenFileSingle(filename, &file, FILE_READ);
00303 if (result != -1)
00304 FS_CloseFile(&file);
00305
00306 return result;
00307 }
00308
00309 #define MAX_READ 0x10000
00310
00324 int FS_Read (void *buffer, int len, qFILE * f)
00325 {
00326 int block, remaining;
00327 int read;
00328 byte *buf;
00329 int tries;
00330
00331 buf = (byte *) buffer;
00332
00333 if (f->z) {
00334 read = unzReadCurrentFile(f->z, buf, len);
00335 if (read == -1)
00336 Sys_Error("FS_Read (zipfile): -1 bytes read");
00337
00338 return read;
00339 }
00340
00341 remaining = len;
00342 tries = 0;
00343 while (remaining) {
00344 block = remaining;
00345 if (block > MAX_READ)
00346 block = MAX_READ;
00347 read = fread(buf, 1, block, f->f);
00348
00349
00350 if (read != block && feof(f->f))
00351 return (len - remaining + read);
00352
00353 if (read == 0) {
00354
00355 if (!tries)
00356 tries = 1;
00357 else
00358 Sys_Error("FS_Read: 0 bytes read");
00359 }
00360
00361 if (read == -1)
00362 Sys_Error("FS_Read: -1 bytes read");
00363
00364
00365 remaining -= read;
00366 buf += read;
00367 }
00368 return len;
00369 }
00370
00379 int FS_LoadFile (const char *path, byte **buffer)
00380 {
00381 qFILE h;
00382 byte *buf;
00383 int len;
00384
00385
00386 len = FS_OpenFile(path, &h, FILE_READ);
00387 if (!h.f && !h.z) {
00388 if (buffer)
00389 *buffer = NULL;
00390 return -1;
00391 }
00392
00393 if (!buffer) {
00394 FS_CloseFile(&h);
00395 return len;
00396 }
00397
00398 assert(com_fileSysPool);
00399 buf = (byte*)_Mem_Alloc(len + 1, qtrue, com_fileSysPool, 0, path, 0);
00400 if (!buf)
00401 return -1;
00402 *buffer = buf;
00403
00404 FS_Read(buf, len, &h);
00405 buf[len] = 0;
00406
00407 FS_CloseFile(&h);
00408
00409 return len;
00410 }
00411
00412 void FS_FreeFile (void *buffer)
00413 {
00414 _Mem_Free(buffer, "FS_FreeFile", 0);
00415 }
00416
00423 static pack_t *FS_LoadPackFile (const char *packfile)
00424 {
00425 const char *extension = Com_GetExtension(packfile);
00426
00427 if (!strcmp(extension, "pk3") || !strcmp(extension, "zip")) {
00428 int i;
00429 pack_t *pack;
00430 packfile_t *newfiles;
00431 unz_file_info file_info;
00432 unz_global_info gi;
00433 unzFile uf = unzOpen(packfile);
00434 unsigned int err = unzGetGlobalInfo(uf, &gi);
00435 char filenameInZip[MAX_QPATH];
00436
00437 if (err != UNZ_OK) {
00438 Com_Printf("Could not load '%s'\n", packfile);
00439 return NULL;
00440 }
00441
00442 unzGoToFirstFile(uf);
00443 for (i = 0; i < gi.number_entry; i++) {
00444 err = unzGetCurrentFileInfo(uf, &file_info, filenameInZip, sizeof(filenameInZip), NULL, 0, NULL, 0);
00445 if (err != UNZ_OK) {
00446 break;
00447 }
00448 unzGoToNextFile(uf);
00449 }
00450
00451 pack = (pack_t *)Mem_PoolAlloc(sizeof(*pack), com_fileSysPool, 0);
00452 Q_strncpyz(pack->filename, packfile, sizeof(pack->filename));
00453 pack->handle.z = uf;
00454 pack->handle.f = NULL;
00455 pack->numfiles = gi.number_entry;
00456 unzGoToFirstFile(uf);
00457
00458
00459 newfiles = (packfile_t *)Mem_PoolAlloc(i * sizeof(*newfiles), com_fileSysPool, 0);
00460
00461 for (i = 0; i < gi.number_entry; i++) {
00462 err = unzGetCurrentFileInfo(uf, &file_info, filenameInZip, sizeof(filenameInZip), NULL, 0, NULL, 0);
00463 if (err != UNZ_OK)
00464 break;
00465 Q_strlwr(filenameInZip);
00466
00467 unzGetCurrentFileInfoPosition(uf, &newfiles[i].filepos);
00468 Q_strncpyz(newfiles[i].name, filenameInZip, sizeof(newfiles[i].name));
00469 newfiles[i].filelen = file_info.compressed_size;
00470 unzGoToNextFile(uf);
00471 }
00472 pack->files = newfiles;
00473
00474
00475 qsort((void *)pack->files, i, sizeof(*newfiles), Q_StringSort);
00476
00477 Com_Printf("Added packfile %s (%li files)\n", packfile, gi.number_entry);
00478 return pack;
00479 } else {
00480
00481 Com_Printf("Pack file type %s unrecognized\n", extension);
00482 return NULL;
00483 }
00484 }
00485
00486 #define MAX_PACKFILES 1024
00487
00488 static const char *pakFileExt[] = {
00489 "pk3", "zip", NULL
00490 };
00491
00496 static void FS_AddGameDirectory (const char *dir)
00497 {
00498 searchpath_t *search;
00499 char **dirnames = NULL;
00500 int ndirs = 0, i;
00501 char pakfile_list[MAX_PACKFILES][MAX_OSPATH];
00502 int pakfile_count = 0;
00503 char pattern[MAX_OSPATH];
00504 const char **extList;
00505
00506 search = fs_searchpaths;
00507 while (search) {
00508 if (!strcmp(search->filename, dir))
00509 return;
00510 search = search->next;
00511 }
00512
00513 Com_Printf("Adding game dir: %s\n", dir);
00514
00515 extList = pakFileExt;
00516 while (*extList) {
00517 Com_sprintf(pattern, sizeof(pattern), "%s/*.%s", dir, *extList);
00518 dirnames = FS_ListFiles(pattern, &ndirs, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
00519 if (dirnames != NULL) {
00520 for (i = 0; i < ndirs - 1; i++) {
00521 if (strrchr(dirnames[i], '/')) {
00522 Q_strncpyz(pakfile_list[pakfile_count], dirnames[i], sizeof(pakfile_list[pakfile_count]));
00523 pakfile_count++;
00524 if (pakfile_count >= MAX_PACKFILES) {
00525 Com_Printf("Warning: Max allowed pakfiles reached (%i) - skipping the rest\n", MAX_PACKFILES);
00526 break;
00527 }
00528 }
00529 Mem_Free(dirnames[i]);
00530 }
00531 Mem_Free(dirnames);
00532 }
00533 extList++;
00534 }
00535
00536
00537 qsort((void *)pakfile_list, pakfile_count, MAX_OSPATH, Q_StringSort);
00538
00539 for (i = 0; i < pakfile_count; i++) {
00540 pack_t *pak = FS_LoadPackFile(pakfile_list[i]);
00541 if (!pak)
00542 continue;
00543
00544 search = (searchpath_t *)Mem_PoolAlloc(sizeof(*search), com_fileSysPool, 0);
00545 search->pack = pak;
00546 search->next = fs_searchpaths;
00547 fs_searchpaths = search;
00548 }
00549
00550
00551 search = (searchpath_t *)Mem_PoolAlloc(sizeof(*search), com_fileSysPool, 0);
00552 Q_strncpyz(search->filename, dir, sizeof(search->filename));
00553 search->next = fs_searchpaths;
00554 fs_searchpaths = search;
00555 }
00556
00564 char **FS_ListFiles (const char *findname, int *numfiles, unsigned musthave, unsigned canthave)
00565 {
00566 char *s;
00567 int nfiles = 0, i;
00568 char **list = NULL;
00569 char tempList[MAX_FILES][MAX_OSPATH];
00570
00571 *numfiles = 0;
00572
00573 s = Sys_FindFirst(findname, musthave, canthave);
00574 while (s) {
00575 if (s[strlen(s) - 1] != '.')
00576 nfiles++;
00577 s = Sys_FindNext(musthave, canthave);
00578 }
00579 Sys_FindClose();
00580
00581 if (!nfiles)
00582 return NULL;
00583
00584 nfiles++;
00585 *numfiles = nfiles;
00586
00587 list = (char **)Mem_PoolAlloc(sizeof(char*) * nfiles, com_fileSysPool, 0);
00588 memset(tempList, 0, sizeof(tempList));
00589
00590 s = Sys_FindFirst(findname, musthave, canthave);
00591 nfiles = 0;
00592 while (s) {
00593 if (s[strlen(s) - 1] != '.') {
00594 Q_strncpyz(tempList[nfiles], s, sizeof(tempList[nfiles]));
00595 #ifdef _WIN32
00596 Q_strlwr(tempList[nfiles]);
00597 #endif
00598 nfiles++;
00599 if (nfiles >= MAX_FILES)
00600 break;
00601 }
00602 s = Sys_FindNext(musthave, canthave);
00603 }
00604 Sys_FindClose();
00605
00606 qsort(tempList, nfiles, MAX_OSPATH, Q_StringSort);
00607 for (i = 0; i < nfiles; i++) {
00608 list[i] = Mem_PoolStrDup(tempList[i], com_fileSysPool, 0);
00609 }
00610
00611 return list;
00612 }
00613
00618 const char *FS_NextPath (const char *prevpath)
00619 {
00620 searchpath_t *s;
00621 char *prev;
00622
00623 if (!prevpath)
00624 return FS_Gamedir();
00625
00626 prev = NULL;
00627 for (s = fs_searchpaths; s; s = s->next) {
00628 if (s->pack)
00629 continue;
00630 if (prev && !strcmp(prevpath, prev))
00631 return s->filename;
00632 prev = s->filename;
00633 }
00634
00635 return NULL;
00636 }
00637
00642 static void FS_AddHomeAsGameDirectory (const char *dir)
00643 {
00644 char gdir[MAX_OSPATH];
00645 char *homedir = Sys_GetHomeDirectory();
00646
00647 if (homedir) {
00648 #ifdef _WIN32
00649 Com_sprintf(gdir, sizeof(gdir), "%s/"UFO_VERSION"/%s", homedir, dir);
00650 #elif defined (__APPLE__) || defined (MACOSX)
00651 Com_sprintf(gdir, sizeof(gdir), "%s/Documents/UFOAI-"UFO_VERSION"/%s", homedir, dir);
00652 #else
00653 Com_sprintf(gdir, sizeof(gdir), "%s/.ufoai/"UFO_VERSION"/%s", homedir, dir);
00654 #endif
00655 FS_CreatePath(va("%s/", gdir));
00656
00657 FS_AddGameDirectory(gdir);
00658 } else {
00659 Com_Printf("could not find the home directory\n");
00660 }
00661 }
00662
00663 #ifdef COMPILE_UFO
00664
00667 void FS_ExecAutoexec (void)
00668 {
00669 char name[MAX_QPATH];
00670 searchpath_t *s;
00671
00672
00673 for (s = fs_searchpaths; s != NULL; s = s->next) {
00674 snprintf(name, sizeof(name), "%s/autoexec.cfg", s->filename);
00675
00676 if (Sys_FindFirst(name, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM)) {
00677 Cbuf_AddText("exec autoexec.cfg\n");
00678 Sys_FindClose();
00679 break;
00680 }
00681
00682 Sys_FindClose();
00683 }
00684
00685 Cbuf_Execute();
00686 }
00687
00691 static void FS_Link_f (void)
00692 {
00693 filelink_t *l, **prev;
00694
00695 if (Cmd_Argc() != 3) {
00696 Com_Printf("Usage: %s <from> <to>\n", Cmd_Argv(0));
00697 return;
00698 }
00699
00700
00701 prev = &fs_links;
00702 for (l = fs_links; l; l = l->next) {
00703 if (!strcmp(l->from, Cmd_Argv(1))) {
00704 Mem_Free(l->to);
00705 if (!strlen(Cmd_Argv(2))) {
00706 *prev = l->next;
00707 Mem_Free(l->from);
00708 Mem_Free(l);
00709 return;
00710 }
00711 l->to = Mem_PoolStrDup(Cmd_Argv(2), com_fileSysPool, 0);
00712 return;
00713 }
00714 prev = &l->next;
00715 }
00716
00717
00718 l = (filelink_t *)Mem_PoolAlloc(sizeof(*l), com_fileSysPool, 0);
00719 l->next = fs_links;
00720 fs_links = l;
00721 l->from = Mem_PoolStrDup(Cmd_Argv(1), com_fileSysPool, 0);
00722 l->fromlength = strlen(l->from);
00723 l->to = Mem_PoolStrDup(Cmd_Argv(2), com_fileSysPool, 0);
00724 }
00725
00731 static void FS_Dir_f (void)
00732 {
00733 const char *path = NULL;
00734 char findname[1024];
00735 char wildcard[1024] = "*.*";
00736 char **dirnames;
00737 int ndirs;
00738
00739 if (Cmd_Argc() != 1)
00740 Q_strncpyz(wildcard, Cmd_Argv(1), sizeof(wildcard));
00741
00742 while ((path = FS_NextPath(path)) != NULL) {
00743 Com_sprintf(findname, sizeof(findname), "%s/%s", path, wildcard);
00744 FS_NormPath(findname);
00745
00746 Com_Printf("Directory of %s\n", findname);
00747 Com_Printf("----\n");
00748
00749 dirnames = FS_ListFiles(findname, &ndirs, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
00750 if (dirnames != NULL) {
00751 int i;
00752
00753 for (i = 0; i < ndirs - 1; i++) {
00754 if (strrchr(dirnames[i], '/'))
00755 Com_Printf("%s\n", strrchr(dirnames[i], '/') + 1);
00756 else
00757 Com_Printf("%s\n", dirnames[i]);
00758
00759 Mem_Free(dirnames[i]);
00760 }
00761 Mem_Free(dirnames);
00762 }
00763 Com_Printf("\n");
00764 }
00765 }
00766
00770 static void FS_Info_f (void)
00771 {
00772 searchpath_t *search;
00773 filelink_t *l;
00774
00775 Com_Printf("Filesystem information\n");
00776 Com_Printf("...write dir: '%s'\n", FS_Gamedir());
00777
00778 for (search = fs_searchpaths; search; search = search->next) {
00779 if (search->pack == NULL)
00780 Com_Printf("...path: '%s'\n", search->filename);
00781 else
00782 Com_Printf("...pakfile: '%s' (%i files)\n", search->pack->filename, search->pack->numfiles);
00783 }
00784
00785 for (l = fs_links; l; l = l->next)
00786 Com_Printf("...link: %s : %s\n", l->from, l->to);
00787 }
00788
00794 static const cmdList_t fs_commands[] = {
00795 {"fs_restart", FS_RestartFilesystem, "Reloads the file subsystem"},
00796 {"link", FS_Link_f, "Create file links"},
00797 {"dir", FS_Dir_f, "Show the filesystem contents - also supports wildcarding"},
00798 {"fs_info", FS_Info_f, "Show information about the virtual filesystem"},
00799
00800 {NULL, NULL, NULL}
00801 };
00802
00803 static void FS_RemoveCommands (void)
00804 {
00805 const cmdList_t *commands;
00806
00807 for (commands = fs_commands; commands->name; commands++)
00808 Cmd_RemoveCommand(commands->name);
00809 }
00810
00811 static void FS_InitCommandsAndCvars (void)
00812 {
00813 const cmdList_t *commands;
00814
00815 for (commands = fs_commands; commands->name; commands++)
00816 Cmd_AddCommand(commands->name, commands->function, commands->description);
00817 }
00818 #endif
00819
00824 void FS_InitFilesystem (qboolean writeToHomeDir)
00825 {
00826 Com_Printf("\n---- filesystem initialization -----\n");
00827
00828 #ifdef PKGDATADIR
00829
00830 FS_AddGameDirectory(PKGDATADIR"/"BASEDIRNAME);
00831 #endif
00832
00833 if (writeToHomeDir) {
00834 FS_AddGameDirectory("./" BASEDIRNAME);
00835 FS_AddHomeAsGameDirectory(BASEDIRNAME);
00836 } else {
00837 FS_AddHomeAsGameDirectory(BASEDIRNAME);
00838 FS_AddGameDirectory("./" BASEDIRNAME);
00839 }
00840
00841 #ifdef COMPILE_UFO
00842 FS_InitCommandsAndCvars();
00843 #endif
00844
00845 Com_Printf("using %s for writing\n", FS_Gamedir());
00846 }
00847
00854 typedef struct listBlock_s {
00855 char path[MAX_QPATH];
00856 linkedList_t *files;
00857 struct listBlock_s *next;
00858 } listBlock_t;
00859
00860 static listBlock_t *fs_blocklist = NULL;
00861
00867 static void _AddToListBlock (linkedList_t** fl, const char* name, qboolean stripPath)
00868 {
00869 const char *f;
00870 const linkedList_t *entry;
00871
00872
00873 if (stripPath)
00874 f = Com_SkipPath(name);
00875 else
00876 f = name;
00877
00878 if (*fl == NULL)
00879 entry = NULL;
00880 else
00881 entry = LIST_ContainsString(*fl, f);
00882
00883
00884 if (entry == NULL)
00885 LIST_AddString(fl, f);
00886 }
00887
00892 int FS_BuildFileList (const char *fileList)
00893 {
00894 listBlock_t *block, *tblock;
00895 searchpath_t *search;
00896 char files[MAX_QPATH];
00897 char findname[1024];
00898 int i;
00899
00900
00901 Q_strncpyz(files, fileList, sizeof(files));
00902 FS_NormPath(files);
00903
00904
00905
00906 for (block = fs_blocklist, tblock = NULL; block;) {
00907 if (!strcmp(block->path, files)) {
00908
00909 if (tblock)
00910 tblock->next = block->next;
00911 else
00912 fs_blocklist = block->next;
00913
00914 LIST_Delete(&block->files);
00915 Mem_Free(block);
00916
00917 if (tblock)
00918 block = tblock->next;
00919 else
00920 block = fs_blocklist;
00921 continue;
00922 }
00923
00924 tblock = block;
00925 block = block->next;
00926 }
00927
00928
00929 block = (listBlock_t *)Mem_PoolAlloc(sizeof(*block), com_fileSysPool, 0);
00930 block->next = fs_blocklist;
00931 fs_blocklist = block;
00932
00933
00934 Q_strncpyz(block->path, files, sizeof(block->path));
00935
00936
00937 LIST_Delete(&block->files);
00938
00939
00940 for (search = fs_searchpaths; search; search = search->next) {
00941
00942 if (search->pack) {
00943 const char *ext = strrchr(files, '.');
00944 const pack_t *pak = search->pack;
00945 size_t l = strlen(files);
00946 if (!ext)
00947 break;
00948 Q_strncpyz(findname, files, sizeof(findname));
00949 FS_NormPath(findname);
00950 l -= (strlen(ext) + 1);
00951 findname[l] = '\0';
00952
00953
00954 for (i = 0; i < pak->numfiles; i++) {
00955
00956 const char *fileNameEntry = pak->files[i].name;
00957 qboolean matchAlsoInSubDirs = (findname[0] == '*' || !strncmp(fileNameEntry, findname, l))
00958 && (ext[0] == '*' || strstr(fileNameEntry, ext));
00959 if (matchAlsoInSubDirs) {
00960 qboolean add = qfalse;
00961 if (strstr(findname, "**"))
00962 add = qtrue;
00963 else {
00964 char pathName[MAX_QPATH];
00965 char pathNameEntry[MAX_QPATH];
00966 Com_FilePath(findname, pathName);
00967 Com_FilePath(fileNameEntry, pathNameEntry);
00968 if (!strcmp(pathNameEntry, pathName))
00969 add = qtrue;
00970 }
00971
00972 if (add)
00973 _AddToListBlock(&block->files, pak->files[i].name, qtrue);
00974 }
00975 }
00976 } else if (strstr(files, "**")) {
00977 linkedList_t *list = NULL, *loopList;
00978 const char *wildcard = strstr(files, "**");
00979 const size_t l = strlen(files) - strlen(wildcard);
00980 const char *name = &findname[l + 1];
00981
00982 Q_strncpyz(findname, files, sizeof(findname));
00983 FS_NormPath(findname);
00984 findname[l] = '\0';
00985 if (l > 0 && findname[l - 1] == '/')
00986 findname[l - 1] = '\0';
00987
00988 Sys_ListFilteredFiles(search->filename, findname, name, &list);
00989
00990 loopList = list;
00991 while (loopList) {
00992 _AddToListBlock(&block->files, (const char *)loopList->data, qfalse);
00993 loopList = loopList->next;
00994 }
00995
00996 LIST_Delete(&list);
00997 } else {
00998 int nfiles = 0;
00999 char **filenames;
01000
01001 Com_sprintf(findname, sizeof(findname), "%s/%s", search->filename, files);
01002 FS_NormPath(findname);
01003
01004 filenames = FS_ListFiles(findname, &nfiles, 0, SFF_HIDDEN | SFF_SYSTEM);
01005 if (filenames != NULL) {
01006 for (i = 0; i < nfiles - 1; i++) {
01007 _AddToListBlock(&block->files, filenames[i], qtrue);
01008 Mem_Free(filenames[i]);
01009 }
01010 Mem_Free(filenames);
01011 }
01012 }
01013 }
01014
01015 return LIST_Count(block->files);
01016 }
01017
01018 const char* FS_NextFileFromFileList (const char *files)
01019 {
01020 static linkedList_t *listEntry = NULL;
01021 static listBlock_t *_block = NULL;
01022 listBlock_t *block;
01023 const char *file = NULL;
01024
01025
01026 if (files == NULL) {
01027 _block = NULL;
01028 return NULL;
01029 }
01030
01031 for (block = fs_blocklist; block; block = block->next)
01032 if (!strncmp(files, block->path, MAX_QPATH))
01033 break;
01034
01035 if (!block) {
01036 FS_BuildFileList(files);
01037 for (block = fs_blocklist; block; block = block->next)
01038 if (!strncmp(files, block->path, MAX_QPATH))
01039 break;
01040 if (!block) {
01041
01042 Com_Printf("FS_NextFileFromFileList: Could not create filelist for %s\n", files);
01043 return NULL;
01044 }
01045 }
01046
01047
01048
01049 if (_block != block) {
01050 _block = block;
01051 listEntry = block->files;
01052 }
01053
01054 if (listEntry) {
01055 file = (const char *)listEntry->data;
01056 listEntry = listEntry->next;
01057 }
01058
01059
01060 return file;
01061 }
01062
01072 const char *FS_GetFileData (const char *files)
01073 {
01074 listBlock_t *block;
01075 static linkedList_t *fileList = NULL;
01076 static byte *buffer = NULL;
01077
01078
01079 if (buffer) {
01080 FS_FreeFile(buffer);
01081 buffer = NULL;
01082 }
01083
01084 if (!files) {
01085 fileList = NULL;
01086 return NULL;
01087 }
01088
01089 for (block = fs_blocklist; block; block = block->next)
01090 if (!strcmp(files, block->path))
01091 break;
01092
01093 if (!block) {
01094
01095 fileList = NULL;
01096 FS_BuildFileList(files);
01097 for (block = fs_blocklist; block; block = block->next)
01098 if (!strcmp(files, block->path))
01099 break;
01100 if (!block) {
01101
01102 Com_Printf("FS_GetFileData: Could not create filelist for %s\n", files);
01103 return NULL;
01104 }
01105 }
01106
01107 if (!fileList)
01108
01109 fileList = block->files;
01110 else
01111
01112 fileList = fileList->next;
01113
01114 if (fileList) {
01115 char filename[MAX_QPATH];
01116
01117
01118 Q_strncpyz(filename, block->path, sizeof(filename));
01119 strcpy(strrchr(filename, '/') + 1, (const char *)fileList->data);
01120
01121 FS_LoadFile(filename, &buffer);
01122 return (const char*)buffer;
01123 }
01124
01125
01126 return NULL;
01127 }
01128
01129 void FS_SkipBlock (const char **text)
01130 {
01131 const char *token;
01132 int depth;
01133
01134 depth = 1;
01135
01136 do {
01137 token = Com_Parse(text);
01138 if (*token == '{')
01139 depth++;
01140 else if (*token == '}')
01141 depth--;
01142 } while (depth && *text);
01143 }
01144
01145 char *FS_NextScriptHeader (const char *files, const char **name, const char **text)
01146 {
01147 static char lastList[MAX_QPATH] = "";
01148 static listBlock_t *lBlock;
01149 static linkedList_t *lFile = NULL;
01150 static byte *lBuffer = NULL;
01151
01152 static char headerType[MAX_VAR];
01153 static char headerName[MAX_VAR];
01154 listBlock_t *block;
01155 const char *token;
01156
01157 if (!text) {
01158 *lastList = 0;
01159 return NULL;
01160 }
01161
01162 if (strcmp(files, lastList)) {
01163
01164 Q_strncpyz(lastList, files, sizeof(lastList));
01165
01166 for (block = fs_blocklist; block; block = block->next)
01167 if (!strcmp(files, block->path))
01168 break;
01169
01170 if (!block)
01171
01172 return NULL;
01173
01174 lBlock = block;
01175 lFile = block->files;
01176 }
01177
01178 while (lBlock) {
01179 if (lBuffer) {
01180
01181 if (*text) {
01182 token = Com_Parse(text);
01183 if (*token == '{') {
01184 FS_SkipBlock(text);
01185 continue;
01186 }
01187
01188 Q_strncpyz(headerType, token, sizeof(headerType));
01189 if (*text) {
01190 token = Com_Parse(text);
01191 Q_strncpyz(headerName, token, sizeof(headerName));
01192 *name = headerName;
01193 return headerType;
01194 }
01195 }
01196
01197
01198 lFile = lFile->next;
01199
01200 while (!lFile && lBlock)
01201
01202 for (lBlock = lBlock->next; lBlock; lBlock = lBlock->next)
01203 if (!strcmp(files, lBlock->path)) {
01204 lFile = lBlock->files;
01205 break;
01206 }
01207 }
01208
01209 if (lFile) {
01210 char filename[MAX_QPATH];
01211
01212
01213 if (lBuffer) {
01214 FS_FreeFile(lBuffer);
01215 lBuffer = NULL;
01216 }
01217
01218
01219 Q_strncpyz(filename, lBlock->path, sizeof(filename));
01220 strcpy(strrchr(filename, '/') + 1, (const char *)lFile->data);
01221
01222 FS_LoadFile(filename, &lBuffer);
01223
01224 if (!lBuffer) {
01225 lFile = lFile->next;
01226 continue;
01227 }
01228 *text = (char*)lBuffer;
01229 } else if (!lBuffer)
01230 break;
01231 }
01232
01233
01234 if (lBuffer) {
01235 FS_FreeFile(lBuffer);
01236 lBuffer = NULL;
01237 }
01238
01239
01240 return NULL;
01241 }
01242
01243
01244 char *fs_maps[MAX_MAPS];
01245 int fs_numInstalledMaps = -1;
01246 static qboolean fs_mapsInstalledInit = qfalse;
01247
01251 static int FS_MapDefSort (const void *map1, const void *map2)
01252 {
01253 const char *mapStr1 = *(const char * const *)map1;
01254 const char *mapStr2 = *(const char * const *)map2;
01255
01256
01257 if (mapStr1[0] == '+')
01258 mapStr1++;
01259 if (mapStr2[0] == '+')
01260 mapStr2++;
01261
01262 return Q_StringSort(mapStr1, mapStr2);
01263 }
01264
01275 static int CheckBSPFile (const char *filename)
01276 {
01277 int i;
01278 int header[2];
01279 qFILE file;
01280 char name[MAX_QPATH];
01281
01282
01283 Com_sprintf(name, sizeof(name), "maps/%s.bsp", filename);
01284
01285 FS_OpenFile(name, &file, FILE_READ);
01286 if (!file.f && !file.z)
01287 return 1;
01288
01289 FS_Read(header, sizeof(header), &file);
01290
01291 FS_CloseFile(&file);
01292
01293 for (i = 0; i < 2; i++)
01294 header[i] = LittleLong(header[i]);
01295
01296 if (header[0] != IDBSPHEADER)
01297 return 2;
01298 if (header[1] != BSPVERSION)
01299 return 3;
01300
01301
01302 return 0;
01303 }
01304
01310 void FS_GetMaps (qboolean reset)
01311 {
01312 char findname[MAX_OSPATH];
01313 char filename[MAX_QPATH];
01314 int status, i;
01315 const char *baseMapName = NULL;
01316 char **dirnames;
01317 int ndirs;
01318 searchpath_t *search;
01319 pack_t *pak;
01320
01321
01322 if (!reset && fs_mapsInstalledInit)
01323 return;
01324 else if (fs_mapsInstalledInit) {
01325 for (i = 0; i <= fs_numInstalledMaps; i++)
01326 Mem_Free(fs_maps[i]);
01327 }
01328
01329 fs_numInstalledMaps = -1;
01330
01331
01332 for (search = fs_searchpaths; search; search = search->next) {
01333
01334 if (search->pack) {
01335
01336 pak = search->pack;
01337 for (i = 0; i < pak->numfiles; i++) {
01338
01339 baseMapName = strchr(pak->files[i].name, '/');
01340 if (baseMapName) {
01342 baseMapName = strchr(baseMapName + 1, '/');
01343
01344 if (baseMapName)
01345 continue;
01346 } else
01347 continue;
01348
01349 if (strstr(pak->files[i].name, ".bsp") || strstr(pak->files[i].name, ".ump") ) {
01350 if (fs_numInstalledMaps + 1 >= MAX_MAPS) {
01351 Com_Printf("FS_GetMaps: Max maps limit hit\n");
01352 break;
01353 }
01354 fs_maps[fs_numInstalledMaps + 1] = (char *)Mem_PoolAlloc(MAX_QPATH * sizeof(char), com_fileSysPool, 0);
01355 if (fs_maps[fs_numInstalledMaps + 1] == NULL) {
01356 Com_Printf("Could not allocate memory in FS_GetMaps\n");
01357 continue;
01358 }
01359 Q_strncpyz(findname, pak->files[i].name, sizeof(findname));
01360 FS_NormPath(findname);
01361 baseMapName = Com_SkipPath(findname);
01362 Com_StripExtension(baseMapName, filename, sizeof(filename));
01363 fs_numInstalledMaps++;
01364 if (strstr(findname, ".ump"))
01365 Com_sprintf(fs_maps[fs_numInstalledMaps], MAX_QPATH, "+%s", filename);
01366 else
01367 Q_strncpyz(fs_maps[fs_numInstalledMaps], filename, MAX_QPATH);
01368 }
01369 }
01370 } else {
01371 Com_sprintf(findname, sizeof(findname), "%s/maps/*.bsp", search->filename);
01372 FS_NormPath(findname);
01373
01374 dirnames = FS_ListFiles(findname, &ndirs, 0, SFF_HIDDEN | SFF_SYSTEM);
01375 if (dirnames != NULL) {
01376 for (i = 0; i < ndirs - 1; i++) {
01377 baseMapName = Com_SkipPath(dirnames[i]);
01378 Com_StripExtension(baseMapName, filename, sizeof(filename));
01379 status = CheckBSPFile(filename);
01380 if (!status) {
01381 if (fs_numInstalledMaps + 1 >= MAX_MAPS) {
01382 Com_Printf("FS_GetMaps: Max maps limit hit\n");
01383 break;
01384 }
01385 fs_maps[fs_numInstalledMaps + 1] = (char *) Mem_PoolAlloc(MAX_QPATH * sizeof(char), com_fileSysPool, 0);
01386 if (fs_maps[fs_numInstalledMaps + 1] == NULL) {
01387 Com_Printf("Could not allocate memory in FS_GetMaps\n");
01388 Mem_Free(dirnames[i]);
01389 continue;
01390 }
01391 fs_numInstalledMaps++;
01392 Q_strncpyz(fs_maps[fs_numInstalledMaps], filename, MAX_QPATH);
01393 } else
01394 Com_Printf("invalid mapstatus: %i (%s)\n", status, dirnames[i]);
01395 Mem_Free(dirnames[i]);
01396 }
01397 Mem_Free(dirnames);
01398 }
01399
01400 Com_sprintf(findname, sizeof(findname), "%s/maps/*.ump", search->filename);
01401 FS_NormPath(findname);
01402
01403 dirnames = FS_ListFiles(findname, &ndirs, 0, SFF_HIDDEN | SFF_SYSTEM);
01404 if (dirnames != NULL) {
01405 for (i = 0; i < ndirs - 1; i++) {
01406 baseMapName = Com_SkipPath(dirnames[i]);
01407 Com_StripExtension(baseMapName, filename, sizeof(filename));
01408 if (fs_numInstalledMaps + 1 >= MAX_MAPS) {
01409 Com_Printf("FS_GetMaps: Max maps limit hit\n");
01410 break;
01411 }
01412 fs_maps[fs_numInstalledMaps + 1] = (char *) Mem_PoolAlloc(MAX_QPATH * sizeof(char), com_fileSysPool, 0);
01413 if (fs_maps[fs_numInstalledMaps + 1] == NULL) {
01414 Com_Printf("Could not allocate memory in FS_GetMaps\n");
01415 Mem_Free(dirnames[i]);
01416 continue;
01417 }
01418 fs_numInstalledMaps++;
01419 Com_sprintf(fs_maps[fs_numInstalledMaps], MAX_QPATH, "+%s", filename);
01420 Mem_Free(dirnames[i]);
01421 }
01422 Mem_Free(dirnames);
01423 }
01424 }
01425 }
01426
01427 fs_mapsInstalledInit = qtrue;
01428
01429 qsort(fs_maps, fs_numInstalledMaps + 1, sizeof(char *), FS_MapDefSort);
01430 }
01431
01436 int FS_Printf (qFILE *f, const char *msg, ...)
01437 {
01438 va_list ap;
01439 int len;
01440 char buf[1024];
01441
01442 va_start(ap, msg);
01443 Q_vsnprintf(buf, sizeof(buf), msg, ap);
01444 len = fprintf(f->f, "%s", buf);
01445 va_end(ap);
01446
01447 return len;
01448 }
01449
01453 int FS_Write (const void *buffer, int len, qFILE * f)
01454 {
01455 int block, remaining;
01456 int written;
01457 const byte *buf;
01458 int tries;
01459
01460 if (!f->f)
01461 return 0;
01462
01463 buf = (const byte *) buffer;
01464
01465 remaining = len;
01466 tries = 0;
01467 while (remaining) {
01468 block = remaining;
01469 written = fwrite(buf, 1, block, f->f);
01470 if (written == 0) {
01471 if (!tries) {
01472 tries = 1;
01473 } else {
01474 Com_Printf("FS_Write: 0 bytes written\n");
01475 return 0;
01476 }
01477 }
01478
01479 if (written == -1) {
01480 Com_Printf("FS_Write: -1 bytes written\n");
01481 return 0;
01482 }
01483
01484 remaining -= written;
01485 buf += written;
01486 }
01487 return len;
01488 }
01489
01490
01491 int FS_WriteFile (const void *buffer, size_t len, const char *filename)
01492 {
01493 qFILE f;
01494 int c, lencheck;
01495
01496 FS_OpenFile(filename, &f, FILE_WRITE);
01497 if (f.f)
01498 c = FS_Write(buffer, len, &f);
01499 else
01500 return 0;
01501
01502 lencheck = FS_FileLength(&f);
01503 FS_CloseFile(&f);
01504
01505
01506 if (c != len || lencheck != len) {
01507 Com_Printf("FS_WriteFile: failed to finish writing '%s'\n", filename);
01508 if (remove(va("%s/%s", FS_Gamedir(), filename)))
01509 Com_Printf("FS_WriteFile: could not remove file: %s\n", filename);
01510 return 0;
01511 }
01512
01513 return c;
01514 }
01515
01519 const char *FS_GetCwd (void)
01520 {
01521 static char buf[MAX_OSPATH];
01522 Q_strncpyz(buf, Sys_Cwd(), sizeof(buf));
01523 FS_NormPath(buf);
01524 return buf;
01525 }
01526
01532 qboolean FS_FileExists (const char *filename)
01533 {
01534 #ifdef _WIN32
01535 return (_access(filename, 00) == 0);
01536 #else
01537 return (access(filename, R_OK) == 0);
01538 #endif
01539 }
01540
01546 void FS_Shutdown (void)
01547 {
01548 searchpath_t *p, *next;
01549
01550 if (fs_openedFiles != 0) {
01551 Com_Printf("There are still %i opened files\n", fs_openedFiles);
01552 }
01553
01554
01555 for (p = fs_searchpaths; p; p = next) {
01556 next = p->next;
01557
01558 if (p->pack) {
01559 unzClose(p->pack->handle.z);
01560 Mem_Free(p->pack->files);
01561 Mem_Free(p->pack);
01562 }
01563 Mem_Free(p);
01564 }
01565
01566
01567 fs_searchpaths = NULL;
01568 fs_links = NULL;
01569 fs_mapsInstalledInit = qfalse;
01570 fs_numInstalledMaps = -1;
01571
01572 #ifdef COMPILE_UFO
01573 FS_RemoveCommands();
01574 #endif
01575
01576 Mem_FreePool(com_fileSysPool);
01577 }
01578
01585 void FS_RestartFilesystem (void)
01586 {
01587
01588 FS_Shutdown();
01589
01590
01591 FS_InitFilesystem(qtrue);
01592
01598 if (FS_CheckFile("default.cfg") < 0) {
01599 Sys_Error("Couldn't load default.cfg");
01600 }
01601 }
01602
01607 void FS_CopyFile (const char *fromOSPath, const char *toOSPath)
01608 {
01609 FILE *f;
01610 int len;
01611 byte *buf;
01612
01613 if (!fs_searchpaths)
01614 Sys_Error("Filesystem call made without initialization");
01615
01616 Com_Printf("FS_CopyFile: copy %s to %s\n", fromOSPath, toOSPath);
01617
01618 f = fopen(fromOSPath, "rb");
01619 if (!f)
01620 return;
01621
01622 fseek(f, 0, SEEK_END);
01623 len = ftell(f);
01624 fseek(f, 0, SEEK_SET);
01625
01626 buf = (byte *)Mem_PoolAlloc(len, com_fileSysPool, 0);
01627 if (fread(buf, 1, len, f) != len)
01628 Sys_Error("Short read in FS_CopyFile");
01629 fclose(f);
01630
01631 FS_CreatePath(toOSPath);
01632
01633 f = fopen(toOSPath, "wb");
01634 if (!f) {
01635 Mem_Free(buf);
01636 return;
01637 }
01638
01639 if (fwrite(buf, 1, len, f) != len)
01640 Sys_Error("Short write in FS_CopyFile()");
01641
01642 fclose(f);
01643 Mem_Free(buf);
01644 }
01645
01649 void FS_RemoveFile (const char *osPath)
01650 {
01651 if (!fs_searchpaths)
01652 Sys_Error("Filesystem call made without initialization");
01653
01654 Com_Printf("FS_RemoveFile: remove %s\n", osPath);
01655 remove(osPath);
01656 }
01657
01666 qboolean FS_RenameFile (const char *from, const char *to, qboolean relative)
01667 {
01668 char from_buf[MAX_OSPATH];
01669 char to_buf[MAX_OSPATH];
01670
01671 if (!fs_searchpaths)
01672 Sys_Error("Filesystem call made without initialization");
01673
01674 if (relative) {
01675 Com_sprintf(from_buf, sizeof(from_buf), "%s/%s", FS_Gamedir(), from);
01676 Com_sprintf(to_buf, sizeof(to_buf), "%s/%s", FS_Gamedir(), to);
01677 from = from_buf;
01678 to = to_buf;
01679 }
01680
01681 if (rename(from, to))
01682 return qfalse;
01683 return qtrue;
01684 }