From 8667ea74c8756f70bd8a8d81132b8a5ea9d87e03 Mon Sep 17 00:00:00 2001 From: janwas Date: Sat, 22 Dec 2007 18:15:52 +0000 Subject: [PATCH] fixes and improvements - directoryPosix: replace most methods with boost filesystem (but not all: the latter cannot efficiently enumerate files AND query their size/mtime) - AllocatorChecker: better name for member functions - file: move the File class here - trace: bugfix - io: move UnalignedWriter to write_buffer.cpp (basically the same thing) - vfs: remove unnecessary "vfs" warts from variable names - vfs_tree: VfsFile now stores single Name/Size/MTime fields instead of the FileInfo record (less clunky) - vfs_path: use boost filesystem's version of the basename/extension functions - lf_alloc: remove (no longer necessary, won't be finished - not worth the trouble) - path_util: remove path_foreach_component (replaced by better path traversal logic) and PathPackage (obsoleted by fs::path) ! resource loading code now receives VfsPath as its filename. there is also OsPath (native absolute path) and Path (relative to binaries/data) - tex is now independent of file loading code; it just en/decodes in-memory buffers - wdll_ver: clean up, use smart pointer to simplify bailout code - wsdl: remove nonexistent failure path from calc_gamma (cruised by here because SDL_SetGamme is failing once after a cold boot at work) - wsnd: simplify OpenAL DLL search, use boost::filesystem - wutil: Wow64 redirection is now packaged in a (RAII) class This was SVN commit r5525. --- source/graphics/tests/test_MeshManager.h | 4 +- source/lib/allocators/allocators.h | 8 +- source/lib/file/archive/archive_zip.cpp | 8 +- source/lib/file/common/real_directory.cpp | 1 + source/lib/file/common/trace.cpp | 2 +- source/lib/file/file.cpp | 124 ++++ source/lib/file/file.h | 57 ++ source/lib/file/file_system_posix.cpp | 40 +- source/lib/file/file_system_posix.h | 7 +- source/lib/file/file_system_util.cpp | 2 +- source/lib/file/io/io.cpp | 212 +------ source/lib/file/io/io.h | 72 +-- source/lib/file/io/write_buffer.cpp | 101 +++ source/lib/file/io/write_buffer.h | 53 +- source/lib/file/vfs/file_cache.cpp | 4 +- source/lib/file/vfs/vfs.cpp | 48 +- source/lib/file/vfs/vfs_lookup.cpp | 56 +- source/lib/file/vfs/vfs_lookup.h | 2 +- source/lib/file/vfs/vfs_path.cpp | 16 +- source/lib/file/vfs/vfs_path.h | 4 +- source/lib/file/vfs/vfs_tree.cpp | 76 ++- source/lib/file/vfs/vfs_tree.h | 22 +- source/lib/lf_alloc.cpp | 576 ------------------ source/lib/path_util.cpp | 116 ---- source/lib/path_util.h | 69 --- source/lib/res/graphics/cursor.cpp | 27 +- source/lib/res/graphics/ogl_shader.cpp | 42 +- source/lib/res/graphics/ogl_shader.h | 2 +- source/lib/res/graphics/ogl_tex.cpp | 30 +- source/lib/res/graphics/ogl_tex.h | 5 +- source/lib/res/graphics/tests/test_tex.h | 9 +- source/lib/res/graphics/unifont.cpp | 23 +- source/lib/res/graphics/unifont.h | 2 +- source/lib/res/h_mgr.cpp | 154 +---- source/lib/res/h_mgr.h | 47 +- source/lib/res/handle.h | 2 + source/lib/res/sound/snd_mgr.cpp | 136 ++--- source/lib/res/sound/snd_mgr.h | 4 +- source/lib/sysdep/win/wdll_ver.cpp | 83 +-- source/lib/sysdep/win/wdll_ver.h | 4 +- source/lib/sysdep/win/wsdl.cpp | 14 +- source/lib/sysdep/win/wsnd.cpp | 57 +- source/lib/sysdep/win/wutil.cpp | 9 +- source/lib/sysdep/win/wutil.h | 12 +- source/lib/tests/test_path_util.h | 28 - source/lib/tex/tex.cpp | 67 +- source/lib/tex/tex.h | 34 +- source/lib/tex/tex_bmp.cpp | 4 +- source/lib/tex/tex_codec.cpp | 14 +- source/lib/tex/tex_codec.h | 13 +- source/lib/tex/tex_dds.cpp | 4 +- source/lib/tex/tex_internal.h | 26 - source/lib/tex/tex_jpg.cpp | 30 +- source/lib/tex/tex_png.cpp | 27 +- source/lib/tex/tex_tga.cpp | 4 +- source/ps/Filesystem.h | 4 +- source/ps/Util.cpp | 31 + source/renderer/Renderer.cpp | 13 +- source/scripting/ScriptingHost.cpp | 6 +- .../simulation/EntityTemplateCollection.cpp | 5 +- source/simulation/FormationCollection.cpp | 2 +- source/simulation/TechnologyCollection.cpp | 2 +- source/sound/CMusicPlayer.cpp | 2 +- source/sound/JSI_Sound.cpp | 2 +- 64 files changed, 779 insertions(+), 1881 deletions(-) create mode 100644 source/lib/file/file.cpp create mode 100644 source/lib/file/file.h create mode 100644 source/lib/file/io/write_buffer.cpp delete mode 100644 source/lib/lf_alloc.cpp diff --git a/source/graphics/tests/test_MeshManager.h b/source/graphics/tests/test_MeshManager.h index 533a881950..b70ba258ba 100644 --- a/source/graphics/tests/test_MeshManager.h +++ b/source/graphics/tests/test_MeshManager.h @@ -38,8 +38,8 @@ class TestMeshManager : public CxxTest::TestSuite directoryPosix.DeleteDirectory(MOD_PATH); directoryPosix.DeleteDirectory(CACHE_PATH); - TS_ASSERT_OK(directoryPosix.CreateDirectory(MOD_PATH)); - TS_ASSERT_OK(directoryPosix.CreateDirectory(CACHE_PATH)); + TS_ASSERT_OK(fs::create_directory(MOD_PATH)); + TS_ASSERT_OK(fs::create_directory(CACHE_PATH)); vfs = CreateVfs(); diff --git a/source/lib/allocators/allocators.h b/source/lib/allocators/allocators.h index b32ed58466..a40400a2b0 100644 --- a/source/lib/allocators/allocators.h +++ b/source/lib/allocators/allocators.h @@ -311,21 +311,21 @@ private: class AllocatorChecker { public: - void notify_alloc(void* p, size_t size) + void OnAllocate(void* p, size_t size) { const Allocs::value_type item = std::make_pair(p, size); std::pair ret = allocs.insert(item); debug_assert(ret.second == true); // wasn't already in map } - void notify_free(void* p, size_t size) + void OnDeallocate(void* p, size_t size) { Allocs::iterator it = allocs.find(p); if(it == allocs.end()) debug_assert(0); // freeing invalid pointer else { - // size must match what was passed to notify_alloc + // size must match what was passed to OnAllocate const size_t allocated_size = it->second; debug_assert(size == allocated_size); @@ -336,7 +336,7 @@ public: /** * allocator is resetting itself, i.e. wiping out all allocs. **/ - void notify_clear() + void OnClear() { allocs.clear(); } diff --git a/source/lib/file/archive/archive_zip.cpp b/source/lib/file/archive/archive_zip.cpp index 257dc7493b..feb6fd99c2 100644 --- a/source/lib/file/archive/archive_zip.cpp +++ b/source/lib/file/archive/archive_zip.cpp @@ -23,10 +23,12 @@ #include "archive.h" #include "codec_zlib.h" #include "stream.h" +#include "lib/file/file.h" #include "lib/file/file_system_posix.h" #include "lib/file/io/io.h" +#include "lib/file/io/write_buffer.h" -static FileSystem_Posix s_posixDirectory; +static FileSystem_Posix s_fileSystemPosix; //----------------------------------------------------------------------------- @@ -359,7 +361,7 @@ public: m_file->Open(pathname, 'r'); FileInfo fileInfo; - s_posixDirectory.GetFileInfo(pathname, &fileInfo); + s_fileSystemPosix.GetFileInfo(pathname, &fileInfo); m_fileSize = fileInfo.Size(); debug_assert(m_fileSize >= sizeof(LFH)+sizeof(CDFH)+sizeof(ECDR)); } @@ -524,7 +526,7 @@ public: LibError AddFile(const Path& pathname) { FileInfo fileInfo; - RETURN_ERR(s_posixDirectory.GetFileInfo(pathname, &fileInfo)); + RETURN_ERR(s_fileSystemPosix.GetFileInfo(pathname, &fileInfo)); const off_t usize = fileInfo.Size(); // skip 0-length files. // rationale: zip.cpp needs to determine whether a CDFH entry is diff --git a/source/lib/file/common/real_directory.cpp b/source/lib/file/common/real_directory.cpp index 51eb35fac9..c2852dc191 100644 --- a/source/lib/file/common/real_directory.cpp +++ b/source/lib/file/common/real_directory.cpp @@ -2,6 +2,7 @@ #include "real_directory.h" #include "lib/path_util.h" +#include "lib/file/file.h" #include "lib/file/io/io.h" diff --git a/source/lib/file/common/trace.cpp b/source/lib/file/common/trace.cpp index a965c4054f..6f02e9b625 100644 --- a/source/lib/file/common/trace.cpp +++ b/source/lib/file/common/trace.cpp @@ -39,7 +39,7 @@ TraceEntry::TraceEntry(const char* text) const char* fmt = "%f: %c \"" STRINGIZE(PATH_MAX) "[^\"]\" %d\n"; char pathname[PATH_MAX]; char action; - const int fieldsRead = sscanf_s(text, fmt, &m_timestamp, &m_action, pathname, &m_size); + const int fieldsRead = sscanf_s(text, fmt, &m_timestamp, &action, pathname, &m_size); debug_assert(fieldsRead == 4); debug_assert(action == 'L' || action == 'S'); m_action = (EAction)action; diff --git a/source/lib/file/file.cpp b/source/lib/file/file.cpp new file mode 100644 index 0000000000..3ef2b9812c --- /dev/null +++ b/source/lib/file/file.cpp @@ -0,0 +1,124 @@ +/** + * ========================================================================= + * File : file.cpp + * Project : 0 A.D. + * Description : simple POSIX file wrapper. + * ========================================================================= + */ + +#include "precompiled.h" +#include "file.h" + +#include "lib/file/common/file_stats.h" +#include "lib/file/path.h" + + +ERROR_ASSOCIATE(ERR::FILE_ACCESS, "Insufficient access rights to open file", EACCES); +ERROR_ASSOCIATE(ERR::IO, "Error during IO", EIO); + + +File::File() +{ +} + + +File::~File() +{ + Close(); +} + + +LibError File::Open(const Path& pathname, char mode) +{ + debug_assert(mode == 'w' || mode == 'r'); + + m_pathname = pathname; + m_mode = mode; + + int oflag = (mode == 'r')? O_RDONLY : O_WRONLY|O_CREAT|O_TRUNC; +#if OS_WIN + oflag |= O_BINARY_NP; +#endif + m_fd = open(m_pathname.external_file_string().c_str(), oflag, S_IRWXO|S_IRWXU|S_IRWXG); + if(m_fd < 0) + WARN_RETURN(ERR::FILE_ACCESS); + + stats_open(); + return INFO::OK; +} + + +void File::Close() +{ + m_mode = '\0'; + + close(m_fd); + m_fd = 0; +} + + +LibError File::Issue(aiocb& req, off_t alignedOfs, u8* alignedBuf, size_t alignedSize) const +{ + memset(&req, 0, sizeof(req)); + req.aio_lio_opcode = (m_mode == 'w')? LIO_WRITE : LIO_READ; + req.aio_buf = (volatile void*)alignedBuf; + req.aio_fildes = m_fd; + req.aio_offset = alignedOfs; + req.aio_nbytes = alignedSize; + struct sigevent* sig = 0; // no notification signal + aiocb* const reqs = &req; + if(lio_listio(LIO_NOWAIT, &reqs, 1, sig) != 0) + return LibError_from_errno(); + return INFO::OK; +} + + +/*static*/ LibError File::WaitUntilComplete(aiocb& req, u8*& alignedBuf, size_t& alignedSize) +{ + // wait for transfer to complete. + while(aio_error(&req) == EINPROGRESS) + { + aiocb* const reqs = &req; + aio_suspend(&reqs, 1, (timespec*)0); // wait indefinitely + } + + const ssize_t bytesTransferred = aio_return(&req); + if(bytesTransferred == -1) // transfer failed + WARN_RETURN(ERR::IO); + + alignedBuf = (u8*)req.aio_buf; // cast from volatile void* + alignedSize = bytesTransferred; + return INFO::OK; +} + + +LibError File::IO(off_t ofs, u8* buf, size_t size) const +{ + ScopedIoMonitor monitor; + + lseek(m_fd, ofs, SEEK_SET); + + errno = 0; + const ssize_t ret = (m_mode == 'w')? write(m_fd, buf, size) : read(m_fd, buf, size); + if(ret < 0) + return LibError_from_errno(); + + const size_t totalTransferred = (size_t)ret; + if(totalTransferred != size) + WARN_RETURN(ERR::IO); + + monitor.NotifyOfSuccess(FI_LOWIO, m_mode, totalTransferred); + return INFO::OK; +} + + +LibError File::Write(off_t ofs, const u8* buf, size_t size) const +{ + return IO(ofs, const_cast(buf), size); +} + + +LibError File::Read(off_t ofs, u8* buf, size_t size) const +{ + return IO(ofs, buf, size); +} diff --git a/source/lib/file/file.h b/source/lib/file/file.h new file mode 100644 index 0000000000..699f583011 --- /dev/null +++ b/source/lib/file/file.h @@ -0,0 +1,57 @@ +/** + * ========================================================================= + * File : file.h + * Project : 0 A.D. + * Description : simple POSIX file wrapper. + * ========================================================================= + */ + +// license: GPL; see lib/license.txt + +#ifndef INCLUDED_FILE +#define INCLUDED_FILE + +#include "path.h" + +namespace ERR +{ + const LibError FILE_ACCESS = -110200; + const LibError IO = -110201; +} + +class LIB_API File +{ +public: + File(); + ~File(); + + LibError Open(const Path& pathname, char mode); + void Close(); + + const Path& Pathname() const + { + return m_pathname; + } + + char Mode() const + { + return m_mode; + } + + LibError Issue(aiocb& req, off_t alignedOfs, u8* alignedBuf, size_t alignedSize) const; + static LibError WaitUntilComplete(aiocb& req, u8*& alignedBuf, size_t& alignedSize); + + LibError Read(off_t ofs, u8* buf, size_t size) const; + LibError Write(off_t ofs, const u8* buf, size_t size) const; + +private: + LibError IO(off_t ofs, u8* buf, size_t size) const; + + Path m_pathname; + char m_mode; + int m_fd; +}; + +typedef shared_ptr PFile; + +#endif // #ifndef INCLUDED_FILE diff --git a/source/lib/file/file_system_posix.cpp b/source/lib/file/file_system_posix.cpp index 236f36c8d5..4a2d4449e8 100644 --- a/source/lib/file/file_system_posix.cpp +++ b/source/lib/file/file_system_posix.cpp @@ -47,11 +47,6 @@ static bool IsDummyDirectory(const char* name) return LibError_from_errno(); shared_ptr osDir(pDir, DirDeleter()); - // (we need absolute paths below; using this instead of path_append avoids - // a strlen call for each entry.) - PathPackage pp; - (void)path_package_set_dir(&pp, path.external_directory_string().c_str()); - for(;;) { errno = 0; @@ -76,8 +71,8 @@ static bool IsDummyDirectory(const char* name) #else // .. call regular stat(). errno = 0; - path_package_append_file(&pp, name); - if(stat(pp->path, &s) != 0) + const Path pathname(path/name); + if(stat(pathname.external_directory_string().c_str(), &s) != 0) return LibError_from_errno(); #endif @@ -111,45 +106,20 @@ LibError FileSystem_Posix::GetFileInfo(const Path& pathname, FileInfo* pfileInfo } -LibError FileSystem_Posix::DeleteFile(const Path& pathname) -{ - errno = 0; - if(unlink(pathname.external_file_string().c_str()) != 0) - return LibError_from_errno(); - - return INFO::OK; -} - - -LibError FileSystem_Posix::CreateDirectory(const Path& path) -{ - errno = 0; - if(mkdir(path.external_directory_string().c_str(), S_IRWXO|S_IRWXU|S_IRWXG) != 0) - return LibError_from_errno(); - - return INFO::OK; -} - - LibError FileSystem_Posix::DeleteDirectory(const Path& path) { // note: we have to recursively empty the directory before it can // be deleted (required by Windows and POSIX rmdir()). - const char* osPath = path.external_directory_string().c_str(); - - PathPackage pp; - RETURN_ERR(path_package_set_dir(&pp, osPath)); - FileInfos files; DirectoryNames subdirectoryNames; RETURN_ERR(GetDirectoryEntries(path, &files, &subdirectoryNames)); // delete files for(size_t i = 0; i < files.size(); i++) { - RETURN_ERR(path_package_append_file(&pp, files[i].Name().c_str())); + const Path pathname(path/files[i].Name()); errno = 0; - if(unlink(pp.path) != 0) + if(unlink(pathname.external_file_string().c_str()) != 0) return LibError_from_errno(); } @@ -158,7 +128,7 @@ LibError FileSystem_Posix::DeleteDirectory(const Path& path) RETURN_ERR(DeleteDirectory(path/subdirectoryNames[i])); errno = 0; - if(rmdir(osPath) != 0) + if(rmdir(path.external_directory_string().c_str()) != 0) return LibError_from_errno(); return INFO::OK; diff --git a/source/lib/file/file_system_posix.h b/source/lib/file/file_system_posix.h index 635a4ef731..0420dd03c7 100644 --- a/source/lib/file/file_system_posix.h +++ b/source/lib/file/file_system_posix.h @@ -15,13 +15,16 @@ #include "lib/file/path.h" #include "lib/file/file_system.h" +// jw 2007-12-20: we'd love to replace this with boost::filesystem, +// but basic_directory_iterator does not yet cache file_size and +// last_write_time in file_status. (they each entail a stat() call, +// which is unacceptably slow.) + struct FileSystem_Posix { virtual LibError GetFileInfo(const Path& pathname, FileInfo* fileInfo) const; virtual LibError GetDirectoryEntries(const Path& path, FileInfos* files, DirectoryNames* subdirectoryNames) const; - LibError DeleteFile(const Path& pathname); - LibError CreateDirectory(const Path& dirPath); LibError DeleteDirectory(const Path& dirPath); }; diff --git a/source/lib/file/file_system_util.cpp b/source/lib/file/file_system_util.cpp index 562b67ad50..a61c50b115 100644 --- a/source/lib/file/file_system_util.cpp +++ b/source/lib/file/file_system_util.cpp @@ -65,7 +65,7 @@ void fs_SortDirectories(DirectoryNames& directories) LibError fs_ForEachFile(PIVFS fs, const VfsPath& path, FileCallback cb, uintptr_t cbData, const char* pattern, unsigned flags) { - debug_assert(IsDirectory(path)); + debug_assert(vfs_path_IsDirectory(path)); // (declare here to avoid reallocations) FileInfos files; DirectoryNames subdirectoryNames; diff --git a/source/lib/file/io/io.cpp b/source/lib/file/io/io.cpp index c414cef6a2..5ab3bbd388 100644 --- a/source/lib/file/io/io.cpp +++ b/source/lib/file/io/io.cpp @@ -14,17 +14,12 @@ #include "lib/allocators/allocators.h" // AllocatorChecker #include "lib/bits.h" // IsAligned, round_up #include "lib/sysdep/cpu.h" // cpu_memcpy +#include "lib/file/file.h" #include "lib/file/common/file_stats.h" -#include "lib/file/path.h" #include "block_cache.h" #include "io_internal.h" -ERROR_ASSOCIATE(ERR::FILE_ACCESS, "Insufficient access rights to open file", EACCES); -ERROR_ASSOCIATE(ERR::IO, "Error during IO", EIO); -ERROR_ASSOCIATE(ERR::IO_EOF, "Reading beyond end of file", -1); - - // the underlying aio implementation likes buffer and offset to be // sector-aligned; if not, the transfer goes through an align buffer, // and requires an extra cpu_memcpy. @@ -83,7 +78,7 @@ public: { debug_assert(m_paddedSize != 0); #ifndef NDEBUG - allocatorChecker.notify_free(mem, m_paddedSize); + allocatorChecker.OnDeallocate(mem, m_paddedSize); #endif page_aligned_free(mem, m_paddedSize); m_paddedSize = 0; @@ -106,124 +101,13 @@ shared_ptr io_Allocate(size_t size, off_t ofs) throw std::bad_alloc(); #ifndef NDEBUG - allocatorChecker.notify_alloc(mem, paddedSize); + allocatorChecker.OnAllocate(mem, paddedSize); #endif return shared_ptr(mem, IoDeleter(paddedSize)); } -//----------------------------------------------------------------------------- -// File -//----------------------------------------------------------------------------- - -File::File() -{ -} - - -File::~File() -{ - Close(); -} - - -LibError File::Open(const Path& pathname, char mode) -{ - debug_assert(mode == 'w' || mode == 'r'); - - m_pathname = pathname; - m_mode = mode; - - int oflag = (mode == 'r')? O_RDONLY : O_WRONLY|O_CREAT|O_TRUNC; -#if OS_WIN - oflag |= O_BINARY_NP; -#endif - m_fd = open(m_pathname.external_file_string().c_str(), oflag, S_IRWXO|S_IRWXU|S_IRWXG); - if(m_fd < 0) - WARN_RETURN(ERR::FILE_ACCESS); - - stats_open(); - return INFO::OK; -} - - -void File::Close() -{ - m_mode = '\0'; - - close(m_fd); - m_fd = 0; -} - - -LibError File::Issue(aiocb& req, off_t alignedOfs, u8* alignedBuf, size_t alignedSize) const -{ - memset(&req, 0, sizeof(req)); - req.aio_lio_opcode = (m_mode == 'w')? LIO_WRITE : LIO_READ; - req.aio_buf = (volatile void*)alignedBuf; - req.aio_fildes = m_fd; - req.aio_offset = alignedOfs; - req.aio_nbytes = alignedSize; - struct sigevent* sig = 0; // no notification signal - aiocb* const reqs = &req; - if(lio_listio(LIO_NOWAIT, &reqs, 1, sig) != 0) - return LibError_from_errno(); - return INFO::OK; -} - - -/*static*/ LibError File::WaitUntilComplete(aiocb& req, u8*& alignedBuf, size_t& alignedSize) -{ - // wait for transfer to complete. - while(aio_error(&req) == EINPROGRESS) - { - aiocb* const reqs = &req; - aio_suspend(&reqs, 1, (timespec*)0); // wait indefinitely - } - - const ssize_t bytesTransferred = aio_return(&req); - if(bytesTransferred == -1) // transfer failed - WARN_RETURN(ERR::IO); - - alignedBuf = (u8*)req.aio_buf; // cast from volatile void* - alignedSize = bytesTransferred; - return INFO::OK; -} - - -LibError File::IO(off_t ofs, u8* buf, size_t size) const -{ - ScopedIoMonitor monitor; - - lseek(m_fd, ofs, SEEK_SET); - - errno = 0; - const ssize_t ret = (m_mode == 'w')? write(m_fd, buf, size) : read(m_fd, buf, size); - if(ret < 0) - return LibError_from_errno(); - - const size_t totalTransferred = (size_t)ret; - if(totalTransferred != size) - WARN_RETURN(ERR::IO); - - monitor.NotifyOfSuccess(FI_LOWIO, m_mode, totalTransferred); - return INFO::OK; -} - - -LibError File::Write(off_t ofs, const u8* buf, size_t size) const -{ - return IO(ofs, const_cast(buf), size); -} - - -LibError File::Read(off_t ofs, u8* buf, size_t size) const -{ - return IO(ofs, buf, size); -} - - //----------------------------------------------------------------------------- // BlockIo //----------------------------------------------------------------------------- @@ -438,93 +322,3 @@ LibError io_ReadAligned(const File& file, off_t alignedOfs, u8* alignedBuf, size IoSplitter splitter(alignedOfs, alignedBuf, (off_t)size); return splitter.Run(file); } - - -//----------------------------------------------------------------------------- -// UnalignedWriter -//----------------------------------------------------------------------------- - -class UnalignedWriter::Impl : boost::noncopyable -{ -public: - Impl(const File& file, off_t ofs) - : m_file(file), m_alignedBuf(io_Allocate(BLOCK_SIZE)) - { - const size_t misalignment = (size_t)ofs % BLOCK_SIZE; - m_alignedOfs = ofs - (off_t)misalignment; - if(misalignment) - io_ReadAligned(m_file, m_alignedOfs, m_alignedBuf.get(), BLOCK_SIZE); - m_bytesUsed = misalignment; - } - - ~Impl() - { - Flush(); - } - - LibError Append(const u8* data, size_t size) const - { - while(size != 0) - { - // optimization: write directly from the input buffer, if possible - const size_t alignedSize = (size / BLOCK_SIZE) * BLOCK_SIZE; - if(m_bytesUsed == 0 && IsAligned(data, BLOCK_SIZE) && alignedSize != 0) - { - RETURN_ERR(io_WriteAligned(m_file, m_alignedOfs, data, alignedSize)); - m_alignedOfs += (off_t)alignedSize; - data += alignedSize; - size -= alignedSize; - } - - const size_t chunkSize = std::min(size, BLOCK_SIZE-m_bytesUsed); - cpu_memcpy(m_alignedBuf.get()+m_bytesUsed, data, chunkSize); - m_bytesUsed += chunkSize; - data += chunkSize; - size -= chunkSize; - - if(m_bytesUsed == BLOCK_SIZE) - RETURN_ERR(WriteBlock()); - } - - return INFO::OK; - } - - void Flush() const - { - if(m_bytesUsed) - { - memset(m_alignedBuf.get()+m_bytesUsed, 0, BLOCK_SIZE-m_bytesUsed); - (void)WriteBlock(); - } - } - -private: - LibError WriteBlock() const - { - RETURN_ERR(io_WriteAligned(m_file, m_alignedOfs, m_alignedBuf.get(), BLOCK_SIZE)); - m_alignedOfs += BLOCK_SIZE; - m_bytesUsed = 0; - return INFO::OK; - } - - const File& m_file; - mutable off_t m_alignedOfs; - shared_ptr m_alignedBuf; - mutable size_t m_bytesUsed; -}; - - -UnalignedWriter::UnalignedWriter(const File& file, off_t ofs) -: impl(new Impl(file, ofs)) -{ -} - -LibError UnalignedWriter::Append(const u8* data, size_t size) const -{ - return impl->Append(data, size); -} - -void UnalignedWriter::Flush() const -{ - impl->Flush(); -} diff --git a/source/lib/file/io/io.h b/source/lib/file/io/io.h index c050480d3b..f18f264708 100644 --- a/source/lib/file/io/io.h +++ b/source/lib/file/io/io.h @@ -11,61 +11,14 @@ #ifndef INCLUDED_IO #define INCLUDED_IO -#include "lib/file/path.h" - -namespace ERR -{ - const LibError FILE_ACCESS = -110200; - const LibError IO = -110201; - const LibError IO_EOF = -110202; -} - - -/** - * simple POSIX file wrapper. - **/ -class LIB_API File -{ -public: - File(); - ~File(); - - LibError Open(const Path& pathname, char mode); - void Close(); - - const Path& Pathname() const - { - return m_pathname; - } - - char Mode() const - { - return m_mode; - } - - LibError Issue(aiocb& req, off_t alignedOfs, u8* alignedBuf, size_t alignedSize) const; - static LibError WaitUntilComplete(aiocb& req, u8*& alignedBuf, size_t& alignedSize); - - LibError Read(off_t ofs, u8* buf, size_t size) const; - LibError Write(off_t ofs, const u8* buf, size_t size) const; - -private: - LibError IO(off_t ofs, u8* buf, size_t size) const; - - Path m_pathname; - char m_mode; - int m_fd; -}; - -typedef shared_ptr PFile; - - // memory will be allocated from the heap, not the (limited) file cache. // this makes sense for write buffers that are never used again, // because we avoid having to displace some other cached items. shared_ptr io_Allocate(size_t size, off_t ofs = 0); +class File; + /** * called after a block IO has completed. * @@ -83,25 +36,4 @@ LIB_API LibError io_Read(const File& file, off_t ofs, u8* alignedBuf, size_t siz LIB_API LibError io_WriteAligned(const File& file, off_t alignedOfs, const u8* alignedData, size_t size); LIB_API LibError io_ReadAligned(const File& file, off_t alignedOfs, u8* alignedBuf, size_t size); -class LIB_API UnalignedWriter -{ -public: - UnalignedWriter(const File& file, off_t ofs); - - /** - * add data to the align buffer, writing it out to disk if full. - **/ - LibError Append(const u8* data, size_t size) const; - - /** - * zero-initialize any remaining space in the align buffer and write - * it to the file. this is called by the destructor. - **/ - void Flush() const; - -private: - class Impl; - shared_ptr impl; -}; - #endif // #ifndef INCLUDED_IO diff --git a/source/lib/file/io/write_buffer.cpp b/source/lib/file/io/write_buffer.cpp new file mode 100644 index 0000000000..9ec2a271de --- /dev/null +++ b/source/lib/file/io/write_buffer.cpp @@ -0,0 +1,101 @@ +#include "precompiled.h" +#include "write_buffer.h" + +#include "lib/bits.h" // IsAligned +#include "lib/sysdep/cpu.h" +#include "io.h" +#include "io_internal.h" + + +WriteBuffer::WriteBuffer() + : m_capacity(4096), m_data(io_Allocate(m_capacity)), m_size(0) +{ +} + + +void WriteBuffer::Append(const void* data, size_t size) +{ + while(m_size + size > m_capacity) + m_capacity *= 2; + shared_ptr newData = io_Allocate(m_capacity); + cpu_memcpy(newData.get(), m_data.get(), m_size); + m_data = newData; + + cpu_memcpy(m_data.get() + m_size, data, size); + m_size += size; +} + + +void WriteBuffer::Overwrite(const void* data, size_t size, size_t offset) +{ + debug_assert(offset+size < m_size); + cpu_memcpy(m_data.get()+offset, data, size); +} + + +//----------------------------------------------------------------------------- +// UnalignedWriter +//----------------------------------------------------------------------------- + +UnalignedWriter::UnalignedWriter(const File& file, off_t ofs) + : m_file(file), m_alignedBuf(io_Allocate(BLOCK_SIZE)) +{ + const size_t misalignment = (size_t)ofs % BLOCK_SIZE; + m_alignedOfs = ofs - (off_t)misalignment; + if(misalignment) + io_ReadAligned(m_file, m_alignedOfs, m_alignedBuf.get(), BLOCK_SIZE); + m_bytesUsed = misalignment; +} + + +UnalignedWriter::~UnalignedWriter() +{ + Flush(); +} + + +LibError UnalignedWriter::Append(const u8* data, size_t size) const +{ + while(size != 0) + { + // optimization: write directly from the input buffer, if possible + const size_t alignedSize = (size / BLOCK_SIZE) * BLOCK_SIZE; + if(m_bytesUsed == 0 && IsAligned(data, BLOCK_SIZE) && alignedSize != 0) + { + RETURN_ERR(io_WriteAligned(m_file, m_alignedOfs, data, alignedSize)); + m_alignedOfs += (off_t)alignedSize; + data += alignedSize; + size -= alignedSize; + } + + const size_t chunkSize = std::min(size, BLOCK_SIZE-m_bytesUsed); + cpu_memcpy(m_alignedBuf.get()+m_bytesUsed, data, chunkSize); + m_bytesUsed += chunkSize; + data += chunkSize; + size -= chunkSize; + + if(m_bytesUsed == BLOCK_SIZE) + RETURN_ERR(WriteBlock()); + } + + return INFO::OK; +} + + +void UnalignedWriter::Flush() const +{ + if(m_bytesUsed) + { + memset(m_alignedBuf.get()+m_bytesUsed, 0, BLOCK_SIZE-m_bytesUsed); + (void)WriteBlock(); + } +} + + +LibError UnalignedWriter::WriteBlock() const +{ + RETURN_ERR(io_WriteAligned(m_file, m_alignedOfs, m_alignedBuf.get(), BLOCK_SIZE)); + m_alignedOfs += BLOCK_SIZE; + m_bytesUsed = 0; + return INFO::OK; +} diff --git a/source/lib/file/io/write_buffer.h b/source/lib/file/io/write_buffer.h index dbce7e5ad7..ea7c6c7faa 100644 --- a/source/lib/file/io/write_buffer.h +++ b/source/lib/file/io/write_buffer.h @@ -1,31 +1,15 @@ #ifndef INCLUDED_WRITE_BUFFER #define INCLUDED_WRITE_BUFFER +class File; + class LIB_API WriteBuffer { public: - WriteBuffer() - : m_capacity(4096), m_data(io_Allocate(m_capacity)), m_size(0) - { - } + WriteBuffer(); - void Append(const void* data, size_t size) - { - while(m_size + size > m_capacity) - m_capacity *= 2; - shared_ptr newData = io_Allocate(m_capacity); - cpu_memcpy(newData.get(), m_data.get(), m_size); - m_data = newData; - - cpu_memcpy(m_data.get() + m_size, data, size); - m_size += size; - } - - void Overwrite(const void* data, size_t size, size_t offset) - { - debug_assert(offset+size < m_size); - cpu_memcpy(m_data.get()+offset, data, size); - } + void Append(const void* data, size_t size); + void Overwrite(const void* data, size_t size, size_t offset); shared_ptr Data() const { @@ -44,4 +28,31 @@ private: size_t m_size; }; + +class LIB_API UnalignedWriter : public boost::noncopyable +{ +public: + UnalignedWriter(const File& file, off_t ofs); + ~UnalignedWriter(); + + /** + * add data to the align buffer, writing it out to disk if full. + **/ + LibError Append(const u8* data, size_t size) const; + + /** + * zero-initialize any remaining space in the align buffer and write + * it to the file. this is called by the destructor. + **/ + void Flush() const; + +private: + LibError WriteBlock() const; + + const File& m_file; + shared_ptr m_alignedBuf; + mutable off_t m_alignedOfs; + mutable size_t m_bytesUsed; +}; + #endif // #ifndef INCLUDED_WRITE_BUFFER diff --git a/source/lib/file/vfs/file_cache.cpp b/source/lib/file/vfs/file_cache.cpp index 167a0f66ea..b80551156c 100644 --- a/source/lib/file/vfs/file_cache.cpp +++ b/source/lib/file/vfs/file_cache.cpp @@ -84,7 +84,7 @@ public: u8* mem = (u8*)m_allocator.Allocate(alignedSize); #ifndef NDEBUG - m_checker.notify_alloc(mem, alignedSize); + m_checker.OnAllocate(mem, alignedSize); #endif return shared_ptr(mem, FileCacheDeleter(size, pthis)); @@ -100,7 +100,7 @@ public: (void)mprotect(mem, size, PROT_READ|PROT_WRITE); #ifndef NDEBUG - m_checker.notify_free(mem, alignedSize); + m_checker.OnDeallocate(mem, alignedSize); #endif m_allocator.Deallocate(mem, alignedSize); diff --git a/source/lib/file/vfs/vfs.cpp b/source/lib/file/vfs/vfs.cpp index b7878d4b2f..2b5efec996 100644 --- a/source/lib/file/vfs/vfs.cpp +++ b/source/lib/file/vfs/vfs.cpp @@ -32,34 +32,34 @@ public: virtual LibError Mount(const VfsPath& mountPoint, const char* path, uint flags /* = 0 */, uint priority /* = 0 */) { - debug_assert(IsDirectory(mountPoint)); + debug_assert(vfs_path_IsDirectory(mountPoint)); debug_assert(strcmp(path, ".") != 0); // "./" isn't supported on Windows. // note: mounting subdirectoryNames is now allowed. - VfsDirectory* vfsDirectory; - CHECK_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, vfsDirectory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE)); + VfsDirectory* directory; + CHECK_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE)); PRealDirectory realDirectory(new RealDirectory(std::string(path), priority, flags)); - vfsDirectory->Attach(realDirectory); + directory->Attach(realDirectory); return INFO::OK; } virtual LibError GetFileInfo(const VfsPath& pathname, FileInfo* pfileInfo) const { - VfsDirectory* vfsDirectory; VfsFile* vfsFile; - LibError ret = vfs_Lookup(pathname, &m_rootDirectory, vfsDirectory, &vfsFile); + VfsDirectory* directory; VfsFile* file; + LibError ret = vfs_Lookup(pathname, &m_rootDirectory, directory, &file); if(!pfileInfo) // just indicate if the file exists without raising warnings. return ret; CHECK_ERR(ret); - vfsFile->GetFileInfo(pfileInfo); + *pfileInfo = FileInfo(file->Name(), file->Size(), file->MTime()); return INFO::OK; } virtual LibError GetDirectoryEntries(const VfsPath& path, FileInfos* files, DirectoryNames* subdirectoryNames) const { - debug_assert(IsDirectory(path)); - VfsDirectory* vfsDirectory; - CHECK_ERR(vfs_Lookup(path, &m_rootDirectory, vfsDirectory, 0)); - vfsDirectory->GetEntries(files, subdirectoryNames); + debug_assert(vfs_path_IsDirectory(path)); + VfsDirectory* directory; + CHECK_ERR(vfs_Lookup(path, &m_rootDirectory, directory, 0)); + directory->GetEntries(files, subdirectoryNames); return INFO::OK; } @@ -67,15 +67,15 @@ public: // coherency (need only invalidate when closing a FILE_WRITE file). virtual LibError CreateFile(const VfsPath& pathname, shared_ptr fileContents, size_t size) { - VfsDirectory* vfsDirectory; - CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, vfsDirectory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE)); + VfsDirectory* directory; + CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE)); - PRealDirectory realDirectory = vfsDirectory->AssociatedDirectory(); + PRealDirectory realDirectory = directory->AssociatedDirectory(); const std::string& name = pathname.leaf(); RETURN_ERR(realDirectory->Store(name, fileContents, size)); - const VfsFile file(FileInfo(name, (off_t)size, time(0)), realDirectory->Priority(), realDirectory); - vfsDirectory->AddFile(file); + const VfsFile file(name, (off_t)size, time(0), realDirectory->Priority(), realDirectory); + directory->AddFile(file); // wipe out any cached blocks. this is necessary to cover the (rare) case // of file cache contents predating the file write. @@ -99,19 +99,19 @@ public: const bool isCacheHit = m_fileCache.Retrieve(pathname, fileContents, size); if(!isCacheHit) { - VfsDirectory* vfsDirectory; VfsFile* vfsFile; - CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, vfsDirectory, &vfsFile)); + VfsDirectory* directory; VfsFile* file; + CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file)); - size = vfsFile->Size(); + size = file->Size(); if(size > ChooseCacheSize()) { fileContents = io_Allocate(size); - RETURN_ERR(vfsFile->Load(fileContents)); + RETURN_ERR(file->Load(fileContents)); } else { fileContents = m_fileCache.Reserve(size); - RETURN_ERR(vfsFile->Load(fileContents)); + RETURN_ERR(file->Load(fileContents)); m_fileCache.Add(pathname, fileContents, size); } } @@ -141,9 +141,9 @@ public: virtual LibError GetRealPath(const VfsPath& pathname, char* realPathname) { - VfsDirectory* vfsDirectory; - CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, vfsDirectory, 0)); - PRealDirectory realDirectory = vfsDirectory->AssociatedDirectory(); + VfsDirectory* directory; + CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0)); + PRealDirectory realDirectory = directory->AssociatedDirectory(); const std::string& name = pathname.leaf(); path_append(realPathname, realDirectory->GetPath().string().c_str(), name.c_str()); return INFO::OK; diff --git a/source/lib/file/vfs/vfs_lookup.cpp b/source/lib/file/vfs/vfs_lookup.cpp index 8307023d13..2ba964dd6d 100644 --- a/source/lib/file/vfs/vfs_lookup.cpp +++ b/source/lib/file/vfs/vfs_lookup.cpp @@ -32,8 +32,8 @@ static size_t s_numArchivedFiles; class PopulateHelper : boost::noncopyable { public: - PopulateHelper(VfsDirectory* vfsDirectory, PRealDirectory realDirectory) - : m_vfsDirectory(vfsDirectory), m_realDirectory(realDirectory) + PopulateHelper(VfsDirectory* directory, PRealDirectory realDirectory) + : m_directory(directory), m_realDirectory(realDirectory) { } @@ -50,8 +50,8 @@ public: private: void AddFile(const FileInfo& fileInfo) const { - const VfsFile file(fileInfo, m_realDirectory->Priority(), m_realDirectory); - const VfsFile* pfile = m_vfsDirectory->AddFile(file); + const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory); + const VfsFile* pfile = m_directory->AddFile(file); // notify archive builder that this file could be archived but // currently isn't; if there are too many of these, archive will @@ -69,10 +69,10 @@ private: // (we have to create missing subdirectoryNames because archivers // don't always place directory entries before their files) const unsigned flags = VFS_LOOKUP_ADD; - VfsDirectory* vfsDirectory; - WARN_ERR(vfs_Lookup(pathname, this_->m_vfsDirectory, vfsDirectory, 0, flags)); - const VfsFile vfsFile(fileInfo, this_->m_realDirectory->Priority(), archiveFile); - vfsDirectory->AddFile(vfsFile); + VfsDirectory* directory; + WARN_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags)); + const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile); + directory->AddFile(file); s_numArchivedFiles++; } @@ -106,27 +106,27 @@ private: if(strcasecmp(subdirectoryNames[i].c_str(), ".svn") == 0) continue; - VfsDirectory* subdirectory = m_vfsDirectory->AddSubdirectory(subdirectoryNames[i]); + VfsDirectory* subdirectory = m_directory->AddSubdirectory(subdirectoryNames[i]); subdirectory->Attach(CreateRealSubdirectory(m_realDirectory, subdirectoryNames[i])); } } - VfsDirectory* const m_vfsDirectory; + VfsDirectory* const m_directory; PRealDirectory m_realDirectory; }; -static LibError Populate(VfsDirectory* vfsDirectory) +static LibError Populate(VfsDirectory* directory) { - if(!vfsDirectory->ShouldPopulate()) + if(!directory->ShouldPopulate()) return INFO::OK; - PRealDirectory realDirectory = vfsDirectory->AssociatedDirectory(); + PRealDirectory realDirectory = directory->AssociatedDirectory(); if(realDirectory->Flags() & VFS_MOUNT_WATCH) realDirectory->Watch(); - PopulateHelper helper(vfsDirectory, realDirectory); + PopulateHelper helper(directory, realDirectory); RETURN_ERR(helper.AddEntries()); return INFO::OK; @@ -135,14 +135,14 @@ static LibError Populate(VfsDirectory* vfsDirectory) //----------------------------------------------------------------------------- -LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* vfsStartDirectory, VfsDirectory*& vfsDirectory, VfsFile** pvfsFile, unsigned flags) +LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDirectory*& directory, VfsFile** pfile, unsigned flags) { TIMER_ACCRUE(tc_lookup); - vfsDirectory = vfsStartDirectory; - if(pvfsFile) - *pvfsFile = 0; - RETURN_ERR(Populate(vfsDirectory)); + directory = startDirectory; + if(pfile) + *pfile = 0; + RETURN_ERR(Populate(directory)); if(pathname.empty()) // early out for root directory return INFO::OK; // (prevents iterator error below) @@ -155,13 +155,13 @@ TIMER_ACCRUE(tc_lookup); { const std::string& subdirectoryName = *it; - VfsDirectory* vfsSubdirectory = vfsDirectory->GetSubdirectory(subdirectoryName); - if(!vfsSubdirectory) + VfsDirectory* subdirectory = directory->GetSubdirectory(subdirectoryName); + if(!subdirectory) { if(!(flags & VFS_LOOKUP_ADD)) return ERR::VFS_DIR_NOT_FOUND; // NOWARN - vfsSubdirectory = vfsDirectory->AddSubdirectory(subdirectoryName); + subdirectory = directory->AddSubdirectory(subdirectoryName); if(flags & VFS_LOOKUP_CREATE) { @@ -171,21 +171,21 @@ TIMER_ACCRUE(tc_lookup); (void)mkdir(currentPath.external_directory_string().c_str(), S_IRWXO|S_IRWXU|S_IRWXG); PRealDirectory realDirectory(new RealDirectory(currentPath.string(), 0, 0)); - vfsSubdirectory->Attach(realDirectory); + subdirectory->Attach(realDirectory); #endif } } - vfsDirectory = vfsSubdirectory; - RETURN_ERR(Populate(vfsDirectory)); + directory = subdirectory; + RETURN_ERR(Populate(directory)); } - if(pvfsFile) + if(pfile) { const std::string& filename = *it; debug_assert(filename != "."); // asked for file but specified directory path - *pvfsFile = vfsDirectory->GetFile(filename); - if(!*pvfsFile) + *pfile = directory->GetFile(filename); + if(!*pfile) return ERR::VFS_FILE_NOT_FOUND; // NOWARN } diff --git a/source/lib/file/vfs/vfs_lookup.h b/source/lib/file/vfs/vfs_lookup.h index 6fd9209f9a..1527a37cc3 100644 --- a/source/lib/file/vfs/vfs_lookup.h +++ b/source/lib/file/vfs/vfs_lookup.h @@ -42,6 +42,6 @@ enum VfsLookupFlags * * to allow noiseless file-existence queries, this does not raise warnings. **/ -extern LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* vfsStartDirectory, VfsDirectory*& vfsDirectory, VfsFile** vfsFile, unsigned flags = 0); +extern LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDirectory*& directory, VfsFile** pfile, unsigned flags = 0); #endif // #ifndef INCLUDED_VFS_LOOKUP diff --git a/source/lib/file/vfs/vfs_path.cpp b/source/lib/file/vfs/vfs_path.cpp index f2ecdb0d7f..ec472943a3 100644 --- a/source/lib/file/vfs/vfs_path.cpp +++ b/source/lib/file/vfs/vfs_path.cpp @@ -1,21 +1,7 @@ #include "precompiled.h" #include "vfs_path.h" -bool IsDirectory(const VfsPath& pathname) +bool vfs_path_IsDirectory(const VfsPath& pathname) { return pathname.empty() || pathname.leaf() == "."; } - -std::string Basename(const VfsPath& pathname) -{ - const std::string name(pathname.leaf()); - const size_t pos = name.rfind('.'); - return name.substr(0, pos); -} - -std::string Extension(const VfsPath& pathname) -{ - const std::string name(pathname.leaf()); - const size_t pos = name.rfind('.'); - return (pos == std::string::npos)? std::string() : name.substr(pos); -} diff --git a/source/lib/file/vfs/vfs_path.h b/source/lib/file/vfs/vfs_path.h index 520609fd36..c08aba2685 100644 --- a/source/lib/file/vfs/vfs_path.h +++ b/source/lib/file/vfs/vfs_path.h @@ -22,8 +22,6 @@ struct VfsPathTraits } }; -extern bool IsDirectory(const VfsPath& pathname); -extern std::string Basename(const VfsPath& pathname); -extern std::string Extension(const VfsPath& pathname); +extern bool vfs_path_IsDirectory(const VfsPath& pathname); #endif // #ifndef INCLUDED_VFS_PATH diff --git a/source/lib/file/vfs/vfs_tree.cpp b/source/lib/file/vfs/vfs_tree.cpp index c681c27c8b..8082198f9b 100644 --- a/source/lib/file/vfs/vfs_tree.cpp +++ b/source/lib/file/vfs/vfs_tree.cpp @@ -17,25 +17,25 @@ //----------------------------------------------------------------------------- -VfsFile::VfsFile(const FileInfo& fileInfo, unsigned priority, PIFileLoader loader) - : m_fileInfo(fileInfo), m_priority(priority), m_loader(loader) +VfsFile::VfsFile(const std::string& name, off_t size, time_t mtime, unsigned priority, PIFileLoader loader) + : m_name(name), m_size(size), m_mtime(mtime), m_priority(priority), m_loader(loader) { } -bool VfsFile::IsSupersededBy(const VfsFile& vfsFile) const +bool VfsFile::IsSupersededBy(const VfsFile& file) const { // 1) priority is lower => no. - if(vfsFile.m_priority < m_priority) + if(file.m_priority < m_priority) return false; // 2) timestamp is older => no. // (note: we need to account for FAT's 2 sec. resolution) - if(difftime(vfsFile.m_fileInfo.MTime(), m_fileInfo.MTime()) < -2.0) + if(difftime(file.MTime(), MTime()) < -2.0) return false; // 3) provider is less efficient => no. - if(vfsFile.m_loader->Precedence() < m_loader->Precedence()) + if(file.m_loader->Precedence() < m_loader->Precedence()) return false; return true; @@ -45,19 +45,19 @@ bool VfsFile::IsSupersededBy(const VfsFile& vfsFile) const void VfsFile::GenerateDescription(char* text, size_t maxChars) const { char timestamp[25]; - const time_t mtime = m_fileInfo.MTime(); + const time_t mtime = MTime(); strftime(timestamp, ARRAY_SIZE(timestamp), "%a %b %d %H:%M:%S %Y", localtime(&mtime)); // build format string (set width of name field so that everything // lines up correctly) const char* fmt = "(%c; %6d; %s)\n"; - sprintf_s(text, maxChars, fmt, m_loader->LocationCode(), m_fileInfo.Size(), timestamp); + sprintf_s(text, maxChars, fmt, m_loader->LocationCode(), Size(), timestamp); } LibError VfsFile::Load(shared_ptr buf) const { - return m_loader->Load(m_fileInfo.Name(), buf, m_fileInfo.Size()); + return m_loader->Load(Name(), buf, Size()); } @@ -69,19 +69,19 @@ VfsDirectory::VfsDirectory() } -VfsFile* VfsDirectory::AddFile(const VfsFile& vfsFile) +VfsFile* VfsDirectory::AddFile(const VfsFile& file) { - std::pair value = std::make_pair(vfsFile.Name(), vfsFile); - std::pair ret = m_vfsFiles.insert(value); + std::pair value = std::make_pair(file.Name(), file); + std::pair ret = m_files.insert(value); if(!ret.second) // already existed { - VfsFile& vfsPreviousFile = ret.first->second; - const VfsFile& vfsNewFile = value.second; - if(vfsPreviousFile.IsSupersededBy(vfsNewFile)) - vfsPreviousFile = vfsNewFile; + VfsFile& previousFile = ret.first->second; + const VfsFile& newFile = value.second; + if(previousFile.IsSupersededBy(newFile)) + previousFile = newFile; } else - stats_vfs_file_add(vfsFile.Size()); + stats_vfs_file_add(file.Size()); return &(*ret.first).second; } @@ -92,15 +92,15 @@ VfsFile* VfsDirectory::AddFile(const VfsFile& vfsFile) VfsDirectory* VfsDirectory::AddSubdirectory(const std::string& name) { std::pair value = std::make_pair(name, VfsDirectory()); - std::pair ret = m_vfsSubdirectories.insert(value); + std::pair ret = m_subdirectories.insert(value); return &(*ret.first).second; } VfsFile* VfsDirectory::GetFile(const std::string& name) { - VfsFiles::iterator it = m_vfsFiles.find(name); - if(it == m_vfsFiles.end()) + VfsFiles::iterator it = m_files.find(name); + if(it == m_files.end()) return 0; return &it->second; } @@ -108,8 +108,8 @@ VfsFile* VfsDirectory::GetFile(const std::string& name) VfsDirectory* VfsDirectory::GetSubdirectory(const std::string& name) { - VfsSubdirectories::iterator it = m_vfsSubdirectories.find(name); - if(it == m_vfsSubdirectories.end()) + VfsSubdirectories::iterator it = m_subdirectories.find(name); + if(it == m_subdirectories.end()) return 0; return &it->second; } @@ -119,19 +119,17 @@ void VfsDirectory::GetEntries(FileInfos* files, DirectoryNames* subdirectoryName { if(files) { - // (note: VfsFile doesn't return a pointer to FileInfo; instead, - // we have it write directly into the files container) - files->resize(m_vfsFiles.size()); - size_t i = 0; - for(VfsFiles::const_iterator it = m_vfsFiles.begin(); it != m_vfsFiles.end(); ++it) - it->second.GetFileInfo(&(*files)[i++]); + files->clear(); + files->reserve(m_files.size()); + for(VfsFiles::const_iterator it = m_files.begin(); it != m_files.end(); ++it) + files->push_back(FileInfo(it->second.Name(), it->second.Size(), it->second.MTime())); } if(subdirectoryNames) { subdirectoryNames->clear(); - subdirectoryNames->reserve(m_vfsSubdirectories.size()); - for(VfsSubdirectories::const_iterator it = m_vfsSubdirectories.begin(); it != m_vfsSubdirectories.end(); ++it) + subdirectoryNames->reserve(m_subdirectories.size()); + for(VfsSubdirectories::const_iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it) subdirectoryNames->push_back(it->first); } } @@ -145,40 +143,40 @@ void VfsDirectory::DisplayR(unsigned depth) const char fmt[20]; sprintf_s(fmt, ARRAY_SIZE(fmt), "%%-%d.%ds %%s", maxNameChars, maxNameChars); - for(VfsFiles::const_iterator it = m_vfsFiles.begin(); it != m_vfsFiles.end(); ++it) + for(VfsFiles::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { const std::string& name = it->first; - const VfsFile& vfsFile = it->second; + const VfsFile& file = it->second; char description[100]; - vfsFile.GenerateDescription(description, ARRAY_SIZE(description)); + file.GenerateDescription(description, ARRAY_SIZE(description)); for(unsigned i = 0; i < depth+1; i++) printf(indent); printf(fmt, name.c_str(), description); } - for(VfsSubdirectories::const_iterator it = m_vfsSubdirectories.begin(); it != m_vfsSubdirectories.end(); ++it) + for(VfsSubdirectories::const_iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it) { const std::string& name = it->first; - const VfsDirectory& vfsDirectory = it->second; + const VfsDirectory& directory = it->second; for(unsigned i = 0; i < depth+1; i++) printf(indent); printf("[%s/]\n", name.c_str()); - vfsDirectory.DisplayR(depth+1); + directory.DisplayR(depth+1); } } void VfsDirectory::ClearR() { - for(VfsSubdirectories::iterator it = m_vfsSubdirectories.begin(); it != m_vfsSubdirectories.end(); ++it) + for(VfsSubdirectories::iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it) it->second.ClearR(); - m_vfsFiles.clear(); - m_vfsSubdirectories.clear(); + m_files.clear(); + m_subdirectories.clear(); m_realDirectory.reset(); m_shouldPopulate = 0; } diff --git a/source/lib/file/vfs/vfs_tree.h b/source/lib/file/vfs/vfs_tree.h index cd15b55323..adad8c9ce2 100644 --- a/source/lib/file/vfs/vfs_tree.h +++ b/source/lib/file/vfs/vfs_tree.h @@ -18,31 +18,33 @@ class VfsFile { public: - VfsFile(const FileInfo& fileInfo, unsigned priority, PIFileLoader provider); + VfsFile(const std::string& name, off_t size, time_t mtime, unsigned priority, PIFileLoader provider); const std::string& Name() const { - return m_fileInfo.Name(); + return m_name; } off_t Size() const { - return m_fileInfo.Size(); + return m_size; } - void GetFileInfo(FileInfo* pfileInfo) const + time_t MTime() const { - *pfileInfo = m_fileInfo; + return m_mtime; } - bool IsSupersededBy(const VfsFile& vfsFile) const; + bool IsSupersededBy(const VfsFile& file) const; void GenerateDescription(char* text, size_t maxChars) const; LibError Load(shared_ptr buf) const; private: - mutable FileInfo m_fileInfo; + std::string m_name; + off_t m_size; + time_t m_mtime; unsigned m_priority; @@ -59,7 +61,7 @@ public: * @return address of existing or newly inserted file; remains * valid until ClearR is called (i.e. VFS is rebuilt). **/ - VfsFile* AddFile(const VfsFile& vfsFile); + VfsFile* AddFile(const VfsFile& file); /** * @return address of existing or newly inserted subdirectory; remains @@ -92,10 +94,10 @@ public: private: typedef std::map VfsFiles; - VfsFiles m_vfsFiles; + VfsFiles m_files; typedef std::map VfsSubdirectories; - VfsSubdirectories m_vfsSubdirectories; + VfsSubdirectories m_subdirectories; PRealDirectory m_realDirectory; volatile uintptr_t m_shouldPopulate; // (cpu_CAS can't be used on bool) diff --git a/source/lib/lf_alloc.cpp b/source/lib/lf_alloc.cpp deleted file mode 100644 index 227ea6b017..0000000000 --- a/source/lib/lf_alloc.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/** - * ========================================================================= - * File : lf_alloc.cpp - * Project : 0 A.D. - * Description : lock-free memory allocation. - * ========================================================================= - */ - -// license: GPL; see lib/license.txt - -#include "precompiled.h" - -#if 0 - -#include -#include - -#include "posix.h" -#include "lib/sysdep/cpu.h" -#include "lockfree.h" -#include "timer.h" - - -// superblock descriptor structure -// one machine word -struct Anchor -{ - uint avail : 10; - uint count : 10; - uint tag : 10; - uint state : 2; - - // convert to uintptr_t for cpu_CAS - operator uintptr_t() const - { - return *(uintptr_t*)this; - } -}; - -cassert(sizeof(Anchor) == sizeof(uintptr_t)); - -enum State -{ - ACTIVE = 0, - FULL = 1, - PARTIAL = 2, - EMPTY = 3 -}; - - -/*typedef void* DescList; - -struct SizeClass -{ - DescList partial; // initially empty - size_t sz; // block size - size_t sb_size; // superblock's size -}; - -struct Descriptor; - -static const uint PTR_BITS = sizeof(void*) * CHAR_BIT; - -struct Active -{ - uint pdesc : PTR_BITS-6; - uint credits : 6; - - Active() - { - } - - // convert to uintptr_t for cpu_CAS - operator uintptr_t() const - { - return *(uintptr_t*)this; - } - - - // - // allow Active to be used as Descriptor* - // - - Active& operator=(Descriptor* desc) - { - *(Descriptor**)this = desc; - debug_assert(credits == 0); // make sure ptr is aligned - return *this; - } - - Active(Descriptor* desc) - { - *this = desc; - } - - // disambiguate (could otherwise be either uintptr_t or Descriptor*) - bool operator!() const - { - return (uintptr_t)*this != 0; - } - - operator Descriptor*() const - { - return *(Descriptor**)this; - } - -}; - -static const uint MAX_CREDITS = 64; // = 2 ** num_credit_bits - -struct ProcHeap -{ - Active active; // initially 0; points to Descriptor - Descriptor* partial; // initially 0 - SizeClass* sc; // parent -}; - -// POD; must be MAX_CREDITS-aligned! -struct Descriptor -{ - Anchor anchor; - Descriptor* next; - u8* sb; // superblock - ProcHeap* heap; // -> owner procheap - size_t sz; // block size - uint maxcount; // superblock size/sz -}; - - - - - - -static u8* AllocNewSB(size_t sb_size) -{ - return 0; -} - -static void FreeSB(u8* sb) -{ -} - - -static Descriptor* DescAvail = 0; - -static const size_t DESCSBSIZE = 128; - -static Descriptor* DescAlloc() -{ - Descriptor* desc; - for(;;) - { - desc = DescAvail; - if(desc) - { - Descriptor* next = desc->next; - if(cpu_CAS(&DescAvail, desc, next)) - break; - } - else - { - desc = (Descriptor*)AllocNewSB(DESCSBSIZE); - // organize descriptors in a linked list - cpu_MemoryFence(); - if(cpu_CAS(&DescAvail, 0, desc->next)) - break; - FreeSB((u8*)desc); - } - } - return desc; -} - -static void DescRetire(Descriptor* desc) -{ - Descriptor* old_head; - do - { - old_head = DescAvail; - desc->next = old_head; - cpu_MemoryFence(); - } - while(!cpu_CAS(&DescAvail, old_head, desc)); -} - -static Descriptor* ListGetPartial(SizeClass* sc) -{ - return 0; -} - -static void ListPutPartial(Descriptor* desc) -{ -} - -static void ListRemoveEmptyDesc(SizeClass* sc) -{ -} - -static ProcHeap* find_heap(SizeClass* sc) -{ - return 0; -} - - -static Descriptor* HeapGetPartial(ProcHeap* heap) -{ - Descriptor* desc; - do - { - desc = heap->partial; - if(!desc) - return ListGetPartial(heap->sc); - } - while(!cpu_CAS(&heap->partial, desc, 0)); - return desc; -} - - -static void HeapPutPartial(Descriptor* desc) -{ - Descriptor* prev; - do - prev = desc->heap->partial; - while(!cpu_CAS(&desc->heap->partial, prev, desc)); - if(prev) - ListPutPartial(prev); -} - - -static void UpdateActive(ProcHeap* heap, Descriptor* desc, uint more_credits) -{ - Active new_active = desc; - new_active.credits = more_credits-1; - if(cpu_CAS(&heap->active, 0, new_active)) - return; - - // someone installed another active sb - // return credits to sb and make it partial - Anchor old_anchor, new_anchor; - do - { - new_anchor = old_anchor = desc->anchor; - new_anchor.count += more_credits; - new_anchor.state = PARTIAL; - } - while(!cpu_CAS(&desc->anchor, old_anchor, new_anchor)); - HeapPutPartial(desc); -} - - - -static void RemoveEmptyDesc(ProcHeap* heap, Descriptor* desc) -{ - if(cpu_CAS(&heap->partial, desc, 0)) - DescRetire(desc); - else - ListRemoveEmptyDesc(heap->sc); -} - - -static void* MallocFromActive(ProcHeap* heap) -{ - // reserve block - Active old_active, new_active; - do - { - new_active = old_active = heap->active; - // no active superblock - will try Partial and then NewSB - if(!old_active) - return 0; - // none left - mark as no longer active - if(old_active.credits == 0) - new_active = 0; - // expected case - reserve - else - new_active.credits--; - } - while(!cpu_CAS(&heap->active, old_active, new_active)); - - u8* p; - - // pop block - Anchor old_anchor, new_anchor; - Descriptor* desc = old_active; - uint more_credits; - do - { - new_anchor = old_anchor = desc->anchor; - p = desc->sb + old_anchor.avail*desc->sz; - new_anchor.avail = *(uint*)p; - new_anchor.tag++; - if(old_active.credits == 0) - { - // state must be ACTIVE - if(old_anchor.count == 0) - new_anchor.state = FULL; - else - { - more_credits = std::min(old_anchor.count, MAX_CREDITS); - new_anchor.count -= more_credits; - } - } - } - while(!cpu_CAS(&desc->anchor, old_anchor, new_anchor)); - if(old_active.credits == 0 && old_anchor.count > 0) - UpdateActive(heap, desc, more_credits); - - *(Descriptor**)p = desc; - return p+sizeof(void*); - -} - - -static void* MallocFromPartial(ProcHeap* heap) -{ -retry: - Descriptor* desc = HeapGetPartial(heap); - if(!desc) - return 0; - desc->heap = heap; - - // reserve blocks - uint more_credits; - Anchor old_anchor, new_anchor; - do - { - new_anchor = old_anchor = desc->anchor; - if(old_anchor.state == EMPTY) - { - DescRetire(desc); - goto retry; - } - // old_anchor state must be PARTIAL - // old_anchor count must be > 0 - more_credits = std::min(old_anchor.count-1, MAX_CREDITS); - new_anchor.count -= more_credits+1; - new_anchor.state = (more_credits > 0)? ACTIVE : FULL; - } - while(!cpu_CAS(&desc->anchor, old_anchor, new_anchor)); - - u8* p; - - // pop reserved block - do - { - new_anchor = old_anchor = desc->anchor; - p = desc->sb + old_anchor.avail*desc->sz; - new_anchor.avail = *(uint*)p; - new_anchor.tag++; - } - while(!cpu_CAS(&desc->anchor, old_anchor, new_anchor)); - - if(more_credits > 0) - UpdateActive(heap, desc, more_credits); - - *(Descriptor**)p = desc; - return p+sizeof(void*); -} - - -static void* MallocFromNewSB(ProcHeap* heap) -{ - Descriptor* desc = DescAlloc(); - desc->sb = AllocNewSB(heap->sc->sb_size); - - //organize blocks in a linked list starting with index 0 - - desc->heap = heap; - desc->anchor.avail = 1; - desc->sz = heap->sc->sz; - desc->maxcount = (uint)(heap->sc->sb_size/desc->sz); - Active new_active = (Active)desc; - new_active.credits = std::min(desc->maxcount-1, MAX_CREDITS)-1; - desc->anchor.count = (desc->maxcount-1)-(new_active.credits+1); - desc->anchor.state = ACTIVE; - cpu_MemoryFence(); - if(!cpu_CAS(&heap->active, 0, new_active)) - { - FreeSB(desc->sb); - return 0; - } - - u8* p = desc->sb; - - *(Descriptor**)p = desc; - return p+sizeof(void*); -} - - -void* lf_malloc(size_t sz) -{ - void* p; - - // use sz and thread id to find heap - ProcHeap* heap = find_heap(0); // TODO: pass SizeClass - // large block - allocate directly - if(!heap) - { - p = malloc(sz); - if(p) - *(size_t*)p = sz|1; - return p; - } - -retry: - p = MallocFromActive(heap); - if(p) - return p; - p = MallocFromPartial(heap); - if(p) - return p; - p = MallocFromNewSB(heap); - if(p) - return p; - goto retry; -} - - -void lf_free(void* p_) -{ - if(!p_) - return; - u8* p = (u8*)p_; - - // get block header - p -= sizeof(void*); - uintptr_t hdr = *(uintptr_t*)p; - - // large block - free directly - if(hdr & 1) - { - free(p); - return; - } - - Descriptor* desc = (Descriptor*)hdr; - u8* sb = desc->sb; - Anchor old_anchor, new_anchor; - ProcHeap* heap; - do - { - new_anchor = old_anchor = desc->anchor; - *(size_t*)p = old_anchor.avail; - new_anchor.avail = (uint)((p-sb) / desc->sz); - if(old_anchor.state == FULL) - new_anchor.state = PARTIAL; - if(old_anchor.count == desc->maxcount-1) - { - heap = desc->heap; - serialize(); - new_anchor.state = EMPTY; - } - else - new_anchor.count++; - cpu_MemoryFence(); - } - while(!cpu_CAS(&desc->anchor, old_anchor, new_anchor)); - if(new_anchor.state == EMPTY) - { - FreeSB(sb); - RemoveEmptyDesc(heap, desc); - } - else if(old_anchor.state == FULL) - HeapPutPartial(desc); -} - -/* -static const int MAX_POOLS = 8; - -// split out of pools[] for more efficient lookup -static size_t pool_element_sizes[MAX_POOLS]; - -struct Pool -{ -u8* bucket_pos; -u8* freelist; -} -pools[MAX_POOLS]; - -static const int num_pools = 0; - - -const size_t BUCKET_SIZE = 8*KiB; - -static u8* bucket_pos; - - -// return the pool responsible for , or 0 if not yet set up and -// there are already too many pools. -static Pool* responsible_pool(size_t size) -{ -Pool* pool = pools; -for(int i = 0; i < MAX_POOLS; i++, pool++) -if(pool->element_size == size) -return pool; - -// need to set up a new pool -// .. but there are too many -debug_assert(0 <= num_pools && num_pools <= MAX_POOLS); -if(num_pools >= MAX_POOLS) -{ -debug_assert(0); // increase MAX_POOLS -return 0; -} - -pool = &pools[num_pools++]; -pool->element_size = size; -return pool; -} - -void* sbh_alloc(size_t size) -{ -// when this allocation is freed, there must be enough room for -// our freelist pointer. also ensures alignment. -size = round_up(size, 8); - -// would overflow a bucket -if(size > BUCKET_SIZE-sizeof(u8*)) -{ -debug_assert(0); // size doesn't fit in a bucket -return 0; -} - - -// - -// - -} - - -TNode* node_alloc(size_t size) -{ -// would overflow a bucket -if(size > BUCKET_SIZE-sizeof(u8*)) -{ -debug_assert(0); // size doesn't fit in a bucket -return 0; -} - -size = round_up(size, 8); -// ensure alignment, since size includes a string -const uintptr_t addr = (uintptr_t)bucket_pos; -const size_t bytes_used = addr % BUCKET_SIZE; -// addr = 0 on first call (no bucket yet allocated) -// bytes_used == 0 if a node fit exactly into a bucket -if(addr == 0 || bytes_used == 0 || bytes_used+size > BUCKET_SIZE) -{ -u8* const prev_bucket = (u8*)addr - bytes_used; -u8* bucket = (u8*)mem_alloc(BUCKET_SIZE, BUCKET_SIZE); -if(!bucket) -return 0; -*(u8**)bucket = prev_bucket; -bucket_pos = bucket+round_up(sizeof(u8*), 8); -} - -TNode* node = (TNode*)bucket_pos; -bucket_pos = (u8*)node+size; -return node; -} - - -static void node_free_all() -{ -const uintptr_t addr = (uintptr_t)bucket_pos; -u8* bucket = bucket_pos - (addr % BUCKET_SIZE); - -// covers bucket_pos == 0 case -while(bucket) -{ -u8* prev_bucket = *(u8**)bucket; -mem_free(bucket); -bucket = prev_bucket; -} -} -*/ -#endif diff --git a/source/lib/path_util.cpp b/source/lib/path_util.cpp index 5bd3c6388c..f5decd95db 100644 --- a/source/lib/path_util.cpp +++ b/source/lib/path_util.cpp @@ -330,119 +330,3 @@ const char* path_extension(const char* fn) const char* ext = dot+1; return ext; } - - -LibError path_foreach_component(const char* path_org, PathComponentCb cb, uintptr_t cbData) -{ - 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 . - for(;;) - { - // at end of string - done. - // (this happens if is empty or ends with slash) - if(*cur_component == '\0') - break; - - // find end of cur_component - char* slash = (char*)strchr(cur_component, '/'); - // .. try other separator - if(!slash) - slash = (char*)strchr(cur_component, '\\'); - - // decide its type and 0-terminate - // .. filename (by definition) - if(!slash) - is_dir = false; - // .. directory - else - *slash = '\0'; // 0-terminate cur_component - - path_component_validate(cur_component); - - LibError ret = cb(cur_component, is_dir, cbData); - // 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 will - // store the complete path up to and including cur_component. - *slash = '/'; - cur_component = slash+1; - } - - return INFO::OK; -} - - -//----------------------------------------------------------------------------- - -// convenience "class" that simplifies successively appending a filename to -// its parent directory. this avoids needing to allocate memory and calling -// strlen/strcat. used by wdll_ver and dir_next_ent. -// we want to maintain C compatibility, so this isn't a C++ class. - -// write the given directory path into our buffer and set end/chars_left -// accordingly. need not but can end with a directory separator. -// -// note: and the filename set via path_package_append_file are separated by -// '/'. this is to allow use on portable paths; the function otherwise -// does not care if paths are relative/portable/absolute. -LibError path_package_set_dir(PathPackage* pp, const char* dir) -{ - // -1 allows for trailing '/' that will be added if not - // already present. - if(strcpy_s(pp->path, ARRAY_SIZE(pp->path)-1, dir) != 0) - WARN_RETURN(ERR::PATH_LENGTH); - size_t len = strlen(pp->path); - // add directory separator if not already present - // .. but only check this if dir != "" (=> len-1 is safe) - if(len != 0) - { - char* last_char = pp->path+len-1; - if(!path_is_dir_sep(*last_char)) - { - *(last_char+1) = '/'; - // note: need to 0-terminate because pp.path is uninitialized - // and we overwrite strcpy_s's terminator above. - *(last_char+2) = '\0'; - // only bump by 1 - filename must overwrite '\0'. - len++; - } - } - - pp->end = pp->path+len; - pp->chars_left = ARRAY_SIZE(pp->path)-len; - return INFO::OK; -} - - -void path_package_copy(PathPackage* pp_dst, const PathPackage* pp_src) -{ - *pp_dst = *pp_src; - const ptrdiff_t end_ofs = pp_src->end - pp_src->path; - pp_dst->end = pp_dst->path + end_ofs; -} - - -// append the given filename to the directory established by the last -// path_package_set_dir on this package. the whole path is accessible at pp->path. -LibError path_package_append_file(PathPackage* pp, const char* path) -{ - CHECK_ERR(strcpy_s(pp->end, pp->chars_left, path)); - return INFO::OK; -} diff --git a/source/lib/path_util.h b/source/lib/path_util.h index 924cd164d7..4cbecec629 100644 --- a/source/lib/path_util.h +++ b/source/lib/path_util.h @@ -21,9 +21,6 @@ #ifndef INCLUDED_PATH_UTIL #define INCLUDED_PATH_UTIL -#include "lib/allocators/string_pool.h" - - namespace ERR { const LibError PATH_LENGTH = -100300; @@ -175,70 +172,4 @@ LIB_API void path_dir_only(const char* path, char* dir); **/ LIB_API const char* path_extension(const char* fn); - -/** - * callback for each component in a path string. - * - * if path is empty (i.e. ""), this is not called. - * - * @param component: 0-terminated name of the component (does not - * include any trailing slash!) - * @param is_dir indicates if it's a directory (i.e. is - * followed by a slash in the original path). - * rationale: a bool 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. - * @param cbData: context parameter that was passed to path_foreach_component. - * @return LibError; 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 INFO::OK) after a filename is - * encountered - that's taken care of automatically. - **/ -typedef LibError (*PathComponentCb)(const char* component, bool is_dir, uintptr_t cbData); - -/** - * call with for each component in . - * - * @return LibError - **/ -LIB_API LibError path_foreach_component(const char* path, PathComponentCb cb, uintptr_t cbData); - - -//----------------------------------------------------------------------------- - -/** - * convenience "class" that simplifies successively appending a filename to - * its parent directory. this avoids needing to allocate memory and calling - * strlen/strcat. used by wdll_ver and dir_next_ent. - * we want to maintain C compatibility, so this isn't a C++ class. - **/ -struct PathPackage -{ - char* end; - size_t chars_left; - char path[PATH_MAX]; -}; - -/** - * write the given directory path into our buffer and set end/chars_left - * accordingly. need not but can end with a directory separator. - * - * note: and the filename set via path_package_append_file are separated by - * '/'. this is to allow use on portable paths; the function otherwise - * does not care if paths are relative/portable/absolute. - * @return LibError - **/ -LIB_API LibError path_package_set_dir(PathPackage* pp, const char* dir); - -/** - * copy one PathPackage into another (doing so directly is incorrect!) - **/ -LIB_API void path_package_copy(PathPackage* pp_dst, const PathPackage* pp_src); - -/** - * append the given filename to the directory established by the last - * path_package_set_dir on this package. the whole path is accessible at pp->path. - * @return LibError - **/ -LIB_API LibError path_package_append_file(PathPackage* pp, const char* path); - #endif // #ifndef INCLUDED_PATH_UTIL diff --git a/source/lib/res/graphics/cursor.cpp b/source/lib/res/graphics/cursor.cpp index 8f90b93bd8..782a96ea6e 100644 --- a/source/lib/res/graphics/cursor.cpp +++ b/source/lib/res/graphics/cursor.cpp @@ -50,7 +50,7 @@ static void* load_empty_sys_cursor() return sys_cursor; } -static void* load_sys_cursor(const char* filename, int hx, int hy) +static void* load_sys_cursor(const VfsPath& pathname, int hx, int hy) { #if !ALLOW_SYS_CURSOR UNUSED2(filename); @@ -59,8 +59,12 @@ static void* load_sys_cursor(const char* filename, int hx, int hy) return 0; #else + shared_ptr file; size_t fileSize; + if(g_VFS->LoadFile(pathname, file, fileSize) < 0) + return 0; + Tex t; - if(tex_load(filename, &t) < 0) + if(tex_decode(file, fileSize, &t) < 0) return 0; { @@ -99,9 +103,9 @@ class GLCursor uint hotspotx, hotspoty; public: - LibError create(const char* filename, uint hotspotx_, uint hotspoty_) + LibError create(const VfsPath& pathname, uint hotspotx_, uint hotspoty_) { - ht = ogl_tex_load(filename); + ht = ogl_tex_load(pathname); RETURN_ERR(ht); (void)ogl_tex_get_size(ht, &w, &h, 0); @@ -178,29 +182,30 @@ static void Cursor_dtor(Cursor* c) c->gl_cursor.destroy(); } -static LibError Cursor_reload(Cursor* c, const char* name, Handle) +static LibError Cursor_reload(Cursor* c, const VfsPath& name, Handle) { - char filename[PATH_MAX]; + const VfsPath path("art/textures/cursors"); + const std::string basename = name.string(); // read pixel offset of the cursor's hotspot [the bit of it that's // drawn at (g_mouse_x,g_mouse_y)] from file. uint hotspotx = 0, hotspoty = 0; { - snprintf(filename, ARRAY_SIZE(filename), "art/textures/cursors/%s.txt", name); + const VfsPath pathname(path / (basename + ".txt")); shared_ptr buf; size_t size; - RETURN_ERR(g_VFS->LoadFile(filename, buf, size)); + RETURN_ERR(g_VFS->LoadFile(pathname, buf, size)); std::stringstream s(std::string((const char*)buf.get(), size)); s >> hotspotx >> hotspoty; } // load actual cursor - snprintf(filename, ARRAY_SIZE(filename), "art/textures/cursors/%s.dds", name); + const VfsPath pathname(path / (basename + ".dds")); // .. try loading as system cursor (2d, hardware accelerated) - c->sys_cursor = load_sys_cursor(filename, hotspotx, hotspoty); + c->sys_cursor = load_sys_cursor(pathname, hotspotx, hotspoty); // .. fall back to GLCursor (system cursor code is disabled or failed) if(!c->sys_cursor) { - LibError err=c->gl_cursor.create(filename, hotspotx, hotspoty); + LibError err=c->gl_cursor.create(pathname, hotspotx, hotspoty); if (err == INFO::OK) c->gl_sys_cursor = load_empty_sys_cursor(); diff --git a/source/lib/res/graphics/ogl_shader.cpp b/source/lib/res/graphics/ogl_shader.cpp index 68a003009e..3680c70f85 100644 --- a/source/lib/res/graphics/ogl_shader.cpp +++ b/source/lib/res/graphics/ogl_shader.cpp @@ -90,7 +90,7 @@ static void Ogl_Shader_init(Ogl_Shader* shdr, va_list args) // have absolutely no effect on a program object that contains these shaders // when the program object is already linked. // So, how can we inform the "parent object" (i.e. the program object) of our change? -static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle UNUSED(h)) +static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const VfsPath& pathname, Handle UNUSED(h)) { LibError err = ERR::FAIL; @@ -98,7 +98,7 @@ static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle return INFO::OK; shared_ptr file; size_t file_size; - RETURN_ERR(g_VFS->LoadFile(filename, file, file_size)); + RETURN_ERR(g_VFS->LoadFile(pathname, file, file_size)); ogl_WarnIfError(); @@ -131,7 +131,7 @@ static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle pglGetInfoLogARB(shdr->id, log_length, 0, infolog); debug_printf("Compile log for shader %hs (type %hs):\n%hs", - filename, + pathname.string().c_str(), shader_type_to_string(shdr->type, typenamebuf, ARRAY_SIZE(typenamebuf)), infolog); @@ -148,7 +148,7 @@ static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle char typenamebuf[32]; debug_printf("Failed to compile shader %hs (type %hs)\n", - filename, + pathname.string().c_str(), shader_type_to_string(shdr->type, typenamebuf, ARRAY_SIZE(typenamebuf))); err = ERR::SHDR_COMPILE; @@ -194,9 +194,9 @@ static LibError Ogl_Shader_to_string(const Ogl_Shader* UNUSED(shdr), char* buf) // Create, load and compile a shader object of the given type // (e.g. GL_VERTEX_SHADER_ARB). The given file will be used as // source code for the shader. -Handle ogl_shader_load(const char* fn, GLenum type) +Handle ogl_shader_load(const VfsPath& pathname, GLenum type) { - return h_alloc(H_Ogl_Shader, fn, 0, type); + return h_alloc(H_Ogl_Shader, pathname, 0, type); } @@ -244,7 +244,7 @@ static void Ogl_Program_init(Ogl_Program* UNUSED(p), va_list UNUSED(args)) // Load the shader associated with one Shader element, // and attach it to our program object. static LibError do_load_shader( - Ogl_Program* p, const char* filename, Handle UNUSED(h), + Ogl_Program* p, const VfsPath& pathname, Handle UNUSED(h), const CXeromyces& XeroFile, const XMBElement& Shader) { #define AT(x) int at_##x = XeroFile.GetAttributeID(#x) @@ -255,8 +255,7 @@ static LibError do_load_shader( if (Type.empty()) { - LOG(ERROR, LOG_CATEGORY, "%hs: Missing attribute \"type\" in element \"Shader\".", - filename); + LOG(ERROR, LOG_CATEGORY, "%hs: Missing attribute \"type\" in element \"Shader\".", pathname); WARN_RETURN(ERR::CORRUPTED); } @@ -264,8 +263,7 @@ static LibError do_load_shader( if (!shadertype) { - LOG(ERROR, LOG_CATEGORY, "%hs: Unknown shader type \"%hs\" (valid are: VERTEX_SHADER, FRAGMENT_SHADER).", - filename, Type.c_str()); + LOG(ERROR, LOG_CATEGORY, "%hs: Unknown shader type \"%hs\" (valid are: VERTEX_SHADER, FRAGMENT_SHADER).", pathname, Type.c_str()); WARN_RETURN(ERR::CORRUPTED); } @@ -273,11 +271,11 @@ static LibError do_load_shader( if (Name.empty()) { - LOG(ERROR, LOG_CATEGORY, "%hs: Missing shader name.", filename); + LOG(ERROR, LOG_CATEGORY, "%hs: Missing shader name.", pathname); WARN_RETURN(ERR::CORRUPTED); } - Handle hshader = ogl_shader_load(Name.c_str(), shadertype); + Handle hshader = ogl_shader_load(Name, shadertype); RETURN_ERR(hshader); ogl_shader_attach(p->id, hshader); @@ -293,7 +291,7 @@ static LibError do_load_shader( // Reload the program object from the source file. -static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle h) +static LibError Ogl_Program_reload(Ogl_Program* p, const VfsPath& pathname_, Handle h) { if (p->id) return INFO::OK; @@ -310,8 +308,9 @@ static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle WARN_RETURN(ERR::SHDR_CREATE); } + const char* pathname = pathname_.string().c_str(); CXeromyces XeroFile; - if (XeroFile.Load(filename) != PSRETURN_OK) + if (XeroFile.Load(pathname) != PSRETURN_OK) WARN_RETURN(ERR::CORRUPTED); // more informative error message? // Define all the elements and attributes used in the XML file @@ -325,7 +324,7 @@ static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle if (Root.GetNodeName() != el_program) { - LOG(ERROR, LOG_CATEGORY, "%hs: XML root was not \"Program\".", filename); + LOG(ERROR, LOG_CATEGORY, "%hs: XML root was not \"Program\".", pathname); WARN_RETURN(ERR::CORRUPTED); } @@ -346,17 +345,16 @@ static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle if (Shader.GetNodeName() != el_shader) { - LOG(ERROR, LOG_CATEGORY, "%hs: Only \"Shader\" may be child of \"Shaders\".", - filename); + LOG(ERROR, LOG_CATEGORY, "%hs: Only \"Shader\" may be child of \"Shaders\".", pathname); WARN_RETURN(ERR::CORRUPTED); } - RETURN_ERR(do_load_shader(p, filename, h, XeroFile, Shader)); + RETURN_ERR(do_load_shader(p, pathname, h, XeroFile, Shader)); } } else { - LOG(WARNING, LOG_CATEGORY, "%hs: Unknown child of \"Program\".", filename); + LOG(WARNING, LOG_CATEGORY, "%hs: Unknown child of \"Program\".", pathname); } } @@ -372,13 +370,13 @@ static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle char* infolog = new char[log_length]; pglGetInfoLogARB(p->id, log_length, 0, infolog); - debug_printf("Linker log for %hs:\n%hs\n", filename, infolog); + debug_printf("Linker log for %hs:\n%hs\n", pathname, infolog); delete[] infolog; } if (!linked) { - debug_printf("Link failed for %hs\n", filename); + debug_printf("Link failed for %hs\n", pathname); WARN_RETURN(ERR::SHDR_LINK); } diff --git a/source/lib/res/graphics/ogl_shader.h b/source/lib/res/graphics/ogl_shader.h index fd881aea06..ef6696bb10 100644 --- a/source/lib/res/graphics/ogl_shader.h +++ b/source/lib/res/graphics/ogl_shader.h @@ -35,7 +35,7 @@ extensions are available, or all bets are off. // Create, load and compile a shader object of the given type // (e.g. GL_VERTEX_SHADER_ARB). The given file will be used as // source code for the shader. -Handle ogl_shader_load(const char* fn, GLenum type); +Handle ogl_shader_load(const VfsPath& pathname, GLenum type); // Free all resources associated with the given handle (subject // to refcounting). diff --git a/source/lib/res/graphics/ogl_tex.cpp b/source/lib/res/graphics/ogl_tex.cpp index 218ddc5cdd..cb5bb296da 100644 --- a/source/lib/res/graphics/ogl_tex.cpp +++ b/source/lib/res/graphics/ogl_tex.cpp @@ -20,6 +20,7 @@ #include "../h_mgr.h" #include "lib/file/vfs/vfs.h" +#include "lib/fnv_hash.h" extern PIVFS g_VFS; @@ -396,7 +397,7 @@ static void OglTex_dtor(OglTex* ot) ot->flags &= ~OT_IS_UPLOADED; } -static LibError OglTex_reload(OglTex* ot, const char* fn, Handle h) +static LibError OglTex_reload(OglTex* ot, const VfsPath& pathname, Handle h) { // we're reusing a freed but still in-memory OglTex object if(ot->flags & OT_IS_UPLOADED) @@ -405,7 +406,12 @@ static LibError OglTex_reload(OglTex* ot, const char* fn, Handle h) // if we don't already have the texture in memory (*), load from file. // * this happens if the texture is "wrapped". if(!(ot->flags & OT_TEX_VALID)) - RETURN_ERR(tex_load(fn, &ot->t)); + { + shared_ptr file; size_t fileSize; + RETURN_ERR(g_VFS->LoadFile(pathname, file, fileSize)); + if(tex_decode(file, fileSize, &ot->t) < 0) + return 0; + } ot->flags |= OT_TEX_VALID; glGenTextures(1, &ot->id); @@ -467,20 +473,21 @@ static LibError OglTex_to_string(const OglTex* ot, char* buf) } -// load and return a handle to the texture given in . +// load and return a handle to the texture given in . // for a list of supported formats, see tex.h's tex_load. -Handle ogl_tex_load(const char* fn, uint flags) +Handle ogl_tex_load(const VfsPath& pathname, uint flags) { Tex* wrapped_tex = 0; // we're loading from file - return h_alloc(H_OglTex, fn, flags, wrapped_tex); + return h_alloc(H_OglTex, pathname, flags, wrapped_tex); } // return Handle to an existing object, if it has been loaded and // is still in memory; otherwise, a negative error code. -Handle ogl_tex_find(const char* fn) +Handle ogl_tex_find(const VfsPath& pathname) { - return h_find(H_OglTex, (uintptr_t)fn); + const uintptr_t key = fnv_hash(pathname.string().c_str(), pathname.string().length()); + return h_find(H_OglTex, key); } @@ -757,16 +764,14 @@ struct UploadParams GLint int_fmt; }; -static void upload_level(uint level, uint level_w, uint level_h, - const u8* RESTRICT level_data, size_t UNUSED(level_data_size), void* RESTRICT cbData) +static void upload_level(uint level, uint level_w, uint level_h, const u8* RESTRICT level_data, size_t UNUSED(level_data_size), void* RESTRICT cbData) { const UploadParams* up = (const UploadParams*)cbData; glTexImage2D(GL_TEXTURE_2D, level, up->int_fmt, level_w, level_h, 0, up->fmt, GL_UNSIGNED_BYTE, level_data); } -static void upload_compressed_level(uint level, uint level_w, uint level_h, - const u8* RESTRICT level_data, size_t level_data_size, void* RESTRICT cbData) +static void upload_compressed_level(uint level, uint level_w, uint level_h, const u8* RESTRICT level_data, size_t level_data_size, void* RESTRICT cbData) { const UploadParams* up = (const UploadParams*)cbData; pglCompressedTexImage2DARB(GL_TEXTURE_2D, level, up->fmt, @@ -807,9 +812,6 @@ LibError ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint H_DEREF(ht, OglTex, ot); Tex* t = &ot->t; - const char* fn = h_filename(ht); - if(!fn) - fn = "(could not determine filename)"; debug_assert(q_flags_valid(q_flags_ovr)); // we don't bother verifying *fmt_ovr - there are too many values diff --git a/source/lib/res/graphics/ogl_tex.h b/source/lib/res/graphics/ogl_tex.h index 2208841b2b..cce9200384 100644 --- a/source/lib/res/graphics/ogl_tex.h +++ b/source/lib/res/graphics/ogl_tex.h @@ -199,12 +199,11 @@ extern void ogl_tex_set_defaults(uint q_flags, GLint filter); /** * Load and return a handle to the texture. * -* @param fn VFS filename of texture. * @param flags h_alloc flags. * @return Handle to texture or negative LibError * for a list of supported formats, see tex.h's tex_load. */ -extern Handle ogl_tex_load(const char* fn, uint flags = 0); +extern Handle ogl_tex_load(const VfsPath& pathname, uint flags = 0); /** * Find and return an existing texture object, if it has already been @@ -213,7 +212,7 @@ extern Handle ogl_tex_load(const char* fn, uint flags = 0); * @param fn VFS filename of texture. * @return Handle to texture or negative LibError */ -extern Handle ogl_tex_find(const char* fn); +extern Handle ogl_tex_find(const VfsPath& pathname); /** * Make the Tex object ready for use as an OpenGL texture diff --git a/source/lib/res/graphics/tests/test_tex.h b/source/lib/res/graphics/tests/test_tex.h index e0d6a670ee..fd9d930ca1 100644 --- a/source/lib/res/graphics/tests/test_tex.h +++ b/source/lib/res/graphics/tests/test_tex.h @@ -6,8 +6,7 @@ class TestTex : public CxxTest::TestSuite { - void generate_encode_decode_compare(uint w, uint h, uint flags, uint bpp, - const char* filename) + void generate_encode_decode_compare(uint w, uint h, uint flags, uint bpp, const std::string& extension) { // generate test data const size_t size = w*h*bpp/8; @@ -21,12 +20,12 @@ class TestTex : public CxxTest::TestSuite // encode to file format DynArray da; - TS_ASSERT_OK(tex_encode(&t, filename, &da)); + TS_ASSERT_OK(tex_encode(&t, extension, &da)); memset(&t, 0, sizeof(t)); // decode from file format - MEM_DTOR dtor = 0; // we'll free da manually - TS_ASSERT_OK(tex_decode(da.base, da.cur_size, dtor, &t)); + shared_ptr ptr(&da.base, DummyDeleter()); + TS_ASSERT_OK(tex_decode(ptr, da.cur_size, 0, &t)); // make sure pixel format gets converted completely to plain TS_ASSERT_OK(tex_transform_to(&t, 0)); diff --git a/source/lib/res/graphics/unifont.cpp b/source/lib/res/graphics/unifont.cpp index 95bf118962..840bb69481 100644 --- a/source/lib/res/graphics/unifont.cpp +++ b/source/lib/res/graphics/unifont.cpp @@ -58,8 +58,9 @@ static void UniFont_dtor(UniFont* f) SAFE_DELETE(f->glyphs_size); } +// basename is e.g. "console"; the files are "fonts/console.fnt" and "fonts/console.tga" // [10..70ms] -static LibError UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h)) +static LibError UniFont_reload(UniFont* f, const VfsPath& basename, Handle UNUSED(h)) { // already loaded if(f->ht > 0) @@ -68,16 +69,13 @@ static LibError UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h)) f->glyphs_id = new glyphmap_id(); f->glyphs_size = new glyphmap_size(); - // fn is the base filename, e.g. "console" - // The font definition file is "fonts/"+fn+".fnt" and the texture is "fonts/"+fn+".tga" - std::string FilenameBase = "fonts/"; FilenameBase += fn; + const VfsPath path("fonts/"); // Read font definition file into a stringstream - const std::string FilenameFnt = FilenameBase+".fnt"; - const char* fnt_fn = FilenameFnt.c_str(); shared_ptr buf; size_t size; - RETURN_ERR(g_VFS->LoadFile(fnt_fn, buf, size)); // [cumulative for 12: 36ms] - std::istringstream FNTStream (std::string((const char*)buf.get(), (int)size)); + const VfsPath fntName(basename.string() + ".fnt"); + RETURN_ERR(g_VFS->LoadFile(path/fntName, buf, size)); // [cumulative for 12: 36ms] + std::istringstream FNTStream (std::string((const char*)buf.get(), size)); int Version; FNTStream >> Version; @@ -145,9 +143,8 @@ static LibError UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h)) // Load glyph texture // [cumulative for 12: 20ms] - std::string FilenameTex = FilenameBase+".tga"; - const char* tex_fn = FilenameTex.c_str(); - Handle ht = ogl_tex_load(tex_fn); + const VfsPath tgaName(basename.string() + ".tga"); + Handle ht = ogl_tex_load(path/tgaName); RETURN_ERR(ht); (void)ogl_tex_set_filter(ht, GL_NEAREST); // override is necessary because the GL format is chosen as LUMINANCE, @@ -191,9 +188,9 @@ static LibError UniFont_to_string(const UniFont* f, char* buf) } -Handle unifont_load(const char* fn, uint flags) +Handle unifont_load(const VfsPath& pathname, uint flags) { - return h_alloc(H_UniFont, fn, flags); + return h_alloc(H_UniFont, pathname, flags); } diff --git a/source/lib/res/graphics/unifont.h b/source/lib/res/graphics/unifont.h index d108c60474..97af27545a 100644 --- a/source/lib/res/graphics/unifont.h +++ b/source/lib/res/graphics/unifont.h @@ -15,7 +15,7 @@ // Load and return a handle to the font defined // in fn+".fnt" with texture fn+".tga" -extern Handle unifont_load(const char* fn, uint flags = 0); +extern Handle unifont_load(const VfsPath& pathname, uint flags = 0); // Release a handle to a previously loaded font extern LibError unifont_unload(Handle& h); diff --git a/source/lib/res/h_mgr.cpp b/source/lib/res/h_mgr.cpp index 4d2033a4ad..b2872e225c 100644 --- a/source/lib/res/h_mgr.cpp +++ b/source/lib/res/h_mgr.cpp @@ -139,7 +139,7 @@ struct HDATA // for statistics uint num_derefs; - const char* fn; + VfsPath pathname; u8 user[HDATA_USER_SIZE]; }; @@ -390,79 +390,10 @@ static void key_remove(uintptr_t key, H_Type type) } -//----------------------------------------------------------------------------- - -// -// path string suballocator (they're stored in HDATA) -// - -static Pool fn_pool; - -// if fn is longer than this, it has to be allocated from the heap. -// choose this to balance internal fragmentation and accessing the heap. -static const size_t FN_POOL_EL_SIZE = 64; - -static void fn_init() -{ - // (if this fails, so will subsequent fn_stores - no need to complain here) - (void)pool_create(&fn_pool, MAX_EXTANT_HANDLES*FN_POOL_EL_SIZE, FN_POOL_EL_SIZE); -} - -static void fn_store(HDATA* hd, const char* fn) -{ - const size_t size = strlen(fn)+1; - - hd->fn = 0; - // stuff it in unused space at the end of HDATA - if(hd->type->user_size+size <= HDATA_USER_SIZE) - hd->fn = (const char*)hd->user + hd->type->user_size; - else if(size <= FN_POOL_EL_SIZE) - hd->fn = (const char*)pool_alloc(&fn_pool, 0); - - // in case none of the above applied and/or were successful: - // fall back to heap alloc. - if(!hd->fn) - { - debug_printf("H_MGR| very long filename (%d) %s\n", size, fn); - hd->fn = (const char*)malloc(size); - // still failed - bail (avoid strcpy to 0) - if(!hd->fn) - WARN_ERR_RETURN(ERR::NO_MEM); - } - - cpu_memcpy((void*)hd->fn, fn, size); // faster than strcpy -} - -// TODO: store this in a flag - faster. -static bool fn_is_in_HDATA(HDATA* hd) -{ - u8* p = (u8*)hd->fn; // needed for type-correct comparison - return (hd->user+hd->type->user_size <= p && p <= hd->user+HDATA_USER_SIZE); -} - -static void fn_free(HDATA* hd) -{ - void* el = (void*)hd->fn; - if(fn_is_in_HDATA(hd)) - { - } - else if(pool_contains(&fn_pool, el)) - pool_free(&fn_pool, el); - else - free(el); -} - -static void fn_shutdown() -{ - pool_destroy(&fn_pool); -} - - //---------------------------------------------------------------------------- // h_alloc //---------------------------------------------------------------------------- - static void warn_if_invalid(HDATA* hd) { #ifndef NDEBUG @@ -478,13 +409,10 @@ static void warn_if_invalid(HDATA* hd) // make sure empty space in control block isn't touched // .. but only if we're not storing a filename there - if(!fn_is_in_HDATA(hd)) - { - const u8* start = hd->user + vtbl->user_size; - const u8* end = hd->user + HDATA_USER_SIZE; - for(const u8* p = start; p < end; p++) - debug_assert(*p == 0); // else: handle user data was overrun! - } + const u8* start = hd->user + vtbl->user_size; + const u8* end = hd->user + HDATA_USER_SIZE; + for(const u8* p = start; p < end; p++) + debug_assert(*p == 0); // else: handle user data was overrun! #else UNUSED2(hd); #endif @@ -550,7 +478,7 @@ static Handle reuse_existing_handle(uintptr_t key, H_Type type, uint flags) } -static LibError call_init_and_reload(Handle h, H_Type type, HDATA* hd, const char* fn, va_list* init_args) +static LibError call_init_and_reload(Handle h, H_Type type, HDATA* hd, const VfsPath& pathname, va_list* init_args) { LibError err = INFO::OK; H_VTbl* vtbl = type; // exact same thing but for clarity @@ -565,7 +493,7 @@ static LibError call_init_and_reload(Handle h, H_Type type, HDATA* hd, const cha // catch exception to simplify reload funcs - let them use new() try { - err = vtbl->reload(hd->user, fn, h); + err = vtbl->reload(hd->user, pathname, h); if(err == INFO::OK) warn_if_invalid(hd); } @@ -579,8 +507,7 @@ static LibError call_init_and_reload(Handle h, H_Type type, HDATA* hd, const cha } -static Handle alloc_new_handle(H_Type type, const char* fn, uintptr_t key, - uint flags, va_list* init_args) +static Handle alloc_new_handle(H_Type type, const VfsPath& pathname, uintptr_t key, uint flags, va_list* init_args) { i32 idx; HDATA* hd; @@ -600,16 +527,12 @@ static Handle alloc_new_handle(H_Type type, const char* fn, uintptr_t key, if(flags & RES_DISALLOW_RELOAD) hd->disallow_reload = 1; hd->unique = (flags & RES_UNIQUE) != 0; - hd->fn = 0; - // .. filename is valid - store in hd - // note: if the original fn param was a key, it was reset to 0 above. - if(fn) - fn_store(hd, fn); + hd->pathname = pathname; if(key && !hd->unique) key_add(key, h); - LibError err = call_init_and_reload(h, type, hd, fn, init_args); + LibError err = call_init_and_reload(h, type, hd, pathname, init_args); if(err < 0) goto fail; @@ -627,30 +550,14 @@ fail: // any further params are passed to type's init routine -Handle h_alloc(H_Type type, const char* fn, uint flags, ...) +Handle h_alloc(H_Type type, const VfsPath& pathname, uint flags, ...) { RETURN_ERR(type_validate(type)); - // get key (either hash of filename, or fn param) - uintptr_t key = 0; - // not backed by file; fn is the key - if(flags & RES_KEY) - { - key = (uintptr_t)fn; - fn = 0; - } - else - { - if(fn) - key = fnv_hash(fn); - } + const uintptr_t key = fnv_hash(pathname.string().c_str(), pathname.string().length()); //debug_printf("alloc %s %s\n", type->name, fn); - // no key => can never be found. disallow caching - if(!key) - flags |= RES_NO_CACHE; - // see if we can reuse an existing handle Handle h = reuse_existing_handle(key, type, flags); RETURN_ERR(h); @@ -660,7 +567,7 @@ Handle h_alloc(H_Type type, const char* fn, uint flags, ...) // .. need to allocate a new one: va_list args; va_start(args, flags); - h = alloc_new_handle(type, fn, key, flags, &args); + h = alloc_new_handle(type, pathname, key, flags, &args); va_end(args); return h; // alloc_new_handle already does CHECK_ERR } @@ -694,16 +601,6 @@ static LibError h_free_idx(i32 idx, HDATA* hd) if(hd->key && !hd->unique) key_remove(hd->key, hd->type); - // get pretty version of filename: start with "not applicable" - const char* fn = "(0)"; - if(hd->fn) - { - // if hd->fn is a filename, strip the path. note: some paths end - // with '/', so display those unaltered. - const char* slash = strrchr(hd->fn, '/'); - fn = (slash && slash[1] != '\0')? slash+1 : hd->fn; - } - #ifndef NDEBUG // to_string is slow for some handles, so avoid calling it if unnecessary if(debug_filter_allows("H_MGR|")) @@ -711,11 +608,11 @@ static LibError h_free_idx(i32 idx, HDATA* hd) char buf[H_STRING_LEN]; if(vtbl->to_string(hd->user, buf) < 0) strcpy(buf, "(error)"); // safe - debug_printf("H_MGR| free %s %s accesses=%d %s\n", hd->type->name, fn, hd->num_derefs, buf); + debug_printf("H_MGR| free %s %s accesses=%d %s\n", hd->type->name, hd->pathname.string().c_str(), hd->num_derefs, buf); } #endif - fn_free(hd); + hd->pathname.~VfsPath(); // FIXME: ugly hack, but necessary to reclaim std::string memory memset(hd, 0, sizeof(*hd)); @@ -771,7 +668,7 @@ void* h_user_data(const Handle h, const H_Type type) } -const char* h_filename(const Handle h) +VfsPath h_filename(const Handle h) { // don't require type check: should be useable for any handle, // even if the caller doesn't know its type. @@ -781,19 +678,14 @@ const char* h_filename(const Handle h) debug_assert(0); return 0; } - return hd->fn; + return hd->pathname; } // TODO: what if iterating through all handles is too slow? -LibError h_reload(const char* fn) +LibError h_reload(const VfsPath& pathname) { - // must not continue - some resources not backed by files have - // key = 0 and reloading those would be disastrous. - if(!fn) - WARN_RETURN(ERR::INVALID_PARAM); - - const u32 key = fnv_hash(fn); + const u32 key = fnv_hash(pathname.string().c_str(), pathname.string().length()); // destroy (note: not free!) all handles backed by this file. // do this before reloading any of them, because we don't specify reload @@ -818,7 +710,7 @@ LibError h_reload(const char* fn) Handle h = handle(i, hd->tag); - LibError err = hd->type->reload(hd->user, hd->fn, h); + LibError err = hd->type->reload(hd->user, hd->pathname, h); // don't stop if an error is encountered - try to reload them all. if(err < 0) { @@ -901,10 +793,6 @@ static ModuleInitState initState; void h_mgr_init() { - if(!ModuleShouldInitialize(&initState)) - return; - - fn_init(); } @@ -945,6 +833,4 @@ void h_mgr_shutdown() free(pages[j]); pages[j] = 0; } - - fn_shutdown(); } diff --git a/source/lib/res/h_mgr.h b/source/lib/res/h_mgr.h index 3b5b8a15f6..3c5d5c7252 100644 --- a/source/lib/res/h_mgr.h +++ b/source/lib/res/h_mgr.h @@ -117,7 +117,7 @@ reload: does all initialization of the resource that requires its source file. called after init; also after dtor every time the file is reloaded. -static LibError Type_reload(Res1* r, const char* filename, Handle); +static LibError Type_reload(Res1* r, const VfsPath& pathname, Handle); { // already loaded; done if(r->data) @@ -126,7 +126,7 @@ static LibError Type_reload(Res1* r, const char* filename, Handle); r->data = malloc(100); if(!r->data) WARN_RETURN(ERR::NO_MEM); - // (read contents of into r->data) + // (read contents of into r->data) return 0; } @@ -187,10 +187,10 @@ static LibError Type_validate(const Res1* r); 5) provide your layer on top of the handle manager: -Handle res1_load(const char* filename, int my_flags) +Handle res1_load(const VfsPath& pathname, int my_flags) { // passes my_flags to init - return h_alloc(H_Res1, filename, 0, my_flags); + return h_alloc(H_Res1, pathname, 0, my_flags); } LibError res1_free(Handle& h) @@ -242,33 +242,10 @@ extern void h_mgr_shutdown(); // handle type (for 'type safety' - can't use a texture handle as a sound) - -// -// rationale: we could use the destructor passed to h_alloc to identify -// the handle, but it's good to have a list of all types, and we avoid having -// to create empty destructors for handle types that wouldn't need them. -// finally, we save memory - this fits in a few bits, vs. needing a pointer. - // registering extension for each module is bad - some may use many // (e.g. texture - many formats). // handle manager shouldn't know about handle types -/* -enum H_Type -{ - H_Mem = 1, - H_ZArchive = 2, - H_ZFile = 3, - H_VFile = 4, - H_VRead = 5, - - H_Tex = 6, - H_Font = 7, - H_Sound = 8, - - NUM_HANDLE_TYPES -}; -*/ /* ///xxx advantage of manual vtbl: @@ -301,7 +278,7 @@ but- has to handle variable params, a bit ugly struct H_VTbl { void (*init)(void* user, va_list); - LibError (*reload)(void* user, const char* fn, Handle); + LibError (*reload)(void* user, const VfsPath& pathname, Handle); void (*dtor)(void* user); LibError (*validate)(const void* user); LibError (*to_string)(const void* user, char* buf); @@ -314,14 +291,14 @@ typedef H_VTbl* H_Type; #define H_TYPE_DEFINE(type)\ /* forward decls */\ static void type##_init(type*, va_list);\ - static LibError type##_reload(type*, const char*, Handle);\ + static LibError type##_reload(type*, const VfsPath&, Handle);\ static void type##_dtor(type*);\ static LibError type##_validate(const type*);\ static LibError type##_to_string(const type*, char* buf);\ static H_VTbl V_##type =\ {\ (void (*)(void*, va_list))type##_init,\ - (LibError (*)(void*, const char*, Handle))type##_reload,\ + (LibError (*)(void*, const VfsPath&, Handle))type##_reload,\ (void (*)(void*))type##_dtor,\ (LibError (*)(const void*))type##_validate,\ (LibError (*)(const void*, char*))type##_to_string,\ @@ -382,10 +359,6 @@ enum // not cached, and will never reuse a previous instance RES_UNIQUE = RES_NO_CACHE|0x10, - // the resource isn't backed by a file. the fn parameter is treated as the search key (uintptr_t) - // currently only used by mem manager - RES_KEY = 0x08, - // object is requesting it never be reloaded (e.g. because it's not // backed by a file) RES_DISALLOW_RELOAD = 0x20 @@ -403,7 +376,7 @@ const size_t H_STRING_LEN = 256; //// user_size is checked to make sure the user data fits in the handle data space. // dtor is associated with type and called when the object is freed. // handle data is initialized to 0; optionally, a pointer to it is returned. -extern Handle h_alloc(H_Type type, const char* fn, uint flags = 0, ...); +extern Handle h_alloc(H_Type type, const VfsPath& pathname, uint flags = 0, ...); extern LibError h_free(Handle& h, H_Type type); @@ -419,10 +392,10 @@ extern Handle h_find(H_Type type, uintptr_t key); // prefer using H_DEREF or H_USER_DATA. extern void* h_user_data(Handle h, H_Type type); -extern const char* h_filename(Handle h); +extern VfsPath h_filename(Handle h); -extern LibError h_reload(const char* fn); +extern LibError h_reload(const VfsPath& pathname); // force the resource to be freed immediately, even if cached. // tag is not checked - this allows the first Handle returned diff --git a/source/lib/res/handle.h b/source/lib/res/handle.h index 3a3b327c86..e1ee78241a 100644 --- a/source/lib/res/handle.h +++ b/source/lib/res/handle.h @@ -11,6 +11,8 @@ #ifndef INCLUDED_HANDLE #define INCLUDED_HANDLE +#include "lib/file/vfs/vfs_path.h" + // 0 = invalid handle value; < 0 is an error code. // 64 bits, because we want tags to remain unique: tag overflow may // let handle use errors slip through, or worse, cause spurious errors. diff --git a/source/lib/res/sound/snd_mgr.cpp b/source/lib/res/sound/snd_mgr.cpp index 38a7b1b98e..8f858cd206 100644 --- a/source/lib/res/sound/snd_mgr.cpp +++ b/source/lib/res/sound/snd_mgr.cpp @@ -765,49 +765,29 @@ if(sd->o) ogg_release(sd->o); // but that load failed). static void hsd_list_add(Handle hsd); -static LibError SndData_reload(SndData * sd, const char* fn, Handle hsd) +static LibError SndData_reload(SndData* sd, const VfsPath& pathname, Handle hsd) { - // - // detect sound format by checking file extension - // - - enum FileType - { - FT_OGG - } - file_type; - - const char* ext = path_extension(fn); - // .. OGG (data will be passed directly to OpenAL) - if(!strcasecmp(ext, "ogg")) - { -#ifdef OGG_HACK -#else - // first use of OGG: check if OpenAL extension is available. - // note: this is required! OpenAL does its init here. - static int ogg_supported = -1; - if(ogg_supported == -1) - ogg_supported = alIsExtensionPresent((ALubyte*)"AL_EXT_vorbis")? 1 : 0; - if(!ogg_supported) - WARN_RETURN(ERR::NO_SYS); - - sd->al_fmt = AL_FORMAT_VORBIS_EXT; - sd->al_freq = 0; -#endif - - file_type = FT_OGG; - } - // .. unknown extension - else - WARN_RETURN(ERR::FAIL); - - // note: WAV is no longer supported. writing our own loader is infeasible + // currently only supports OGG; WAV is no longer supported. writing our own loader is infeasible // due to a seriously watered down spec with many incompatible variants. // pulling in an external library (e.g. freealut) is deemed not worth the // effort - OGG should be better in all cases. +#ifdef OGG_HACK +#else + // first use of OGG: check if OpenAL extension is available. + // note: this is required! OpenAL does its init here. + static int ogg_supported = -1; + if(ogg_supported == -1) + ogg_supported = alIsExtensionPresent((ALubyte*)"AL_EXT_vorbis")? 1 : 0; + if(!ogg_supported) + WARN_RETURN(ERR::NO_SYS); + + sd->al_fmt = AL_FORMAT_VORBIS_EXT; + sd->al_freq = 0; +#endif + shared_ptr file; size_t file_size; - RETURN_ERR(g_VFS->LoadFile(fn, file, file_size)); + RETURN_ERR(g_VFS->LoadFile(pathname, file, file_size)); ALvoid* al_data = (ALvoid*)file.get(); ALsizei al_size = (ALsizei)file_size; @@ -815,29 +795,22 @@ static LibError SndData_reload(SndData * sd, const char* fn, Handle hsd) #ifdef OGG_HACK std::vector data; data.reserve(500000); - if(file_type == FT_OGG) + sd->o = ogg_create(); + ogg_give_raw(sd->o, file.get(), file_size); + ogg_open(sd->o, sd->al_fmt, sd->al_freq); + size_t datasize=0; + size_t bytes_read; + do { - sd->o = ogg_create(); - ogg_give_raw(sd->o, file.get(), file_size); - ogg_open(sd->o, sd->al_fmt, sd->al_freq); - size_t datasize=0; - size_t bytes_read; - do - { - const size_t bufsize = 32*KiB; - char buf[bufsize]; - bytes_read = ogg_read(sd->o, buf, bufsize); - data.insert(data.end(), &buf[0], &buf[bytes_read]); - datasize += bytes_read; - } - while(bytes_read > 0); - al_data = &data[0]; - al_size = (ALsizei)datasize; - } - else - { - sd->o = NULL; + const size_t bufsize = 32*KiB; + char buf[bufsize]; + bytes_read = ogg_read(sd->o, buf, bufsize); + data.insert(data.end(), &buf[0], &buf[bytes_read]); + datasize += bytes_read; } + while(bytes_read > 0); + al_data = &data[0]; + al_size = (ALsizei)datasize; #endif sd->al_buf = al_buf_alloc(al_data, al_size, sd->al_fmt, sd->al_freq); @@ -859,7 +832,7 @@ static LibError SndData_validate(const SndData * sd) return INFO::OK; } -static LibError SndData_to_string(const SndData * sd, char * buf) +static LibError SndData_to_string(const SndData* sd, char* buf) { const char* type = "clip"; snprintf(buf, H_STRING_LEN, "%s; al_buf=%d", type, sd->al_buf); @@ -870,18 +843,17 @@ static LibError SndData_to_string(const SndData * sd, char * buf) /** * open and return a handle to a sound file's data. * - * @param fn VFS filename * @param is_stream (default false) indicates whether this file should be * streamed in (opening is faster, it won't be kept in memory, but * only one instance can be open at a time; makes sense for large music files) * or loaded immediately. * @return Handle or LibError on failure */ -static Handle snd_data_load(const char* fn, bool is_stream) +static Handle snd_data_load(const VfsPath& pathname, bool is_stream) { debug_assert(!is_stream); // no longer supported - return h_alloc(H_SndData, fn); + return h_alloc(H_SndData, pathname); } @@ -1227,7 +1199,7 @@ static void VSrc_dtor(VSrc* vs) (void)snd_data_free(vs->hsd); } -static LibError VSrc_reload(VSrc* vs, const char* fn, Handle hvs) +static LibError VSrc_reload(VSrc* vs, const VfsPath& pathname, Handle hvs) { // cannot wait till play(), need to init here: // must load OpenAL so that snd_data_load can check for OGG extension. @@ -1238,34 +1210,24 @@ static LibError VSrc_reload(VSrc* vs, const char* fn, Handle hvs) // .. catch genuine errors during init. RETURN_ERR(err); - // - // if extension is "txt", fn is a definition file containing the - // sound file name and its gain; otherwise, read directly from fn - // and assume default gain (1.0). - // + VfsPath dataPathname; - const char* snd_fn; // actual sound file name - std::string snd_fn_s; - // extracted from stringstream; - // declare here so that it doesn't go out of scope below. - - const char* ext = path_extension(fn); - if(!strcasecmp(ext, "txt")) + // pathname is a definition file containing the data file name and + // its gain. + if(fs::extension((const fs::path&)pathname) == ".txt") { shared_ptr buf; size_t size; - RETURN_ERR(g_VFS->LoadFile(fn, buf, size)); + RETURN_ERR(g_VFS->LoadFile(pathname, buf, size)); std::istringstream def(std::string((char*)buf.get(), (int)size)); - float gain_percent; - def >> snd_fn_s; - def >> gain_percent; - - snd_fn = snd_fn_s.c_str(); - vs->gain = gain_percent / 100.0f; + def >> dataPathname; + def >> vs->gain; + vs->gain /= 100.0f; // is stored as percent } + // read the sound file directly and assume default gain (1.0). else { - snd_fn = fn; + dataPathname = pathname; vs->gain = 1.0f; } @@ -1277,7 +1239,7 @@ static LibError VSrc_reload(VSrc* vs, const char* fn, Handle hvs) // needed so we can snd_free ourselves when done playing. bool is_stream = (vs->flags & VS_IS_STREAM) != 0; - vs->hsd = snd_data_load(snd_fn, is_stream); + vs->hsd = snd_data_load(dataPathname, is_stream); RETURN_ERR(vs->hsd); return INFO::OK; @@ -1309,7 +1271,7 @@ static LibError VSrc_to_string(const VSrc* vs, char * buf) /** * open and return a handle to a sound instance. * - * @param snd_fn VFS filename. if a text file (extension ".txt"), + * @param pathname. if a text file (extension ".txt"), * it is assumed to be a definition file containing the * sound file name and its gain (0.0 .. 1.0). * otherwise, it is taken to be the sound file name and @@ -1320,14 +1282,14 @@ static LibError VSrc_to_string(const VSrc* vs, char * buf) * or loaded immediately. * @return Handle or LibError on failure */ -Handle snd_open(const char* snd_fn, bool is_stream) +Handle snd_open(const VfsPath& pathname, bool is_stream) { uint flags = 0; if(is_stream) flags |= VS_IS_STREAM; // note: RES_UNIQUE forces each instance to get a new resource // (which is of course what we want). - return h_alloc(H_VSrc, snd_fn, RES_UNIQUE, flags); + return h_alloc(H_VSrc, pathname, RES_UNIQUE, flags); } diff --git a/source/lib/res/sound/snd_mgr.h b/source/lib/res/sound/snd_mgr.h index 31b8154250..3fdb4d3a58 100644 --- a/source/lib/res/sound/snd_mgr.h +++ b/source/lib/res/sound/snd_mgr.h @@ -153,7 +153,7 @@ extern LibError snd_set_master_gain(float gain); * open and return a handle to a sound instance. * this loads the sound data and makes it ready for other snd_* APIs. * - * @param snd_fn input filename. if a text file (extension "txt"), it is + * @param pathname. if a text file (extension ".txt"), it is * assumed to be a definition file containing the sound file name and * its gain (0.0 .. 1.0). * otherwise, it is taken to be the sound file name and @@ -164,7 +164,7 @@ extern LibError snd_set_master_gain(float gain); * only one instance can be open at a time. * @return Handle or LibError **/ -extern Handle snd_open(const char* snd_fn, bool stream = false); +extern Handle snd_open(const VfsPath& name, bool stream = false); /** * close the sound instance. if it was playing, it will be stopped. diff --git a/source/lib/sysdep/win/wdll_ver.cpp b/source/lib/sysdep/win/wdll_ver.cpp index 3277992976..50760738fe 100644 --- a/source/lib/sysdep/win/wdll_ver.cpp +++ b/source/lib/sysdep/win/wdll_ver.cpp @@ -14,10 +14,11 @@ #include #include -#include "lib/path_util.h" #include "win.h" #include "wutil.h" +#include "lib/file/io/io.h" // io_Allocate + #if MSC_VERSION #pragma comment(lib, "version.lib") // DLL version #endif @@ -25,31 +26,34 @@ //----------------------------------------------------------------------------- -// helper function that does all the work; caller wraps it and takes care of -// undoing various operations if we fail midway. -static LibError get_ver_impl(const char* module_path, char* out_ver, size_t out_ver_len, u8*& mem) +static LibError ReadVersionString(const OsPath& modulePathname_, char* out_ver, size_t out_ver_len) { + WIN_SAVE_LAST_ERROR; // GetFileVersion*, Ver* + WinScopedDisableWow64Redirection noRedirect; + + const std::string modulePathname = modulePathname_.external_file_string(); + #ifndef NDEBUG // make sure the file exists (rules out that problem as a cause of // GetFileVersionInfoSize failing, since it doesn't SetLastError) - HMODULE hModule = LoadLibraryEx(module_path, 0, LOAD_LIBRARY_AS_DATAFILE); + HMODULE hModule = LoadLibraryEx(modulePathname.c_str(), 0, LOAD_LIBRARY_AS_DATAFILE); debug_assert(hModule != 0); FreeLibrary(hModule); #endif // determine size of and allocate memory for version information. DWORD unused; - const DWORD ver_size = GetFileVersionInfoSize(module_path, &unused); + const DWORD ver_size = GetFileVersionInfoSize(modulePathname.c_str(), &unused); if(!ver_size) WARN_RETURN(ERR::FAIL); - mem = new u8[ver_size]; - if(!GetFileVersionInfo(module_path, 0, ver_size, mem)) + shared_ptr mem = io_Allocate(ver_size); + if(!GetFileVersionInfo(modulePathname.c_str(), 0, ver_size, mem.get())) WARN_RETURN(ERR::FAIL); u16* lang; // -> 16 bit language ID, 16 bit codepage uint lang_len; - const BOOL ok = VerQueryValue(mem, "\\VarFileInfo\\Translation", (void**)&lang, &lang_len); + const BOOL ok = VerQueryValue(mem.get(), "\\VarFileInfo\\Translation", (void**)&lang, &lang_len); if(!ok || !lang || lang_len != 4) WARN_RETURN(ERR::FAIL); @@ -57,34 +61,13 @@ static LibError get_ver_impl(const char* module_path, char* out_ver, size_t out_ sprintf(subblock, "\\StringFileInfo\\%04X%04X\\FileVersion", lang[0], lang[1]); const char* in_ver; uint in_ver_len; - if(!VerQueryValue(mem, subblock, (void**)&in_ver, &in_ver_len)) + if(!VerQueryValue(mem.get(), subblock, (void**)&in_ver, &in_ver_len)) WARN_RETURN(ERR::FAIL); strcpy_s(out_ver, out_ver_len, in_ver); - return INFO::OK; -} -// get version information for the specified DLL. -static LibError get_ver(const char* module_path, char* out_ver, size_t out_ver_len) -{ - LibError ret; - - WIN_SAVE_LAST_ERROR; // GetFileVersion*, Ver* - { - PVOID wasRedirectionEnabled; - wutil_DisableWow64Redirection(wasRedirectionEnabled); - { - u8* mem = 0; - ret = get_ver_impl(module_path, out_ver, out_ver_len, mem); - delete[] mem; - } - wutil_RevertWow64Redirection(wasRedirectionEnabled); - } WIN_RESTORE_LAST_ERROR; - - if(ret != INFO::OK) - out_ver[0] = '\0'; - return ret; + return INFO::OK; } // @@ -108,41 +91,35 @@ void wdll_ver_list_init(char* buf, size_t chars) // name should preferably be the complete path to DLL, to make sure // we don't inadvertently load another one on the library search path. // we add the .dll extension if necessary. -LibError wdll_ver_list_add(const char* name) +LibError wdll_ver_list_add(const OsPath& pathname) { // not be called before wdll_ver_list_init or after failure if(!ver_list_pos) WARN_RETURN(ERR::LOGIC); - // some driver names are stored in the registry without .dll extension. - // if necessary, copy to new buffer and add it there. - // note: do not change extension if present; some drivers have a - // ".sys" extension, so always appending ".dll" is incorrect. - char buf[MAX_PATH]; - const char* dll_name = name; - const char* ext = path_extension(name); - if(ext[0] == '\0') // no extension - { - snprintf(buf, ARRAY_SIZE(buf), "%s.dll", name); - dll_name = buf; - } + // pathname may not have an extension (e.g. driver names from the + // registry). note that always appending ".dll" would be incorrect + // since some have ".sys" extension. + OsPath modulePathname(pathname); + if(fs::extension(modulePathname).empty()) + modulePathname = fs::change_extension(modulePathname, ".dll"); // read file version. - char dll_ver[500] = "unknown"; // enclosed in () below - (void)get_ver(dll_name, dll_ver, sizeof(dll_ver)); - // if this fails, default is already set and we don't want to abort. + // (note: we can ignore the return value since the default + // text has already been set) + char versionString[500] = "unknown"; // enclosed in () below + (void)ReadVersionString(modulePathname, versionString, ARRAY_SIZE(versionString)); + // reserve enough room for subsequent comma and "..." strings. const ssize_t max_chars_to_write = (ssize_t)ver_list_chars - (ver_list_pos-ver_list_buf) - 10; - // reserves enough room for subsequent comma and "..." strings. - + // not first time: prepend comma to string (room was reserved above). if(ver_list_pos != ver_list_buf) ver_list_pos += sprintf(ver_list_pos, ", "); // extract filename. - const char* dll_fn = path_name_only(dll_name); - - int len = snprintf(ver_list_pos, max_chars_to_write, "%s (%s)", dll_fn, dll_ver); + const std::string moduleName(modulePathname.leaf()); + int len = snprintf(ver_list_pos, max_chars_to_write, "%s (%s)", moduleName.c_str(), versionString); // success if(len > 0) { diff --git a/source/lib/sysdep/win/wdll_ver.h b/source/lib/sysdep/win/wdll_ver.h index 4052695701..c236fbb312 100644 --- a/source/lib/sysdep/win/wdll_ver.h +++ b/source/lib/sysdep/win/wdll_ver.h @@ -11,6 +11,8 @@ #ifndef INCLUDED_WDLL_VER #define INCLUDED_WDLL_VER +#include "lib/os_path.h" + // WARNING: not re-entrant or thread-safe! // set output buffer into which DLL names and their versions will be written. @@ -21,6 +23,6 @@ extern void wdll_ver_list_init(char* buf, size_t chars); // name should preferably be the complete path to DLL, to make sure // we don't inadvertently load another one on the library search path. // we add the .dll extension if necessary. -extern LibError wdll_ver_list_add(const char* name); +extern LibError wdll_ver_list_add(const OsPath& name); #endif // #ifndef INCLUDED_WDLL_VER diff --git a/source/lib/sysdep/win/wsdl.cpp b/source/lib/sysdep/win/wsdl.cpp index ec9d4e8266..3f08de1bc6 100644 --- a/source/lib/sysdep/win/wsdl.cpp +++ b/source/lib/sysdep/win/wsdl.cpp @@ -71,7 +71,7 @@ static u16 cur_ramp[3][256]; // ramp: 8.8 fixed point -static LibError calc_gamma_ramp(float gamma, u16* ramp) +static void calc_gamma_ramp(float gamma, u16* ramp) { // assume identity if invalid if(gamma <= 0.0f) @@ -82,7 +82,7 @@ static LibError calc_gamma_ramp(float gamma, u16* ramp) { for(u16 i = 0; i < 256; i++) ramp[i] = (i << 8); - return INFO::OK; + return; } const double inv_gamma = 1.0 / gamma; @@ -94,8 +94,6 @@ static LibError calc_gamma_ramp(float gamma, u16* ramp) // need a temp variable to disambiguate pow() argument type. ramp[i] = u16_from_double(pow(frac, inv_gamma)); } - - return INFO::OK; } @@ -124,11 +122,9 @@ int SDL_SetGamma(float r, float g, float b) if(!GetDeviceGammaRamp(hDC, org_ramp)) return -1; - LibError err1 = calc_gamma_ramp(r, cur_ramp[0]); - LibError err2 = calc_gamma_ramp(g, cur_ramp[1]); - LibError err3 = calc_gamma_ramp(b, cur_ramp[2]); - if(err1 != INFO::OK || err2 != INFO::OK || err3 != INFO::OK) - return -1; + calc_gamma_ramp(r, cur_ramp[0]); + calc_gamma_ramp(g, cur_ramp[1]); + calc_gamma_ramp(b, cur_ramp[2]); if(!SetDeviceGammaRamp(hDC, cur_ramp)) return -1; diff --git a/source/lib/sysdep/win/wsnd.cpp b/source/lib/sysdep/win/wsnd.cpp index 3ad8b7947d..973797cd3a 100644 --- a/source/lib/sysdep/win/wsnd.cpp +++ b/source/lib/sysdep/win/wsnd.cpp @@ -17,72 +17,45 @@ #include #include "lib/path_util.h" -#include "wposix/wfilesystem.h" // see add_oal_dlls_in_dir #include "wdll_ver.h" #include "win.h" #include "wutil.h" #include "wmi.h" -// indicate if this filename corresponds to the OpenAL DLL name format. -// (matches "*oal.dll" and "*OpenAL*", as with OpenAL router's search) -static LibError IsOpenAlDllName(const char* name) +static LibError IsOpenAlDllName(const std::string& name) { - const size_t len = strlen(name); - - if(len >= 7 && !strcasecmp(name+len-7, "oal.dll")) - return true; - - if(strstr(name, "OpenAL") != 0) - return true; - - return false; + // (matches "*oal.dll" and "*OpenAL*", as with OpenAL router's search) + return name.find("oal.dll") != std::string::npos || name.find("OpenAL") != std::string::npos; } // ensures each OpenAL DLL is only listed once (even if present in several // directories on our search path). typedef std::set StringSet; -// find all OpenAL DLLs in a dir (via readdir and IsOpenAlDll). +// find all OpenAL DLLs in a dir. // call in library search order (exe dir, then win sys dir); otherwise, // DLLs in the executable's starting directory hide those of the // same name in the system directory. -static LibError add_oal_dlls_in_dir(const char* path, StringSet* dlls) +static void add_oal_dlls_in_dir(const OsPath& path, StringSet& dlls) { - // note: wdll_ver_list_add requires the full DLL path but readdir only - // gives us the name. for efficiency, we append this via PathPackage. - PathPackage pp; - RETURN_ERR(path_package_set_dir(&pp, path)); - - // note: we can't use the dir_open/DirIterator interface because it - // expects a portable (relative) path and is absolute. using - // POSIX opendir is slightly more complex but works. - errno = 0; - DIR* dir = opendir(path); - if(!dir) - return LibError_from_errno(); - - for(;;) // instead of while to avoid warning + for(OsDirectoryIterator it(path); it != OsDirectoryIterator(); ++it) { - dirent* ent = readdir(dir); - if(!ent) - break; + if(!fs::is_regular(it->status())) + continue; - if(!IsOpenAlDllName(ent->d_name)) + const OsPath& pathname = it->path(); + const std::string& name = pathname.leaf(); + if(!IsOpenAlDllName(name)) continue; // already in StringSet (i.e. has already been wdll_ver_list_add-ed) - std::pair ret = dlls->insert(ent->d_name); + std::pair ret = dlls.insert(name); if(!ret.second) // insert failed - element already there continue; - (void)path_package_append_file(&pp, ent->d_name); - (void)wdll_ver_list_add(pp.path); + (void)wdll_ver_list_add(pathname); } - - int ret = closedir(dir); - debug_assert(ret == 0); - return INFO::OK; } @@ -149,7 +122,7 @@ LibError win_get_snd_info() if(wutil_WindowsVersion() < WUTIL_VERSION_VISTA) (void)wdll_ver_list_add(GetDirectSoundDriverPath()); StringSet dlls; // ensures uniqueness - (void)add_oal_dlls_in_dir(win_exe_dir, &dlls); - (void)add_oal_dlls_in_dir(win_sys_dir, &dlls); + (void)add_oal_dlls_in_dir(win_exe_dir, dlls); + (void)add_oal_dlls_in_dir(win_sys_dir, dlls); return INFO::OK; } diff --git a/source/lib/sysdep/win/wutil.cpp b/source/lib/sysdep/win/wutil.cpp index 0587274827..f43de64be6 100644 --- a/source/lib/sysdep/win/wutil.cpp +++ b/source/lib/sysdep/win/wutil.cpp @@ -398,7 +398,8 @@ bool wutil_IsWow64() return isWow64; } -void wutil_DisableWow64Redirection(void*& wasRedirectionEnabled) + +WinScopedDisableWow64Redirection::WinScopedDisableWow64Redirection() { // note: don't just check if the function pointers are valid. 32-bit // Vista includes them but isn't running Wow64, so calling the functions @@ -406,15 +407,15 @@ void wutil_DisableWow64Redirection(void*& wasRedirectionEnabled) // more need to verify the pointers (their existence is implied). if(!wutil_IsWow64()) return; - BOOL ok = pWow64DisableWow64FsRedirection(&wasRedirectionEnabled); + BOOL ok = pWow64DisableWow64FsRedirection(&m_wasRedirectionEnabled); WARN_IF_FALSE(ok); } -void wutil_RevertWow64Redirection(void* wasRedirectionEnabled) +WinScopedDisableWow64Redirection::~WinScopedDisableWow64Redirection() { if(!wutil_IsWow64()) return; - BOOL ok = pWow64RevertWow64FsRedirection(wasRedirectionEnabled); + BOOL ok = pWow64RevertWow64FsRedirection(m_wasRedirectionEnabled); WARN_IF_FALSE(ok); } diff --git a/source/lib/sysdep/win/wutil.h b/source/lib/sysdep/win/wutil.h index a28945eb00..3288ba83c2 100644 --- a/source/lib/sysdep/win/wutil.h +++ b/source/lib/sysdep/win/wutil.h @@ -142,8 +142,16 @@ extern uint wutil_WindowsVersion(); // extern bool wutil_IsWow64(); -extern void wutil_DisableWow64Redirection(void*& wasRedirectionEnabled); -extern void wutil_RevertWow64Redirection(void* wasRedirectionEnabled); + +class WinScopedDisableWow64Redirection +{ +public: + WinScopedDisableWow64Redirection(); + ~WinScopedDisableWow64Redirection(); + +private: + void* m_wasRedirectionEnabled; +}; /** diff --git a/source/lib/tests/test_path_util.h b/source/lib/tests/test_path_util.h index 02a409b086..1cce95040f 100644 --- a/source/lib/tests/test_path_util.h +++ b/source/lib/tests/test_path_util.h @@ -49,16 +49,6 @@ class TestPathUtil : public CxxTest::TestSuite TS_ASSERT_STR_EQUALS(result, correct_result); } - void TEST_PATH_PACKAGE(const char* path, const char* fn, - const char* correct_result) - { - PathPackage pp; - TS_ASSERT_OK(path_package_set_dir(&pp, path)); - TS_ASSERT_OK(path_package_append_file(&pp, fn)); - TS_ASSERT_STR_EQUALS(pp.path, correct_result); - } - - public: void test_subpath() @@ -195,22 +185,4 @@ public: TEST_PATH_EXT("c", ""); // no extension TEST_PATH_EXT("", ""); // empty } - - // testing path_foreach_component is difficult; currently skipped. - - void test_path_package() - { - // normal - TEST_PATH_PACKAGE("a/b", "c", "a/b/c"); - // nonportable slash - TEST_PATH_PACKAGE("a\\b", "c", "a\\b/c"); - // slash already present - TEST_PATH_PACKAGE("a/b/", "c", "a/b/c"); - // nonportable slash already present - TEST_PATH_PACKAGE("a\\b\\", "c", "a\\b\\c"); - // mixed slashes - TEST_PATH_PACKAGE("a/b\\c", "d", "a/b\\c/d"); - // mixed slashes (2) - TEST_PATH_PACKAGE("a\\b/c", "d", "a\\b/c/d"); - } }; diff --git a/source/lib/tex/tex.cpp b/source/lib/tex/tex.cpp index 84923d9d14..aaa7ed38f7 100644 --- a/source/lib/tex/tex.cpp +++ b/source/lib/tex/tex.cpp @@ -20,10 +20,6 @@ #include "tex_codec.h" -#include "lib/file/vfs/vfs.h" -#include "lib/file/io/io_internal.h" -extern PIVFS g_VFS; - ERROR_ASSOCIATE(ERR::TEX_FMT_INVALID, "Invalid/unsupported texture format", -1); ERROR_ASSOCIATE(ERR::TEX_INVALID_COLOR_TYPE, "Invalid color type", -1); @@ -479,7 +475,8 @@ bool tex_is_known_extension(const char* filename) { const TexCodecVTbl* dummy; // found codec for it => known extension - if(tex_codec_for_filename(filename, &dummy) == INFO::OK) + const std::string extension = fs::extension(filename); + if(tex_codec_for_filename(extension, &dummy) == INFO::OK) return true; return false; @@ -504,7 +501,7 @@ LibError tex_wrap(uint w, uint h, uint bpp, uint flags, shared_ptr data, siz t->bpp = bpp; t->flags = flags; t->data = data; - t->dataSize = w * h * bpp / 8; + t->dataSize = ofs + w*h*bpp/8; t->ofs = ofs; CHECK_TEX(t); @@ -577,7 +574,9 @@ size_t tex_img_size(const Tex* t) size_t tex_hdr_size(const char* fn) { const TexCodecVTbl* c; - CHECK_ERR(tex_codec_for_filename(fn, &c)); + + const std::string extension = fs::extension(fn); + CHECK_ERR(tex_codec_for_filename(extension, &c)); return c->hdr_size(0); } @@ -622,11 +621,13 @@ LibError tex_decode(shared_ptr data, size_t data_size, Tex* t) flip_to_global_orientation(t); + CHECK_TEX(t); + return INFO::OK; } -LibError tex_encode(Tex* t, const char* fn, DynArray* da) +LibError tex_encode(Tex* t, const std::string& extension, DynArray* da) { CHECK_TEX(t); CHECK_ERR(tex_validate_plain_format(t->bpp, t->flags)); @@ -640,7 +641,7 @@ LibError tex_encode(Tex* t, const char* fn, DynArray* da) RETURN_ERR(da_alloc(da, max_out_size)); const TexCodecVTbl* c; - CHECK_ERR(tex_codec_for_filename(fn, &c)); + CHECK_ERR(tex_codec_for_filename(extension, &c)); // encode into LibError err = c->encode(t, da); @@ -652,51 +653,3 @@ LibError tex_encode(Tex* t, const char* fn, DynArray* da) return INFO::OK; } - - -// load the specified image from file into the given Tex object. -// currently supports BMP, TGA, JPG, JP2, PNG, DDS. -LibError tex_load(const char* fn, Tex* t) -{ - // load file - shared_ptr file; size_t file_size; - RETURN_ERR(g_VFS->LoadFile(fn, file, file_size)); - - LibError ret = tex_decode(file, file_size, t); - if(ret < 0) - { - tex_free(t); - RETURN_ERR(ret); - } - - // do not free data! it either still holds the image data (i.e. texture - // wasn't compressed) or was replaced by a new buffer for the image data. - - CHECK_TEX(t); - return INFO::OK; -} - - -// write the specified texture to disk. -// note: cannot be made const because the image may have to be -// transformed to write it out in the format determined by 's extension. -LibError tex_write(Tex* t, const char* fn) -{ - DynArray da; - RETURN_ERR(tex_encode(t, fn, &da)); - - // write to disk - LibError ret = INFO::OK; - { - (void)da_set_size(&da, round_up(da.cur_size, BLOCK_SIZE)); - shared_ptr file(da.base, DummyDeleter()); - const ssize_t bytes_written = g_VFS->CreateFile(fn, file, da.pos); - if(bytes_written > 0) - debug_assert(bytes_written == (ssize_t)da.pos); - else - ret = (LibError)bytes_written; - } - - (void)da_free(&da); - return ret; -} diff --git a/source/lib/tex/tex.h b/source/lib/tex/tex.h index 4fc7501cb6..d40b68c1ed 100644 --- a/source/lib/tex/tex.h +++ b/source/lib/tex/tex.h @@ -233,10 +233,6 @@ extern LibError tex_validate(const Tex* t); extern void tex_set_global_orientation(int orientation); -// -// open/close -// - /** * manually register codecs. must be called before first use of a * codec (e.g. loading a texture). @@ -249,16 +245,28 @@ extern void tex_set_global_orientation(int orientation); extern void tex_codec_register_all(); /** - * load the specified image from file into a Tex object. + * decode an in-memory texture file into texture object. * * FYI, currently BMP, TGA, JPG, JP2, PNG, DDS are supported - but don't * rely on this (not all codecs may be included). * - * @param fn filename - * @param t output texture object + * @param data input data + * @param data_size its size [bytes] + * @param t output texture object. + * @return LibError. + **/ +extern LibError tex_decode(shared_ptr data, size_t data_size, Tex* t); + +/** + * encode a texture into a memory buffer in the desired file format. + * + * @param t input texture object + * @param extension (including '.') + * @param da output memory array. allocated here; caller must free it + * when no longer needed. invalid unless function succeeds. * @return LibError **/ -extern LibError tex_load(const char* fn, Tex* t); +extern LibError tex_encode(Tex* t, const std::string& extension, DynArray* da); /** * store the given image data into a Tex object; this will be as if @@ -420,14 +428,4 @@ extern bool tex_is_known_extension(const char* filename); **/ extern size_t tex_hdr_size(const char* fn); -/** - * write the specified texture to disk. - * - * @param t input texture object. note: cannot be made const because the - * image may have to be transformed to write it out in the format - * determined by 's extension. - * @return LibError - **/ -extern LibError tex_write(Tex* t, const char* fn); - #endif // INCLUDED_TEX diff --git a/source/lib/tex/tex_bmp.cpp b/source/lib/tex/tex_bmp.cpp index c1c4591c06..d750078d26 100644 --- a/source/lib/tex/tex_bmp.cpp +++ b/source/lib/tex/tex_bmp.cpp @@ -58,9 +58,9 @@ static bool bmp_is_hdr(const u8* file) } -static bool bmp_is_ext(const char* ext) +static bool bmp_is_ext(const std::string& extension) { - return !strcasecmp(ext, "bmp"); + return !strcasecmp(extension.c_str(), ".bmp"); } diff --git a/source/lib/tex/tex_codec.cpp b/source/lib/tex/tex_codec.cpp index 21a795163e..a15ae2a210 100644 --- a/source/lib/tex/tex_codec.cpp +++ b/source/lib/tex/tex_codec.cpp @@ -41,13 +41,12 @@ int tex_codec_register(TexCodecVTbl* c) // or return ERR::TEX_UNKNOWN_FORMAT if unknown. // note: does not raise a warning because it is used by // tex_is_known_extension. -LibError tex_codec_for_filename(const char* fn, const TexCodecVTbl** c) +LibError tex_codec_for_filename(const std::string& extension, const TexCodecVTbl** c) { - const char* ext = path_extension(fn); for(*c = codecs; *c; *c = (*c)->next) { // we found it - if((*c)->is_ext(ext)) + if((*c)->is_ext(extension)) return INFO::OK; } @@ -131,12 +130,11 @@ void tex_codec_register_all() // // note: we don't allocate the data param ourselves because this function is // needed for encoding, too (where data is already present). -LibError tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch, - uint src_flags, uint dst_orientation, RowArray& rows) +shared_ptr tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch, uint src_flags, uint dst_orientation) { const bool flip = !tex_orientations_match(src_flags, dst_orientation); - rows = new RowPtr[h]; + shared_ptr rows(new RowPtr[h]); // determine start position and direction RowPtr pos = flip? data+pitch*(h-1) : data; @@ -145,12 +143,12 @@ LibError tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch, for(size_t i = 0; i < h; i++) { - rows[i] = pos; + rows.get()[i] = pos; pos += add; } debug_assert(pos == end); - return INFO::OK; + return rows; } diff --git a/source/lib/tex/tex_codec.h b/source/lib/tex/tex_codec.h index e6dd6576e5..3dc8be48ee 100644 --- a/source/lib/tex/tex_codec.h +++ b/source/lib/tex/tex_codec.h @@ -77,11 +77,10 @@ struct TexCodecVTbl * caller compare it (-> smaller code) because a codec's file format * may have several valid extensions (e.g. jpg and jpeg). * - * @param ext non-NULL extension string; does not contain '.'. - * must be compared as case-insensitive. + * @param extension (including '.') * @return bool **/ - bool (*is_ext)(const char* ext); + bool (*is_ext)(const std::string& extension); /** * return size of the file header supported by this codec. @@ -149,14 +148,12 @@ extern int tex_codec_register(TexCodecVTbl* c); /** * find codec that recognizes the desired output file extension. * - * @param fn filename; only the extension (that after '.') is used. - * case-insensitive. * @param c (out) vtbl of responsible codec * @return LibError; ERR::RES_UNKNOWN_FORMAT (without warning, because this is * called by tex_is_known_extension) if no codec indicates they can * handle the given extension. **/ -extern LibError tex_codec_for_filename(const char* fn, const TexCodecVTbl** c); +extern LibError tex_codec_for_filename(const std::string& extension, const TexCodecVTbl** c); /** * find codec that recognizes the header's magic field. @@ -215,9 +212,7 @@ extern LibError tex_codec_transform(Tex* t, uint transforms); * @return LibError **/ typedef const u8* RowPtr; -typedef RowPtr* RowArray; -extern LibError tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch, - uint src_flags, uint dst_orientation, RowArray& rows); +extern shared_ptr tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch, uint src_flags, uint dst_orientation); /** * apply transforms and then copy header and image into output buffer. diff --git a/source/lib/tex/tex_dds.cpp b/source/lib/tex/tex_dds.cpp index 1ec0e3c633..bd379443cb 100644 --- a/source/lib/tex/tex_dds.cpp +++ b/source/lib/tex/tex_dds.cpp @@ -589,9 +589,9 @@ static bool dds_is_hdr(const u8* file) } -static bool dds_is_ext(const char* ext) +static bool dds_is_ext(const std::string& extension) { - return !strcasecmp(ext, "dds"); + return !strcasecmp(extension.c_str(), ".dds"); } diff --git a/source/lib/tex/tex_internal.h b/source/lib/tex/tex_internal.h index 660d5adb42..1d2d54df94 100644 --- a/source/lib/tex/tex_internal.h +++ b/source/lib/tex/tex_internal.h @@ -41,30 +41,4 @@ extern LibError tex_validate_plain_format(uint bpp, uint flags); **/ extern bool tex_orientations_match(uint src_flags, uint dst_orientation); - -/** - * decode an in-memory texture file into texture object. - * - * split out of tex_load to ease resource cleanup and allow - * decoding images without needing to write out to disk. - * - * @param data input data - * @param data_size its size [bytes] - * @param t output texture object. - * @return LibError. - **/ -extern LibError tex_decode(const u8* data, size_t data_size, Tex* t); - -/** - * encode a texture into a memory buffer in the desired file format. - * - * @param t input texture object - * @param fn filename; only used to determine the desired file format - * (via extension) - * @param da output memory array. allocated here; caller must free it - * when no longer needed. invalid unless function succeeds. - * @return LibError - **/ -extern LibError tex_encode(Tex* t, const char* fn, DynArray* da); - #endif // #ifndef INCLUDED_TEX_INTERNAL diff --git a/source/lib/tex/tex_jpg.cpp b/source/lib/tex/tex_jpg.cpp index 936943d8be..69db3050a2 100644 --- a/source/lib/tex/tex_jpg.cpp +++ b/source/lib/tex/tex_jpg.cpp @@ -409,7 +409,7 @@ static LibError jpg_transform(Tex* UNUSED(t), uint UNUSED(transforms)) // due to less copying. -static LibError jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, RowArray& rows, Tex* t) +static LibError jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, Tex* t) { src_prepare(cinfo, da); @@ -449,10 +449,10 @@ static LibError jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, Row shared_ptr data = io_Allocate(img_size); // read rows - RETURN_ERR(tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0, rows)); + shared_ptr rows = tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0); // could use cinfo->output_scanline to keep track of progress, // but we need to count lines_left anyway (paranoia). - JSAMPARRAY row = (JSAMPARRAY)rows; + JSAMPARRAY row = (JSAMPARRAY)rows.get(); JDIMENSION lines_left = h; while(lines_left != 0) { @@ -467,8 +467,6 @@ static LibError jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, Row // mem data source. (void)jpeg_finish_decompress(cinfo); - delete[] rows; - LibError ret = INFO::OK; if(cinfo->err->num_warnings != 0) ret = WARN::TEX_INVALID_DATA; @@ -486,7 +484,7 @@ static LibError jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, Row } -static LibError jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, RowArray& rows, DynArray* da) +static LibError jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, DynArray* da) { dst_prepare(cinfo, da); @@ -509,11 +507,11 @@ static LibError jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, RowArray& r const size_t pitch = t->w * t->bpp / 8; u8* data = tex_get_data(t); - RETURN_ERR(tex_codec_alloc_rows(data, t->h, pitch, t->flags, TEX_TOP_DOWN, rows)); + shared_ptr rows = tex_codec_alloc_rows(data, t->h, pitch, t->flags, TEX_TOP_DOWN); // could use cinfo->output_scanline to keep track of progress, // but we need to count lines_left anyway (paranoia). - JSAMPARRAY row = (JSAMPARRAY)rows; + JSAMPARRAY row = (JSAMPARRAY)rows.get(); JDIMENSION lines_left = t->h; while(lines_left != 0) { @@ -526,8 +524,6 @@ static LibError jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, RowArray& r jpeg_finish_compress(cinfo); - delete[] rows; - LibError ret = INFO::OK; if(cinfo->err->num_warnings != 0) ret = WARN::TEX_INVALID_DATA; @@ -545,9 +541,9 @@ static bool jpg_is_hdr(const u8* file) } -static bool jpg_is_ext(const char* ext) +static bool jpg_is_ext(const std::string& extension) { - return !strcasecmp(ext, "jpg") || !strcasecmp(ext, "jpeg"); + return !strcasecmp(extension.c_str(), ".jpg") || !strcasecmp(extension.c_str(), ".jpeg"); } @@ -569,10 +565,7 @@ static LibError jpg_decode(DynArray* RESTRICT da, Tex* RESTRICT t) jpeg_create_decompress(&cinfo); - // array of pointers to scanlines (see rationale above) - RowArray rows = 0; - LibError ret = jpg_decode_impl(da, &cinfo, rows, t); - free(rows); + LibError ret = jpg_decode_impl(da, &cinfo, t); jpeg_destroy_decompress(&cinfo); // releases a "good deal" of memory @@ -593,10 +586,7 @@ static LibError jpg_encode(Tex* RESTRICT t, DynArray* RESTRICT da) jpeg_create_compress(&cinfo); - // array of pointers to scanlines (see rationale above) - RowArray rows = 0; - LibError ret = jpg_encode_impl(t, &cinfo, rows, da); - free(rows); + LibError ret = jpg_encode_impl(t, &cinfo, da); jpeg_destroy_compress(&cinfo); // releases a "good deal" of memory diff --git a/source/lib/tex/tex_png.cpp b/source/lib/tex/tex_png.cpp index 16f92701a2..30a2f172a0 100644 --- a/source/lib/tex/tex_png.cpp +++ b/source/lib/tex/tex_png.cpp @@ -70,7 +70,7 @@ static LibError png_transform(Tex* UNUSED(t), uint UNUSED(transforms)) // split out of png_decode to simplify resource cleanup and avoid // "dtor / setjmp interaction" warning. -static LibError png_decode_impl(DynArray* da, png_structp png_ptr, png_infop info_ptr, RowArray& rows, Tex* t) +static LibError png_decode_impl(DynArray* da, png_structp png_ptr, png_infop info_ptr, Tex* t) { png_set_read_fn(png_ptr, da, io_read); @@ -97,11 +97,9 @@ static LibError png_decode_impl(DynArray* da, png_structp png_ptr, png_infop inf const size_t img_size = pitch * h; shared_ptr data = io_Allocate(img_size); - RETURN_ERR(tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0, rows)); - - png_read_image(png_ptr, (png_bytepp)rows); + shared_ptr rows = tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0); + png_read_image(png_ptr, (png_bytepp)rows.get()); png_read_end(png_ptr, info_ptr); - delete[] rows; // success; make sure all data was consumed. debug_assert(da->pos == da->cur_size); @@ -121,7 +119,7 @@ static LibError png_decode_impl(DynArray* da, png_structp png_ptr, png_infop inf // split out of png_encode to simplify resource cleanup and avoid // "dtor / setjmp interaction" warning. -static LibError png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, RowArray& rows, DynArray* da) +static LibError png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, DynArray* da) { const png_uint_32 w = t->w, h = t->h; const size_t pitch = w * t->bpp / 8; @@ -148,14 +146,13 @@ static LibError png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); u8* data = tex_get_data(t); - RETURN_ERR(tex_codec_alloc_rows(data, h, pitch, t->flags, TEX_TOP_DOWN, rows)); + shared_ptr rows = tex_codec_alloc_rows(data, h, pitch, t->flags, TEX_TOP_DOWN); // PNG is native RGB. const int png_transforms = (t->flags & TEX_BGR)? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY; - png_set_rows(png_ptr, info_ptr, (png_bytepp)rows); + png_set_rows(png_ptr, info_ptr, (png_bytepp)rows.get()); png_write_png(png_ptr, info_ptr, png_transforms, 0); - delete[] rows; return INFO::OK; } @@ -170,9 +167,9 @@ static bool png_is_hdr(const u8* file) } -static bool png_is_ext(const char* ext) +static bool png_is_ext(const std::string& extension) { - return !strcasecmp(ext, "png"); + return !strcasecmp(extension.c_str(), ".png"); } @@ -206,9 +203,7 @@ TIMER_ACCRUE(tc_png_decode); goto fail; } - RowArray rows = 0; - ret = png_decode_impl(da, png_ptr, info_ptr, rows, t); - free(rows); + ret = png_decode_impl(da, png_ptr, info_ptr, t); fail: png_destroy_read_struct(&png_ptr, &info_ptr, 0); @@ -238,9 +233,7 @@ static LibError png_encode(Tex* RESTRICT t, DynArray* RESTRICT da) goto fail; } - RowArray rows = 0; - ret = png_encode_impl(t, png_ptr, info_ptr, rows, da); - free(rows); + ret = png_encode_impl(t, png_ptr, info_ptr, da); // shared cleanup fail: diff --git a/source/lib/tex/tex_tga.cpp b/source/lib/tex/tex_tga.cpp index 87a00479a4..98545e6cd2 100644 --- a/source/lib/tex/tex_tga.cpp +++ b/source/lib/tex/tex_tga.cpp @@ -77,9 +77,9 @@ static bool tga_is_hdr(const u8* file) } -static bool tga_is_ext(const char* ext) +static bool tga_is_ext(const std::string& extension) { - return !strcasecmp(ext, "tga"); + return !strcasecmp(extension.c_str(), ".tga"); } diff --git a/source/ps/Filesystem.h b/source/ps/Filesystem.h index 1221feacd3..1e14640b9c 100644 --- a/source/ps/Filesystem.h +++ b/source/ps/Filesystem.h @@ -2,10 +2,10 @@ #define INCLUDED_FILESYSTEM #include "lib/path_util.h" -#include "lib/file/path.h" +#include "lib/file/file.h" +#include "lib/file/io/io.h" #include "lib/file/vfs/vfs.h" #include "lib/file/file_system_util.h" -#include "lib/file/io/io.h" #include "lib/file/io/write_buffer.h" #include "ps/CStr.h" diff --git a/source/ps/Util.cpp b/source/ps/Util.cpp index 52ce4858d1..dc631a267d 100644 --- a/source/ps/Util.cpp +++ b/source/ps/Util.cpp @@ -4,10 +4,13 @@ #include "lib/posix/posix_sock.h" #include "lib/ogl.h" #include "lib/timer.h" +#include "lib/bits.h" // round_up +#include "lib/allocators/allocators.h" #include "lib/sysdep/gfx.h" #include "lib/sysdep/snd.h" #include "lib/sysdep/cpu.h" #include "lib/tex/tex.h" +#include "lib/file/io/io_internal.h" // BLOCK_SIZE #include "ps/GameSetup/Config.h" #include "ps/GameSetup/GameSetup.h" @@ -163,6 +166,34 @@ const wchar_t* ErrorString(int err) } + +// write the specified texture to disk. +// note: cannot be made const because the image may have to be +// transformed to write it out in the format determined by 's extension. +static LibError tex_write(Tex* t, const char* fn) +{ + const std::string extension = fs::extension(fn); + + DynArray da; + RETURN_ERR(tex_encode(t, extension, &da)); + + // write to disk + LibError ret = INFO::OK; + { + (void)da_set_size(&da, round_up(da.cur_size, BLOCK_SIZE)); + shared_ptr file(da.base, DummyDeleter()); + const ssize_t bytes_written = g_VFS->CreateFile(fn, file, da.pos); + if(bytes_written > 0) + debug_assert(bytes_written == (ssize_t)da.pos); + else + ret = (LibError)bytes_written; + } + + (void)da_free(&da); + return ret; +} + + static unsigned s_nextScreenshotNumber; // identifies the file format that is to be written diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index 7d962e1be7..d31850c7a7 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -13,6 +13,7 @@ #include #include #include "lib/bits.h" // is_pow2 +#include "lib/res/graphics/ogl_tex.h" #include "Renderer.h" #include "maths/Matrix3D.h" #include "maths/MathUtil.h" @@ -20,6 +21,7 @@ #include "ps/CLogger.h" #include "ps/Game.h" #include "ps/Profile.h" +#include "ps/Filesystem.h" #include "ps/Game.h" #include "ps/World.h" #include "ps/Player.h" @@ -27,11 +29,6 @@ #include "ps/ProfileViewer.h" #include "simulation/LOSManager.h" #include "simulation/TerritoryManager.h" - -#include "lib/path_util.h" -#include "lib/file/io/io.h" // io_Allocate -#include "lib/res/graphics/ogl_tex.h" - #include "graphics/Camera.h" #include "graphics/Texture.h" #include "graphics/LightEnv.h" @@ -1530,8 +1527,7 @@ int CRenderer::LoadAlphaMaps() // load all textures and store Handle in array // Handle textures[NumAlphaMaps] = {0}; - PathPackage pp; - (void)path_package_set_dir(&pp, "art/textures/terrain/alphamaps/special"); + VfsPath path("art/textures/terrain/alphamaps/special"); const char* fnames[NumAlphaMaps] = { "blendcircle.dds", "blendlshape.dds", @@ -1554,10 +1550,9 @@ int CRenderer::LoadAlphaMaps() uint bpp = 0; for(uint i=0;i buf; size_t size; - if(g_VFS->LoadFile(fn, buf, size) != INFO::OK) // ERRTODO: translate/pass it on + if(g_VFS->LoadFile(filename, buf, size) != INFO::OK) // ERRTODO: translate/pass it on throw PSERROR_Scripting_LoadFile_OpenFailed(); const char* script = (const char*)buf.get(); - RunMemScript(script, size, fn, 1, globalObject); + RunMemScript(script, size, filename.c_str(), 1, globalObject); } jsval ScriptingHost::CallFunction(const std::string & functionName, jsval * params, int numParams) diff --git a/source/simulation/EntityTemplateCollection.cpp b/source/simulation/EntityTemplateCollection.cpp index 0c9e438433..21d38d690e 100644 --- a/source/simulation/EntityTemplateCollection.cpp +++ b/source/simulation/EntityTemplateCollection.cpp @@ -10,13 +10,16 @@ #define LOG_CATEGORY "entity" + +#include + void CEntityTemplateCollection::LoadFile( const VfsPath& pathname ) { // Build the entity name -> filename mapping. This is done so that // the entity 'x' can be in units/x.xml, structures/x.xml, etc, and // we don't have to search every directory for x.xml. - CStrW basename(Basename(pathname)); + const CStrW basename(fs::basename((const fs::path&)pathname)); m_templateFilenames[basename] = pathname.string(); } diff --git a/source/simulation/FormationCollection.cpp b/source/simulation/FormationCollection.cpp index 21c1321913..3aebe56c31 100644 --- a/source/simulation/FormationCollection.cpp +++ b/source/simulation/FormationCollection.cpp @@ -14,7 +14,7 @@ void CFormationCollection::LoadFile( const VfsPath& pathname ) // the formation 'x' can be in units/x.xml, structures/x.xml, etc, and // we don't have to search every directory for x.xml. - CStrW basename(Basename(pathname)); + const CStrW basename(fs::basename((const fs::path&)pathname)); m_templateFilenames[basename] = pathname.string(); } diff --git a/source/simulation/TechnologyCollection.cpp b/source/simulation/TechnologyCollection.cpp index e8bc7e1226..1f4ad362d3 100644 --- a/source/simulation/TechnologyCollection.cpp +++ b/source/simulation/TechnologyCollection.cpp @@ -8,7 +8,7 @@ void CTechnologyCollection::LoadFile( const VfsPath& pathname ) { - CStrW basename(Basename(pathname)); + const CStrW basename(fs::basename((const fs::path&)pathname)); m_techFilenames[basename] = pathname.string(); } diff --git a/source/sound/CMusicPlayer.cpp b/source/sound/CMusicPlayer.cpp index be59b48ab0..6a8d26a71a 100644 --- a/source/sound/CMusicPlayer.cpp +++ b/source/sound/CMusicPlayer.cpp @@ -57,7 +57,7 @@ void CMusicPlayer::Open(char* UNUSED(filename)) for(int i = 0; i < NUM_BUFS; i++) { alGenBuffers(1, &bufs[i].al_buffer); - bufs[i].raw_buf = mem_alloc(RAW_BUF_SIZE, 4096); + bufs[i].raw_buf = malloc(RAW_BUF_SIZE); } alGenSources(1, &source); diff --git a/source/sound/JSI_Sound.cpp b/source/sound/JSI_Sound.cpp index d268b1e61f..1adb9d9896 100644 --- a/source/sound/JSI_Sound.cpp +++ b/source/sound/JSI_Sound.cpp @@ -168,7 +168,7 @@ void JSI_Sound::ScriptingInit() CStr JSI_Sound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) { - return "[object Sound: " + CStr(h_filename(m_Handle)) + "]"; + return "[object Sound: " + h_filename(m_Handle).string() + "]"; } JSBool JSI_Sound::Construct(JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval)