forked from 0ad/0ad
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:
parent
58bed083ba
commit
e6be7e36d2
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
||||
//
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -2,4 +2,5 @@
|
||||
|
||||
#include "h_mgr.h"
|
||||
#include "file/vfs.h"
|
||||
#include "lib/path_util.h"
|
||||
#include "mem.h"
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "ps/CStr.h"
|
||||
#include "lib/path_util.h" // for convenience
|
||||
#include "lib/res/file/vfs.h"
|
||||
|
||||
namespace VFSUtil
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 );\
|
||||
|
@ -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 ) )
|
||||
|
Loading…
Reference in New Issue
Block a user