FILE_WRITE_TO_MOD is now FILE_WRITE_TO_TARGET

ERR_(FILE|PATH)_NOT_FOUND -> ERR_TNODE_NOT_FOUND, ERR_NOT_(FILE|DIR) ->
ERR_TNODE_WRONG TYPE
path_util:
- path_append: bugfix (no more extraneous /); add support for appending
slash
- path_replace: bugfix (no more warnings for expected error)
- add path_last_component, path_foreach_component
- move some defs to the header that belong there from vfs.h/path.h

file: add dir_create; move PosixFile definition here and provide
accessor for fileio
vfs: no longer automatically append slash; instead, make sure caller
does
vfs_mount:
- mount_realpath: bugfix (strip trailing /), interface improvement
- document write_target; clarify MULTIPLE_MOUNTINGS; add support for
creating real dirs
vfs_tree:
- refactor TDir::add into find, add, find_and_add
- fix TDir and TNode for dir-ends-with-slash requirement
- split up lookup into lookup_cb and path_foreach_component
- add support for inserting dirs with given mount point: tree_add_path
(needed when mounting)

wposix: get rid of weird PATH_MAX = 260 (Win32 weirdness)


TextureManager: remove no longer needed SupportedTextureFormats
GameSetup, Xeromyces: setup XMB location to data/cache/mods/official/xmb
Util: HardcodedErrorString now uses error_description_r
VFSUtil, i18n: fixes for dir slash issue

Closes #79, #80

This was SVN commit r3874.
This commit is contained in:
janwas 2006-05-17 14:48:18 +00:00
parent 58bed083ba
commit e6be7e36d2
37 changed files with 565 additions and 269 deletions

View File

