mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-12 02:01:29 -10:00
use #define's for path lengths when possible so it's easier to move between functions
fix huge Cfile_stack[] issue (how the hell did that get through ✌️ QA?)
add Int3() check on cfopen() so it's easier to know if it get's called before cfile is ready to use
move path separators to pstypes.h
fix possible string overruns when setting up CFILE roots
make sure we don't try to init current directory again thinking it's a CD-ROM
add the list of VP roots to debug log, this will undoubtedly be useful
when -nosound is use go ahead and set -nomusic too to both checks are correct
add list of cmdline options to debug log
fix possible overwrite issues with get_version_string() and remove '(fs2_open)' from string plus change OGL->OpenGL, D3D->Direct3D
1745 lines
43 KiB
C++
1745 lines
43 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* $Logfile: /Freespace2/code/CFile/CfileSystem.cpp $
|
|
* $Revision: 2.32 $
|
|
* $Date: 2005-12-06 03:13:49 $
|
|
* $Author: taylor $
|
|
*
|
|
* Functions to keep track of and find files that can exist
|
|
* on the harddrive, cd-rom, or in a pack file on either of those.
|
|
* This keeps a list of all the files in packfiles or on CD-rom
|
|
* and when you need a file you call one function which then searches
|
|
* all those locations, inherently enforcing precedence orders.
|
|
*
|
|
* $Log: not supported by cvs2svn $
|
|
* Revision 2.31 2005/11/16 21:25:34 taylor
|
|
* switch from strchr() to strrchr() for extension checks in order to allow for multiple periods in a filename
|
|
*
|
|
* Revision 2.30 2005/10/23 11:44:07 taylor
|
|
* fix memory errors from new[]/delete[] mismatch
|
|
*
|
|
* Revision 2.29 2005/10/23 05:37:59 wmcoolmon
|
|
* Ack, missed testing compile under an Inferno build
|
|
*
|
|
* Revision 2.28 2005/10/17 00:13:28 wmcoolmon
|
|
* Some cfile changes that slipped by
|
|
*
|
|
* Revision 2.27 2005/10/16 23:15:46 wmcoolmon
|
|
* Hardened cfile against array overflows
|
|
*
|
|
* Revision 2.26 2005/05/12 17:49:10 taylor
|
|
* use vm_malloc(), vm_free(), vm_realloc(), vm_strdup() rather than system named macros
|
|
* fixes various problems and is past time to make the switch
|
|
*
|
|
* Revision 2.25 2005/01/30 12:50:08 taylor
|
|
* merge with Linux/OSX tree - p0130
|
|
*
|
|
* Revision 2.24 2004/11/21 11:27:31 taylor
|
|
* some 64-bit OS comaptibility fixes
|
|
*
|
|
* Revision 2.23 2004/10/31 21:28:38 taylor
|
|
* support new pilot code, Linux code tree merge, don't use same pilot directory for Inferno builds
|
|
*
|
|
* Revision 2.22 2004/07/26 20:47:25 Kazan
|
|
* remove MCD complete
|
|
*
|
|
* Revision 2.21 2004/07/17 09:26:00 taylor
|
|
* add CF_SORT_REVERSE to real sort routine, makes CF_SORT_TIME work again
|
|
*
|
|
* Revision 2.20 2004/07/12 16:32:42 Kazan
|
|
* MCD - define _MCD_CHECK to use memory tracking
|
|
*
|
|
* Revision 2.19 2004/05/26 22:02:36 wmcoolmon
|
|
* Fixed cf_matches_spec; small bug was introduced.
|
|
*
|
|
* Revision 2.18 2004/05/26 21:02:26 wmcoolmon
|
|
* Added weapons_expl modular table, updated cfilesystem to work with modular tables, fixed loading order, fixed ship loading error messages
|
|
*
|
|
* Revision 2.17 2004/05/03 21:22:19 Kazan
|
|
* Abandon strdup() usage for mod list processing - it was acting odd and causing crashing on free()
|
|
* Fix condition where alt_tab_pause() would flipout and trigger failed assert if game minimizes during startup (like it does a lot during debug)
|
|
* Nav Point / Auto Pilot code (All disabled via #ifdefs)
|
|
*
|
|
* Revision 2.16 2004/05/01 21:47:43 Kazan
|
|
* bah! stupid me "NULL" not "null" - i hate when i do that
|
|
*
|
|
* Revision 2.15 2004/05/01 21:45:31 Kazan
|
|
* changed delete[] to free(), checked for null, if null ERROR
|
|
*
|
|
* Revision 2.14 2004/05/01 19:49:31 Kazan
|
|
* delete[] str_temp
|
|
* A null check on str_temp is not needed - take a close look at the for loop
|
|
*
|
|
* Revision 2.13 2004/05/01 19:33:20 taylor
|
|
* add CF_TYPE_MISSIONS to physical check to avoid network transfer problems
|
|
*
|
|
* Revision 2.12 2004/05/01 17:10:37 Kazan
|
|
* Multiple -mod - "-mod ModA,ModB,ModC" in order of priority
|
|
* Giving you:
|
|
* Root: ModA
|
|
* Root: ModB
|
|
* Root: ModC
|
|
* Root: Fs2Default
|
|
* Root: CDRom
|
|
*
|
|
* Revision 2.11 2004/04/26 00:26:37 taylor
|
|
* use absolute moddir path, faster cfilesystem searching
|
|
*
|
|
* Revision 2.10 2004/04/03 18:11:19 Kazan
|
|
* FRED fixes
|
|
*
|
|
* Revision 2.9 2004/03/05 09:01:54 Goober5000
|
|
* Uber pass at reducing #includes
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.8 2003/09/14 18:32:41 wmcoolmon
|
|
* Added "-safeloading" command line parameter, which uses old fs2_retail-style loading code -C
|
|
*
|
|
* Revision 2.7 2003/08/20 08:14:50 wmcoolmon
|
|
* Modified find_file_location to speed up file loading.
|
|
*
|
|
* Revision 2.6 2002/11/10 16:28:08 DTP
|
|
* -DTP reworked mod support,
|
|
*
|
|
* Revision 2.5 2002/10/30 06:29:45 DTP
|
|
* doh!, used upper case in include, dont know how much it matters for *nix systems, but here it is
|
|
*
|
|
* Revision 2.4 2002/10/30 06:26:11 DTP
|
|
* DTP Implemented basic VP files handling. mission and campaign files inside VP files found in mod dir still not supported, cheking / creating directories not implented either
|
|
*
|
|
* Revision 2.3 2002/08/01 01:41:04 penguin
|
|
* The big include file move
|
|
*
|
|
* Revision 2.2 2002/07/29 19:52:48 penguin
|
|
* added #ifdef _WIN32 around windows-specific system headers
|
|
*
|
|
* Revision 2.1 2002/07/07 19:55:58 penguin
|
|
* Back-port to MSVC
|
|
*
|
|
* Revision 2.0 2002/06/03 04:02:21 penguin
|
|
* Warpcore CVS sync
|
|
*
|
|
* Revision 1.6 2002/05/24 16:44:10 mharris
|
|
* Fix unix "find files" operations so they return relative path
|
|
*
|
|
* Revision 1.5 2002/05/21 15:38:00 mharris
|
|
* Unix glob changes -- still needs some work as we are getting full pathnames
|
|
*
|
|
* Revision 1.4 2002/05/17 23:38:33 mharris
|
|
* Fixed silly bug in unix find-files code
|
|
*
|
|
* Revision 1.3 2002/05/16 06:03:29 mharris
|
|
* Unix port changes
|
|
*
|
|
* Revision 1.2 2002/05/07 13:22:52 mharris
|
|
* Added errno.h include
|
|
*
|
|
* Revision 1.1 2002/05/02 18:03:04 mharris
|
|
* Initial checkin - converted filenames and includes to lower case
|
|
*
|
|
*
|
|
* 6 9/08/99 10:01p Dave
|
|
* Make sure game won't run in a drive's root directory. Make sure
|
|
* standalone routes suqad war messages properly to the host.
|
|
*
|
|
* 5 9/03/99 1:31a Dave
|
|
* CD checking by act. Added support to play 2 cutscenes in a row
|
|
* seamlessly. Fixed super low level cfile bug related to files in the
|
|
* root directory of a CD. Added cheat code to set campaign mission # in
|
|
* main hall.
|
|
*
|
|
* 4 2/22/99 10:31p Andsager
|
|
* Get rid of unneeded includes.
|
|
*
|
|
* 3 10/13/98 9:19a Andsager
|
|
* Add localization support to cfile. Optional parameter with cfopen that
|
|
* looks for localized files.
|
|
*
|
|
* 2 10/07/98 10:52a Dave
|
|
* Initial checkin.
|
|
*
|
|
* 1 10/07/98 10:48a Dave
|
|
*
|
|
* 14 8/31/98 2:06p Dave
|
|
* Make cfile sort the ordering or vp files. Added support/checks for
|
|
* recognizing "mission disk" players.
|
|
*
|
|
* 13 6/23/98 4:18p Hoffoss
|
|
* Fixed some bugs with AC release build.
|
|
*
|
|
* 12 5/20/98 10:46p John
|
|
* Added code that doesn't include duplicate filenames in any file list
|
|
* functions.
|
|
*
|
|
* 11 5/14/98 2:14p Lawrance2
|
|
* Use filespec filtering for packfiles
|
|
*
|
|
* 10 5/03/98 11:53a John
|
|
* Fixed filename case mangling.
|
|
*
|
|
* 9 5/02/98 11:06p Allender
|
|
* correctly deal with pack pathnames
|
|
*
|
|
* 8 5/01/98 11:41a Allender
|
|
* Fixed bug with mission saving in Fred.
|
|
*
|
|
* 7 5/01/98 10:21a John
|
|
* Added code to find all pack files in all trees. Added code to create
|
|
* any directories that we write to.
|
|
*
|
|
* 6 4/30/98 10:21p John
|
|
* Added code to cleanup cfilesystem
|
|
*
|
|
* 5 4/30/98 10:18p John
|
|
* added source safe header
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#include <direct.h>
|
|
#include <windows.h>
|
|
#include <winbase.h> /* needed for memory mapping of file functions */
|
|
#endif
|
|
|
|
#ifdef SCP_UNIX
|
|
#include <glob.h>
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <fnmatch.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "cmdline/cmdline.h"
|
|
#include "cfile/cfile.h"
|
|
#include "cfile/cfilesystem.h"
|
|
#include "localization/localize.h"
|
|
|
|
|
|
|
|
#define CF_ROOTTYPE_PATH 0
|
|
#define CF_ROOTTYPE_PACK 1
|
|
|
|
// for a defined and specifically set location to get/send pilot/campaign files
|
|
char *Pilot_file_path = NULL;
|
|
|
|
// Created by:
|
|
// specifying hard drive tree
|
|
// searching for pack files on hard drive // Found by searching all known paths
|
|
// specifying cd-rom tree
|
|
// searching for pack files on CD-rom tree
|
|
typedef struct cf_root {
|
|
char path[CF_MAX_PATHNAME_LENGTH]; // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
|
|
int roottype; // CF_ROOTTYPE_PATH = Path, CF_ROOTTYPE_PACK =Pack file
|
|
} cf_root;
|
|
|
|
// convenient type for sorting (see cf_build_pack_list())
|
|
typedef struct cf_root_sort {
|
|
char path[CF_MAX_PATHNAME_LENGTH];
|
|
int roottype;
|
|
int cf_type;
|
|
} cf_root_sort;
|
|
|
|
#define CF_NUM_ROOTS_PER_BLOCK 32
|
|
#define CF_MAX_ROOT_BLOCKS 256 // Can store 32*256 = 8192 Roots
|
|
#define CF_MAX_ROOTS (CF_NUM_ROOTS_PER_BLOCK * CF_MAX_ROOT_BLOCKS)
|
|
|
|
typedef struct cf_root_block {
|
|
cf_root roots[CF_NUM_ROOTS_PER_BLOCK];
|
|
} cf_root_block;
|
|
|
|
static int Num_roots = 0;
|
|
static cf_root_block *Root_blocks[CF_MAX_ROOT_BLOCKS];
|
|
|
|
static int Num_path_roots = 0;
|
|
|
|
// Created by searching all roots in order. This means Files is then sorted by precedence.
|
|
typedef struct cf_file {
|
|
char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
|
|
int root_index; // Where in Roots this is located
|
|
int pathtype_index; // Where in Paths this is located
|
|
time_t write_time; // When it was last written
|
|
int size; // How big it is in bytes
|
|
int pack_offset; // For pack files, where it is at. 0 if not in a pack file. This can be used to tell if in a pack file.
|
|
} cf_file;
|
|
|
|
#define CF_NUM_FILES_PER_BLOCK 256
|
|
#define CF_MAX_FILE_BLOCKS 128 // Can store 256*128 = 32768 files
|
|
|
|
typedef struct cf_file_block {
|
|
cf_file files[CF_NUM_FILES_PER_BLOCK];
|
|
} cf_file_block;
|
|
|
|
static int Num_files = 0;
|
|
static cf_file_block *File_blocks[CF_MAX_FILE_BLOCKS];
|
|
|
|
|
|
// Return a pointer to to file 'index'.
|
|
cf_file *cf_get_file(int index)
|
|
{
|
|
int block = index / CF_NUM_FILES_PER_BLOCK;
|
|
int offset = index % CF_NUM_FILES_PER_BLOCK;
|
|
|
|
return &File_blocks[block]->files[offset];
|
|
}
|
|
|
|
// Create a new file and return a pointer to it.
|
|
cf_file *cf_create_file()
|
|
{
|
|
int block = Num_files / CF_NUM_FILES_PER_BLOCK;
|
|
int offset = Num_files % CF_NUM_FILES_PER_BLOCK;
|
|
|
|
if ( File_blocks[block] == NULL ) {
|
|
File_blocks[block] = (cf_file_block *)vm_malloc( sizeof(cf_file_block) );
|
|
Assert( File_blocks[block] != NULL);
|
|
}
|
|
|
|
Num_files++;
|
|
|
|
return &File_blocks[block]->files[offset];
|
|
}
|
|
|
|
extern int cfile_inited;
|
|
|
|
// Create a new root and return a pointer to it. The structure is assumed unitialized.
|
|
cf_root *cf_get_root(int n)
|
|
{
|
|
int block = n / CF_NUM_ROOTS_PER_BLOCK;
|
|
int offset = n % CF_NUM_ROOTS_PER_BLOCK;
|
|
|
|
if (!cfile_inited)
|
|
return NULL;
|
|
|
|
return &Root_blocks[block]->roots[offset];
|
|
}
|
|
|
|
|
|
// Create a new root and return a pointer to it. The structure is assumed unitialized.
|
|
cf_root *cf_create_root()
|
|
{
|
|
int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK;
|
|
int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK;
|
|
|
|
if ( Root_blocks[block] == NULL ) {
|
|
Root_blocks[block] = (cf_root_block *)vm_malloc( sizeof(cf_root_block) );
|
|
Assert(Root_blocks[block] != NULL);
|
|
}
|
|
|
|
Num_roots++;
|
|
|
|
return &Root_blocks[block]->roots[offset];
|
|
}
|
|
|
|
// return the # of packfiles which exist
|
|
int cf_get_packfile_count(cf_root *root)
|
|
{
|
|
char filespec[MAX_PATH_LEN];
|
|
int i;
|
|
int packfile_count;
|
|
|
|
// count up how many packfiles we're gonna have
|
|
packfile_count = 0;
|
|
for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ )
|
|
{
|
|
strcpy( filespec, root->path );
|
|
|
|
if(strlen(Pathtypes[i].path))
|
|
{
|
|
if(strlen(filespec) + strlen(Pathtypes[i].path) + strlen(DIR_SEPARATOR_STR)
|
|
>= sizeof(filespec))
|
|
{
|
|
Warning(LOCATION, "Path '%s' too long to add '%s' safely.", filespec, Pathtypes[i].path);
|
|
continue;
|
|
}
|
|
strcat( filespec, Pathtypes[i].path );
|
|
if ( filespec[strlen(filespec)-1] != DIR_SEPARATOR_CHAR )
|
|
{
|
|
strcat( filespec, DIR_SEPARATOR_STR );
|
|
}
|
|
}
|
|
|
|
#if defined _WIN32
|
|
if(strlen(filespec) + 4 >= sizeof(filespec)) {
|
|
Warning(LOCATION, "Could not concatenate '*.vp' to filespec; path too long.");
|
|
continue;
|
|
}
|
|
strcat( filespec, "*.vp" );
|
|
|
|
int find_handle;
|
|
_finddata_t find;
|
|
|
|
find_handle = _findfirst( filespec, &find );
|
|
|
|
if (find_handle != -1) {
|
|
do {
|
|
if (!(find.attrib & _A_SUBDIR)) {
|
|
packfile_count++;
|
|
}
|
|
|
|
} while (!_findnext(find_handle, &find));
|
|
|
|
_findclose( find_handle );
|
|
}
|
|
#elif defined SCP_UNIX
|
|
if(strlen(filespec) + 10 >= sizeof(filespec)) {
|
|
Warning(LOCATION, "Could not concatenate '*.vp' to filespec; path too long.");
|
|
continue;
|
|
}
|
|
strcat( filespec, "*.[vV][pP]" );
|
|
|
|
glob_t globinfo;
|
|
memset(&globinfo, 0, sizeof(globinfo));
|
|
int status = glob(filespec, 0, NULL, &globinfo);
|
|
if (status == 0) {
|
|
for (unsigned int j = 0; j < globinfo.gl_pathc; j++) {
|
|
// Determine if this is a regular file
|
|
struct stat statbuf;
|
|
memset(&statbuf, 0, sizeof(statbuf));
|
|
stat(globinfo.gl_pathv[j], &statbuf);
|
|
if (S_ISREG(statbuf.st_mode)) {
|
|
packfile_count++;
|
|
}
|
|
}
|
|
globfree(&globinfo);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return packfile_count;
|
|
}
|
|
|
|
// packfile sort function
|
|
int cf_packfile_sort_func(const void *elem1, const void *elem2)
|
|
{
|
|
cf_root_sort *r1, *r2;
|
|
r1 = (cf_root_sort*)elem1;
|
|
r2 = (cf_root_sort*)elem2;
|
|
|
|
// if the 2 directory types are the same, do a string compare
|
|
if(r1->cf_type == r2->cf_type){
|
|
return stricmp(r1->path, r2->path);
|
|
}
|
|
|
|
// otherwise return them in order of CF_TYPE_* precedence
|
|
return (r1->cf_type < r2->cf_type) ? -1 : 1;
|
|
}
|
|
|
|
// Go through a root and look for pack files
|
|
void cf_build_pack_list( cf_root *root )
|
|
{
|
|
char filespec[MAX_PATH_LEN];
|
|
int i;
|
|
cf_root_sort *temp_roots_sort, *rptr_sort;
|
|
int temp_root_count, root_index;
|
|
|
|
// determine how many packfiles there are
|
|
temp_root_count = cf_get_packfile_count(root);
|
|
if(temp_root_count <= 0){
|
|
return;
|
|
}
|
|
|
|
// allocate a temporary array of temporary roots so we can easily sort them
|
|
temp_roots_sort = (cf_root_sort*)vm_malloc(sizeof(cf_root_sort) * temp_root_count);
|
|
if(temp_roots_sort == NULL){
|
|
Int3();
|
|
return;
|
|
}
|
|
|
|
// now just setup all the root info
|
|
root_index = 0;
|
|
for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
|
|
strcpy( filespec, root->path );
|
|
|
|
if(strlen(Pathtypes[i].path)) {
|
|
strcat( filespec, Pathtypes[i].path );
|
|
if ( filespec[strlen(filespec)-1] != DIR_SEPARATOR_CHAR ) {
|
|
strcat( filespec, DIR_SEPARATOR_STR );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#if defined _WIN32
|
|
strcat( filespec, "*.vp" );
|
|
|
|
int find_handle;
|
|
_finddata_t find;
|
|
|
|
find_handle = _findfirst( filespec, &find );
|
|
|
|
if (find_handle != -1) {
|
|
do {
|
|
// add the new item
|
|
if (!(find.attrib & _A_SUBDIR)) {
|
|
Assert(root_index < temp_root_count);
|
|
|
|
// get a temp pointer
|
|
rptr_sort = &temp_roots_sort[root_index++];
|
|
|
|
// fill in all the proper info
|
|
strcpy(rptr_sort->path, root->path);
|
|
|
|
if(strlen(Pathtypes[i].path)) {
|
|
|
|
strcat(rptr_sort->path, Pathtypes[i].path );
|
|
strcat(rptr_sort->path, DIR_SEPARATOR_STR);
|
|
}
|
|
|
|
strcat(rptr_sort->path, find.name );
|
|
rptr_sort->roottype = CF_ROOTTYPE_PACK;
|
|
rptr_sort->cf_type = i;
|
|
}
|
|
|
|
} while (!_findnext(find_handle, &find));
|
|
|
|
_findclose( find_handle );
|
|
}
|
|
#elif defined SCP_UNIX
|
|
strcat( filespec, "*.[vV][pP]" );
|
|
glob_t globinfo;
|
|
memset(&globinfo, 0, sizeof(globinfo));
|
|
int status = glob(filespec, 0, NULL, &globinfo);
|
|
if (status == 0) {
|
|
for (unsigned int j = 0; j < globinfo.gl_pathc; j++) {
|
|
// Determine if this is a regular file
|
|
struct stat statbuf;
|
|
memset(&statbuf, 0, sizeof(statbuf));
|
|
stat(globinfo.gl_pathv[j], &statbuf);
|
|
if (S_ISREG(statbuf.st_mode)) {
|
|
Assert(root_index < temp_root_count);
|
|
|
|
// get a temp pointer
|
|
rptr_sort = &temp_roots_sort[root_index++];
|
|
|
|
// fill in all the proper info
|
|
strcpy(rptr_sort->path, globinfo.gl_pathv[j] );
|
|
rptr_sort->roottype = CF_ROOTTYPE_PACK;
|
|
rptr_sort->cf_type = i;
|
|
}
|
|
}
|
|
globfree(&globinfo);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// these should always be the same
|
|
Assert(root_index == temp_root_count);
|
|
|
|
// sort tht roots
|
|
qsort(temp_roots_sort, temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
|
|
|
|
// now insert them all into the real root list properly
|
|
cf_root *new_root;
|
|
for(i=0; i<temp_root_count; i++){
|
|
new_root = cf_create_root();
|
|
strcpy( new_root->path, root->path );
|
|
|
|
// mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
|
|
// to find the files.
|
|
strcpy(new_root->path, temp_roots_sort[i].path);
|
|
new_root->roottype = CF_ROOTTYPE_PACK;
|
|
}
|
|
|
|
// free up the temp list
|
|
vm_free(temp_roots_sort);
|
|
}
|
|
|
|
|
|
void cf_build_root_list(char *cdrom_dir)
|
|
{
|
|
Num_roots = 0;
|
|
Num_path_roots = 0;
|
|
|
|
cf_root *root;
|
|
char str_temp[CF_MAX_PATHNAME_LENGTH], *cur_pos;
|
|
|
|
#ifdef SCP_UNIX
|
|
// =========================================================================
|
|
// now look for mods under the users HOME directory to use before system ones
|
|
if (Cmdline_mod) {
|
|
for (cur_pos=Cmdline_mod; strlen(cur_pos) != 0; cur_pos+= (strlen(cur_pos)+1))
|
|
{
|
|
memset(str_temp, 0, CF_MAX_PATHNAME_LENGTH);
|
|
strncpy(str_temp, cur_pos, CF_MAX_PATHNAME_LENGTH-1);
|
|
|
|
strncat(str_temp, DIR_SEPARATOR_STR, (CF_MAX_PATHNAME_LENGTH - strlen(str_temp) - 1));
|
|
|
|
// truncated string check
|
|
if ( (strlen(Cfile_user_dir) + strlen(str_temp) + 1) >= CF_MAX_PATHNAME_LENGTH ) {
|
|
Int3();
|
|
}
|
|
|
|
root = cf_create_root();
|
|
|
|
strncpy( root->path, Cfile_user_dir, CF_MAX_PATHNAME_LENGTH-1 );
|
|
|
|
// do we already have a slash? as in the case of a root directory install
|
|
if ( (strlen(root->path) < (CF_MAX_PATHNAME_LENGTH-1)) && (root->path[strlen(root->path)-1] != DIR_SEPARATOR_CHAR) ) {
|
|
strcat(root->path, DIR_SEPARATOR_STR); // put trailing backslash on for easier path construction
|
|
}
|
|
|
|
strncat(root->path, str_temp, (CF_MAX_PATHNAME_LENGTH - strlen(root->path) - 1));
|
|
|
|
root->roottype = CF_ROOTTYPE_PATH;
|
|
cf_build_pack_list(root);
|
|
}
|
|
}
|
|
// =========================================================================
|
|
|
|
// =========================================================================
|
|
// set users HOME directory as default for loading and saving files
|
|
root = cf_create_root();
|
|
strncpy( root->path, Cfile_user_dir, CF_MAX_PATHNAME_LENGTH-1 );
|
|
|
|
// do we already have a slash? as in the case of a root directory install
|
|
if( (strlen(root->path) < (CF_MAX_PATHNAME_LENGTH-1)) && (root->path[strlen(root->path)-1] != DIR_SEPARATOR_CHAR) ) {
|
|
strcat(root->path, DIR_SEPARATOR_STR); // put trailing backslash on for easier path construction
|
|
}
|
|
root->roottype = CF_ROOTTYPE_PATH;
|
|
|
|
// set the default player location to here
|
|
if ( Pilot_file_path == NULL )
|
|
Pilot_file_path = root->path;
|
|
|
|
// Next, check any VP files under the current directory.
|
|
cf_build_pack_list(root);
|
|
// =========================================================================
|
|
#endif
|
|
|
|
if(Cmdline_mod) {
|
|
// stackable Mod support -- Kazan
|
|
//This for statement is a work of art :D
|
|
for (cur_pos=Cmdline_mod; strlen(cur_pos) != 0; cur_pos+= (strlen(cur_pos)+1))
|
|
{
|
|
memset(str_temp, 0, CF_MAX_PATHNAME_LENGTH);
|
|
strncpy(str_temp, cur_pos, CF_MAX_PATHNAME_LENGTH-1);
|
|
|
|
strncat(str_temp, DIR_SEPARATOR_STR, (CF_MAX_PATHNAME_LENGTH - strlen(str_temp) - 1));
|
|
root = cf_create_root();
|
|
|
|
if ( !_getcwd(root->path, CF_MAX_PATHNAME_LENGTH ) ) {
|
|
Error(LOCATION, "Can't get current working directory -- %d", errno );
|
|
}
|
|
|
|
// truncated string check
|
|
if ( (strlen(root->path) + strlen(str_temp) + 1) >= CF_MAX_PATHNAME_LENGTH ) {
|
|
Int3();
|
|
}
|
|
|
|
// do we already have a slash? as in the case of a root directory install
|
|
if ( (strlen(root->path) < (CF_MAX_PATHNAME_LENGTH-1)) && (root->path[strlen(root->path)-1] != DIR_SEPARATOR_CHAR) ) {
|
|
strcat(root->path, DIR_SEPARATOR_STR); // put trailing backslash on for easier path construction
|
|
}
|
|
|
|
strncat(root->path, str_temp, (CF_MAX_PATHNAME_LENGTH - strlen(root->path) - 1));
|
|
root->roottype = CF_ROOTTYPE_PATH;
|
|
cf_build_pack_list(root);
|
|
}
|
|
}
|
|
|
|
root = cf_create_root();
|
|
|
|
if ( !_getcwd(root->path, CF_MAX_PATHNAME_LENGTH ) ) {
|
|
Error(LOCATION, "Can't get current working directory -- %d", errno );
|
|
}
|
|
|
|
// do we already have a slash? as in the case of a root directory install
|
|
if ( (strlen(root->path) < (CF_MAX_PATHNAME_LENGTH-1)) && (root->path[strlen(root->path)-1] != DIR_SEPARATOR_CHAR) ) {
|
|
strcat(root->path, DIR_SEPARATOR_STR); // put trailing backslash on for easier path construction
|
|
}
|
|
|
|
root->roottype = CF_ROOTTYPE_PATH;
|
|
|
|
// set the default path for pilot files
|
|
if ( Pilot_file_path == NULL )
|
|
Pilot_file_path = root->path;
|
|
|
|
//======================================================
|
|
// Next, check any VP files under the current directory.
|
|
cf_build_pack_list(root);
|
|
|
|
|
|
//======================================================
|
|
// Check the real CD if one...
|
|
if ( cdrom_dir && (strlen(cdrom_dir) < CF_MAX_PATHNAME_LENGTH) ) {
|
|
root = cf_create_root();
|
|
strcpy( root->path, cdrom_dir );
|
|
root->roottype = CF_ROOTTYPE_PATH;
|
|
|
|
//======================================================
|
|
// Next, check any VP files in the CD-ROM directory.
|
|
cf_build_pack_list(root);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Given a lower case list of file extensions
|
|
// separated by spaces, return zero if ext is
|
|
// not in the list.
|
|
int is_ext_in_list( char *ext_list, char *ext )
|
|
{
|
|
char tmp_ext[128];
|
|
|
|
strncpy( tmp_ext, ext, 127 );
|
|
strlwr(tmp_ext);
|
|
if ( strstr(ext_list, tmp_ext )) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cf_search_root_path(int root_index)
|
|
{
|
|
int i;
|
|
|
|
cf_root *root = cf_get_root(root_index);
|
|
|
|
mprintf(( "Searching root '%s'\n", root->path ));
|
|
|
|
char search_path[CF_MAX_PATHNAME_LENGTH];
|
|
|
|
for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
|
|
|
|
// we don't want to add player files to the cache - taylor
|
|
if ( (i == CF_TYPE_SINGLE_PLAYERS) || (i == CF_TYPE_MULTI_PLAYERS) ) {
|
|
continue;
|
|
}
|
|
|
|
strcpy( search_path, root->path );
|
|
|
|
if(strlen(Pathtypes[i].path)) {
|
|
strcat( search_path, Pathtypes[i].path );
|
|
if ( search_path[strlen(search_path)-1] != DIR_SEPARATOR_CHAR ) {
|
|
strcat( search_path, DIR_SEPARATOR_STR );
|
|
}
|
|
}
|
|
|
|
#if defined _WIN32
|
|
strcat( search_path, "*.*" );
|
|
|
|
int find_handle;
|
|
_finddata_t find;
|
|
|
|
find_handle = _findfirst( search_path, &find );
|
|
|
|
if (find_handle != -1) {
|
|
do {
|
|
if (!(find.attrib & _A_SUBDIR)) {
|
|
|
|
char *ext = strrchr( find.name, '.' );
|
|
if ( ext ) {
|
|
if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
|
|
// Found a file!!!!
|
|
cf_file *file = cf_create_file();
|
|
|
|
strcpy( file->name_ext, find.name );
|
|
file->root_index = root_index;
|
|
file->pathtype_index = i;
|
|
file->write_time = find.time_write;
|
|
file->size = find.size;
|
|
file->pack_offset = 0; // Mark as a non-packed file
|
|
|
|
//mprintf(( "Found file '%s'\n", file->name_ext ));
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
} while (!_findnext(find_handle, &find));
|
|
|
|
_findclose( find_handle );
|
|
}
|
|
#elif defined SCP_UNIX
|
|
DIR *dirp;
|
|
struct dirent *dir;
|
|
|
|
dirp = opendir (search_path);
|
|
if ( dirp ) {
|
|
while ((dir = readdir (dirp)) != NULL)
|
|
{
|
|
if (!fnmatch ("*.*", dir->d_name, 0))
|
|
{
|
|
char fn[MAX_PATH];
|
|
snprintf(fn, MAX_PATH-1, "%s%s", search_path, dir->d_name);
|
|
fn[MAX_PATH-1] = 0;
|
|
|
|
struct stat buf;
|
|
if (stat(fn, &buf) == -1) {
|
|
continue;
|
|
}
|
|
|
|
if (!S_ISREG(buf.st_mode)) {
|
|
continue;
|
|
}
|
|
|
|
char *ext = strrchr( dir->d_name, '.' );
|
|
if ( ext ) {
|
|
if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
|
|
// Found a file!!!!
|
|
cf_file *file = cf_create_file();
|
|
|
|
strcpy( file->name_ext, dir->d_name );
|
|
file->root_index = root_index;
|
|
file->pathtype_index = i;
|
|
|
|
|
|
file->write_time = buf.st_mtime;
|
|
file->size = buf.st_size;
|
|
|
|
file->pack_offset = 0; // Mark as a non-packed file
|
|
|
|
//mprintf(( "Found file '%s'\n", file->name_ext ));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
closedir(dirp);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct VP_FILE_HEADER {
|
|
char id[4];
|
|
int version;
|
|
int index_offset;
|
|
int num_files;
|
|
} VP_FILE_HEADER;
|
|
|
|
typedef struct VP_FILE {
|
|
int offset;
|
|
int size;
|
|
char filename[32];
|
|
_fs_time_t write_time;
|
|
} VP_FILE;
|
|
|
|
void cf_search_root_pack(int root_index)
|
|
{
|
|
cf_root *root = cf_get_root(root_index);
|
|
|
|
Assert( root != NULL );
|
|
mprintf(( "Searching root pack '%s'\n", root->path ));
|
|
|
|
// Open data
|
|
FILE *fp = fopen( root->path, "rb" );
|
|
// Read the file header
|
|
if (!fp) {
|
|
return;
|
|
}
|
|
|
|
VP_FILE_HEADER VP_header;
|
|
|
|
Assert( sizeof(VP_header) == 16 );
|
|
fread(&VP_header, 1, sizeof(VP_header), fp);
|
|
|
|
VP_header.version = INTEL_INT( VP_header.version );
|
|
VP_header.index_offset = INTEL_INT( VP_header.index_offset );
|
|
VP_header.num_files = INTEL_INT( VP_header.num_files );
|
|
|
|
// Read index info
|
|
fseek(fp, VP_header.index_offset, SEEK_SET);
|
|
|
|
char search_path[CF_MAX_PATHNAME_LENGTH];
|
|
|
|
strcpy( search_path, "" );
|
|
|
|
// Go through all the files
|
|
int i;
|
|
for (i=0; i<VP_header.num_files; i++ ) {
|
|
VP_FILE find;
|
|
|
|
fread( &find, sizeof(VP_FILE), 1, fp );
|
|
|
|
find.offset = INTEL_INT( find.offset );
|
|
find.size = INTEL_INT( find.size );
|
|
find.write_time = INTEL_INT( find.write_time );
|
|
|
|
if ( find.size == 0 ) {
|
|
if ( !stricmp( find.filename, ".." )) {
|
|
int l = strlen(search_path);
|
|
char *p = &search_path[l-1];
|
|
while( (p > search_path) && (*p != DIR_SEPARATOR_CHAR) ) {
|
|
p--;
|
|
}
|
|
*p = 0;
|
|
} else {
|
|
if ( strlen(search_path) && (search_path[strlen(search_path)-1] != DIR_SEPARATOR_CHAR) ) {
|
|
strcat( search_path, DIR_SEPARATOR_STR );
|
|
}
|
|
strcat( search_path, find.filename );
|
|
}
|
|
|
|
//mprintf(( "Current dir = '%s'\n", search_path ));
|
|
} else {
|
|
|
|
int j;
|
|
|
|
for (j=CF_TYPE_ROOT; j<CF_MAX_PATH_TYPES; j++ ) {
|
|
|
|
if ( !stricmp( search_path, Pathtypes[j].path )) {
|
|
char *ext = strrchr( find.filename, '.' );
|
|
if ( ext ) {
|
|
if ( is_ext_in_list( Pathtypes[j].extensions, ext ) ) {
|
|
// Found a file!!!!
|
|
cf_file *file = cf_create_file();
|
|
strcpy( file->name_ext, find.filename );
|
|
file->root_index = root_index;
|
|
file->pathtype_index = j;
|
|
file->write_time = (time_t)find.write_time;
|
|
file->size = find.size;
|
|
file->pack_offset = find.offset; // Mark as a packed file
|
|
//mprintf(( "Found pack file '%s'\n", file->name_ext ));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cf_build_file_list()
|
|
{
|
|
int i;
|
|
|
|
Num_files = 0;
|
|
|
|
// For each root, find all files...
|
|
for (i=0; i<Num_roots; i++ ) {
|
|
cf_root *root = cf_get_root(i);
|
|
if ( root->roottype == CF_ROOTTYPE_PATH ) {
|
|
cf_search_root_path(i);
|
|
} else if ( root->roottype == CF_ROOTTYPE_PACK ) {
|
|
cf_search_root_pack(i);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void cf_build_secondary_filelist(char *cdrom_dir)
|
|
{
|
|
int i;
|
|
|
|
// Assume no files
|
|
Num_roots = 0;
|
|
Num_files = 0;
|
|
|
|
// Init the root blocks
|
|
for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
|
|
Root_blocks[i] = NULL;
|
|
}
|
|
|
|
// Init the file blocks
|
|
for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
|
|
File_blocks[i] = NULL;
|
|
}
|
|
|
|
mprintf(( "Building file index...\n" ));
|
|
|
|
// build the list of searchable roots
|
|
cf_build_root_list(cdrom_dir);
|
|
|
|
// build the list of files themselves
|
|
cf_build_file_list();
|
|
|
|
mprintf(( "Found %d roots and %d files.\n", Num_roots, Num_files ));
|
|
}
|
|
|
|
void cf_free_secondary_filelist()
|
|
{
|
|
int i;
|
|
|
|
// Free the root blocks
|
|
for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
|
|
if ( Root_blocks[i] ) {
|
|
vm_free( Root_blocks[i] );
|
|
Root_blocks[i] = NULL;
|
|
}
|
|
}
|
|
Num_roots = 0;
|
|
|
|
// Init the file blocks
|
|
for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
|
|
if ( File_blocks[i] ) {
|
|
vm_free( File_blocks[i] );
|
|
File_blocks[i] = NULL;
|
|
}
|
|
}
|
|
Num_files = 0;
|
|
}
|
|
|
|
// Searches for a file. Follows all rules and precedence and searches
|
|
// CD's and pack files.
|
|
// Input: filespace - Filename & extension
|
|
// pathtype - See CF_TYPE_ defines in CFILE.H
|
|
// Output: pack_filename - Absolute path and filename of this file. Could be a packfile or the actual file.
|
|
// size - File size
|
|
// offset - Offset into pack file. 0 if not a packfile.
|
|
// Returns: If not found returns 0.
|
|
int cf_find_file_location( char *filespec, int filespec_max, int pathtype, char *pack_filename, int *size, int *offset, bool localize )
|
|
{
|
|
int i;
|
|
int cfs_slow_search = 0;
|
|
|
|
Assert(filespec && strlen(filespec));
|
|
|
|
// see if we have something other than just a filename
|
|
// our current rules say that any file that specifies a direct
|
|
// path will try to be opened on that path. If that open
|
|
// fails, then we will open the file based on the extension
|
|
// of the file
|
|
|
|
// NOTE: full path should also include localization, if so desired
|
|
#ifdef SCP_UNIX
|
|
if ( strpbrk(filespec, "/") ) { // do we have a full path already?
|
|
#else
|
|
if ( strpbrk(filespec,"/\\:") ) { // do we have a full path already?
|
|
#endif
|
|
FILE *fp = fopen(filespec, "rb" );
|
|
if (fp) {
|
|
if ( size ) *size = filelength(fileno(fp));
|
|
if ( offset ) *offset = 0;
|
|
if ( pack_filename ) {
|
|
strcpy( pack_filename, filespec );
|
|
}
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
|
|
return 0; // If they give a full path, fail if not found.
|
|
}
|
|
|
|
// Search the hard drive for files first.
|
|
int num_search_dirs = 0;
|
|
int search_order[CF_MAX_PATH_TYPES];
|
|
|
|
if ( CF_TYPE_SPECIFIED(pathtype) ) {
|
|
search_order[num_search_dirs++] = pathtype;
|
|
}
|
|
|
|
for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++) {
|
|
if ( i != pathtype ) {
|
|
search_order[num_search_dirs++] = i;
|
|
}
|
|
}
|
|
|
|
#if defined WIN32
|
|
long findhandle;
|
|
_finddata_t findstruct;
|
|
#endif
|
|
|
|
for (i=0; i<num_search_dirs; i++ ) {
|
|
char longname[MAX_PATH_LEN];
|
|
|
|
switch (search_order[i]) {
|
|
case CF_TYPE_ROOT:
|
|
case CF_TYPE_DATA:
|
|
case CF_TYPE_SINGLE_PLAYERS:
|
|
case CF_TYPE_MULTI_PLAYERS:
|
|
case CF_TYPE_MULTI_CACHE:
|
|
case CF_TYPE_MISSIONS:
|
|
case CF_TYPE_CACHE:
|
|
cfs_slow_search = 1;
|
|
break;
|
|
|
|
default:
|
|
cfs_slow_search = 0;
|
|
break;
|
|
}
|
|
|
|
if (cfs_slow_search) {
|
|
cf_create_default_path_string( longname, sizeof(longname)-1, search_order[i], filespec, localize );
|
|
|
|
#if defined _WIN32
|
|
if(!Cmdline_safeloading)
|
|
{
|
|
findhandle = _findfirst(longname, &findstruct);
|
|
if (findhandle != -1) {
|
|
if ( size ) {
|
|
*size = findstruct.size;
|
|
}
|
|
|
|
_findclose(findhandle);
|
|
|
|
if ( offset ) *offset = 0;
|
|
if ( pack_filename ) {
|
|
strcpy( pack_filename, longname );
|
|
};
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FILE *fp = fopen(longname, "rb" );
|
|
if(fp) {
|
|
if( size ) {
|
|
*size = filelength( fileno(fp) );
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
if(offset) *offset = 0;
|
|
if( pack_filename ) {
|
|
strcpy(pack_filename, longname);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
#elif defined SCP_UNIX
|
|
FILE *fp = fopen(longname, "rb" );
|
|
if (fp) {
|
|
if ( size ) {
|
|
struct stat statbuf;
|
|
fstat(fileno(fp), &statbuf);
|
|
*size = statbuf.st_size;
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
if ( offset ) *offset = 0;
|
|
if ( pack_filename ) {
|
|
strcpy( pack_filename, longname );
|
|
}
|
|
return 1;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Search the pak files and CD-ROM.
|
|
|
|
for (i=0; i<Num_files; i++ ) {
|
|
cf_file * f = cf_get_file(i);
|
|
|
|
// only search paths we're supposed to...
|
|
if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
|
|
continue;
|
|
}
|
|
|
|
if (localize) {
|
|
// create localized filespec
|
|
char *temp = new char[strlen(filespec)+1];
|
|
strcpy(temp, filespec);
|
|
if(lcl_add_dir_to_path_with_filename(filespec, filespec_max))
|
|
{
|
|
if ( !stricmp(filespec, f->name_ext) ) {
|
|
if ( size ) *size = f->size;
|
|
if ( offset ) *offset = f->pack_offset;
|
|
if ( pack_filename ) {
|
|
cf_root * r = cf_get_root(f->root_index);
|
|
|
|
strcpy( pack_filename, r->path );
|
|
if ( f->pack_offset < 1 ) {
|
|
strcat( pack_filename, Pathtypes[f->pathtype_index].path );
|
|
if ( pack_filename[strlen(pack_filename)-1] != DIR_SEPARATOR_CHAR ) {
|
|
strcat( pack_filename, DIR_SEPARATOR_STR );
|
|
}
|
|
strcat( pack_filename, f->name_ext );
|
|
}
|
|
}
|
|
delete[] temp;
|
|
return 1;
|
|
}
|
|
}
|
|
// restore original filespec
|
|
strcpy(filespec, temp);
|
|
delete[] temp;
|
|
}
|
|
|
|
// file either not localized or localized version not found
|
|
if ( !stricmp(filespec, f->name_ext) ) {
|
|
if ( size ) *size = f->size;
|
|
if ( offset ) *offset = f->pack_offset;
|
|
if ( pack_filename ) {
|
|
cf_root * r = cf_get_root(f->root_index);
|
|
|
|
strcpy( pack_filename, r->path );
|
|
if ( f->pack_offset < 1 ) {
|
|
|
|
if(strlen(Pathtypes[f->pathtype_index].path)){
|
|
strcat( pack_filename, Pathtypes[f->pathtype_index].path );
|
|
if ( pack_filename[strlen(pack_filename)-1] != DIR_SEPARATOR_CHAR ) {
|
|
strcat( pack_filename, DIR_SEPARATOR_STR );
|
|
}
|
|
}
|
|
|
|
strcat( pack_filename, f->name_ext );
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Returns true if filename matches filespec, else zero if not
|
|
int cf_matches_spec(char *filespec, char *filename)
|
|
{
|
|
char *src_ext, *dst_ext;
|
|
|
|
src_ext = strrchr(filespec, '*');
|
|
if(!src_ext)
|
|
{
|
|
src_ext = strrchr(filespec, '.');
|
|
if (!src_ext)
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
src_ext++;
|
|
}
|
|
|
|
if(strlen(filespec) > strlen(filename))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
dst_ext = filename + strlen(filename) - ((filespec + strlen(filespec)) - src_ext);
|
|
if (!dst_ext)
|
|
return 1;
|
|
|
|
if(src_ext == filespec)
|
|
{
|
|
return !stricmp(dst_ext, src_ext);
|
|
}
|
|
else
|
|
{
|
|
return (!stricmp(dst_ext, src_ext) && !strnicmp(dst_ext, src_ext, src_ext - filespec));
|
|
}
|
|
}
|
|
|
|
int (*Get_file_list_filter)(char *filename) = NULL;
|
|
int Skip_packfile_search = 0;
|
|
|
|
int cf_file_already_in_list( int num_files, char **list, char *filename )
|
|
{
|
|
int i;
|
|
|
|
char name_no_extension[MAX_PATH_LEN];
|
|
|
|
strcpy(name_no_extension, filename );
|
|
char *p = strrchr( name_no_extension, '.' );
|
|
if ( p ) *p = 0;
|
|
|
|
for (i=0; i<num_files; i++ ) {
|
|
if ( !stricmp(list[i], name_no_extension ) ) {
|
|
// Match found!
|
|
return 1;
|
|
}
|
|
}
|
|
// Not found
|
|
return 0;
|
|
}
|
|
|
|
// An alternative cf_get_file_list(), dynamic list version.
|
|
// This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
|
|
// location, 'filter' only needs to be the filter itself, with no path information.
|
|
// See above descriptions of cf_get_file_list() for more information about how it all works.
|
|
int cf_get_file_list( int max, char **list, int pathtype, char *filter, int sort, file_list_info *info )
|
|
{
|
|
char *ptr;
|
|
int i, l, num_files = 0, own_flag = 0;
|
|
|
|
if (max < 1) {
|
|
Get_file_list_filter = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
Assert(list);
|
|
|
|
if (!info && (sort == CF_SORT_TIME)) {
|
|
info = (file_list_info *) vm_malloc(sizeof(file_list_info) * max);
|
|
own_flag = 1;
|
|
}
|
|
|
|
char filespec[MAX_PATH_LEN];
|
|
|
|
#if defined _WIN32
|
|
cf_create_default_path_string( filespec, sizeof(filespec)-1, pathtype, filter );
|
|
|
|
_finddata_t find;
|
|
int find_handle;
|
|
|
|
find_handle = _findfirst( filespec, &find );
|
|
if (find_handle != -1) {
|
|
do {
|
|
if (num_files >= max)
|
|
|
|
break;
|
|
|
|
if (!(find.attrib & _A_SUBDIR)) {
|
|
if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
|
|
ptr = strrchr(find.name, '.');
|
|
if (ptr)
|
|
l = ptr - find.name;
|
|
else
|
|
l = strlen(find.name);
|
|
|
|
list[num_files] = (char *)vm_malloc(l + 1);
|
|
strncpy(list[num_files], find.name, l);
|
|
list[num_files][l] = 0;
|
|
if (info)
|
|
info[num_files].write_time = find.time_write;
|
|
|
|
num_files++;
|
|
}
|
|
}
|
|
|
|
} while (!_findnext(find_handle, &find));
|
|
|
|
_findclose( find_handle );
|
|
}
|
|
|
|
#elif defined SCP_UNIX
|
|
cf_create_default_path_string( filespec, sizeof(filespec)-1, pathtype, NULL );
|
|
|
|
DIR *dirp;
|
|
struct dirent *dir;
|
|
|
|
dirp = opendir (filespec);
|
|
if ( dirp ) {
|
|
while ((dir = readdir (dirp)) != NULL)
|
|
{
|
|
if (num_files >= max)
|
|
break;
|
|
|
|
if (fnmatch(filter, dir->d_name, 0) != 0)
|
|
continue;
|
|
|
|
char fn[MAX_PATH];
|
|
snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
|
|
fn[MAX_PATH-1] = 0;
|
|
|
|
struct stat buf;
|
|
if (stat(fn, &buf) == -1) {
|
|
continue;
|
|
}
|
|
|
|
if (!S_ISREG(buf.st_mode)) {
|
|
continue;
|
|
}
|
|
|
|
if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
|
|
ptr = strrchr(dir->d_name, '.');
|
|
if (ptr)
|
|
l = ptr - dir->d_name;
|
|
else
|
|
l = strlen(dir->d_name);
|
|
|
|
list[num_files] = (char *)vm_malloc(l + 1);
|
|
strncpy(list[num_files], dir->d_name, l);
|
|
list[num_files][l] = 0;
|
|
if (info)
|
|
info[num_files].write_time = buf.st_mtime;
|
|
|
|
num_files++;
|
|
}
|
|
}
|
|
|
|
closedir(dirp);
|
|
}
|
|
#endif
|
|
|
|
// Search all the packfiles and CD.
|
|
if ( !Skip_packfile_search ) {
|
|
for (i=0; i<Num_files; i++ ) {
|
|
cf_file * f = cf_get_file(i);
|
|
|
|
// only search paths we're supposed to...
|
|
if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
|
|
continue;
|
|
}
|
|
|
|
if (num_files >= max)
|
|
break;
|
|
|
|
if ( !cf_matches_spec( filter,f->name_ext)) {
|
|
continue;
|
|
}
|
|
|
|
if ( cf_file_already_in_list(num_files,list,f->name_ext)) {
|
|
continue;
|
|
}
|
|
|
|
if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
|
|
|
|
//mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
|
|
|
|
ptr = strrchr(f->name_ext, '.');
|
|
if (ptr)
|
|
l = ptr - f->name_ext;
|
|
else
|
|
l = strlen(f->name_ext);
|
|
|
|
list[num_files] = (char *)vm_malloc(l + 1);
|
|
strncpy(list[num_files], f->name_ext, l);
|
|
list[num_files][l] = 0;
|
|
|
|
if (info) {
|
|
info[num_files].write_time = f->write_time;
|
|
}
|
|
|
|
num_files++;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if (sort != CF_SORT_NONE) {
|
|
cf_sort_filenames( num_files, list, sort, info );
|
|
}
|
|
|
|
if (own_flag) {
|
|
vm_free(info);
|
|
}
|
|
|
|
Get_file_list_filter = NULL;
|
|
|
|
return num_files;
|
|
}
|
|
|
|
int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], char *filename )
|
|
{
|
|
int i;
|
|
|
|
char name_no_extension[MAX_PATH_LEN];
|
|
|
|
strcpy(name_no_extension, filename );
|
|
char *p = strrchr( name_no_extension, '.' );
|
|
if ( p ) *p = 0;
|
|
|
|
for (i=0; i<num_files; i++ ) {
|
|
if ( !stricmp(arr[i], name_no_extension ) ) {
|
|
// Match found!
|
|
return 1;
|
|
}
|
|
}
|
|
// Not found
|
|
return 0;
|
|
}
|
|
|
|
// An alternative cf_get_file_list(), fixed array version.
|
|
// This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
|
|
// location, 'filter' only needs to be the filter itself, with no path information.
|
|
// See above descriptions of cf_get_file_list() for more information about how it all works.
|
|
int cf_get_file_list_preallocated( int max, char arr[][MAX_FILENAME_LEN], char **list, int pathtype, char *filter, int sort, file_list_info *info )
|
|
{
|
|
int i, num_files = 0, own_flag = 0;
|
|
|
|
if (max < 1) {
|
|
Get_file_list_filter = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (list) {
|
|
for (i=0; i<max; i++) {
|
|
list[i] = arr[i];
|
|
}
|
|
} else {
|
|
sort = CF_SORT_NONE; // sorting of array directly not supported. Sorting done on list only
|
|
}
|
|
|
|
if (!info && (sort == CF_SORT_TIME)) {
|
|
info = (file_list_info *) vm_malloc(sizeof(file_list_info) * max);
|
|
|
|
if ( info )
|
|
own_flag = 1;
|
|
}
|
|
|
|
char filespec[MAX_PATH_LEN];
|
|
|
|
// Search the default directories
|
|
#if defined _WIN32
|
|
cf_create_default_path_string( filespec, sizeof(filespec)-1, pathtype, filter );
|
|
mprintf(("cf_get_file_list_preallocated looking for type=%d, filter=\"%s\"\n",
|
|
pathtype, filter));
|
|
|
|
int find_handle;
|
|
_finddata_t find;
|
|
|
|
find_handle = _findfirst( filespec, &find );
|
|
if (find_handle != -1) {
|
|
do {
|
|
if (num_files >= max)
|
|
break;
|
|
|
|
if (!(find.attrib & _A_SUBDIR)) {
|
|
|
|
if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
|
|
|
|
strncpy(arr[num_files], find.name, MAX_FILENAME_LEN - 1 );
|
|
char *ptr = strrchr(arr[num_files], '.');
|
|
if ( ptr ) {
|
|
*ptr = 0;
|
|
}
|
|
|
|
if (info) {
|
|
info[num_files].write_time = find.time_write;
|
|
}
|
|
|
|
num_files++;
|
|
}
|
|
}
|
|
|
|
} while (!_findnext(find_handle, &find));
|
|
|
|
_findclose( find_handle );
|
|
}
|
|
|
|
#elif defined SCP_UNIX
|
|
cf_create_default_path_string( filespec, sizeof(filespec)-1, pathtype, NULL );
|
|
|
|
DIR *dirp;
|
|
struct dirent *dir;
|
|
|
|
dirp = opendir (filespec);
|
|
if ( dirp ) {
|
|
while ((dir = readdir (dirp)) != NULL)
|
|
{
|
|
if (num_files >= max)
|
|
break;
|
|
|
|
if (fnmatch(filter, dir->d_name, 0) != 0)
|
|
continue;
|
|
|
|
char fn[MAX_PATH];
|
|
snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
|
|
fn[MAX_PATH-1] = 0;
|
|
|
|
struct stat buf;
|
|
if (stat(fn, &buf) == -1) {
|
|
continue;
|
|
}
|
|
|
|
if (!S_ISREG(buf.st_mode)) {
|
|
continue;
|
|
}
|
|
|
|
if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
|
|
|
|
strncpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN - 1 );
|
|
char *ptr = strrchr(arr[num_files], '.');
|
|
if ( ptr ) {
|
|
*ptr = 0;
|
|
}
|
|
|
|
if (info) {
|
|
info[num_files].write_time = buf.st_mtime;
|
|
}
|
|
|
|
num_files++;
|
|
}
|
|
}
|
|
closedir(dirp);
|
|
}
|
|
#endif
|
|
|
|
// Search all the packfiles and CD.
|
|
if ( !Skip_packfile_search ) {
|
|
for (i=0; i<Num_files; i++ ) {
|
|
cf_file * f = cf_get_file(i);
|
|
|
|
// only search paths we're supposed to...
|
|
if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
|
|
continue;
|
|
}
|
|
|
|
if (num_files >= max)
|
|
|
|
break;
|
|
|
|
if ( !cf_matches_spec( filter,f->name_ext)) {
|
|
continue;
|
|
}
|
|
|
|
if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext )) {
|
|
continue;
|
|
}
|
|
|
|
if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
|
|
|
|
//mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
|
|
|
|
strncpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN - 1 );
|
|
char *ptr = strrchr(arr[num_files], '.');
|
|
if ( ptr ) {
|
|
*ptr = 0;
|
|
}
|
|
|
|
if (info) {
|
|
info[num_files].write_time = f->write_time;
|
|
}
|
|
|
|
num_files++;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (sort != CF_SORT_NONE) {
|
|
Assert(list);
|
|
cf_sort_filenames( num_files, list, sort, info );
|
|
}
|
|
|
|
if (own_flag) {
|
|
vm_free(info);
|
|
}
|
|
|
|
Get_file_list_filter = NULL;
|
|
|
|
return num_files;
|
|
}
|
|
|
|
// Returns the default storage path for files given a
|
|
// particular pathtype. In other words, the path to
|
|
// the unpacked, non-cd'd, stored on hard drive path.
|
|
// If filename isn't null it will also tack the filename
|
|
// on the end, creating a completely valid filename.
|
|
// Input: pathtype - CF_TYPE_??
|
|
// path_max - Maximum characters in the path
|
|
// filename - optional, if set, tacks the filename onto end of path.
|
|
// Output: path - Fully qualified pathname.
|
|
//Returns 0 if the result would be too long (invalid result)
|
|
int cf_create_default_path_string( char *path, uint path_max, int pathtype, char *filename, bool localize )
|
|
{
|
|
#ifdef SCP_UNIX
|
|
if ( filename && strpbrk(filename,"/") ) {
|
|
#else
|
|
if ( filename && strpbrk(filename,"/\\:") ) {
|
|
#endif
|
|
// Already has full path
|
|
//check it's not too long
|
|
if(strlen(filename) > path_max) {
|
|
return 0;
|
|
}
|
|
strcpy( path, filename );
|
|
|
|
} else {
|
|
cf_root *root = cf_get_root(0);
|
|
|
|
if (!root) {
|
|
if(strlen(filename) > path_max) {
|
|
return 0;
|
|
}
|
|
strcpy(path, filename);
|
|
return 1;
|
|
}
|
|
|
|
Assert(CF_TYPE_SPECIFIED(pathtype));
|
|
|
|
// force a specific directory to search for player files
|
|
if ( (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS) )
|
|
{
|
|
//check it's not too long
|
|
if(strlen(Pilot_file_path) > path_max) {
|
|
return 0;
|
|
}
|
|
strcpy(path, Pilot_file_path);
|
|
} else {
|
|
//check it's not too long
|
|
if(strlen(root->path) > path_max) {
|
|
return 0;
|
|
}
|
|
strcpy(path, root->path);
|
|
}
|
|
|
|
//check that path isn't too long
|
|
if(strlen(path) + strlen(Pathtypes[pathtype].path) + strlen(DIR_SEPARATOR_STR) > path_max) {
|
|
return 0;
|
|
}
|
|
strcat(path, Pathtypes[pathtype].path);
|
|
|
|
// Don't add slash for root directory
|
|
if (Pathtypes[pathtype].path[0] != '\0')
|
|
{
|
|
if ( path[strlen(path)-1] != DIR_SEPARATOR_CHAR )
|
|
{
|
|
if(strlen(path) + strlen(DIR_SEPARATOR_STR) > path_max) {
|
|
return 0;
|
|
}
|
|
|
|
strcat(path, DIR_SEPARATOR_STR);
|
|
}
|
|
}
|
|
|
|
#ifdef INF_BUILD
|
|
// keep pilot files separated for an Inferno build since they aren't compatible
|
|
if ( pathtype == CF_TYPE_SINGLE_PLAYERS )
|
|
{
|
|
if(strlen(path) + strlen("inferno") + strlen(DIR_SEPARATOR_STR) > path_max) {
|
|
return 0;
|
|
}
|
|
strcat(path, "inferno");
|
|
strcat(path, DIR_SEPARATOR_STR);
|
|
}
|
|
#endif
|
|
|
|
// add filename
|
|
if (filename)
|
|
{
|
|
if(strlen(path) + strlen(filename) > path_max) {
|
|
return 0;
|
|
}
|
|
strcat(path, filename);
|
|
|
|
// localize filename
|
|
if (localize)
|
|
{
|
|
// create copy of path
|
|
char *temp_path = new char[path_max+1];
|
|
strcpy(temp_path, path);
|
|
|
|
// localize the path
|
|
if(lcl_add_dir_to_path_with_filename(path, path_max))
|
|
{
|
|
|
|
// verify localized path
|
|
FILE *fp = fopen(path, "rb");
|
|
if (fp)
|
|
{
|
|
fclose(fp);
|
|
delete[] temp_path;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
//If localization didn't work, go back to the original string
|
|
strcpy(path, temp_path);
|
|
delete[] temp_path;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|