From cb7549741280355c6715ce4179fa62dc437272c2 Mon Sep 17 00:00:00 2001 From: janwas Date: Thu, 3 Jun 2004 13:57:00 +0000 Subject: [PATCH] documentation! w00t This was SVN commit r385. --- source/lib/res/vfs.cpp | 185 ++++++++++++++++++++++++++++++----------- source/lib/res/vfs.h | 151 ++++++++++++++++++++++++--------- 2 files changed, 249 insertions(+), 87 deletions(-) diff --git a/source/lib/res/vfs.cpp b/source/lib/res/vfs.cpp index 5eb7d6bd85..1a12ac7e80 100755 --- a/source/lib/res/vfs.cpp +++ b/source/lib/res/vfs.cpp @@ -688,6 +688,12 @@ void vfs_shutdown(void) } +// mount either a single archive or a directory into the VFS at +// , which is created if it does not yet exist. +// new files override the previous VFS contents if pri(ority) is higher. +// if is a directory, all archives in that directory (but not +// its subdirs - see add_dirent_cb) are also mounted in alphabetical order. +// name = "." or "./" isn't allowed - see implementation for rationale. int vfs_mount(const char* const vfs_mount_point, const char* const name, const uint pri) { ONCE(atexit2(vfs_shutdown)); @@ -700,6 +706,10 @@ int vfs_mount(const char* const vfs_mount_point, const char* const name, const u return -1; } + CHECK_PATH(name); + + // TODO: disallow mounting parent directory of a previous mounting + // disallow . because "./" isn't supported on Windows. // the more important reason is that mount points must not overlap // (i.e. mount $install/data and then $install/data/mods/official - @@ -718,6 +728,12 @@ int vfs_mount(const char* const vfs_mount_point, const char* const name, const u } +// rebuild the VFS, i.e. re-mount everything. open files are not affected. +// necessary after loose files or directories change, so that the VFS +// "notices" the changes and updates file locations. res calls this after +// FAM reports changes; can also be called from the console after a +// rebuild command. there is no provision for updating single VFS dirs - +// it's not worth the trouble. int vfs_rebuild() { tree_clear(); @@ -728,6 +744,7 @@ int vfs_rebuild() } +// unmount a previously mounted item, and rebuild the VFS afterwards. int vfs_unmount(const char* name) { for(MountIt it = mounts.begin(); it != mounts.end(); ++it) @@ -752,46 +769,13 @@ int vfs_unmount(const char* name) /////////////////////////////////////////////////////////////////////////////// -int vfs_realpath(const char* fn, char* full_path) -{ - const FileLoc* loc; - CHECK_ERR(tree_lookup(fn, &loc)); - - if(loc->archive > 0) - { - const char* archive_fn = h_filename(loc->archive); - if(!archive_fn) - return -1; - strncpy(full_path, archive_fn, PATH_MAX); - } - else - { - strncpy(full_path, loc->dir.c_str(), PATH_MAX); - } - - return 0; -} - - -int vfs_stat(const char* fn, struct stat* s) -{ - const FileLoc* loc; - CHECK_ERR(tree_lookup(fn, &loc)); - - if(loc->archive > 0) - return zip_stat(loc->archive, fn, s); - else - { - const char* dir = loc->dir.c_str(); - return file_stat(dir, s); - } -} - - struct VDir { // we need to cache the complete contents of the directory: - // + // if we reference the real directory and it changes, + // the c_str pointers may become invalid, and some files + // may be returned out of order / not at all. + // we copy the directory's subdirectory and file containers. SubDirs* subdirs; SubDirIt subdir_it; Files* files; @@ -817,6 +801,7 @@ static int VDir_reload(VDir* vd, const char* path) Dir* dir; CHECK_ERR(tree_lookup(path, 0, &dir)); + // rationale for copy: see VDir definition vd->subdirs = new SubDirs(dir->subdirs); vd->subdir_it = vd->subdirs->begin(); vd->files = new Files(dir->files); @@ -825,28 +810,34 @@ static int VDir_reload(VDir* vd, const char* path) } -Handle vfs_open_dir(const char* const path) +// open a directory for reading its entries via vfs_next_dirent. +// directory contents are cached here; subsequent changes to the dir +// are not returned by this handle. rationale: see VDir definition. +Handle vfs_open_dir(const char* const dir) { - return h_alloc(H_VDir, path, 0); + return h_alloc(H_VDir, dir, 0); } +// close the handle to a directory. +// all vfsDirEnt.name strings are now invalid. int vfs_close_dir(Handle& hd) { return h_free(hd, H_VDir); } -// filter: -// 0: any file -// ".": file without extension (filename doesn't contain '.') -// ".ext": file with extension (which must not contain '.') -// "/": subdirectory +// get the next directory entry (in alphabetical order) that matches filter. +// return 0 on success. filter values: +// - 0: any file; +// - ".": any file without extension (filename doesn't contain '.'); +// - ".ext": any file with extension ".ext" (which must not contain '.'); +// - "/": any subdirectory int vfs_next_dirent(const Handle hd, vfsDirEnt* ent, const char* const filter) { H_DEREF(hd, VDir, vd); - // interpret filter + // interpret filter (paranoid) bool filter_dir = false; bool filter_no_ext = false; if(filter) @@ -871,6 +862,13 @@ int vfs_next_dirent(const Handle hd, vfsDirEnt* ent, const char* const filter) } } + // rationale: the filename is currently stored internally as + // std::string (=> less manual memory allocation). we don't want to + // return a reference, because that would break C compatibility. + // we're trying to avoid fixed-size buffers, so that is out as well. + // finally, allocating a copy is not so good because it has to be + // freed by the user (won't happen). returning a volatile pointer + // to the string itself via c_str is the only remaining option. const char* fn; // caller wants a subdirectory; return the next one. @@ -914,6 +912,50 @@ have_match: } +// return actual path to the specified file: +// "/fn" or "/fn". +int vfs_realpath(const char* fn, char* full_path) +{ + const FileLoc* loc; + CHECK_ERR(tree_lookup(fn, &loc)); + + const char* dir; + + // file is in normal directory + if(loc->archive <= 0) + dir = loc->dir.c_str(); + // file is in archive + { + // "dir" is the archive filename + dir = h_filename(loc->archive); + if(!dir) + return -1; + } + + CHECK_ERR(path_append(full_path, dir, fn)); + return 0; +} + + +// return information about the specified file as in stat(2), +// most notably size. stat buffer is undefined on error. +int vfs_stat(const char* fn, struct stat* s) +{ + const FileLoc* loc; + CHECK_ERR(tree_lookup(fn, &loc)); + + if(loc->archive > 0) + return zip_stat(loc->archive, fn, s); + else + { + // similar to realpath, but don't bother splitting it out. + char path[VFS_MAX_PATH]; + path_append(path, loc->dir.c_str(), fn); + return file_stat(path, s); + } +} + + /////////////////////////////////////////////////////////////////////////////// // // file @@ -1038,6 +1080,8 @@ static int VFile_reload(VFile* vf, const char* path) } +// open the file for synchronous or asynchronous IO. write access is +// requested via VFS_WRITE flag, and is not possible for files in archives. Handle vfs_open(const char* fn, uint flags /* = 0 */) { Handle h = h_alloc(H_VFile, fn, 0, flags); @@ -1051,6 +1095,7 @@ debug_out("vfs_open fn=%s %I64x\n", fn, h); } +// close the handle to a file. inline int vfs_close(Handle& h) { #ifdef PARANOIA @@ -1061,7 +1106,11 @@ debug_out("vfs_close %I64x\n", h); } -ssize_t vfs_io(Handle hf, off_t ofs, size_t size, void*& p) +// try to transfer bytes, starting at , to/from the given file. +// (read or write access was chosen at file-open time). +// return bytes of actual data transferred, or a negative error code. +// TODO: buffer types +ssize_t vfs_io(const Handle hf, const off_t ofs, const size_t size, void*& p) { #ifdef PARANOIA debug_out("vfs_io ofs=%d size=%d\n", ofs, size); @@ -1080,7 +1129,9 @@ debug_out("vfs_io ofs=%d size=%d\n", ofs, size); } -Handle vfs_load(const char* fn, void*& p, size_t& size) +// load the entire file into memory; return a handle to the memory +// and the buffer address/size. output parameters are zeroed on failure. +Handle vfs_load(const char* const fn, void*& p, size_t& size) { #ifdef PARANOIA debug_out("vfs_load fn=%s\n", fn); @@ -1144,9 +1195,12 @@ int vfs_store(const char* const fn, void* p, const size_t size) } +/////////////////////////////////////////////////////////////////////////////// // // memory mapping // +/////////////////////////////////////////////////////////////////////////////// + // map the entire file into memory. if already currently mapped, // return the previous mapping (reference-counted). @@ -1185,3 +1239,40 @@ int vfs_unmap(const Handle hf) else return file_unmap(&vf->f); } + + +/////////////////////////////////////////////////////////////////////////////// +// +// asynchronous I/O +// +/////////////////////////////////////////////////////////////////////////////// + + +// begin transferring bytes, starting at . get result +// with vfs_wait_read; when no longer needed, free via vfs_discard_io. +Handle vfs_start_io(Handle hf, off_t ofs, size_t size, void* buf) +{ + H_DEREF(hf, VFile, vf); + if(vf_flags(vf) & VF_ZIP) + ; + + return 0; +} + + +// wait until the transfer completes, and return its buffer. +// output parameters are zeroed on error. +int vfs_wait_io(Handle hio, void*& p, size_t& size) +{ + p = 0; + size = 0; + + return 0; +} + + +// finished with transfer - free its buffer (returned by vfs_wait_read) +int vfs_discard_io(Handle& hio) +{ + return 0; +} \ No newline at end of file diff --git a/source/lib/res/vfs.h b/source/lib/res/vfs.h index 14381d3bbc..1b8e8afa04 100755 --- a/source/lib/res/vfs.h +++ b/source/lib/res/vfs.h @@ -20,25 +20,109 @@ #ifndef __VFS_H__ #define __VFS_H__ -#include "h_mgr.h" +#include "h_mgr.h" // Handle #include "posix.h" // struct stat -// the VFS doesn't require this length restriction - VFS internal storage -// is not fixed-length. the purpose here is to allow fixed-sized path buffers -// allocated on the stack. + // -// length includes trailing '\0'. +// VFS tree +// + +// the VFS doesn't require this length restriction - VFS internal storage +// is not fixed-length. the purpose here is to give an indication of how +// large fixed-size user buffers should be. length includes trailing '\0'. #define VFS_MAX_PATH 256 -extern int vfs_mount(const char* vfs_mount_point, const char* name, uint pri); -extern int vfs_umount(const char* name); +// VFS paths are of the form: +// "[dir/{subdir/}]file" or "[dir/{subdir/}]dir[/]". +// in English: '/' as path separator; trailing '/' allowed for dir names; +// no leading '/', since "" is the root dir. -extern int vfs_stat(const char* fn, struct stat*); +// mount either a single archive or a directory into the VFS at +// , which is created if it does not yet exist. +// new files override the previous VFS contents if pri(ority) is higher. +// if is a directory, all archives in that directory (but not +// its subdirs - see add_dirent_cb) are also mounted in alphabetical order. +// name = "." or "./" isn't allowed - see implementation for rationale. +extern int vfs_mount(const char* vfs_mount_point, const char* name, uint pri); + +// rebuild the VFS, i.e. re-mount everything. open files are not affected. +// necessary after loose files or directories change, so that the VFS +// "notices" the changes and updates file locations. res calls this after +// FAM reports changes; can also be called from the console after a +// rebuild command. there is no provision for updating single VFS dirs - +// it's not worth the trouble. +extern int vfs_rebuild(); + +// unmount a previously mounted item, and rebuild the VFS afterwards. +extern int vfs_unmount(const char* name); + + +// +// directory entry +// + +// information about a directory entry, returned by vfs_next_dirent. +struct vfsDirEnt +{ + // name of directory entry - does not include path. + // valid until the directory handle is closed. must not be modified! + // rationale for pointer and invalidation: see vfs_next_dirent. + const char* name; +}; + +// open the directory for reading its entries via vfs_next_dirent. +// directory contents are cached here; subsequent changes to the dir +// are not returned by this handle. rationale: see VDir definition. +extern Handle vfs_open_dir(const char* dir); + +// close the handle to a directory. +// all vfsDirEnt.name strings are now invalid. +extern int vfs_close_dir(Handle& hd); + +// get the next directory entry (in alphabetical order) that matches filter. +// return 0 on success. filter values: +// - 0: any file; +// - ".": any file without extension (filename doesn't contain '.'); +// - ".ext": any file with extension ".ext" (which must not contain '.'); +// - "/": any subdirectory +extern int vfs_next_dirent(Handle hd, vfsDirEnt* ent, const char* filter); + + +// +// file +// + +// return actual path to the specified file: +// "/fn" or "/fn". extern int vfs_realpath(const char* fn, char* realpath); -extern Handle vfs_load(const char* fn, void*& p, size_t& size); +// return information about the specified file as in stat(2), +// most notably size. stat buffer is undefined on error. +extern int vfs_stat(const char* fn, struct stat*); +// vfs_open flags - keep in sync with file.cpp flag definitions! +enum vfsOpenFlags +{ + // write-only access; otherwise, read only + VFS_WRITE = 0x01, + + // buffers returned may be read-only (allows some caching optimizations) + VFS_MEM_READONLY = 0x02, + + // don't cache the whole file, e.g. if kept in memory elsewhere anyway. + VFS_NOCACHE = 0x04, + + // random access hint + VFS_RANDOM = 0x08 + +}; + +// open the file for synchronous or asynchronous IO. write access is +// requested via VFS_WRITE flag, and is not possible for files in archives. extern Handle vfs_open(const char* fn, uint flags = 0); + +// close the handle to a file. extern int vfs_close(Handle& h); @@ -65,47 +149,34 @@ extern int vfs_unmap(Handle hf); // -// directory entry enumeration +// asynchronous I/O // -struct vfsDirEnt -{ - // the filename is currently stored internally as std::string. returning as char* for C compat - // stored internally as std::string. returning as char* for C compat - // would mean we have to return a copy. we try to avoid fixed-size - // buffers, so that leaves a reference. - const char* name; -}; +// begin transferring bytes, starting at . get result +// with vfs_wait_read; when no longer needed, free via vfs_discard_io. +extern Handle vfs_start_io(Handle hf, off_t ofs, size_t size, void* buf); -extern Handle vfs_open_dir(const char* path); -extern int vfs_close_dir(Handle& hd); -extern int vfs_next_dirent(Handle hd, vfsDirEnt* ent, const char* filter); +// wait until the transfer completes, and return its buffer. +// output parameters are zeroed on error. +extern int vfs_wait_io(Handle hio, void*& p, size_t& size); + +// finished with transfer - free its buffer (returned by vfs_wait_read). +extern int vfs_discard_io(Handle& hio); // -// async read interface +// synchronous I/O // -extern Handle vfs_start_read(const Handle hf, off_t ofs, size_t& advance, void* buf); -extern int vfs_wait_read(Handle hr, void*& p, size_t& size); -extern int vfs_discard_read(Handle& hr); - +// try to transfer bytes, starting at . +// (read or write access was chosen at file-open time). +// return bytes of actual data transferred, or a negative error code. +// TODO: buffer types extern ssize_t vfs_io(Handle hf, off_t ofs, size_t size, void*& p); - -// keep in sync with File flags! - -enum -{ - VFS_WRITE = 1, // write-only access; otherwise, read only - VFS_MEM_READONLY = 2, // !want to be able to change in memory data - VFS_NOCACHE = 4, // don't cache whole file, e.g. if cached on a higher level - VFS_RANDOM = 8 // random access hint, allow offset -}; - - - -extern int vfs_rebuild(); +// load the entire file into memory; return a memory handle to the +// buffer and its address/size. output parameters are zeroed on failure. +extern Handle vfs_load(const char* fn, void*& p, size_t& size); #endif // #ifndef __VFS_H__