@ -156,7 +156,7 @@ void CMapWriter::PackTerrain(CFilePacker& packer, CTerrain *pTerrain)
void CMapWriter::WriteXML(const char* filename, CUnitManager* pUnitMan, CLightEnv *pLightEnv, CCamera *pCamera)
{
Handle h = vfs_open(filename, FILE_WRITE_TO_MOD|FILE_NO_AIO);
Handle h = vfs_open(filename, FILE_WRITE_TO_TARGET|FILE_NO_AIO);
if (h <= 0)
{
debug_warn("Failed to open map XML file");

View File

@ -19,9 +19,6 @@
using namespace std;
// filter for vfs_next_dirent
static const char* SupportedTextureFormats[] = { "*.png", "*.dds", "*.tga", "*.bmp" };
CTextureManager::CTextureManager():
m_LastGroupIndex(0)
{}
@ -176,7 +173,7 @@ void CTextureManager::RecurseDirectory(CTerrainPropertiesPtr parentProps, const
int CTextureManager::LoadTerrainTextures()
{
RecurseDirectory(CTerrainPropertiesPtr(), "art/textures/terrain/types");
RecurseDirectory(CTerrainPropertiesPtr(), "art/textures/terrain/types/");
return 0;
}

View File

@ -124,7 +124,7 @@ LibError in_playback(const char* fn)
f = fopen(fn, "rb");
if(!f)
WARN_RETURN(ERR_FILE_NOT_FOUND);
WARN_RETURN(ERR_TNODE_NOT_FOUND);
u32 rec_start_time;
fread(&rec_start_time, sizeof(u32), 1, f);

View File

@ -85,7 +85,7 @@ LibError LibError_from_errno(bool warn_if_failed)
case EINVAL: err = ERR_INVALID_PARAM; break;
case ENOSYS: err = ERR_NOT_IMPLEMENTED; break;
case ENOENT: err = ERR_PATH_NOT_FOUND; break;
case ENOENT: err = ERR_TNODE_NOT_FOUND; break;
case EACCES: err = ERR_FILE_ACCESS; break;
case EIO: err = ERR_IO; break;
case ENAMETOOLONG: err = ERR_PATH_LENGTH; break;
@ -122,7 +122,7 @@ static int return_errno_from_LibError(LibError err)
case ERR_INVALID_PARAM: return EINVAL;
case ERR_NOT_IMPLEMENTED: return ENOSYS;
case ERR_PATH_NOT_FOUND: return ENOENT;
case ERR_TNODE_NOT_FOUND: return ENOENT;
case ERR_FILE_ACCESS: return EACCES;
case ERR_IO: return EIO;
case ERR_PATH_LENGTH: return ENAMETOOLONG;

View File

@ -425,28 +425,27 @@ ERR(-100302, ERR_PATH_NOT_RELATIVE, "Path is not relative")
ERR(-100303, ERR_PATH_NON_PORTABLE, "Path contains OS-specific dir separator")
ERR(-100304, ERR_PATH_NON_CANONICAL, "Path contains unsupported .. or ./")
ERR(-100305, ERR_PATH_COMPONENT_SEPARATOR, "Path component contains dir separator")
// .. tree node
ERR(-100310, ERR_TNODE_NOT_FOUND, "VFile not found")
ERR(-100311, ERR_TNODE_WRONG_TYPE, "Not a directory")
// .. open
ERR(-100310, ERR_FILE_NOT_FOUND, "VFile not found")
ERR(-100311, ERR_NOT_FILE, "Not a file")
ERR(-100312, ERR_FILE_ACCESS, "Insufficient access rights to open file")
ERR(-100320, ERR_FILE_ACCESS, "Insufficient access rights to open file")
// .. enum
ERR(-100320, ERR_PATH_NOT_FOUND, "VDir not found")
ERR(-100321, ERR_NOT_DIR, "Not a directory")
ERR(-100322, ERR_DIR_END, "End of directory reached (no more files)")
ERR(-100330, ERR_DIR_END, "End of directory reached (no more files)")
// .. IO
ERR(-100330, ERR_IO, "Error during IO")
ERR(-100331, ERR_EOF, "Reading beyond end of file")
ERR(-100340, ERR_IO, "Error during IO")
ERR(-100341, ERR_EOF, "Reading beyond end of file")
// .. mount
ERR(-100340, ERR_ALREADY_MOUNTED, "Directory (tree) already mounted")
ERR(-100341, ERR_NOT_MOUNTED, "Specified directory is not mounted")
ERR(-100342, ERR_INVALID_MOUNT_TYPE, "Invalid mount type (memory corruption?)")
ERR(-100343, ERR_ROOT_DIR_ALREADY_SET, "Attempting to set FS root dir more than once")
ERR(-100350, ERR_ALREADY_MOUNTED, "Directory (tree) already mounted")
ERR(-100351, ERR_NOT_MOUNTED, "Specified directory is not mounted")
ERR(-100352, ERR_INVALID_MOUNT_TYPE, "Invalid mount type (memory corruption?)")
ERR(-100353, ERR_ROOT_DIR_ALREADY_SET, "Attempting to set FS root dir more than once")
// .. misc
ERR(-100350, ERR_UNKNOWN_CMETHOD, "Unknown/unsupported compression method")
ERR(-100351, ERR_IS_COMPRESSED, "Invalid operation for a compressed file")
ERR(-100352, ERR_NOT_MAPPED, "File was not mapped")
ERR(-100353, ERR_NOT_IN_CACHE, "[Internal] Entry not found in cache")
ERR(-100354, ERR_TRACE_EMPTY, "No valid entries in trace")
ERR(-100360, ERR_UNKNOWN_CMETHOD, "Unknown/unsupported compression method")
ERR(-100361, ERR_IS_COMPRESSED, "Invalid operation for a compressed file")
ERR(-100362, ERR_NOT_MAPPED, "File was not mapped")
ERR(-100363, ERR_NOT_IN_CACHE, "[Internal] Entry not found in cache")
ERR(-100364, ERR_TRACE_EMPTY, "No valid entries in trace")
// file format
ERR(-100400, ERR_UNKNOWN_FORMAT, "Unknown file format")

View File

@ -145,22 +145,35 @@ void path_copy(char* dst, const char* src)
// if necessary, a directory separator is added between the paths.
// each may be empty, filenames, or full paths.
// total path length (including '\0') must not exceed PATH_MAX.
LibError path_append(char* dst, const char* path1, const char* path2)
LibError path_append(char* dst, const char* path1, const char* path2, uint flags)
{
const size_t len1 = strlen(path1);
const size_t len2 = strlen(path2);
size_t total_len = len1 + len2 + 1; // includes '\0'
const bool no_end_slash1 = (len1 == 0 || !is_dir_sep(path1[len1-1]));
const bool no_end_slash2 = (len2 == 0 || !is_dir_sep(path2[len2-1]));
// check if we need to add '/' between path1 and path2
// note: the second can't start with '/' (not allowed by path_validate)
// notes:
// - the second can't start with '/' (not allowed by path_validate)
// - must check len2 as well - if it's empty, we'd end up
// inadvertently terminating the string with '/'.
bool need_separator = false;
if(len1 != 0 && !is_dir_sep(path1[len1-1]))
if(len2 != 0 && len1 != 0 && no_end_slash1)
{
total_len++; // for '/'
need_separator = true;
}
if(total_len+1 > PATH_MAX)
// check if trailing slash requested and not already present
bool need_terminator = false;
if(flags & PATH_APPEND_SLASH && no_end_slash2)
{
total_len++; // for '/'
need_terminator = true;
}
if(total_len > PATH_MAX)
WARN_RETURN(ERR_PATH_LENGTH);
strcpy(dst, path1); // safe
@ -168,21 +181,26 @@ LibError path_append(char* dst, const char* path1, const char* path2)
if(need_separator)
*dst++ = '/';
strcpy(dst, path2); // safe
if(need_terminator)
strcpy(dst+len2, "/"); // safe
return ERR_OK;
}
// strip <remove> from the start of <src>, prepend <replace>,
// and write to <dst>.
// returns ERR_FAIL if the beginning of <src> doesn't match <remove>.
// returns ERR_FAIL (without warning!) if the beginning of <src> doesn't
// match <remove>.
LibError path_replace(char* dst, const char* src, const char* remove, const char* replace)
{
// remove doesn't match start of <src>
const size_t remove_len = strlen(remove);
if(strncmp(src, remove, remove_len) != 0)
WARN_RETURN(ERR_FAIL);
return ERR_FAIL; // NOWARN
// get rid of trailing / in src (must not be included in remove)
// if removing will leave a separator at beginning of src, remove it
// (example: "a/b"; removing "a" would yield "/b")
const char* start = src+remove_len;
if(is_dir_sep(*start))
start++;
@ -193,6 +211,8 @@ LibError path_replace(char* dst, const char* src, const char* remove, const char
}
//-----------------------------------------------------------------------------
// split paths into specific parts
@ -218,6 +238,30 @@ const char* path_name_only(const char* path)
}
// return last component within path. this is similar to path_name_only,
// but correctly handles VFS paths, which must end with '/'.
// (path_name_only would return "")
const char* path_last_component(const char* path)
{
// ('\0' is end of set string)
static const char separators[3] = { DIR_SEP, '/', '\0' };
const char* pos = path;
const char* last_component = path;
for(;;)
{
if(*pos == '\0')
break;
last_component = pos;
const size_t component_len = strcspn(pos, separators);
pos += component_len+1; // +1 for separator
}
return last_component;
}
// if <path> contains a name component, it is stripped away.
void path_strip_fn(char* path)
{
@ -248,6 +292,62 @@ const char* path_extension(const char* fn)
}
// call <cb> with <ctx> for each component in <path>.
LibError path_foreach_component(const char* path_org, PathComponentCb cb, void* ctx)
{
CHECK_PATH(path_org);
// copy into (writeable) buffer so we can 'tokenize' path components by
// replacing '/' with '\0'.
char path[PATH_MAX];
strcpy_s(path, ARRAY_SIZE(path), path_org);
char* cur_component = path;
bool is_dir = true; // until we find a component without slash
// successively navigate to the next component in <path>.
for(;;)
{
// at end of string - done.
// (this happens if <path> is empty or ends with slash)
if(*cur_component == '\0')
break;
// find end of cur_component
char* slash = (char*)strchr(cur_component, '/');
// .. try platform-specific separator
if(!slash)
slash = (char*)strchr(cur_component, DIR_SEP);
// decide its type and 0-terminate
// .. filename (by definition)
if(!slash)
is_dir = false;
// .. directory
else
*slash = '\0'; // 0-terminate cur_component
LibError ret = cb(cur_component, is_dir, ctx);
// callback wants to abort - return its value.
if(ret != INFO_CB_CONTINUE)
return ret;
// filename is by definition the last component. abort now
// in case the callback didn't.
if(!is_dir)
break;
// advance to next component
// .. undo having replaced '/' with '\0' - this means <path> will
// store the complete path up to and including cur_component.
*slash = '/';
cur_component = slash+1;
}
return ERR_OK;
}
//-----------------------------------------------------------------------------
// convenience "class" that simplifies successively appending a filename to

View File

@ -35,6 +35,16 @@
#include "posix.h" // PATH_MAX
// if path is invalid (see source for criteria), return a
// descriptive error code, otherwise ERR_OK.
extern LibError path_validate(const char* path);
#define CHECK_PATH(path) RETURN_ERR(path_validate(path))
// if name is invalid, (see source for criteria), return a
// descriptive error code, otherwise ERR_OK.
extern LibError path_component_validate(const char* name);
// is s2 a subpath of s1, or vice versa?
extern bool path_is_subpath(const char* s1, const char* s2);
@ -48,21 +58,36 @@ extern LibError path_component_validate(const char* name);
// copy path strings (provided for convenience).
extern void path_copy(char* dst, const char* src);
enum PathAppendFlags
{
// make sure <dst> ends up with a trailing slash. this is useful for
// VFS directory paths, which have that requirement.
PATH_APPEND_SLASH = 1
};
// combine <path1> and <path2> into one path, and write to <dst>.
// if necessary, a directory separator is added between the paths.
// each may be empty, filenames, or full paths.
// total path length (including '\0') must not exceed PATH_MAX.
extern LibError path_append(char* dst, const char* path1, const char* path2);
extern LibError path_append(char* dst, const char* path1, const char* path2,
uint flags = 0);
// strip <remove> from the start of <src>, prepend <replace>,
// and write to <dst>.
// returns ERR_FAIL if the beginning of <src> doesn't match <remove>.
// returns ERR_FAIL (without warning!) if the beginning of <src> doesn't
// match <remove>.
extern LibError path_replace(char* dst, const char* src, const char* remove, const char* replace);
// return pointer to the name component within path (i.e. skips over all
// characters up to the last dir separator, if any).
extern const char* path_name_only(const char* path);
// return last component within path. this is similar to path_name_only,
// but correctly handles VFS paths, which must end with '/'.
// (path_name_only would return "")
extern const char* path_last_component(const char* path);
// if <path> contains a name component, it is stripped away.
extern void path_strip_fn(char* path);
@ -76,6 +101,29 @@ extern void path_dir_only(const char* path, char* dir);
extern const char* path_extension(const char* fn);
// called for each component in a path string, indicating if it's
// a directory (i.e. <component> is followed by a slash in the original
// path).
// if path is empty (i.e. ""), this is not called.
//
// component: 0-terminated name of the component (does not include any
// trailing slash!)
// ctx: context parameter that was passed to path_foreach_component.
// return: INFO_CB_CONTINUE to continue operation normally; anything else
// will cause path_foreach_component to abort immediately and return that.
// no need to 'abort' (e.g. return ERR_OK) after a filename is encountered -
// that's taken care of automatically.
//
// rationale:
// - we indicate if it's a directory via bool. this isn't as nice as a
// flag or enum, but vfs_tree already has TNodeType and we don't want
// to expose that or create a new one.
typedef LibError (*PathComponentCb)(const char* component, bool is_dir, void* ctx);
// call <cb> with <ctx> for each component in <path>.
extern LibError path_foreach_component(const char* path, PathComponentCb cb, void* ctx);
//-----------------------------------------------------------------------------
// convenience "class" that simplifies successively appending a filename to

View File

@ -182,7 +182,7 @@ static LibError archive_get_file_info(Archive* a, const char* fn, uintptr_t meme
}
}
WARN_RETURN(ERR_FILE_NOT_FOUND);
WARN_RETURN(ERR_TNODE_NOT_FOUND);
}

View File

@ -185,6 +185,20 @@ LibError dir_close(DirIterator* di)
}
LibError dir_create(const char* P_path, mode_t mode)
{
char N_path[PATH_MAX];
RETURN_ERR(file_make_full_native_path(P_path, N_path));
struct stat s;
int ret = stat(N_path, &s);
if(ret == 0)
return INFO_ALREADY_PRESENT;
errno = 0;
ret = mkdir(N_path, mode);
return LibError_from_posix(ret);
}
@ -255,8 +269,23 @@ LibError file_delete(const char* fn)
// and the Handle approach doesn't guard against some idiot calling
// close(our_fd_value) directly, either.
struct PosixFile
{
int fd;
// for reference counted memory-mapping
void* mapping;
uint map_refs;
};
cassert(sizeof(PosixFile) < FILE_OPAQUE_SIZE);
int file_fd_from_PosixFile(File* f)
{
const PosixFile* pf = (const PosixFile*)f->opaque;
return pf->fd;
}
LibError file_validate(const File* f)
{
@ -298,7 +327,7 @@ LibError file_open(const char* P_fn, uint flags, File* f)
// get file size
struct stat s;
if(stat(N_fn, &s) < 0)
WARN_RETURN(ERR_FILE_NOT_FOUND);
WARN_RETURN(ERR_TNODE_NOT_FOUND);
size = s.st_size;
// note: despite increased overhead, the AIO read method is still
@ -312,7 +341,7 @@ LibError file_open(const char* P_fn, uint flags, File* f)
// make sure <N_fn> is a regular file
if(!S_ISREG(s.st_mode))
WARN_RETURN(ERR_NOT_FILE);
WARN_RETURN(ERR_TNODE_WRONG_TYPE);
}
#if OS_WIN

View File

@ -78,7 +78,7 @@ extern const char* file_get_random_name();
//
// dir_next_ent
// directory
//
const size_t DIR_ITERATOR_OPAQUE_SIZE = 40;
@ -151,6 +151,9 @@ extern LibError dir_next_ent(DirIterator* d, DirEnt* ent);
extern LibError dir_close(DirIterator* d);
extern LibError dir_create(const char* P_path, mode_t mode);
#ifdef __cplusplus
typedef std::vector<DirEnt> DirEnts;
@ -282,8 +285,8 @@ enum FileFlags
// (only relevant for VFS) file will be written into the
// appropriate subdirectory of the mount point established by
// vfs_mod_set_write_target. see documentation there.
FILE_WRITE_TO_MOD = FILE_WRITE|0x100,
// vfs_set_write_target. see documentation there.
FILE_WRITE_TO_TARGET = FILE_WRITE|0x100,
// sum of all flags above. used when validating flag parameters.
FILE_FLAG_ALL = 0x1FF

View File

@ -83,13 +83,6 @@ extern LibError file_io_call_back(const void* block, size_t size,
// higher-level code such as VfsUtil.
extern LibError dir_filtered_next_ent(DirIterator* di, DirEnt* ent, const char* filter);
// used by file.cpp and file_io.cpp
struct PosixFile
{
int fd;
// for reference counted memory-mapping
void* mapping;
uint map_refs;
};
// returns file descriptor (int) given File (assumed to represent PosixFile).
// this avoids the need for declaring PosixFile here for file_io's use.
extern int file_fd_from_PosixFile(File* f);

View File

@ -109,7 +109,6 @@ LibError file_io_issue(File* f, off_t ofs, size_t size, void* p, FileIo* io)
WARN_RETURN(ERR_INVALID_PARAM);
const bool is_write = (f->flags & FILE_WRITE) != 0;
PosixFile* pf = (PosixFile*)f->opaque;
PosixFileIo* pio = (PosixFileIo*)io;
// note: cutting off at EOF is necessary to avoid transfer errors,
@ -137,7 +136,7 @@ LibError file_io_issue(File* f, off_t ofs, size_t size, void* p, FileIo* io)
// send off async read/write request
cb->aio_lio_opcode = is_write? LIO_WRITE : LIO_READ;
cb->aio_buf = (volatile void*)p;
cb->aio_fildes = pf->fd;
cb->aio_fildes = file_fd_from_PosixFile(f);
cb->aio_offset = ofs;
cb->aio_nbytes = size;
int err = lio_listio(LIO_NOWAIT, &cb, 1, (struct sigevent*)0);
@ -380,9 +379,7 @@ class IOManager
ssize_t lowio()
{
const PosixFile* pf = (const PosixFile*)f->opaque;
const int fd = pf->fd;
const int fd = file_fd_from_PosixFile(f);
lseek(fd, start_ofs, SEEK_SET);
// emulate temp buffers - we take care of allocating and freeing.

View File

@ -129,7 +129,7 @@ LibError file_make_full_portable_path(const char* n_full_path, char* path)
debug_assert(path != n_full_path); // doesn't work in-place
if(strncmp(n_full_path, n_root_dir, n_root_dir_len) != 0)
WARN_RETURN(ERR_PATH_NOT_FOUND);
WARN_RETURN(ERR_TNODE_NOT_FOUND);
return convert_path(path, n_full_path+n_root_dir_len, TO_PORTABLE);
}

View File

@ -25,35 +25,6 @@
#include "lib.h"
// internal use only:
// if path is invalid (see source for criteria), return a
// descriptive error code, otherwise ERR_OK.
extern LibError path_validate(const char* path);
#define CHECK_PATH(path) RETURN_ERR(path_validate(path))
// if name is invalid, (see source for criteria), return a
// descriptive error code, otherwise ERR_OK.
extern LibError path_component_validate(const char* name);
// strip <remove> from the start of <src>, prepend <replace>,
// and write to <dst>.
// used when converting VFS <--> real paths.
extern LibError path_replace(char* dst, const char* src, const char* remove, const char* replace);
// fill V_dir_only with the path portion of V_src_fn
// ("" if root dir, otherwise ending with /)
extern void path_dir_only(const char* V_src_fn, char* V_dir_only);
// return pointer to the name component within V_src_fn
extern const char* path_name_only(const char* V_src_fn);
extern const char* path_extension(const char* fn);
extern void path_strip_fn(char* fn);
struct NextNumberedFilenameInfo
{
int next_num;
@ -76,6 +47,11 @@ extern void next_numbered_filename(const char* V_fn_fmt,
extern bool path_is_atom_fn(const char* fn);
extern const char* file_get_random_name();
// note: other functions are declared directly in the public file.h header.
extern void path_init();
extern void path_shutdown();

View File

@ -264,7 +264,7 @@ LibError trace_read_from_file(const char* trace_filename, Trace* t)
RETURN_ERR(file_make_full_native_path(trace_filename, N_fn));
FILE* f = fopen(N_fn, "rt");
if(!f)
WARN_RETURN(ERR_FILE_NOT_FOUND);
WARN_RETURN(ERR_TNODE_NOT_FOUND);
// we use trace_add, which is the same mechanism called by trace_notify*;
// therefore, tracing needs to be enabled.

View File

@ -113,14 +113,17 @@ static void VDir_dtor(VDir* vd)
}
}
static LibError VDir_reload(VDir* vd, const char* path, Handle UNUSED(hvd))
static LibError VDir_reload(VDir* vd, const char* V_path, Handle UNUSED(hvd))
{
debug_assert(*V_path == '\0' || V_path[strlen(V_path)-1] == '/');
/*/*SLASH
// add required trailing slash if not already present to make
// caller's life easier.
char V_path_slash[PATH_MAX];
RETURN_ERR(path_append(V_path_slash, path, ""));
*/
RETURN_ERR(xdir_open(V_path_slash, &vd->di));
RETURN_ERR(xdir_open(V_path, &vd->di));
vd->di_valid = 1;
return ERR_OK;
}
@ -202,7 +205,8 @@ LibError vfs_realpath(const char* V_path, char* realpath)
CHECK_ERR(tree_lookup(V_path, &tf));
const char* atom_fn = tfile_get_atom_fn(tf);
return mount_realpath(atom_fn, tf, realpath);
const Mount* m = tfile_get_mount(tf);
return mount_realpath(atom_fn, m, realpath);
}
@ -289,12 +293,12 @@ static LibError VFile_reload(VFile* vf, const char* V_path, Handle)
return err;
}
// careful! FILE_WRITE_TO_MOD consists of 2 bits; they must both be
// careful! FILE_WRITE_TO_TARGET consists of 2 bits; they must both be
// set (one of them is FILE_WRITE, which can be set independently).
// this is a bit ugly but better than requiring users to write
// FILE_WRITE|FILE_WRITE_TO_MOD.
if((flags & FILE_WRITE_TO_MOD) == FILE_WRITE_TO_MOD)
RETURN_ERR(set_mount_to_mod_target(tf));
// FILE_WRITE|FILE_WRITE_TO_TARGET.
if((flags & FILE_WRITE_TO_TARGET) == FILE_WRITE_TO_TARGET)
RETURN_ERR(set_mount_to_write_target(tf));
RETURN_ERR(xfile_open(V_path, flags, tf, &vf->f));

View File

@ -232,17 +232,6 @@ extern void vfs_display(void);
// for an an indication of how large fixed-size user buffers should be,
// use PATH_MAX.
// convenience function
extern void path_copy(char* dst, const char* src);
// combine <path1> and <path2> into one path, and write to <dst>.
// if necessary, a directory separator is added between the paths.
// each may be empty, filenames, or full paths.
// total path length (including '\0') must not exceed PATH_MAX.
extern LibError path_append(char* dst, const char* path1, const char* path2);
extern const char* path_extension(const char* fn);
// VFS paths are of the form: "(dir/)*file?"
// in English: '/' as path separator; trailing '/' required for dir names;
// no leading '/', since "" is the root dir.
@ -290,12 +279,12 @@ extern LibError vfs_unmount(const char* name);
// set current "mod write directory" to P_target_dir, which must
// already have been mounted into the VFS.
// all files opened for writing with the FILE_WRITE_TO_MOD flag set will
// all files opened for writing with the FILE_WRITE_TO_TARGET flag set will
// be written into the appropriate subdirectory of this mount point.
//
// this allows e.g. the editor to write files that are already
// stored in archives, which are read-only.
extern LibError vfs_mod_set_write_target(const char* P_target_dir);
extern LibError vfs_set_write_target(const char* P_target_dir);
//

View File

@ -170,15 +170,20 @@ bool mount_should_replace(const Mount* m_old, const Mount* m_new,
}
// given a file's TFile and V_path, return its actual location (portable path).
LibError mount_realpath(const char* V_path, const TFile* tf, char* P_real_path)
// given Mount and V_path, return its actual location (portable path).
LibError mount_realpath(const char* V_path, const Mount* m, char* P_real_path)
{
const Mount* m = tfile_get_mount(tf);
const char* P_parent_path = m->P_name.c_str();
const char* remove = m->V_mount_point.c_str();
const char* replace = P_parent_path;
return path_replace(P_real_path, V_path, remove, replace);
const char* replace = m->P_name.c_str(); // P_parent_path
CHECK_ERR(path_replace(P_real_path, V_path, remove, replace));
// if P_real_path ends with '/' (a remnant from V_path), strip
// it because that's not acceptable for portable paths.
const size_t P_len = strlen(P_real_path);
if(P_len != 0 && P_real_path[P_len-1] == '/')
P_real_path[P_len-1] = '\0';
return ERR_OK;
}
@ -500,8 +505,10 @@ static LibError mount_dir_tree(TDir* td, const Mount& m)
// pop to come at end of loop => this is easiest)
dir_queue.push_back(TDirAndPath(td, m.P_name.c_str()));
static int seq2;
do
{
seq2++;
TDir* const td = dir_queue.front().td;
const char* P_path = dir_queue.front().path.c_str();
@ -585,7 +592,7 @@ static const Mount& add_mount(const char* V_mount_point, const char* P_real_path
static LibError remount(const Mount& m)
{
TDir* td;
CHECK_ERR(tree_lookup_dir(m.V_mount_point.c_str(), &td, LF_CREATE_MISSING));
CHECK_ERR(tree_add_path(m.V_mount_point.c_str(), &m, &td));
switch(m.type)
{
@ -706,7 +713,7 @@ LibError vfs_unmount(const char* P_name)
std::bind2nd(Mount::equal_to(), P_name));
// none were removed - need to complain so that the caller notices.
if(last == end)
WARN_RETURN(ERR_PATH_NOT_FOUND);
WARN_RETURN(ERR_TNODE_NOT_FOUND);
// trim list and actually remove 'invalidated' entries.
mounts.erase(last, end);
@ -732,26 +739,55 @@ LibError mount_make_vfs_path(const char* P_path, char* V_path)
const char* remove = m.P_name.c_str();
const char* replace = m.V_mount_point.c_str();
if(path_replace(V_path, P_path, remove, replace) == 0)
if(path_replace(V_path, P_path, remove, replace) == ERR_OK)
return ERR_OK;
}
WARN_RETURN(ERR_PATH_NOT_FOUND);
WARN_RETURN(ERR_TNODE_NOT_FOUND);
}
static const Mount* mod_target;
static const Mount* write_target;
// 2006-05-09 JW note: we are wanting to move XMB files into a separate
// folder tree (no longer interspersed with XML), so that deleting them is
// easier and dirs are less cluttered.
//
// if several mods are active, VFS would have several RealDirs mounted
// and could no longer automatically determine the write target.
//
// one solution would be to use this set_write_target support to choose the
// correct dir; however, XMB files may be generated whilst editing
// (which also requires a write_target to write files that are actually
// currently in archives), so we'd need to save/restore write_target.
// this would't be thread-safe => disaster.
//
// a vfs_store_to(filename, flags, N_actual_path) API would work, but it'd
// impose significant burden on users (finding the actual native dir),
// and be prone to abuse. additionally, it would be difficult to
// propagate N_actual_path to VFile_reload where it is needed;
// this would end up messy.
//
// instead, we'll write XMB files into VFS path "mods/$MODNAME/..",
// into which the realdir of the same name (located in some writable folder)
// is mounted; VFS therefore can write without problems.
//
// however, other code (e.g. archive builder) doesn't know about this
// trick - it only sees the flat VFS namespace, which doesn't
// include mods/$MODNAME (that is hidden). to solve this, we also mount
// any active mod's XMB dir into VFS root for read access.
// set current "mod write directory" to P_target_dir, which must
// already have been mounted into the VFS.
// all files opened for writing with the FILE_WRITE_TO_MOD flag set will
// all files opened for writing with the FILE_WRITE_TO_TARGET flag set will
// be written into the appropriate subdirectory of this mount point.
//
// this allows e.g. the editor to write files that are already
// stored in archives, which are read-only.
LibError vfs_mod_set_write_target(const char* P_target_dir)
LibError vfs_set_write_target(const char* P_target_dir)
{
for(MountIt it = mounts.begin(); it != mounts.end(); ++it)
{
@ -763,7 +799,7 @@ LibError vfs_mod_set_write_target(const char* P_target_dir)
// found it in list of mounted dirs
if(!strcmp(m.P_name.c_str(), P_target_dir))
{
mod_target = &m;
write_target = &m;
return ERR_OK;
}
}
@ -772,17 +808,17 @@ LibError vfs_mod_set_write_target(const char* P_target_dir)
}
// 'relocate' tf to the mounting established by vfs_mod_set_write_target.
// call if <tf> is being opened with FILE_WRITE_TO_MOD flag set.
LibError set_mount_to_mod_target(TFile* tf)
// 'relocate' tf to the mounting established by vfs_set_write_target.
// call if <tf> is being opened with FILE_WRITE_TO_TARGET flag set.
LibError set_mount_to_write_target(TFile* tf)
{
if(!mod_target)
if(!write_target)
WARN_RETURN(ERR_NOT_MOUNTED);
tfile_set_mount(tf, mod_target);
tfile_set_mount(tf, write_target);
// invalidate the previous values. we don't need to be clever and
// set size to that of the file in the new mod_target mount point.
// set size to that of the file in the new write_target mount point.
// this is because we're only called for files that are being
// opened for writing, which will change these values anyway.
tree_update_file(tf, 0, 0);
@ -807,16 +843,20 @@ void mount_shutdown()
}
static const Mount* MULTIPLE_MOUNTINGS = (const Mount*)-1;
// RDTODO: when should this be called? TDir ctor can already set this.
LibError mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, uint flags)
{
// more than one real dir mounted into VFS dir
// (=> can't create files for writing here)
if(rd->m)
rd->m = (const Mount*)-1;
{
// HACK: until RealDir reorg is done, we're going to have to deal with
// "attaching" to real dirs twice. don't mess up rd->m if m is the same.
if(rd->m != m)
rd->m = MULTIPLE_MOUNTINGS;
}
else
rd->m = m;
@ -848,6 +888,18 @@ void mount_detach_real_dir(RealDir* rd)
}
LibError mount_create_real_dir(const char* V_path, const Mount* m)
{
if(!m || m == MULTIPLE_MOUNTINGS || m->type != MT_FILE)
return ERR_OK;
char P_path[PATH_MAX];
RETURN_ERR(mount_realpath(V_path, m, P_path));
return dir_create(P_path, S_IRWXU|S_IRWXG|S_IRWXO);
}
LibError mount_populate(TDir* td, RealDir* rd)
{
UNUSED2(td);

View File

@ -61,9 +61,8 @@ extern char mount_get_type(const Mount* m);
extern Handle mount_get_archive(const Mount* m);
// given a file's TFile and V_path, return its actual location (portable path).
extern LibError mount_realpath(const char* V_path, const TFile* tf, char* P_real_path);
// given Mount and V_path, return its actual location (portable path).
extern LibError mount_realpath(const char* V_path, const Mount* m, char* P_real_path);
@ -86,6 +85,8 @@ struct RealDir
extern LibError mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, uint flags);
extern void mount_detach_real_dir(RealDir* rd);
extern LibError mount_create_real_dir(const char* V_path, const Mount* m);
extern LibError mount_populate(TDir* td, RealDir* rd);
@ -95,9 +96,9 @@ extern LibError mount_populate(TDir* td, RealDir* rd);
// because this call leaves the VFS in limbo!!
extern void mount_release_all_archives();
// 'relocate' tf to the mounting established by vfs_mod_set_write_target.
// call if <tf> is being opened with FILE_WRITE_TO_MOD flag set.
extern LibError set_mount_to_mod_target(TFile* tf);
// 'relocate' tf to the mounting established by vfs_set_write_target.
// call if <tf> is being opened with FILE_WRITE_TO_TARGET flag set.
extern LibError set_mount_to_write_target(TFile* tf);
// rebuild the VFS, i.e. re-mount everything. open files are not affected.

View File

@ -46,7 +46,8 @@ LibError file_open_vfs(const char* V_path, uint flags, TFile* tf,
File* f) // out
{
char N_path[PATH_MAX];
RETURN_ERR(mount_realpath(V_path, tf, N_path));
const Mount* m = tfile_get_mount(tf);
RETURN_ERR(mount_realpath(V_path, m, N_path));
RETURN_ERR(file_open(N_path, flags|FILE_DONT_SET_FN, f));
// file_open didn't set fc.atom_fn due to FILE_DONT_SET_FN.
f->atom_fn = file_make_unique_fn_copy(V_path);
@ -148,6 +149,8 @@ LibError xfile_open(const char* V_path, uint flags, TFile* tf, File* f)
{
// find out who is providing this file
const Mount* m = tfile_get_mount(tf);
debug_assert(m != 0);
// HACK: see decl of vtbls. ideally vtbl would already be stored in
// Mount, but that's not implemented yet.
char c = mount_get_type(m);

View File

@ -96,6 +96,8 @@ class TNode
{
public:
TNodeType type;
// allocated and owned by vfs_mount
const Mount* m;
// rationale: we store both entire path and name component.
// this increases size of VFS (2 pointers needed here) and
@ -103,10 +105,14 @@ public:
// iterate over all dir name components.
// we could retrieve name via strrchr(path, '/'), but that is slow.
const char* V_path;
// ends with slash if this is a directory!
// this is compared as a normal string (not pointer comparison), but
// the pointer passed must obviously remain valid, so it is
// usually an atom_fn.
const char* name;
TNode(TNodeType type_, const char* V_path_, const char* name_)
: type(type_), V_path(V_path_), name(name_)
TNode(TNodeType type_, const char* V_path_, const char* name_, const Mount* m_)
: type(type_), V_path(V_path_), name(name_), m(m_)
{
}
};
@ -115,19 +121,14 @@ public:
class TFile : public TNode
{
public:
// required:
const Mount* m;
// allocated and owned by caller (mount code)
off_t size;
time_t mtime;
uintptr_t memento;
TFile(const char* V_path, const char* name, const Mount* m_)
: TNode(NT_FILE, V_path, name)
TFile(const char* V_path, const char* name, const Mount* m)
: TNode(NT_FILE, V_path, name, m)
{
m = m_;
size = 0;
mtime = 0;
memento = 0;
@ -187,20 +188,21 @@ class TDir : public TNode
{
uint flags; // enum TDirFlags
RealDir rd;
TChildren children;
public:
TDir(const char* V_path, const char* name)
: TNode(NT_DIR, V_path, name), children()
RealDir rd; // HACK; removeme
TDir(const char* V_path, const char* name, const Mount* m_)
: TNode(NT_DIR, V_path, name, 0), children()
{
flags = 0;
rd.m = 0;
rd.m = m_;
rd.watch = 0;
mount_create_real_dir(V_path, rd.m);
}
TNode* find(const char* name) const { return children.find(name); }
TChildrenIt begin() const { return children.begin(); }
TChildrenIt end() const { return children.end(); }
@ -218,36 +220,42 @@ public:
}
}
LibError add(const char* name_tmp, TNodeType type, TNode** pnode)
TNode* find(const char* name) const
{
return children.find(name);
}
// must not be called if already exists! use find() first or
// find_and_add instead.
LibError add(const char* name_tmp, TNodeType type, TNode** pnode, const Mount* m_override = 0)
{
// note: must be done before path_append for security
// (otherwise, '/' in <name_tmp> wouldn't be caught)
RETURN_ERR(path_component_validate(name_tmp));
char V_new_path_tmp[PATH_MAX];
path_append(V_new_path_tmp, V_path, name_tmp);
const uint flags = (type == NT_DIR)? PATH_APPEND_SLASH : 0;
RETURN_ERR(path_append(V_new_path_tmp, V_path, name_tmp, flags));
const char* V_new_path = file_make_unique_fn_copy(V_new_path_tmp);
const char* name = path_name_only(V_new_path);
if(type == NT_DIR)
name = file_make_unique_fn_copy(name_tmp);
RETURN_ERR(path_component_validate(name));
TNode* node = children.find(name);
if(node)
{
if(node->type != type)
return (type == NT_FILE)? ERR_NOT_FILE : ERR_NOT_DIR;
*pnode = node;
return INFO_ALREADY_PRESENT;
}
const Mount* m = rd.m;
if(m_override)
m = m_override;
// note: if anything below fails, this mem remains allocated in the
// pool, but that "can't happen" and is OK because pool is big enough.
void* mem = node_alloc();
if(!mem)
WARN_RETURN(ERR_NO_MEM);
TNode* node;
#include "nommgr.h"
if(type == NT_FILE)
node = new(mem) TFile(V_new_path, name, rd.m);
node = new(mem) TFile(V_new_path, name, m);
else
node = new(mem) TDir(V_new_path, name);
node = new(mem) TDir (V_new_path, name, m);
#include "mmgr.h"
children.insert(name, node);
@ -256,6 +264,23 @@ public:
return ERR_OK;
}
LibError find_and_add(const char* name, TNodeType type, TNode** pnode, const Mount* m = 0)
{
TNode* node = children.find(name);
if(node)
{
// wrong type (dir vs. file)
if(node->type != type)
WARN_RETURN(ERR_TNODE_WRONG_TYPE);
*pnode = node;
return INFO_ALREADY_PRESENT;
}
return add(name, type, pnode, m);
}
// empty this directory and all subdirectories; used when rebuilding VFS.
void clearR()
{
@ -368,77 +393,56 @@ static void displayR(TDir* td, int indent_level)
}
struct LookupCbParams
{
const bool create_missing;
TDir* td; // current dir; assigned from node
TNode* node; // latest node returned (dir or file)
LookupCbParams(uint flags, TDir* td_)
: create_missing((flags & LF_CREATE_MISSING) != 0), td(td_)
{
// init in case lookup's <path> is "".
// this works because TDir is derived from TNode.
node = (TNode*)td;
}
};
static LibError lookup_cb(const char* component, bool is_dir, void* ctx)
{
LookupCbParams* p = (LookupCbParams*)ctx;
const TNodeType type = is_dir? NT_DIR : NT_FILE;
p->td->populate();
p->node = p->td->find(component);
if(!p->node)
{
if(p->create_missing)
RETURN_ERR(p->td->add(component, type, &p->node));
else
// complaining is left to callers; vfs_exists must be
// able to fail quietly.
return ERR_TNODE_NOT_FOUND; // NOWARN
}
if(p->node->type != type)
WARN_RETURN(ERR_TNODE_WRONG_TYPE);
if(is_dir)
p->td = (TDir*)p->node;
return INFO_CB_CONTINUE;
}
static LibError lookup(TDir* td, const char* path, uint flags, TNode** pnode)
{
// early out: "" => return this directory (usually VFS root)
if(path[0] == '\0')
{
*pnode = (TNode*)td; // HACK: TDir is at start of TNode
return ERR_OK;
}
CHECK_PATH(path);
debug_assert( (flags & ~(LF_CREATE_MISSING|LF_START_DIR)) == 0 );
// no undefined bits set
debug_assert( (flags & ~(LF_CREATE_MISSING|LF_START_DIR)) == 0 );
const bool create_missing = !!(flags & LF_CREATE_MISSING);
// copy into (writeable) buffer so we can 'tokenize' path components
// by replacing '/' with '\0'.
char V_path[PATH_MAX];
strcpy_s(V_path, sizeof(V_path), path);
char* cur_component = V_path;
TNodeType type = NT_DIR;
// successively navigate to the next component in <path>.
TNode* node = 0;
for(;;)
{
// "extract" cur_component string (0-terminate by replacing '/')
char* slash = (char*)strchr(cur_component, '/');
if(!slash)
{
// string ended in slash => return the current dir node.
if(*cur_component == '\0')
break;
// it's a filename
type = NT_FILE;
}
// normal operation (cur_component is a directory)
else
{
td->populate();
*slash = '\0';
}
// create <cur_component> (no-op if it already exists)
if(create_missing)
RETURN_ERR(td->add(V_path, type, &node));
else
{
node = td->find(cur_component);
if(!node)
return slash? ERR_PATH_NOT_FOUND : ERR_FILE_NOT_FOUND;
if(node->type != type)
return slash? ERR_NOT_DIR : ERR_NOT_FILE;
}
// cur_component was a filename => we're done
if(!slash)
break;
// else: it was a directory; advance
// .. undo having replaced '/' with '\0' - this means V_path will
// store the complete path up to and including cur_component.
*slash = '/';
cur_component = slash+1;
td = (TDir*)node;
}
LookupCbParams p(flags, td);
RETURN_ERR(path_foreach_component(path, lookup_cb, &p));
// success.
*pnode = node;
*pnode = p.node;
return ERR_OK;
}
@ -468,7 +472,7 @@ static void tree_root_init()
#include "nommgr.h" // placement new
void* mem = node_alloc();
if(mem)
tree_root = new(mem) TDir("", "");
tree_root = new(mem) TDir("", "", 0);
#include "mmgr.h"
}
@ -540,7 +544,7 @@ LibError tree_add_file(TDir* td, const char* name,
const Mount* m, off_t size, time_t mtime, uintptr_t memento)
{
TNode* node;
LibError ret = td->add(name, NT_FILE, &node);
LibError ret = td->find_and_add(name, NT_FILE, &node);
RETURN_ERR(ret);
if(ret == INFO_ALREADY_PRESENT)
{
@ -566,7 +570,7 @@ LibError tree_add_file(TDir* td, const char* name,
LibError tree_add_dir(TDir* td, const char* name, TDir** ptd)
{
TNode* node;
RETURN_ERR(td->add(name, NT_DIR, &node));
RETURN_ERR(td->find_and_add(name, NT_DIR, &node));
*ptd = (TDir*)node;
return ERR_OK;
}
@ -577,7 +581,7 @@ LibError tree_lookup_dir(const char* path, TDir** ptd, uint flags)
{
// path is not a directory; TDir::lookup might return a file node
if(path[0] != '\0' && path[strlen(path)-1] != '/')
WARN_RETURN(ERR_NOT_DIR);
WARN_RETURN(ERR_TNODE_WRONG_TYPE);
TDir* td = (flags & LF_START_DIR)? *ptd : tree_root;
TNode* node;
@ -592,7 +596,7 @@ LibError tree_lookup(const char* path, TFile** pfile, uint flags)
{
// path is not a file; TDir::lookup might return a directory node
if(path[0] == '\0' || path[strlen(path)-1] == '/')
WARN_RETURN(ERR_NOT_FILE);
WARN_RETURN(ERR_TNODE_WRONG_TYPE);
TNode* node;
LibError ret = lookup(tree_root, path, flags, &node);
@ -602,11 +606,46 @@ LibError tree_lookup(const char* path, TFile** pfile, uint flags)
}
struct AddPathCbParams
{
const Mount* const m;
TDir* td;
AddPathCbParams(const Mount* m_)
: m(m_), td(tree_root) {}
};
static LibError add_path_cb(const char* component, bool is_dir, void* ctx)
{
AddPathCbParams* p = (AddPathCbParams*)ctx;
// should only be called for directory paths, so complain if not dir.
if(!is_dir)
WARN_RETURN(ERR_TNODE_WRONG_TYPE);
TNode* node;
RETURN_ERR(p->td->find_and_add(component, NT_DIR, &node, p->m));
p->td = (TDir*)node;
return INFO_CB_CONTINUE;
}
// iterate over all components in V_dir_path (must reference a directory,
// i.e. end in slash). for any that are missing, add them with the
// specified mount point. this is useful for mounting directories.
//
// passes back the last directory encountered.
LibError tree_add_path(const char* V_dir_path, const Mount* m, TDir** ptd)
{
AddPathCbParams p(m);
RETURN_ERR(path_foreach_component(V_dir_path, add_path_cb, &p));
*ptd = p.td;
return ERR_OK;
}
//////////////////////////////////////////////////////////////////////////////
// rationale: see DirIterator definition in file.h.
struct TreeDirIterator
{

View File

@ -91,6 +91,14 @@ extern LibError tree_lookup(const char* path, TFile** ptf, uint flags = 0);
extern LibError tree_lookup_dir(const char* path, TDir** ptd, uint flags = 0);
// iterate over all components in V_dir_path (must reference a directory,
// i.e. end in slash). for any that are missing, add them with the
// specified mount point. this is useful for mounting directories.
//
// passes back the last directory encountered.
extern LibError tree_add_path(const char* V_dir_path, const Mount* m, TDir** ptd);
extern LibError tree_dir_open(const char* path_slash, DirIterator* di);
extern LibError tree_dir_next_ent(DirIterator* di, DirEnt* ent);
extern LibError tree_dir_close(DirIterator* di);

View File

@ -2,4 +2,5 @@
#include "h_mgr.h"
#include "file/vfs.h"
#include "lib/path_util.h"
#include "mem.h"

View File

@ -250,7 +250,7 @@ static LibError aio_h_set(const int fd, const HANDLE h)
// setting invalid handle
if(!is_valid_file_handle(h))
{
err = ERR_NOT_FILE;
err = ERR_TNODE_WRONG_TYPE;
goto fail;
}
}

View File

@ -290,6 +290,8 @@ static LibError ia32_walk_stack(STACKFRAME64* sf)
// read stack frame
void* fp = ((void**)prev_fp)[0];
void* ret_addr = ((void**)prev_fp)[1];
if(!fp)
return INFO_ALL_COMPLETE;
if(!debug_is_stack_ptr(fp))
WARN_RETURN(ERR_14);
if(!debug_is_code_ptr(ret_addr))

View File

@ -55,9 +55,8 @@ static LibError LibError_from_GLE(bool warn_if_failed = true)
case ERROR_ACCESS_DENIED:
err = ERR_FILE_ACCESS; break;
case ERROR_FILE_NOT_FOUND:
err = ERR_FILE_NOT_FOUND; break;
case ERROR_PATH_NOT_FOUND:
err = ERR_PATH_NOT_FOUND; break;
err = ERR_TNODE_NOT_FOUND; break;
default:
err = ERR_FAIL; break;

View File

@ -59,7 +59,8 @@ extern "C" {
// <limits.h>
//
#define PATH_MAX 260 // matches Win32 MAX_PATH
// Win32 MAX_PATH is 260; our number may be a bit more efficient.
#define PATH_MAX 256
#if OS_WIN
# ifndef SIZE_MAX // VC2005 already defines this in limits.h

View File

@ -34,7 +34,7 @@ CFilePacker::CFilePacker(u32 version, const char magicstr[4])
void CFilePacker::Write(const char* filename)
{
// write out all data (including header)
if(vfs_store(filename, &m_Data[0], m_Data.size(), FILE_NO_AIO|FILE_WRITE_TO_MOD) < 0)
if(vfs_store(filename, &m_Data[0], m_Data.size(), FILE_NO_AIO|FILE_WRITE_TO_TARGET) < 0)
throw PSERROR_File_WriteFailed();
}

View File

@ -571,12 +571,23 @@ static void InitVfs(const char* argv0)
(void)file_set_root_dir(argv0, "../data");
vfs_init();
vfs_mount("", "mods/official", VFS_MOUNT_RECURSIVE|VFS_MOUNT_ARCHIVES|VFS_MOUNT_WATCH|VFS_MOUNT_ARCHIVABLE);
vfs_mount("screenshots/", "screenshots");
vfs_mount("profiles/", "profiles", VFS_MOUNT_RECURSIVE);
// rationale:
// - this is in a separate real directory so that it can later be moved
// to $APPDATA to allow running without Admin access.
// - we mount as archivable so that all files will be added to archive.
// even though we write out XMBs here, they will eventually be read,
// so putting them in an archive boosts performance.
vfs_mount("cache/", "cache", VFS_MOUNT_RECURSIVE|VFS_MOUNT_ARCHIVES|VFS_MOUNT_ARCHIVABLE);
// --- this belongs in a LoadMod function
vfs_mount("", "mods/official", VFS_MOUNT_RECURSIVE|VFS_MOUNT_ARCHIVES|VFS_MOUNT_WATCH|VFS_MOUNT_ARCHIVABLE);
// ---
// TODO: once people can load multiple mods, set the top one to be the write target
vfs_mod_set_write_target("mods/official");
vfs_set_write_target("mods/official");
// don't try vfs_display yet: SDL_Init hasn't yet redirected stdout
}

View File

@ -132,25 +132,17 @@ no_ip:
}
// not thread-safe!
static const wchar_t* HardcodedErrorString(int err)
{
#define E(sym) case sym: return L ## #sym;
switch(err)
{
E(ERR_NO_MEM)
E(ERR_FILE_NOT_FOUND)
E(ERR_INVALID_HANDLE)
E(ERR_INVALID_PARAM)
E(ERR_EOF)
E(ERR_PATH_NOT_FOUND)
E(ERR_PATH_LENGTH)
default:
return 0;
}
char description[200];
error_description_r((LibError)err, description, ARRAY_SIZE(description));
static wchar_t output_buf[200];
mbstowcs(output_buf, description, ARRAY_SIZE(output_buf));
return output_buf;
}
// not thread-safe!
const wchar_t* ErrorString(int err)
{
// language file not available (yet)

View File

@ -26,7 +26,10 @@ bool VFSUtil::FindFiles (const CStr& dirname, const char* filter, FileList& file
DirEnt entry;
while ((err = vfs_dir_next_ent(dir, &entry, filter)) == 0)
{
files.push_back(dirname+"/"+entry.name);
CStr path = dirname+entry.name;
if(DIRENT_IS_DIR(&entry))
path += '/';
files.push_back(path);
}
if (err != ERR_DIR_END)

View File

@ -1,4 +1,5 @@
#include "ps/CStr.h"
#include "lib/path_util.h" // for convenience
#include "lib/res/file/vfs.h"
namespace VFSUtil

View File

@ -170,6 +170,37 @@ void CXeromyces::Terminate()
}
}
// Find out write location of the XMB file corresponding to xmlFilename
void CXeromyces::getXMBPath(const char* xmlFilename, const char* xmbFilename,
char* xmbPath)
{
// rationale:
// - it is necessary to write out XMB files into a subdirectory
// corresponding to the mod from which the XML file is taken.
// this avoids confusion when multiple mods are active -
// their XMB files' VFS filename would otherwise be indistinguishable.
// - we group files in the cache/ mount point first by mod, and only
// then XMB. this is so that all output files for a given mod can
// easily be deleted. the operation of deleting all old/unused
// XMB files requires a program anyway (to find out which are no
// longer needed), so it's not a problem that XMB files reside in
// a subdirectory (which would make manually deleting all harder).
// get real path of XML file (e.g. mods/official/entities/...)
char P_XMBRealPath[PATH_MAX];
vfs_realpath(xmlFilename, P_XMBRealPath);
// extract mod name from that
char modName[PATH_MAX];
// .. NOTE: can't use %s, of course (keeps going beyond '/')
int matches = sscanf(P_XMBRealPath, "mods/%[^/]", modName);
debug_assert(matches == 1);
// build full name: cache, then mod name, XMB subdir, original XMB path
snprintf(xmbPath, PATH_MAX, "cache/mods/%s/xmb/%s", modName, xmbFilename);
}
PSRETURN CXeromyces::Load(const char* filename)
{
// Make sure the .xml actually exists
@ -224,11 +255,14 @@ PSRETURN CXeromyces::Load(const char* filename)
}
xmbFilename += buf;
char xmbPath[PATH_MAX];
getXMBPath(filename, xmbFilename, xmbPath);
// If the file exists, use it
if (vfs_exists(xmbFilename))
if (vfs_exists(xmbPath))
{
if (ReadXMBFile(xmbFilename))
if (ReadXMBFile(xmbPath))
return PSRETURN_OK;
else
return PSRETURN_Xeromyces_XMLOpenFailed;
@ -292,7 +326,7 @@ PSRETURN CXeromyces::Load(const char* filename)
handler.CreateXMB();
// Save the file to disk, so it can be loaded quickly next time
vfs_store(xmbFilename, handler.buffer.buffer, handler.buffer.length, FILE_NO_AIO);
vfs_store(xmbPath, handler.buffer.buffer, handler.buffer.length, FILE_NO_AIO);
// Store the buffer so it can be freed later
XMBBuffer = handler.buffer.steal_buffer();

View File

@ -30,6 +30,11 @@ public:
static void Terminate();
private:
// Find out write location of the XMB file corresponding to xmlFilename
void getXMBPath(const char* xmlFilename, const char* xmbFilename,
char* xmbPath);
bool ReadXMBFile(const char* filename);
XMBFile* XMB;

View File

@ -42,7 +42,7 @@ bool I18n::LoadLanguage(const char* name)
// Automatically delete the pointer when returning early
std::auto_ptr<CLocale_interface> locale (locale_ptr);
CStr dirname = CStr("language/")+name;
CStr dirname = CStr("language/")+name+"/";
VFSUtil::FileList files;
VFSUtil::FileList::iterator filename;

View File

@ -13,7 +13,7 @@
// shared error handling code
#define JS_CHECK_FILE_ERR(err)\
/* this is liable to happen often, so don't complain */\
if(err == ERR_FILE_NOT_FOUND)\
if(err == ERR_TNODE_NOT_FOUND)\
{\
*rval = JSVAL_NULL;\
return( JS_TRUE );\

View File

@ -11,8 +11,13 @@ IEventTarget::~IEventTarget()
delete( it->second );
}
TIMER_ADD_CLIENT(tc_dispatch_total);
TIMER_ADD_CLIENT(tc_dispatch_js_total);
bool IEventTarget::_DispatchEvent( CScriptEvent* evt, IEventTarget* target )
{
TIMER_ACCRUE(tc_dispatch_total);
PROFILE_START( "_DispatchEvent" );
// TODO: Deal correctly with multiple handlers
@ -29,10 +34,12 @@ bool IEventTarget::_DispatchEvent( CScriptEvent* evt, IEventTarget* target )
for( it = handlers.begin(); it != handlers.end(); it++ )
{
DOMEventHandler id = *it;
{TIMER_ACCRUE(tc_dispatch_js_total);
if( id && id->DispatchEvent( GetScriptExecContext( target ), evt ) )
{
return( true );
}
}
}
HandlerRange range = m_Handlers_name.equal_range( evt->m_Type );
@ -40,10 +47,12 @@ bool IEventTarget::_DispatchEvent( CScriptEvent* evt, IEventTarget* target )
for( itm = range.first; itm != range.second; itm++ )
{
DOMEventHandler id = itm->second;
{TIMER_ACCRUE(tc_dispatch_js_total);
if( id && id->DispatchEvent( GetScriptExecContext( target ), evt ) )
{
return( true );
}
}
}
if( after && after->_DispatchEvent( evt, target ) )