From a859562ea7c600c2eba68dea21701f7c0e81ab8a Mon Sep 17 00:00:00 2001 From: janwas Date: Mon, 7 Jan 2008 20:03:19 +0000 Subject: [PATCH] improvements and fixes: - properly differentiate between buffer/offset alignment and length alignment (relevant since block size has been increased to 256k) - use VfsPath for most game paths instead of CStr - clean up timer interface and implementation - self-tests no longer crash - file_cache.cpp: fix for the case where allocation fails (prevent deleter from seeing a null pointer) - allocators: move all shared_ptr-related stuff to its own component; add DummySharedPtr - codec: disable checksums (important for performance at work) - File: made into an interface class to avoid export problems. not entirely sure about this.. - vfs_path.h, path.h, os_path.h: proper fix for using fs::change_extension and similar utility functions with derivatives of basic_path - lib_api: automatically link against import lib if building lib/ as a DLL - path_util: remove unused functions (this component is deprecated) - compiler.h: add INLINE - Xeromyces.cpp: pass PIVFS so that GetXMBPath works in self-test (should do this mostly everywhere rather than have one singleton g_VFS) This was SVN commit r5537. --- source/graphics/ColladaManager.cpp | 27 +- source/graphics/ColladaManager.h | 5 +- source/graphics/MapReader.cpp | 4 +- source/graphics/MeshManager.cpp | 2 +- source/graphics/ModelDef.cpp | 4 +- source/graphics/ModelDef.h | 5 +- source/graphics/SkeletonAnimDef.cpp | 2 +- source/graphics/SkeletonAnimDef.h | 3 +- source/graphics/SkeletonAnimManager.cpp | 2 +- source/graphics/tests/test_MeshManager.h | 67 ++-- source/gui/CGUI.cpp | 8 +- source/gui/GUITooltip.cpp | 2 +- source/gui/MiniMap.cpp | 2 +- source/lib/allocators/allocators.h | 41 +- source/lib/allocators/bucket.h | 8 +- source/lib/allocators/shared_ptr.cpp | 57 +++ source/lib/allocators/shared_ptr.h | 31 ++ source/lib/allocators/tests/test_headerless.h | 2 - source/lib/debug.cpp | 11 +- source/lib/external_libraries/zlib.h | 17 +- source/lib/file/archive/archive_builder.cpp | 4 +- source/lib/file/archive/archive_zip.cpp | 63 ++-- source/lib/file/archive/codec.h | 2 +- source/lib/file/archive/stream.cpp | 6 + source/lib/file/common/file_stats.cpp | 4 +- source/lib/file/common/real_directory.cpp | 8 +- source/lib/file/common/trace.cpp | 4 +- source/lib/file/common/trace.h | 2 +- source/lib/file/file.cpp | 205 +++++----- source/lib/file/file.h | 40 +- source/lib/file/file_system_util.cpp | 14 +- source/lib/file/file_system_util.h | 6 +- source/lib/file/io/io.cpp | 37 +- source/lib/file/io/io.h | 13 +- source/lib/file/io/io_internal.h | 7 +- source/lib/file/io/write_buffer.cpp | 6 +- source/lib/file/io/write_buffer.h | 10 +- source/lib/file/path.h | 16 +- source/lib/file/vfs/file_cache.cpp | 26 +- source/lib/file/vfs/vfs.cpp | 13 +- source/lib/file/vfs/vfs.h | 5 +- source/lib/file/vfs/vfs_path.h | 11 + source/lib/file/vfs/vfs_tree.cpp | 2 - source/lib/lib_api.h | 12 + source/lib/path_util.cpp | 89 ----- source/lib/path_util.h | 60 +-- source/lib/res/graphics/tests/test_tex.h | 7 +- source/lib/res/sound/snd_mgr.cpp | 8 +- source/lib/self_test.cpp | 4 +- source/lib/sysdep/compiler.h | 6 + source/lib/sysdep/ia32/ia32.cpp | 12 +- source/lib/sysdep/ia32/ia32.h | 16 +- source/lib/sysdep/win/wdbg_sym.cpp | 9 +- source/lib/sysdep/win/wdll_ver.cpp | 4 +- source/lib/sysdep/win/wposix/wdlfcn.cpp | 14 +- source/lib/tests/test_lockfree.h | 4 +- source/lib/tests/test_path_util.h | 62 --- source/lib/tests/test_secure_crt.h | 2 +- source/lib/tex/tex.cpp | 5 +- source/lib/tex/tex.h | 2 +- source/lib/timer.cpp | 188 +++++++--- source/lib/timer.h | 355 +++++++----------- source/main.cpp | 6 +- source/network/NetLog.cpp | 2 +- source/network/NetLog.h | 4 +- source/ps/CConsole.cpp | 2 +- source/ps/CConsole.h | 77 ++-- source/ps/FilePacker.cpp | 2 +- source/ps/FilePacker.h | 3 +- source/ps/FileUnpacker.cpp | 2 +- source/ps/FileUnpacker.h | 3 +- source/ps/Filesystem.cpp | 10 +- source/ps/Filesystem.h | 3 +- source/ps/GameSetup/Config.cpp | 8 +- source/ps/GameSetup/GameSetup.cpp | 5 +- source/ps/Interact.cpp | 4 +- source/ps/Loader.cpp | 4 +- source/ps/Loader.h | 4 +- source/ps/Profile.cpp | 10 +- source/ps/Util.cpp | 36 +- source/ps/XML/XML.h | 3 +- source/ps/XML/XMLUtils.cpp | 4 +- source/ps/XML/Xeromyces.cpp | 44 +-- source/ps/XML/Xeromyces.h | 7 +- source/ps/XML/tests/test_Xeromyces.h | 10 +- source/renderer/SkyManager.cpp | 2 +- source/renderer/WaterManager.cpp | 2 +- source/scripting/DOMEvent.cpp | 2 +- source/scripting/ScriptGlue.cpp | 34 +- .../simulation/EntityTemplateCollection.cpp | 2 +- source/simulation/FormationCollection.cpp | 2 +- source/simulation/TechnologyCollection.cpp | 2 +- source/tools/atlas/GameInterface/GameLoop.cpp | 8 +- .../atlas/GameInterface/MessagePasserImpl.cpp | 6 +- source/tools/atlas/GameInterface/View.cpp | 4 +- 95 files changed, 950 insertions(+), 1030 deletions(-) create mode 100644 source/lib/allocators/shared_ptr.cpp create mode 100644 source/lib/allocators/shared_ptr.h diff --git a/source/graphics/ColladaManager.cpp b/source/graphics/ColladaManager.cpp index 9c4516c04d..fc9ddd3e9e 100644 --- a/source/graphics/ColladaManager.cpp +++ b/source/graphics/ColladaManager.cpp @@ -50,7 +50,7 @@ public: set_logger(NULL); // unregister the log handler } - bool Convert(const CStr& daeFilename, const CStr& pmdFilename, CColladaManager::FileType type) + bool Convert(const VfsPath& daeFilename, const VfsPath& pmdFilename, CColladaManager::FileType type) { // To avoid always loading the DLL when it's usually not going to be // used (and to do the same on Linux where delay-loading won't help), @@ -137,7 +137,7 @@ CColladaManager::~CColladaManager() delete m; } -CStr CColladaManager::GetLoadableFilename(const CStr& sourceName, FileType type) +VfsPath CColladaManager::GetLoadableFilename(const CStr& sourceName, FileType type) { const char* extn = NULL; switch (type) @@ -175,11 +175,11 @@ CStr CColladaManager::GetLoadableFilename(const CStr& sourceName, FileType type) // (TODO: the comments and variable names say "pmd" but actually they can // be "psa" too.) - CStr dae = sourceName + ".dae"; + VfsPath dae(sourceName + ".dae"); if (! FileExists(dae)) { // No .dae - got to use the .pmd, assuming there is one - return sourceName + extn; + return VfsPath(sourceName + extn); } // There is a .dae - see if there's an up-to-date cached copy @@ -188,8 +188,8 @@ CStr CColladaManager::GetLoadableFilename(const CStr& sourceName, FileType type) if (g_VFS->GetFileInfo(dae, &fileInfo) < 0) { // This shouldn't occur for any sensible reasons - LOG(CLogger::Error, "collada", "Failed to stat DAE file '%s'", dae.c_str()); - return ""; + LOG(CLogger::Error, "collada", "Failed to stat DAE file '%s'", dae.string().c_str()); + return VfsPath(); } // Build a struct of all the data we want to hash. @@ -205,20 +205,17 @@ CStr CColladaManager::GetLoadableFilename(const CStr& sourceName, FileType type) u32 hash = fnv_hash(static_cast(&hashSource), sizeof(hashSource)); char hashString[9]; sprintf(hashString, "%08x", hash); + std::string extension("_"); + extension += hashString; + extension += extn; // realDaePath is "mods/whatever/art/meshes/whatever.dae" - char realDaePath[PATH_MAX]; + Path realDaePath; g_VFS->GetRealPath(dae, realDaePath); // cachedPmdVfsPath is "cache/mods/whatever/art/meshes/whatever_{hash}.pmd" - CStr cachedPmdVfsPath = "cache/"; - cachedPmdVfsPath += realDaePath; - // Remove the .dae extension (which will certainly be there) - cachedPmdVfsPath = cachedPmdVfsPath.substr(0, cachedPmdVfsPath.length()-4); - // Add a _hash.pmd extension - cachedPmdVfsPath += "_"; - cachedPmdVfsPath += hashString; - cachedPmdVfsPath += extn; + VfsPath cachedPmdVfsPath = VfsPath("cache/") / realDaePath.string(); + cachedPmdVfsPath = change_extension(cachedPmdVfsPath, extension); // If it's not in the cache, we'll have to create it first if (! FileExists(cachedPmdVfsPath)) diff --git a/source/graphics/ColladaManager.h b/source/graphics/ColladaManager.h index 5a8bb0adb9..3bdeb4650e 100644 --- a/source/graphics/ColladaManager.h +++ b/source/graphics/ColladaManager.h @@ -1,8 +1,9 @@ #ifndef INCLUDED_COLLADAMANAGER #define INCLUDED_COLLADAMANAGER -class CStr8; +#include "lib/file/vfs/vfs_path.h" +class CStr8; class CColladaManagerImpl; class CColladaManager @@ -23,7 +24,7 @@ public: * @return full VFS path (including extension) of file to load; or empty * string if there was a problem and it could not be loaded. */ - CStr8 GetLoadableFilename(const CStr8& sourceName, FileType type); + VfsPath GetLoadableFilename(const CStr8& sourceName, FileType type); private: CColladaManagerImpl* m; diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index 35cb9e093c..2ac0563c1d 100644 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -134,7 +134,7 @@ int CMapReader::UnpackTerrain() { // yield after this time is reached. balances increased progress bar // smoothness vs. slowing down loading. - const double end_time = get_time() + 200e-3; + const double end_time = timer_Time() + 200e-3; // first call to generator (this is skipped after first call, // i.e. when the loop below was interrupted) @@ -960,7 +960,7 @@ int CXMLReader::ProgressiveRead() { // yield after this time is reached. balances increased progress bar // smoothness vs. slowing down loading. - const double end_time = get_time() + 200e-3; + const double end_time = timer_Time() + 200e-3; int ret; diff --git a/source/graphics/MeshManager.cpp b/source/graphics/MeshManager.cpp index b97f8480e7..d380c6ad56 100644 --- a/source/graphics/MeshManager.cpp +++ b/source/graphics/MeshManager.cpp @@ -39,7 +39,7 @@ CModelDefPtr CMeshManager::GetMesh(const CStr& filename) PROFILE( "load mesh" ); - CStr pmdFilename = m_ColladaManager.GetLoadableFilename(name, CColladaManager::PMD); + VfsPath pmdFilename = m_ColladaManager.GetLoadableFilename(name, CColladaManager::PMD); if (pmdFilename.empty()) { diff --git a/source/graphics/ModelDef.cpp b/source/graphics/ModelDef.cpp index a5b79f2260..a5c1d85b64 100644 --- a/source/graphics/ModelDef.cpp +++ b/source/graphics/ModelDef.cpp @@ -107,7 +107,7 @@ SPropPoint* CModelDef::FindPropPoint(const char* name) const } // Load: read and return a new CModelDef initialised with data from given file -CModelDef* CModelDef::Load(const char* filename, const char* name) +CModelDef* CModelDef::Load(const VfsPath& filename, const char* name) { CFileUnpacker unpacker; @@ -192,7 +192,7 @@ CModelDef* CModelDef::Load(const char* filename, const char* name) } // Save: write the given CModelDef to the given file -void CModelDef::Save(const char* filename,const CModelDef* mdef) +void CModelDef::Save(const VfsPath& filename,const CModelDef* mdef) { CFilePacker packer(FILE_VERSION, "PSMD"); diff --git a/source/graphics/ModelDef.h b/source/graphics/ModelDef.h index 2ec9fd3b59..2536a999e1 100644 --- a/source/graphics/ModelDef.h +++ b/source/graphics/ModelDef.h @@ -12,6 +12,7 @@ #include "ps/CStr.h" #include "maths/Vector3D.h" #include "maths/Quaternion.h" +#include "lib/file/vfs/vfs_path.h" #include class CBoneState; @@ -96,7 +97,7 @@ public: // model I/O functions - static void Save(const char* filename,const CModelDef* mdef); + static void Save(const VfsPath& filename,const CModelDef* mdef); /** * Loads a PMD file. @@ -105,7 +106,7 @@ public: * @return the model - always non-NULL * @throw PSERROR_File if it can't load the model */ - static CModelDef* Load(const char* filename, const char* name); + static CModelDef* Load(const VfsPath& filename, const char* name); public: // accessor: get vertex data diff --git a/source/graphics/SkeletonAnimDef.cpp b/source/graphics/SkeletonAnimDef.cpp index f53bca67b9..4fa0c5923a 100644 --- a/source/graphics/SkeletonAnimDef.cpp +++ b/source/graphics/SkeletonAnimDef.cpp @@ -75,7 +75,7 @@ void CSkeletonAnimDef::BuildBoneMatrices(float time, CMatrix3D* matrices, bool l /////////////////////////////////////////////////////////////////////////////////////////// // Load: try to load the anim from given file; return a new anim if successful -CSkeletonAnimDef* CSkeletonAnimDef::Load(const char* filename) +CSkeletonAnimDef* CSkeletonAnimDef::Load(const VfsPath& filename) { CFileUnpacker unpacker; unpacker.Read(filename,"PSSA"); diff --git a/source/graphics/SkeletonAnimDef.h b/source/graphics/SkeletonAnimDef.h index 3e9d9ecba1..9ee5f2ac49 100644 --- a/source/graphics/SkeletonAnimDef.h +++ b/source/graphics/SkeletonAnimDef.h @@ -11,6 +11,7 @@ #include "maths/Vector3D.h" #include "maths/Quaternion.h" +#include "lib/file/vfs/vfs_path.h" //////////////////////////////////////////////////////////////////////////////////////// // CBoneState: structure describing state of a bone at some point @@ -64,7 +65,7 @@ public: void BuildBoneMatrices(float time, CMatrix3D* matrices, bool loop) const; // anim I/O functions - static CSkeletonAnimDef* Load(const char* filename); + static CSkeletonAnimDef* Load(const VfsPath& filename); static void Save(const char* filename, const CSkeletonAnimDef* anim); public: diff --git a/source/graphics/SkeletonAnimManager.cpp b/source/graphics/SkeletonAnimManager.cpp index ca90cb4597..0e78d1c2d0 100644 --- a/source/graphics/SkeletonAnimManager.cpp +++ b/source/graphics/SkeletonAnimManager.cpp @@ -54,7 +54,7 @@ CSkeletonAnimDef* CSkeletonAnimManager::GetAnimation(const CStr& filename) CSkeletonAnimDef* def = NULL; // Find the file to load - CStr psaFilename = m_ColladaManager.GetLoadableFilename(name, CColladaManager::PSA); + VfsPath psaFilename = m_ColladaManager.GetLoadableFilename(name, CColladaManager::PSA); if (psaFilename.empty()) { diff --git a/source/graphics/tests/test_MeshManager.h b/source/graphics/tests/test_MeshManager.h index b70ba258ba..c3a7b1905f 100644 --- a/source/graphics/tests/test_MeshManager.h +++ b/source/graphics/tests/test_MeshManager.h @@ -1,9 +1,9 @@ #include "lib/self_test.h" -#include "lib/file/vfs/vfs.h" +#include "lib/file/VFS/VFS.h" #include "lib/file/io/io.h" #include "lib/file/path.h" -#include "lib/file/directory_posix.h" +#include "lib/file/file_system_posix.h" #include "graphics/ColladaManager.h" #include "graphics/MeshManager.h" @@ -11,18 +11,20 @@ #include "ps/CLogger.h" -#define MOD_PATH "mods/_test.mesh" -#define CACHE_PATH "_testcache" +static Path MOD_PATH("mods/_test.mesh"); +static Path CACHE_PATH("_testcache"); -const char* srcDAE = "tests/collada/sphere.dae"; -const char* srcPMD = "tests/collada/sphere.pmd"; -const char* testDAE = "art/meshes/skeletal/test.dae"; -const char* testPMD = "art/meshes/skeletal/test.pmd"; -const char* testBase = "art/meshes/skeletal/test"; +const char* srcDAE = "collada/sphere.dae"; +const char* srcPMD = "collada/sphere.pmd"; +const char* testDAE = "art/skeletons/test.dae"; +const char* testPMD = "art/skeletons/test.pmd"; +const char* testBase = "art/skeletons/test"; -const char* srcSkeletonDefs = "tests/collada/skeletons.xml"; +const char* srcSkeletonDefs = "collada/skeletons.xml"; const char* testSkeletonDefs = "art/skeletons/skeletons.xml"; +extern PIVFS g_VFS; + class TestMeshManager : public CxxTest::TestSuite { void initVfs() @@ -35,37 +37,41 @@ class TestMeshManager : public CxxTest::TestSuite // Make sure the required directories doesn't exist when we start, // in case the previous test aborted and left them full of junk - directoryPosix.DeleteDirectory(MOD_PATH); - directoryPosix.DeleteDirectory(CACHE_PATH); +// if(exists(MOD_PATH)) +// fsPosix.DeleteDirectory(MOD_PATH); +// if(exists(MOD_PATH)) +// fsPosix.DeleteDirectory(CACHE_PATH); - TS_ASSERT_OK(fs::create_directory(MOD_PATH)); - TS_ASSERT_OK(fs::create_directory(CACHE_PATH)); + TS_ASSERT(fs::create_directory(MOD_PATH.external_directory_string())); + TS_ASSERT(fs::create_directory(CACHE_PATH.external_directory_string())); - vfs = CreateVfs(); + g_VFS = CreateVfs(); - // Mount the mod on / - TS_ASSERT_OK(vfs->Mount("", MOD_PATH, VFS_MOUNT_ARCHIVABLE)); + TS_ASSERT_OK(g_VFS->Mount("", MOD_PATH)); + TS_ASSERT_OK(g_VFS->Mount("collada/", "tests/collada")); // Mount _testcache onto virtual /cache - don't use the normal cache // directory because that's full of loads of cached files from the // proper game and takes a long time to load. - TS_ASSERT_OK(vfs->Mount("cache/", CACHE_PATH, VFS_MOUNT_ARCHIVABLE)); + TS_ASSERT_OK(g_VFS->Mount("cache/", CACHE_PATH)); } void deinitVfs() { - directoryPosix.DeleteDirectory(MOD_PATH); - directoryPosix.DeleteDirectory(CACHE_PATH); +// fsPosix.DeleteDirectory(MOD_PATH); +// fsPosix.DeleteDirectory(CACHE_PATH); path_ResetRootDir(); + + g_VFS.reset(); } void copyFile(const char* src, const char* dst) { // Copy a file into the mod directory, so we can work on it: shared_ptr data; size_t size = 0; - TS_ASSERT_OK(vfs->LoadFile(src, data, size)); - TS_ASSERT_OK(vfs->CreateFile(dst, data, size)); + TS_ASSERT_OK(g_VFS->LoadFile(src, data, size)); + TS_ASSERT_OK(g_VFS->CreateFile(dst, data, size)); } void buildArchive() @@ -80,8 +86,7 @@ class TestMeshManager : public CxxTest::TestSuite CColladaManager* colladaManager; CMeshManager* meshManager; - PIVFS vfs; - FileSystem_Posix directoryPosix; + FileSystem_Posix fsPosix; public: @@ -105,7 +110,7 @@ public: //buildArchive(); shared_ptr buf = io_Allocate(100); SAFE_STRCPY((char*)buf.get(), "Test"); - vfs->CreateFile(testDAE, buf, 4); + g_VFS->CreateFile(testDAE, buf, 4); } void test_load_pmd_with_extension() @@ -151,10 +156,10 @@ public: copyFile(srcDAE, testDAE); copyFile(srcSkeletonDefs, testSkeletonDefs); - CStr daeName1 = colladaManager->GetLoadableFilename(testBase, CColladaManager::PMD); - CStr daeName2 = colladaManager->GetLoadableFilename(testBase, CColladaManager::PMD); - TS_ASSERT(daeName1.length()); - TS_ASSERT_STR_EQUALS(daeName1, daeName2); + VfsPath daeName1 = colladaManager->GetLoadableFilename(testBase, CColladaManager::PMD); + VfsPath daeName2 = colladaManager->GetLoadableFilename(testBase, CColladaManager::PMD); + TS_ASSERT(!daeName1.empty()); + TS_ASSERT_STR_EQUALS(daeName1.string(), daeName2.string()); // TODO: it'd be nice to test that it really isn't doing the DAE->PMD // conversion a second time, but there doesn't seem to be an easy way // to check that @@ -167,7 +172,7 @@ public: copyFile(srcDAE, testDAE); shared_ptr buf = io_Allocate(100); SAFE_STRCPY((char*)buf.get(), "Not valid XML"); - vfs->CreateFile(testSkeletonDefs, buf, 13); + g_VFS->CreateFile(testSkeletonDefs, buf, 13); CModelDefPtr modeldef = meshManager->GetMesh(testDAE); TS_ASSERT(! modeldef); @@ -181,7 +186,7 @@ public: copyFile(srcSkeletonDefs, testSkeletonDefs); shared_ptr buf = io_Allocate(100); SAFE_STRCPY((char*)buf.get(), "Not valid XML"); - vfs->CreateFile(testDAE, buf, 13); + g_VFS->CreateFile(testDAE, buf, 13); CModelDefPtr modeldef = meshManager->GetMesh(testDAE); TS_ASSERT(! modeldef); diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp index 629b24659c..9f96101b8a 100644 --- a/source/gui/CGUI.cpp +++ b/source/gui/CGUI.cpp @@ -206,8 +206,8 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev) case SDL_BUTTON_LEFT: if (pNearest) { - double timeElapsed = get_time() - pNearest->m_LastClickTime[SDL_BUTTON_LEFT]; - pNearest->m_LastClickTime[SDL_BUTTON_LEFT] = get_time(); + double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_LEFT]; + pNearest->m_LastClickTime[SDL_BUTTON_LEFT] = timer_Time(); //Double click? if (timeElapsed < SELECT_DBLCLICK_RATE) @@ -227,8 +227,8 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev) case SDL_BUTTON_RIGHT: if (pNearest) { - double timeElapsed = get_time() - pNearest->m_LastClickTime[SDL_BUTTON_RIGHT]; - pNearest->m_LastClickTime[SDL_BUTTON_RIGHT] = get_time(); + double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_RIGHT]; + pNearest->m_LastClickTime[SDL_BUTTON_RIGHT] = timer_Time(); //Double click? if (timeElapsed < SELECT_DBLCLICK_RATE) diff --git a/source/gui/GUITooltip.cpp b/source/gui/GUITooltip.cpp index 19d8cf7e59..19f14f359e 100644 --- a/source/gui/GUITooltip.cpp +++ b/source/gui/GUITooltip.cpp @@ -210,7 +210,7 @@ void GUITooltip::Update(IGUIObject* Nearest, CPos MousePos, CGUI* GUI) // Called once per frame, so efficiency isn't vital - double now = get_time(); + double now = timer_Time(); CStr style; diff --git a/source/gui/MiniMap.cpp b/source/gui/MiniMap.cpp index e4a3b0e43c..a30e8c716d 100644 --- a/source/gui/MiniMap.cpp +++ b/source/gui/MiniMap.cpp @@ -263,7 +263,7 @@ void CMiniMap::Draw() // (note: since units only move a few pixels per second on the minimap, // we can get away with infrequent updates; this is slow, ~20ms) static double last_time; - const double cur_time = get_time(); + const double cur_time = timer_Time(); if(cur_time - last_time > 100e-3) // 10 updates/sec { last_time = cur_time; diff --git a/source/lib/allocators/allocators.h b/source/lib/allocators/allocators.h index a40400a2b0..0bf8dcc732 100644 --- a/source/lib/allocators/allocators.h +++ b/source/lib/allocators/allocators.h @@ -47,28 +47,6 @@ LIB_API void* page_aligned_alloc(size_t unaligned_size); LIB_API void page_aligned_free(void* p, size_t unaligned_size); -// adapter that allows calling page_aligned_free as a shared_ptr deleter. -class PageAlignedDeleter -{ -public: - PageAlignedDeleter(size_t size) - : m_size(size) - { - debug_assert(m_size != 0); - } - - void operator()(u8* p) - { - debug_assert(m_size != 0); - page_aligned_free(p, m_size); - m_size = 0; - } - -private: - size_t m_size; -}; - - // // matrix allocator // @@ -86,7 +64,7 @@ private: * @return 0 if out of memory, otherwise matrix that should be cast to * type** (sizeof(type) == el_size). must be freed via matrix_free. **/ -LIB_API void** matrix_alloc(uint cols, uint rows, size_t el_size); +extern void** matrix_alloc(uint cols, uint rows, size_t el_size); /** * free the given matrix. @@ -95,7 +73,7 @@ LIB_API void** matrix_alloc(uint cols, uint rows, size_t el_size); * callers will likely want to pass variables of a different type * (e.g. int**); they must be cast to void**. **/ -LIB_API void matrix_free(void** matrix); +extern void matrix_free(void** matrix); //----------------------------------------------------------------------------- @@ -120,7 +98,7 @@ LIB_API void matrix_free(void** matrix); * @return allocated memory (typically = , but falls back to * malloc if that's in-use), or 0 (with warning) if out of memory. **/ -LIB_API void* single_calloc(void* storage, volatile uintptr_t* in_use_flag, size_t size); +extern void* single_calloc(void* storage, volatile uintptr_t* in_use_flag, size_t size); /** * Free a memory block that had been allocated by single_calloc. @@ -129,7 +107,7 @@ LIB_API void* single_calloc(void* storage, volatile uintptr_t* in_use_flag, size * @param in_use_flag Exact value passed to single_calloc. * @param Exact value returned by single_calloc. **/ -LIB_API void single_free(void* storage, volatile uintptr_t* in_use_flag, void* p); +extern void single_free(void* storage, volatile uintptr_t* in_use_flag, void* p); #ifdef __cplusplus @@ -219,7 +197,7 @@ void InitObject() * * raises a warning if there's not enough room (indicates incorrect usage) **/ -LIB_API void* static_calloc(StaticStorage* ss, size_t size); +extern void* static_calloc(StaticStorage* ss, size_t size); // (no need to free static_calloc-ed memory since it's in the BSS) @@ -346,13 +324,4 @@ private: Allocs allocs; }; - -template -struct DummyDeleter -{ - void operator()(T* UNUSED(t)) - { - } -}; - #endif // #ifndef INCLUDED_ALLOCATORS diff --git a/source/lib/allocators/bucket.h b/source/lib/allocators/bucket.h index 335c39e528..d243a87d80 100644 --- a/source/lib/allocators/bucket.h +++ b/source/lib/allocators/bucket.h @@ -59,7 +59,7 @@ struct Bucket * will be returned by bucket_alloc (whose size parameter is then ignored). * @return LibError. **/ -LIB_API LibError bucket_create(Bucket* b, size_t el_size); +extern LibError bucket_create(Bucket* b, size_t el_size); /** * free all memory that ensued from . @@ -68,7 +68,7 @@ LIB_API LibError bucket_create(Bucket* b, size_t el_size); * * @param Bucket* **/ -LIB_API void bucket_destroy(Bucket* b); +extern void bucket_destroy(Bucket* b); /** * Dole out memory from the Bucket. @@ -79,7 +79,7 @@ LIB_API void bucket_destroy(Bucket* b); * @return allocated memory, or 0 if the Bucket would have to be expanded and * there isn't enough memory to do so. **/ -LIB_API void* bucket_alloc(Bucket* b, size_t size); +extern void* bucket_alloc(Bucket* b, size_t size); /** * make an entry available for reuse in the given Bucket. @@ -91,6 +91,6 @@ LIB_API void* bucket_alloc(Bucket* b, size_t size); * @param Bucket* * @param el entry allocated via bucket_alloc. **/ -LIB_API void bucket_free(Bucket* b, void* el); +extern void bucket_free(Bucket* b, void* el); #endif // #ifndef INCLUDED_BUCKET diff --git a/source/lib/allocators/shared_ptr.cpp b/source/lib/allocators/shared_ptr.cpp new file mode 100644 index 0000000000..808c4a3a2b --- /dev/null +++ b/source/lib/allocators/shared_ptr.cpp @@ -0,0 +1,57 @@ +#include "precompiled.h" +#include "shared_ptr.h" + +#include "allocators.h" // AllocatorChecker + + +PageAlignedDeleter::PageAlignedDeleter(size_t size) + : m_size(size) +{ + debug_assert(m_size != 0); +} + +void PageAlignedDeleter::operator()(u8* p) +{ + debug_assert(m_size != 0); + page_aligned_free(p, m_size); + m_size = 0; +} + + +#ifndef NDEBUG +static AllocatorChecker s_allocatorChecker; +#endif + +class CheckedDeleter +{ +public: + CheckedDeleter(size_t size) + : m_size(size) + { + } + + void operator()(u8* p) + { + debug_assert(m_size != 0); +#ifndef NDEBUG + s_allocatorChecker.OnDeallocate(p, m_size); +#endif + delete[] p; + m_size = 0; + } + +private: + size_t m_size; +}; + +shared_ptr Allocate(size_t size) +{ + debug_assert(size != 0); + + u8* p = new u8[size]; +#ifndef NDEBUG + s_allocatorChecker.OnAllocate(p, size); +#endif + + return shared_ptr(p, CheckedDeleter(size)); +} diff --git a/source/lib/allocators/shared_ptr.h b/source/lib/allocators/shared_ptr.h new file mode 100644 index 0000000000..6f193cf070 --- /dev/null +++ b/source/lib/allocators/shared_ptr.h @@ -0,0 +1,31 @@ +#ifndef INCLUDED_SHARED_PTR +#define INCLUDED_SHARED_PTR + +// adapter that allows calling page_aligned_free as a shared_ptr deleter. +class PageAlignedDeleter +{ +public: + PageAlignedDeleter(size_t size); + void operator()(u8* p); + +private: + size_t m_size; +}; + +template +struct DummyDeleter +{ + void operator()(T*) + { + } +}; + +template +shared_ptr DummySharedPtr(T* ptr) +{ + return shared_ptr(ptr, DummyDeleter()); +} + +LIB_API shared_ptr Allocate(size_t size); + +#endif // #ifndef INCLUDED_SHARED_PTR diff --git a/source/lib/allocators/tests/test_headerless.h b/source/lib/allocators/tests/test_headerless.h index 8b5fb0ad5e..011c3c585d 100644 --- a/source/lib/allocators/tests/test_headerless.h +++ b/source/lib/allocators/tests/test_headerless.h @@ -36,8 +36,6 @@ public: void test_Free() { -return; - // Deallocate allows immediate reuse of the freed pointer HeaderlessAllocator a(4096); void* p1 = a.Allocate(1024); diff --git a/source/lib/debug.cpp b/source/lib/debug.cpp index 71ae8407ab..c5367baea2 100644 --- a/source/lib/debug.cpp +++ b/source/lib/debug.cpp @@ -15,10 +15,11 @@ #include #include "app_hooks.h" +#include "os_path.h" #include "path_util.h" #include "debug_stl.h" +#include "lib/allocators/allocators.h" // page_aligned_alloc #include "fnv_hash.h" -#include "lib/allocators/allocators.h" #include "lib/posix/posix_pthread.h" #include "lib/sysdep/cpu.h" // cpu_CAS #include "lib/sysdep/sysdep.h" @@ -224,12 +225,8 @@ LibError debug_write_crashlog(const wchar_t* text) if(!cpu_CAS(&in_progress, 0, 1)) return ERR::REENTERED; // NOWARN - // note: we go through some gyrations here (strcpy+strcat) to avoid - // dependency on file code (path_append). - char N_path[PATH_MAX]; - strcpy_s(N_path, ARRAY_SIZE(N_path), ah_get_log_dir()); - strcat_s(N_path, ARRAY_SIZE(N_path), "crashlog.txt"); - FILE* f = fopen(N_path, "w"); + OsPath path = OsPath(ah_get_log_dir())/"crashlog.txt"; + FILE* f = fopen(path.string().c_str(), "w"); if(!f) { in_progress = 0; diff --git a/source/lib/external_libraries/zlib.h b/source/lib/external_libraries/zlib.h index f019831fb4..f42e30908c 100644 --- a/source/lib/external_libraries/zlib.h +++ b/source/lib/external_libraries/zlib.h @@ -20,15 +20,26 @@ # define WINAPIV __cdecl #endif +#ifndef FOM_ZLIB #define ZLIB_DLL +#endif + #include // automatically link against the required library #if MSC_VERSION -# ifdef NDEBUG -# pragma comment(lib, "zlib1.lib") +# ifdef FOM_ZLIB +# ifdef NDEBUG +# pragma comment(lib, "fom_zlib.lib") +# else +# pragma comment(lib, "fom_zlib_d.lib") +# endif # else -# pragma comment(lib, "zlib1d.lib") +# ifdef NDEBUG +# pragma comment(lib, "zlib1.lib") +# else +# pragma comment(lib, "zlib1d.lib") +# endif # endif #endif diff --git a/source/lib/file/archive/archive_builder.cpp b/source/lib/file/archive/archive_builder.cpp index 5c304ef1fa..648edbc9ab 100644 --- a/source/lib/file/archive/archive_builder.cpp +++ b/source/lib/file/archive/archive_builder.cpp @@ -714,7 +714,7 @@ LibError archive_build_init(const char* P_archive_filename, Filenames V_fns, Arc int archive_build_continue(ArchiveBuildState* ab) { - const double end_time = get_time() + 200e-3; + const double end_time = timer_Time() + 200e-3; for(;;) { @@ -731,7 +731,7 @@ int archive_build_continue(ArchiveBuildState* ab) ab->i++; - if(get_time() > end_time) + if(timer_Time() > end_time) { int progress_percent = (ab->i*100 / ab->num_files); // 0 means "finished", so don't return that! diff --git a/source/lib/file/archive/archive_zip.cpp b/source/lib/file/archive/archive_zip.cpp index feb6fd99c2..22e71cb9ff 100644 --- a/source/lib/file/archive/archive_zip.cpp +++ b/source/lib/file/archive/archive_zip.cpp @@ -52,7 +52,7 @@ class LFH public: void Init(const FileInfo& fileInfo, off_t csize, ZipMethod method, u32 checksum, const VfsPath& pathname_) { - const std::string& pathname = pathname_.string(); + const std::string& pathnameString = pathname_.string(); m_magic = lfh_magic; m_x1 = to_le16(0); @@ -62,10 +62,10 @@ public: m_crc = to_le32(checksum); m_csize = to_le32(u32_from_larger(csize)); m_usize = to_le32(u32_from_larger(fileInfo.Size())); - m_fn_len = to_le16(u16_from_larger(pathname.length())); + m_fn_len = to_le16(u16_from_larger(pathnameString.length())); m_e_len = to_le16(0); - cpu_memcpy((char*)this + sizeof(LFH), pathname.c_str(), pathname.length()); + cpu_memcpy((char*)this + sizeof(LFH), pathnameString.c_str(), pathnameString.length()); } size_t Size() const @@ -99,7 +99,7 @@ class CDFH public: void Init(const FileInfo& fileInfo, off_t ofs, off_t csize, ZipMethod method, u32 checksum, const VfsPath& pathname_, size_t slack) { - const std::string& pathname = pathname_.string(); + const std::string& pathnameString = pathname_.string(); m_magic = cdfh_magic; m_x1 = to_le32(0); m_flags = to_le16(0); @@ -108,21 +108,21 @@ public: m_crc = to_le32(checksum); m_csize = to_le32(u32_from_larger(csize)); m_usize = to_le32(u32_from_larger(fileInfo.Size())); - m_fn_len = to_le16(u16_from_larger(pathname.length())); + m_fn_len = to_le16(u16_from_larger(pathnameString.length())); m_e_len = to_le16(0); m_c_len = to_le16(u16_from_larger((uint)slack)); m_x2 = to_le32(0); m_x3 = to_le32(0); m_lfh_ofs = to_le32(ofs); - cpu_memcpy((char*)this + sizeof(CDFH), pathname.c_str(), pathname.length()); + cpu_memcpy((char*)this + sizeof(CDFH), pathnameString.c_str(), pathnameString.length()); } - VfsPath GetPathname() const + void GetPathname(std::string& pathname) const { const size_t length = (size_t)read_le16(&m_fn_len); const char* fn = (const char*)this + sizeof(CDFH); // not 0-terminated! - return VfsPath(std::string(fn, length)); + pathname = std::string(fn, length); } off_t HeaderOffset() const @@ -226,7 +226,7 @@ cassert(sizeof(ECDR) == 22); class ArchiveFile_Zip : public IArchiveFile { public: - ArchiveFile_Zip(PFile file, off_t ofs, off_t csize, u32 checksum, ZipMethod method) + ArchiveFile_Zip(PIFile file, off_t ofs, off_t csize, u32 checksum, ZipMethod method) : m_file(file), m_ofs(ofs) , m_csize(csize), m_checksum(checksum), m_method((u16)method) , m_flags(NeedsFixup) @@ -262,7 +262,7 @@ public: Stream stream(codec); stream.SetOutputBuffer(buf.get(), size); - RETURN_ERR(io_Scan(*m_file, m_ofs, m_csize, FeedStream, (uintptr_t)&stream)); + RETURN_ERR(io_Scan(m_file, m_ofs, m_csize, FeedStream, (uintptr_t)&stream)); RETURN_ERR(stream.Finish()); #if CODEC_COMPUTE_CHECKSUM debug_assert(m_checksum == stream.Checksum()); @@ -333,11 +333,11 @@ private: // previously read file (i.e. both are small). LFH lfh; LFH_Copier params = { (u8*)&lfh, sizeof(LFH) }; - if(io_Scan(*m_file, m_ofs, sizeof(LFH), lfh_copier_cb, (uintptr_t)¶ms) == INFO::OK) + if(io_Scan(m_file, m_ofs, sizeof(LFH), lfh_copier_cb, (uintptr_t)¶ms) == INFO::OK) m_ofs += (off_t)lfh.Size(); } - PFile m_file; + PIFile m_file; // all relevant LFH/CDFH fields not covered by FileInfo mutable off_t m_ofs; @@ -356,7 +356,7 @@ class ArchiveReader_Zip : public IArchiveReader { public: ArchiveReader_Zip(const Path& pathname) - : m_file(new File) + : m_file(CreateFile_Posix()) { m_file->Open(pathname, 'r'); @@ -370,10 +370,10 @@ public: { // locate and read Central Directory off_t cd_ofs; uint cd_numEntries; off_t cd_size; - RETURN_ERR(LocateCentralDirectory(*m_file, m_fileSize, cd_ofs, cd_numEntries, cd_size)); + RETURN_ERR(LocateCentralDirectory(m_file, m_fileSize, cd_ofs, cd_numEntries, cd_size)); shared_ptr buf = io_Allocate(cd_size, cd_ofs); u8* cd; - RETURN_ERR(io_Read(*m_file, cd_ofs, buf.get(), cd_size, cd)); + RETURN_ERR(io_Read(m_file, cd_ofs, buf.get(), cd_size, cd)); // iterate over Central Directory const u8* pos = cd; @@ -384,14 +384,21 @@ public: if(!cdfh) WARN_RETURN(ERR::CORRUPTED); - const std::string& pathname = cdfh->GetPathname().string(); - const size_t lastSlash = pathname.find_last_of('/'); - if(lastSlash != pathname.length()-1) // we only want files + std::string zipPathname; + cdfh->GetPathname(zipPathname); + const size_t lastSlash = zipPathname.find_last_of('/'); + if(lastSlash != zipPathname.length()-1) // we only want files { - const std::string name(pathname.begin()+lastSlash+1, pathname.end()); - FileInfo fileInfo(name, cdfh->USize(), cdfh->MTime()); + std::string name; + std::string* pname = &zipPathname; // assume zipPathname only has a name component + if(lastSlash != std::string::npos) + { + name = zipPathname.substr(lastSlash, zipPathname.length()-lastSlash); + pname = &name; + } + FileInfo fileInfo(*pname, cdfh->USize(), cdfh->MTime()); shared_ptr archiveFile(new ArchiveFile_Zip(m_file, cdfh->HeaderOffset(), cdfh->CSize(), cdfh->Checksum(), cdfh->Method())); - cb(pathname, fileInfo, archiveFile, cbData); + cb(zipPathname, fileInfo, archiveFile, cbData); } pos += cdfh->Size(); @@ -432,7 +439,7 @@ private: // search for ECDR in the last bytes of the file. // if found, fill with a copy of the (little-endian) ECDR and // return INFO::OK, otherwise IO error or ERR::CORRUPTED. - static LibError ScanForEcdr(const File& file, off_t fileSize, u8* buf, off_t maxScanSize, uint& cd_numEntries, off_t& cd_ofs, off_t& cd_size) + static LibError ScanForEcdr(PIFile file, off_t fileSize, u8* buf, off_t maxScanSize, uint& cd_numEntries, off_t& cd_ofs, off_t& cd_size) { // don't scan more than the entire file const off_t scanSize = std::min(maxScanSize, fileSize); @@ -451,7 +458,7 @@ private: return INFO::OK; } - static LibError LocateCentralDirectory(const File& file, off_t fileSize, off_t& cd_ofs, uint& cd_numEntries, off_t& cd_size) + static LibError LocateCentralDirectory(PIFile file, off_t fileSize, off_t& cd_ofs, uint& cd_numEntries, off_t& cd_size) { const off_t maxScanSize = 66000u; // see below shared_ptr buf = io_Allocate(maxScanSize, ~0); // assume worst-case for alignment @@ -482,7 +489,7 @@ private: WARN_RETURN(ERR::ARCHIVE_UNKNOWN_FORMAT); } - PFile m_file; + PIFile m_file; off_t m_fileSize; }; @@ -503,7 +510,7 @@ public: : m_fileSize(0), m_unalignedWriter(m_file, 0) , m_numEntries(0) { - THROW_ERR(m_file.Open(archivePathname, 'w')); + THROW_ERR(m_file->Open(archivePathname, 'w')); THROW_ERR(pool_create(&m_cdfhPool, 10*MiB, 0)); } @@ -539,8 +546,8 @@ public: if(!usize) return INFO::SKIPPED; - File file; - RETURN_ERR(file.Open(pathname, 'r')); + PIFile file = CreateFile_Posix(); + RETURN_ERR(file->Open(pathname, 'r')); const size_t pathnameLength = pathname.string().length(); const VfsPath vfsPathname(pathname.string()); @@ -622,7 +629,7 @@ private: return false; } - File m_file; + PIFile m_file; off_t m_fileSize; UnalignedWriter m_unalignedWriter; diff --git a/source/lib/file/archive/codec.h b/source/lib/file/archive/codec.h index 27dbd95c98..63644f99da 100644 --- a/source/lib/file/archive/codec.h +++ b/source/lib/file/archive/codec.h @@ -15,7 +15,7 @@ // besides ZLib. it also simplifies the interface for user code and // does error checking, etc. -#define CODEC_COMPUTE_CHECKSUM 1 +#define CODEC_COMPUTE_CHECKSUM 0 struct ICodec { diff --git a/source/lib/file/archive/stream.cpp b/source/lib/file/archive/stream.cpp index fab7f64a1b..9452c87d58 100644 --- a/source/lib/file/archive/stream.cpp +++ b/source/lib/file/archive/stream.cpp @@ -12,7 +12,11 @@ #include "stream.h" #include "lib/allocators/allocators.h" // page_aligned_alloc +#include "lib/allocators/shared_ptr.h" #include "codec.h" +//#include "lib/timer.h" + +//TIMER_ADD_CLIENT(tc_stream); OutputBufferManager::OutputBufferManager() @@ -121,6 +125,8 @@ LibError Stream::Finish() LibError FeedStream(uintptr_t cbData, const u8* in, size_t inSize) { +// TIMER_ACCRUE(tc_stream); + Stream& stream = *(Stream*)cbData; return stream.Feed(in, inSize); } diff --git a/source/lib/file/common/file_stats.cpp b/source/lib/file/common/file_stats.cpp index 2758a0731c..f3d8bc09b5 100644 --- a/source/lib/file/common/file_stats.cpp +++ b/source/lib/file/common/file_stats.cpp @@ -59,11 +59,11 @@ static void timer_start(double* start_time_storage = &start_time) // make sure no measurement is currently active // (since start_time is shared static storage) debug_assert(*start_time_storage == 0.0); - *start_time_storage = get_time(); + *start_time_storage = timer_Time(); } static double timer_reset(double* start_time_storage = &start_time) { - double elapsed = get_time() - *start_time_storage; + double elapsed = timer_Time() - *start_time_storage; *start_time_storage = 0.0; return elapsed; } diff --git a/source/lib/file/common/real_directory.cpp b/source/lib/file/common/real_directory.cpp index c2852dc191..686dc22e6b 100644 --- a/source/lib/file/common/real_directory.cpp +++ b/source/lib/file/common/real_directory.cpp @@ -26,8 +26,8 @@ RealDirectory::RealDirectory(const Path& path, unsigned priority, unsigned flags /*virtual*/ LibError RealDirectory::Load(const std::string& name, shared_ptr buf, size_t size) const { - File file; - RETURN_ERR(file.Open(m_path/name, 'r')); + PIFile file = CreateFile_Posix(); + RETURN_ERR(file->Open(m_path/name, 'r')); RETURN_ERR(io_ReadAligned(file, 0, buf.get(), size)); return INFO::OK; @@ -39,8 +39,8 @@ LibError RealDirectory::Store(const std::string& name, shared_ptr fileConten const Path pathname(m_path/name); { - File file; - RETURN_ERR(file.Open(pathname, 'w')); + PIFile file = CreateFile_Posix(); + RETURN_ERR(file->Open(pathname, 'w')); RETURN_ERR(io_WriteAligned(file, 0, fileContents.get(), size)); } diff --git a/source/lib/file/common/trace.cpp b/source/lib/file/common/trace.cpp index 6f02e9b625..e16a6876ff 100644 --- a/source/lib/file/common/trace.cpp +++ b/source/lib/file/common/trace.cpp @@ -12,7 +12,7 @@ #include "trace.h" #include "lib/allocators/pool.h" -#include "lib/timer.h" // get_time +#include "lib/timer.h" // timer_Time #include "lib/nommgr.h" // placement new /*virtual*/ ITrace::~ITrace() @@ -24,7 +24,7 @@ //----------------------------------------------------------------------------- TraceEntry::TraceEntry(EAction action, const char* pathname, size_t size) -: m_timestamp(get_time()) +: m_timestamp(timer_Time()) , m_action(action) , m_pathname(strdup(pathname)) , m_size(size) diff --git a/source/lib/file/common/trace.h b/source/lib/file/common/trace.h index 83b8d82471..57acdad405 100644 --- a/source/lib/file/common/trace.h +++ b/source/lib/file/common/trace.h @@ -54,7 +54,7 @@ private: // note: keep an eye on the class size because all instances are kept // in memory (see ITrace) - // time (as returned by get_time) after the operation completes. + // time (as returned by timer_Time) after the operation completes. // rationale: when loading, the VFS doesn't know file size until // querying the cache or retrieving file information. float m_timestamp; diff --git a/source/lib/file/file.cpp b/source/lib/file/file.cpp index 3ef2b9812c..fa79ad4635 100644 --- a/source/lib/file/file.cpp +++ b/source/lib/file/file.cpp @@ -17,108 +17,121 @@ ERROR_ASSOCIATE(ERR::FILE_ACCESS, "Insufficient access rights to open file", EAC ERROR_ASSOCIATE(ERR::IO, "Error during IO", EIO); -File::File() +class File_Posix : public IFile { -} - - -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) +public: + ~File_Posix() { - aiocb* const reqs = &req; - aio_suspend(&reqs, 1, (timespec*)0); // wait indefinitely + Close(); } - const ssize_t bytesTransferred = aio_return(&req); - if(bytesTransferred == -1) // transfer failed - WARN_RETURN(ERR::IO); + virtual LibError Open(const Path& pathname, char mode) + { + debug_assert(mode == 'w' || mode == 'r'); - alignedBuf = (u8*)req.aio_buf; // cast from volatile void* - alignedSize = bytesTransferred; - return INFO::OK; -} + 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; + } + + virtual void Close() + { + m_mode = '\0'; + + close(m_fd); + m_fd = 0; + } + + virtual const Path& Pathname() const + { + return m_pathname; + } + + virtual char Mode() const + { + return m_mode; + } + + virtual LibError 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; + } + + virtual LibError 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; + } + + virtual LibError Write(off_t ofs, const u8* buf, size_t size) const + { + return IO(ofs, const_cast(buf), size); + } + + virtual LibError Read(off_t ofs, u8* buf, size_t size) const + { + return IO(ofs, buf, size); + } + +private: + LibError 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; + } + + Path m_pathname; + char m_mode; + int m_fd; +}; -LibError File::IO(off_t ofs, u8* buf, size_t size) const +PIFile CreateFile_Posix() { - 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); + return PIFile(new File_Posix); } diff --git a/source/lib/file/file.h b/source/lib/file/file.h index 699f583011..3ab38525b4 100644 --- a/source/lib/file/file.h +++ b/source/lib/file/file.h @@ -19,39 +19,23 @@ namespace ERR const LibError IO = -110201; } -class LIB_API File +struct IFile { -public: - File(); - ~File(); + virtual LibError Open(const Path& pathname, char mode) = 0; + virtual void Close() = 0; - LibError Open(const Path& pathname, char mode); - void Close(); + virtual const Path& Pathname() const = 0; + virtual char Mode() const = 0; - const Path& Pathname() const - { - return m_pathname; - } + virtual LibError Issue(aiocb& req, off_t alignedOfs, u8* alignedBuf, size_t alignedSize) const = 0; + virtual LibError WaitUntilComplete(aiocb& req, u8*& alignedBuf, size_t& alignedSize) = 0; - 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; + virtual LibError Read(off_t ofs, u8* buf, size_t size) const = 0; + virtual LibError Write(off_t ofs, const u8* buf, size_t size) const = 0; }; -typedef shared_ptr PFile; +typedef shared_ptr PIFile; + +LIB_API PIFile CreateFile_Posix(); #endif // #ifndef INCLUDED_FILE diff --git a/source/lib/file/file_system_util.cpp b/source/lib/file/file_system_util.cpp index a61c50b115..0fe2b1b964 100644 --- a/source/lib/file/file_system_util.cpp +++ b/source/lib/file/file_system_util.cpp @@ -102,7 +102,7 @@ LibError fs_ForEachFile(PIVFS fs, const VfsPath& path, FileCallback cb, uintptr_ } -void fs_NextNumberedFilename(PIVFS fs, const char* pathnameFmt, unsigned& nextNumber, char* nextPathname) +void fs_NextNumberedFilename(PIVFS fs, const VfsPath& pathnameFormat, unsigned& nextNumber, VfsPath& nextPathname) { // (first call only:) scan directory and set nextNumber according to // highest matching filename found. this avoids filling "holes" in @@ -111,8 +111,8 @@ void fs_NextNumberedFilename(PIVFS fs, const char* pathnameFmt, unsigned& nextNu // add 3rd -> without this measure it would get number 1, not 3. if(nextNumber == 0) { - char path[PATH_MAX]; char nameFmt[PATH_MAX]; - path_split(pathnameFmt, path, nameFmt); + const std::string nameFormat = pathnameFormat.leaf(); + const VfsPath path = pathnameFormat.branch_path(); unsigned maxNumber = 0; FileInfos files; @@ -120,7 +120,7 @@ void fs_NextNumberedFilename(PIVFS fs, const char* pathnameFmt, unsigned& nextNu for(size_t i = 0; i < files.size(); i++) { unsigned number; - if(sscanf(files[i].Name().c_str(), nameFmt, &number) == 1) + if(sscanf(files[i].Name().c_str(), nameFormat.c_str(), &number) == 1) maxNumber = std::max(number, maxNumber); } @@ -133,6 +133,10 @@ void fs_NextNumberedFilename(PIVFS fs, const char* pathnameFmt, unsigned& nextNu // someone may have added files in the meantime) // we don't bother with binary search - this isn't a bottleneck. do - snprintf(nextPathname, PATH_MAX, pathnameFmt, nextNumber++); + { + char pathnameBuf[PATH_MAX]; + snprintf(pathnameBuf, PATH_MAX, pathnameFormat.string().c_str(), nextNumber++); + nextPathname = VfsPath(pathnameBuf); + } while(fs->GetFileInfo(nextPathname, 0) == INFO::OK); } diff --git a/source/lib/file/file_system_util.h b/source/lib/file/file_system_util.h index ea3f148ed0..5462a85257 100644 --- a/source/lib/file/file_system_util.h +++ b/source/lib/file/file_system_util.h @@ -55,13 +55,13 @@ extern LibError fs_ForEachFile(PIVFS fs, const VfsPath& path, FileCallback cb, u * this is useful when creating new files without overwriting the previous * ones (screenshots are a good example). * - * @param pathnameFmt format string for the pathname; must contain one + * @param pathnameFormat format string for the pathname; must contain one * format specifier for an (unsigned) int. * example: "screenshots/screenshot%04d.png" * @param nextNumber in: the first number to try; out: the next number. * if 0, numbers corresponding to existing files are skipped. - * @param receives the output; must hold at least PATH_MAX characters. + * @param nextPathname receives the output. **/ -extern void fs_NextNumberedFilename(PIVFS fs, const char* pathnameFmt, unsigned& nextNumber, char* nextPathname); +extern void fs_NextNumberedFilename(PIVFS fs, const VfsPath& pathnameFormat, unsigned& nextNumber, VfsPath& nextPathname); #endif // #ifndef INCLUDED_FILE_SYSTEM_UTIL diff --git a/source/lib/file/io/io.cpp b/source/lib/file/io/io.cpp index 5ab3bbd388..c2dbaee6df 100644 --- a/source/lib/file/io/io.cpp +++ b/source/lib/file/io/io.cpp @@ -93,7 +93,7 @@ shared_ptr io_Allocate(size_t size, off_t ofs) { debug_assert(size != 0); - const size_t misalignment = ofs % BLOCK_SIZE; + const size_t misalignment = ofs % SECTOR_SIZE; const size_t paddedSize = (size_t)round_up(size+misalignment, BLOCK_SIZE); u8* mem = (u8*)page_aligned_alloc(paddedSize); @@ -115,10 +115,11 @@ shared_ptr io_Allocate(size_t size, off_t ofs) class BlockIo { public: - LibError Issue(const File& file, off_t alignedOfs, u8* alignedBuf) + LibError Issue(PIFile file, off_t alignedOfs, u8* alignedBuf) { - m_blockId = BlockId(file.Pathname(), alignedOfs); - if(file.Mode() == 'r' && s_blockCache.Retrieve(m_blockId, m_cachedBlock)) + m_file = file; + m_blockId = BlockId(file->Pathname(), alignedOfs); + if(file->Mode() == 'r' && s_blockCache.Retrieve(m_blockId, m_cachedBlock)) { stats_block_cache(CR_HIT); @@ -153,7 +154,7 @@ public: m_alignedBuf = const_cast(m_tempBlock.get()); } - return file.Issue(m_req, alignedOfs, m_alignedBuf, BLOCK_SIZE); + return file->Issue(m_req, alignedOfs, m_alignedBuf, BLOCK_SIZE); } } @@ -166,7 +167,7 @@ public: return INFO::OK; } - RETURN_ERR(File::WaitUntilComplete(m_req, const_cast(block), blockSize)); + RETURN_ERR(m_file->WaitUntilComplete(m_req, const_cast(block), blockSize)); if(m_tempBlock) s_blockCache.Add(m_blockId, m_tempBlock); @@ -177,6 +178,8 @@ public: private: static BlockCache s_blockCache; + PIFile m_file; + BlockId m_blockId; // the address that WaitUntilComplete will return @@ -203,12 +206,12 @@ public: : m_ofs(ofs), m_alignedBuf(alignedBuf), m_size(size) , m_totalIssued(0), m_totalTransferred(0) { - m_misalignment = ofs % BLOCK_SIZE; + m_misalignment = ofs % SECTOR_SIZE; m_alignedOfs = ofs - (off_t)m_misalignment; m_alignedSize = round_up(m_misalignment+size, BLOCK_SIZE); } - LibError Run(const File& file, IoCallback cb = 0, uintptr_t cbData = 0) + LibError Run(PIFile file, IoCallback cb = 0, uintptr_t cbData = 0) { ScopedIoMonitor monitor; @@ -234,7 +237,7 @@ public: debug_assert(m_totalIssued >= m_totalTransferred && m_totalTransferred >= m_size); - monitor.NotifyOfSuccess(FI_AIO, file.Mode(), m_totalTransferred); + monitor.NotifyOfSuccess(FI_AIO, file->Mode(), m_totalTransferred); return INFO::OK; } @@ -287,7 +290,7 @@ private: }; -LibError io_Scan(const File& file, off_t ofs, off_t size, IoCallback cb, uintptr_t cbData) +LibError io_Scan(PIFile file, off_t ofs, off_t size, IoCallback cb, uintptr_t cbData) { u8* alignedBuf = 0; // use temporary block buffers IoSplitter splitter(ofs, alignedBuf, size); @@ -295,7 +298,7 @@ LibError io_Scan(const File& file, off_t ofs, off_t size, IoCallback cb, uintptr } -LibError io_Read(const File& file, off_t ofs, u8* alignedBuf, size_t size, u8*& data) +LibError io_Read(PIFile file, off_t ofs, u8* alignedBuf, size_t size, u8*& data) { IoSplitter splitter(ofs, alignedBuf, (off_t)size); RETURN_ERR(splitter.Run(file)); @@ -304,20 +307,20 @@ LibError io_Read(const File& file, off_t ofs, u8* alignedBuf, size_t size, u8*& } -LibError io_WriteAligned(const File& file, off_t alignedOfs, const u8* alignedData, size_t size) +LibError io_WriteAligned(PIFile file, off_t alignedOfs, const u8* alignedData, size_t size) { - debug_assert(IsAligned(alignedOfs, BLOCK_SIZE)); - debug_assert(IsAligned(alignedData, BLOCK_SIZE)); + debug_assert(IsAligned(alignedOfs, SECTOR_SIZE)); + debug_assert(IsAligned(alignedData, SECTOR_SIZE)); IoSplitter splitter(alignedOfs, const_cast(alignedData), (off_t)size); return splitter.Run(file); } -LibError io_ReadAligned(const File& file, off_t alignedOfs, u8* alignedBuf, size_t size) +LibError io_ReadAligned(PIFile file, off_t alignedOfs, u8* alignedBuf, size_t size) { - debug_assert(IsAligned(alignedOfs, BLOCK_SIZE)); - debug_assert(IsAligned(alignedBuf, BLOCK_SIZE)); + debug_assert(IsAligned(alignedOfs, SECTOR_SIZE)); + debug_assert(IsAligned(alignedBuf, SECTOR_SIZE)); IoSplitter splitter(alignedOfs, alignedBuf, (off_t)size); return splitter.Run(file); diff --git a/source/lib/file/io/io.h b/source/lib/file/io/io.h index f18f264708..ae716d314b 100644 --- a/source/lib/file/io/io.h +++ b/source/lib/file/io/io.h @@ -11,14 +11,13 @@ #ifndef INCLUDED_IO #define INCLUDED_IO +#include "lib/file/file.h" + // 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. * @@ -29,11 +28,11 @@ class File; **/ typedef LibError (*IoCallback)(uintptr_t cbData, const u8* block, size_t blockSize); -LIB_API LibError io_Scan(const File& file, off_t ofs, off_t size, IoCallback cb, uintptr_t cbData); +LIB_API LibError io_Scan(PIFile file, off_t ofs, off_t size, IoCallback cb, uintptr_t cbData); -LIB_API LibError io_Read(const File& file, off_t ofs, u8* alignedBuf, size_t size, u8*& data); +LIB_API LibError io_Read(PIFile file, off_t ofs, u8* alignedBuf, size_t size, u8*& data); -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); +LIB_API LibError io_WriteAligned(PIFile file, off_t alignedOfs, const u8* alignedData, size_t size); +LIB_API LibError io_ReadAligned(PIFile file, off_t alignedOfs, u8* alignedBuf, size_t size); #endif // #ifndef INCLUDED_IO diff --git a/source/lib/file/io/io_internal.h b/source/lib/file/io/io_internal.h index 75d06a5d84..cdc8873a9f 100644 --- a/source/lib/file/io/io_internal.h +++ b/source/lib/file/io/io_internal.h @@ -6,6 +6,11 @@ * (blocks are also thereby page-aligned, which allows write-protecting * file buffers without worrying about their boundaries.) **/ -static const size_t BLOCK_SIZE = 64*KiB; +static const size_t BLOCK_SIZE = 256*KiB; + +// note: *sizes* are aligned to blocks to allow zero-copy block cache. +// that the *buffer* and *offset* must be sector aligned (we assume 4kb for simplicity) +// is a requirement of the OS. +static const size_t SECTOR_SIZE = 4*KiB; static const unsigned ioDepth = 8; diff --git a/source/lib/file/io/write_buffer.cpp b/source/lib/file/io/write_buffer.cpp index 9ec2a271de..cb6d1b0a5e 100644 --- a/source/lib/file/io/write_buffer.cpp +++ b/source/lib/file/io/write_buffer.cpp @@ -37,10 +37,10 @@ void WriteBuffer::Overwrite(const void* data, size_t size, size_t offset) // UnalignedWriter //----------------------------------------------------------------------------- -UnalignedWriter::UnalignedWriter(const File& file, off_t ofs) +UnalignedWriter::UnalignedWriter(PIFile file, off_t ofs) : m_file(file), m_alignedBuf(io_Allocate(BLOCK_SIZE)) { - const size_t misalignment = (size_t)ofs % BLOCK_SIZE; + const size_t misalignment = (size_t)ofs % SECTOR_SIZE; m_alignedOfs = ofs - (off_t)misalignment; if(misalignment) io_ReadAligned(m_file, m_alignedOfs, m_alignedBuf.get(), BLOCK_SIZE); @@ -60,7 +60,7 @@ LibError UnalignedWriter::Append(const u8* data, size_t size) const { // 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) + if(m_bytesUsed == 0 && IsAligned(data, SECTOR_SIZE) && alignedSize != 0) { RETURN_ERR(io_WriteAligned(m_file, m_alignedOfs, data, alignedSize)); m_alignedOfs += (off_t)alignedSize; diff --git a/source/lib/file/io/write_buffer.h b/source/lib/file/io/write_buffer.h index ea7c6c7faa..60b05e54a0 100644 --- a/source/lib/file/io/write_buffer.h +++ b/source/lib/file/io/write_buffer.h @@ -1,9 +1,9 @@ #ifndef INCLUDED_WRITE_BUFFER #define INCLUDED_WRITE_BUFFER -class File; +#include "lib/file/file.h" -class LIB_API WriteBuffer +class WriteBuffer { public: WriteBuffer(); @@ -29,10 +29,10 @@ private: }; -class LIB_API UnalignedWriter : public boost::noncopyable +class UnalignedWriter : public boost::noncopyable { public: - UnalignedWriter(const File& file, off_t ofs); + UnalignedWriter(PIFile file, off_t ofs); ~UnalignedWriter(); /** @@ -49,7 +49,7 @@ public: private: LibError WriteBlock() const; - const File& m_file; + PIFile m_file; shared_ptr m_alignedBuf; mutable off_t m_alignedOfs; mutable size_t m_bytesUsed; diff --git a/source/lib/file/path.h b/source/lib/file/path.h index 6a83a013dd..b4ff3ddec5 100644 --- a/source/lib/file/path.h +++ b/source/lib/file/path.h @@ -28,12 +28,20 @@ struct PathTraits typedef std::string internal_string_type; typedef std::string external_string_type; - static external_string_type to_external(const Path&, const internal_string_type& src); - static internal_string_type to_internal(const external_string_type& src); + LIB_API static external_string_type to_external(const Path&, const internal_string_type& src); + LIB_API static internal_string_type to_internal(const external_string_type& src); }; -extern bool exists(const Path& path); - +namespace boost +{ + namespace filesystem + { + template<> struct is_basic_path + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + } +} namespace ERR { diff --git a/source/lib/file/vfs/file_cache.cpp b/source/lib/file/vfs/file_cache.cpp index b80551156c..7ff908820e 100644 --- a/source/lib/file/vfs/file_cache.cpp +++ b/source/lib/file/vfs/file_cache.cpp @@ -16,6 +16,7 @@ #include "lib/cache_adt.h" // Cache #include "lib/bits.h" // round_up #include "lib/allocators/allocators.h" +#include "lib/allocators/shared_ptr.h" #include "lib/allocators/headerless.h" #include "lib/allocators/mem_util.h" // mem_PageSize @@ -80,13 +81,16 @@ public: shared_ptr Allocate(size_t size, PAllocator pthis) { const size_t alignedSize = round_up(size, BLOCK_SIZE); - stats_buf_alloc(size, alignedSize); u8* mem = (u8*)m_allocator.Allocate(alignedSize); + if(!mem) + return DummySharedPtr(0); // (prevent FileCacheDeleter from seeing a null pointer) + #ifndef NDEBUG m_checker.OnAllocate(mem, alignedSize); #endif + stats_buf_alloc(size, alignedSize); return shared_ptr(mem, FileCacheDeleter(size, pthis)); } @@ -150,17 +154,21 @@ public: // of space in a full cache) for(;;) { - shared_ptr data = m_allocator->Allocate(size, m_allocator); - if(data) - return data; + { + shared_ptr data = m_allocator->Allocate(size, m_allocator); + if(data) + return data; + } // remove least valuable entry from cache (if users are holding // references, the contents won't actually be deallocated) - shared_ptr discardedData; size_t discardedSize; - bool removed = m_cache.remove_least_valuable(&discardedData, &discardedSize); - // only false if cache is empty, which can't be the case because - // allocation failed. - debug_assert(removed); + { + shared_ptr discardedData; size_t discardedSize; + bool removed = m_cache.remove_least_valuable(&discardedData, &discardedSize); + // only false if cache is empty, which can't be the case because + // allocation failed. + debug_assert(removed); + } } } diff --git a/source/lib/file/vfs/vfs.cpp b/source/lib/file/vfs/vfs.cpp index 2b5efec996..3fa8032e38 100644 --- a/source/lib/file/vfs/vfs.cpp +++ b/source/lib/file/vfs/vfs.cpp @@ -30,15 +30,14 @@ public: { } - virtual LibError Mount(const VfsPath& mountPoint, const char* path, uint flags /* = 0 */, uint priority /* = 0 */) + virtual LibError Mount(const VfsPath& mountPoint, const Path& path, uint flags /* = 0 */, uint priority /* = 0 */) { debug_assert(vfs_path_IsDirectory(mountPoint)); - debug_assert(strcmp(path, ".") != 0); // "./" isn't supported on Windows. - // note: mounting subdirectoryNames is now allowed. + // note: mounting subdirectories is now allowed. 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)); + PRealDirectory realDirectory(new RealDirectory(path, priority, flags)); directory->Attach(realDirectory); return INFO::OK; } @@ -71,6 +70,7 @@ public: CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE)); PRealDirectory realDirectory = directory->AssociatedDirectory(); +if(!realDirectory) return ERR::FAIL; // WORKAROUND: vfs doesn't create real dirs by itself const std::string& name = pathname.leaf(); RETURN_ERR(realDirectory->Store(name, fileContents, size)); @@ -139,13 +139,12 @@ public: m_rootDirectory.DisplayR(0); } - virtual LibError GetRealPath(const VfsPath& pathname, char* realPathname) + virtual LibError GetRealPath(const VfsPath& pathname, Path& realPathname) { 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()); + realPathname = realDirectory->GetPath() / pathname.leaf(); return INFO::OK; } diff --git a/source/lib/file/vfs/vfs.h b/source/lib/file/vfs/vfs.h index 911489250c..d1c900850e 100644 --- a/source/lib/file/vfs/vfs.h +++ b/source/lib/file/vfs/vfs.h @@ -13,6 +13,7 @@ #define INCLUDED_VFS #include "lib/file/file_system.h" // FileInfo +#include "lib/file/path.h" #include "lib/file/vfs/vfs_path.h" // VFS paths are of the form: "(dir/)*file?" @@ -66,7 +67,7 @@ struct IVFS * if files with archive extensions are seen, their contents are added * as well. **/ - virtual LibError Mount(const VfsPath& mountPoint, const char* path, uint flags = 0, uint priority = 0) = 0; + virtual LibError Mount(const VfsPath& mountPoint, const Path& path, uint flags = 0, uint priority = 0) = 0; virtual LibError GetFileInfo(const VfsPath& pathname, FileInfo* pfileInfo) const = 0; @@ -95,7 +96,7 @@ struct IVFS virtual void Display() const = 0; virtual void Clear() = 0; - virtual LibError GetRealPath(const VfsPath& pathname, char* path) = 0; + virtual LibError GetRealPath(const VfsPath& pathname, Path& path) = 0; }; typedef shared_ptr PIVFS; diff --git a/source/lib/file/vfs/vfs_path.h b/source/lib/file/vfs/vfs_path.h index c08aba2685..102250986f 100644 --- a/source/lib/file/vfs/vfs_path.h +++ b/source/lib/file/vfs/vfs_path.h @@ -22,6 +22,17 @@ struct VfsPathTraits } }; +namespace boost +{ + namespace filesystem + { + template<> struct is_basic_path + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + } +} + 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 8082198f9b..9813d0ab34 100644 --- a/source/lib/file/vfs/vfs_tree.cpp +++ b/source/lib/file/vfs/vfs_tree.cpp @@ -184,8 +184,6 @@ void VfsDirectory::ClearR() void VfsDirectory::Attach(PRealDirectory realDirectory) { -debug_printf("ATTACH %s\n", realDirectory->GetPath().string().c_str()); - if(!cpu_CAS(&m_shouldPopulate, 0, 1)) { debug_assert(0); // multiple Attach() calls without an intervening ShouldPopulate() diff --git a/source/lib/lib_api.h b/source/lib/lib_api.h index a6cfea0b16..6f868d48bd 100644 --- a/source/lib/lib_api.h +++ b/source/lib/lib_api.h @@ -1,3 +1,5 @@ +#include "lib/sysdep/compiler.h" + // note: EXTERN_C cannot be used because shared_ptr is often returned // by value, which requires C++ linkage. @@ -10,3 +12,13 @@ #else # define LIB_API #endif + +#if defined(LIB_DLL) && !defined(LIB_BUILD) +# if MSC_VERSION +# ifdef NDEBUG +# pragma comment(lib, "lib.lib") +# else +# pragma comment(lib, "lib_d.lib") +# endif +# endif +#endif diff --git a/source/lib/path_util.cpp b/source/lib/path_util.cpp index f5decd95db..60326ec0be 100644 --- a/source/lib/path_util.cpp +++ b/source/lib/path_util.cpp @@ -203,55 +203,8 @@ LibError path_append(char* dst, const char* path1, const char* path2, uint flags } -// strip from the start of , prepend , -// and write to . -// returns ERR::FAIL (without warning!) if the beginning of doesn't -// match . -LibError path_replace(char* dst, const char* src, const char* remove, const char* replace) -{ - // remove doesn't match start of - const size_t remove_len = strlen(remove); - if(strncmp(src, remove, remove_len) != 0) - return ERR::FAIL; // NOWARN - - // if removing will leave a separator at beginning of src, remove it - // (example: "a/b"; removing "a" would yield "/b") - const char* start = src+remove_len; - if(path_is_dir_sep(*start)) - start++; - - // prepend replace. - RETURN_ERR(path_append(dst, replace, start)); - return INFO::OK; -} - - - - //----------------------------------------------------------------------------- -// split paths into specific parts - -void path_split(const char* pathname, char* path, char* name) -{ - const char* namePos = path_name_only(pathname); - - if(name) - { - path_copy(name, namePos); - - path_component_validate(name); - } - - if(path) - { - path_copy(path, pathname); - path[namePos-pathname] = '\0'; // strip filename - - debug_assert(path_IsDirectory(path)); - } -} - // return pointer to the name component within path (i.e. skips over all // characters up to the last dir separator, if any). const char* path_name_only(const char* path) @@ -269,37 +222,6 @@ const char* path_name_only(const char* path) return name; } - -// return last component within path. this is similar to path_name_only, -// but correctly handles VFS paths, which must end with '/'. -// (path_name_only would return "") -const char* path_last_component(const char* path) -{ - // ('\0' is end of set string) - static const char separators[3] = { '/', '\\', '\0' }; - - const char* pos = path; - const char* last_component = path; - - for(;;) - { - // catches empty path and those with trailing separator - if(*pos == '\0') - break; - last_component = pos; - const size_t component_len = strcspn(pos, separators); - // catches paths without trailing separator - // (the 'pos +=' would skip their 0-terminator) - if(pos[component_len] == '\0') - break; - pos += component_len+1; // +1 for separator - } - - path_component_validate(last_component); - return last_component; -} - - // if contains a name component, it is stripped away. void path_strip_fn(char* path) { @@ -309,17 +231,6 @@ void path_strip_fn(char* path) } -// fill with the directory path portion of -// ("" if root dir, otherwise ending with '/'). -// note: copies to and proceeds to path_strip_fn it. -void path_dir_only(const char* pathname, char* path) -{ - path_copy(path, pathname); - path_strip_fn(path); - // (path_strip_fn already ensures its output is a directory path) -} - - // return extension of , or "" if there is none. // NOTE: does not include the period; e.g. "a.bmp" yields "bmp". const char* path_extension(const char* fn) diff --git a/source/lib/path_util.h b/source/lib/path_util.h index 4cbecec629..8e9e45dc6f 100644 --- a/source/lib/path_util.h +++ b/source/lib/path_util.h @@ -37,7 +37,7 @@ namespace ERR * * @return LibError (ERR::PATH_* or INFO::OK) **/ -LIB_API LibError path_validate(const char* path); +extern LibError path_validate(const char* path); /** * return appropriate code if path is invalid, otherwise continue. @@ -49,7 +49,7 @@ LIB_API LibError path_validate(const char* path); * * @return LibError (ERR::PATH_* or INFO::OK) **/ -LIB_API LibError path_component_validate(const char* name); +extern LibError path_component_validate(const char* name); /** * is the given character a path separator character? @@ -57,14 +57,14 @@ LIB_API LibError path_component_validate(const char* name); * @param c character to test * @return bool **/ -LIB_API bool path_is_dir_sep(char c); +extern bool path_is_dir_sep(char c); /** * is the given path(name) a directory? * * @return bool **/ -LIB_API bool path_IsDirectory(const char* path); +extern bool path_IsDirectory(const char* path); /** * is s2 a subpath of s1, or vice versa? (equal counts as subpath) @@ -72,7 +72,7 @@ LIB_API bool path_IsDirectory(const char* path); * @param s1, s2 comparand strings * @return bool **/ -LIB_API bool path_is_subpath(const char* s1, const char* s2); +extern bool path_is_subpath(const char* s1, const char* s2); /** @@ -82,7 +82,7 @@ LIB_API bool path_is_subpath(const char* s1, const char* s2); * and should hold PATH_MAX chars. * @param src source; should not exceed PATH_MAX chars **/ -LIB_API void path_copy(char* dst, const char* src); +extern void path_copy(char* dst, const char* src); /** * flags controlling path_append behavior @@ -106,27 +106,7 @@ enum PathAppendFlags * @param flags see PathAppendFlags. * @return LibError **/ -LIB_API LibError path_append(char* dst, const char* path1, const char* path2, uint flags = 0); - -/** - * at the start of a path, replace the given substring with another. - * - * @param dst destination; must hold at least PATH_MAX chars. - * @param src source string. - * @param remove substring to remove; must match (case-sensitive) the - * start of src. - * @param replace string to prepend to output after stripping . - * @return LibError; ERR::FAIL (without warning!) if doesn't - * match . - **/ -LIB_API LibError path_replace(char* dst, const char* src, const char* remove, const char* replace); - -/** - * combination of path_name_only and path_dir_only2 - * (more efficient than calling them separately) - **/ -LIB_API void path_split(const char* pathname, char* path, char* name); - +extern LibError path_append(char* dst, const char* path1, const char* path2, uint flags = 0); /** * get the name component of a path. @@ -135,34 +115,14 @@ LIB_API void path_split(const char* pathname, char* path, char* name); * @param path input path. * @return pointer to name component within . **/ -LIB_API const char* path_name_only(const char* path); - -/** - * get the last component of a path. - * - * this is similar to path_name_only, but correctly handles VFS paths, - * which must end with '/'. (path_name_only would return "") - * @param path input path. - * @return pointer to last component within . - **/ -LIB_API const char* path_last_component(const char* path); +extern const char* path_name_only(const char* path); /** * strip away the name component in a path. * * @param path input and output; chopped by inserting '\0'. **/ -LIB_API void path_strip_fn(char* path); - -/** - * retrieve only the directory path portion of a path. - * - * @param path source path. - * @param dir output directory path ("" if root dir, - * otherwise it ends with '/'). - * note: implementation via path_copy and path_strip_fn. - **/ -LIB_API void path_dir_only(const char* path, char* dir); +extern void path_strip_fn(char* path); /** * get filename's extension. @@ -170,6 +130,6 @@ LIB_API void path_dir_only(const char* path, char* dir); * @return pointer to extension within , or "" if there is none. * NOTE: does not include the period; e.g. "a.bmp" yields "bmp". **/ -LIB_API const char* path_extension(const char* fn); +extern const char* path_extension(const char* fn); #endif // #ifndef INCLUDED_PATH_UTIL diff --git a/source/lib/res/graphics/tests/test_tex.h b/source/lib/res/graphics/tests/test_tex.h index 783f9c0433..687f798605 100644 --- a/source/lib/res/graphics/tests/test_tex.h +++ b/source/lib/res/graphics/tests/test_tex.h @@ -2,7 +2,7 @@ #include "lib/tex/tex.h" #include "lib/tex/tex_codec.h" -#include "lib/allocators/allocators.h" // DummyDeleter +#include "lib/allocators/shared_ptr.h" class TestTex : public CxxTest::TestSuite { @@ -103,7 +103,7 @@ public: // have mipmaps be created for a test image; check resulting size and pixels void test_mipmap_create() { - u8 imgData[] = { 0x10,0x20,0x30, 0x40,0x60,0x80, 0xA0,0xA4,0xA8, 0xC0,0xC1,0xC2 }; + static u8 imgData[] = { 0x10,0x20,0x30, 0x40,0x60,0x80, 0xA0,0xA4,0xA8, 0xC0,0xC1,0xC2 }; shared_ptr img(imgData, DummyDeleter()); // assumes 2x2 box filter algorithm with rounding static const u8 mipmap[] = { 0x6C,0x79,0x87 }; @@ -118,8 +118,7 @@ public: void test_img_size() { - u8 imgStorage[100*100*4]; // (prevent a needless heap alloc) - shared_ptr img(imgStorage, DummyDeleter()); + shared_ptr img(new u8[100*100*4]); Tex t; TS_ASSERT_OK(tex_wrap(100, 100, 32, TEX_ALPHA, img, 0, &t)); diff --git a/source/lib/res/sound/snd_mgr.cpp b/source/lib/res/sound/snd_mgr.cpp index 8f858cd206..25e570eb27 100644 --- a/source/lib/res/sound/snd_mgr.cpp +++ b/source/lib/res/sound/snd_mgr.cpp @@ -1035,7 +1035,7 @@ enum FadeRet * not yet necessary, though. * * @param fi Describes the fade operation - * @param cur_time typically returned via get_time() + * @param cur_time typically returned via timer_Time() * @param out_val Output gain value, i.e. the current result of the fade. * @return FadeRet */ @@ -1214,7 +1214,7 @@ static LibError VSrc_reload(VSrc* vs, const VfsPath& pathname, Handle hvs) // pathname is a definition file containing the data file name and // its gain. - if(fs::extension((const fs::path&)pathname) == ".txt") + if(fs::extension(pathname) == ".txt") { shared_ptr buf; size_t size; RETURN_ERR(g_VFS->LoadFile(pathname, buf, size)); @@ -1828,7 +1828,7 @@ LibError snd_fade(Handle hvs, float initial_gain, float final_gain, if(initial_gain < 0.0f) initial_gain = vs->gain; - const double cur_time = get_time(); + const double cur_time = timer_Time(); FadeInfo& fi = vs->fade; fi.type = type; @@ -2015,7 +2015,7 @@ LibError snd_update(const float * pos, const float * dir, const float * up) vm_update(); // for each source: add / remove buffers; carry out fading. - snd_update_time = get_time(); // see decl + snd_update_time = timer_Time(); // see decl list_foreach((void (*)(VSrc*))vsrc_update); return INFO::OK; diff --git a/source/lib/self_test.cpp b/source/lib/self_test.cpp index 005908b566..dfd0e95a16 100644 --- a/source/lib/self_test.cpp +++ b/source/lib/self_test.cpp @@ -44,7 +44,7 @@ int self_test_register(SelfTestRecord* r) void self_test_run_all() { debug_printf("SELF TESTS:\n"); - const double t0 = get_time(); + const double t0 = timer_Time(); // someone somewhere may want to run self-tests twice (e.g. to help // track down memory corruption), so don't destroy the list while @@ -56,7 +56,7 @@ void self_test_run_all() r = r->next; } - const double dt = get_time() - t0; + const double dt = timer_Time() - t0; debug_printf("-- done (elapsed time %.0f ms)\n", dt*1e3); } diff --git a/source/lib/sysdep/compiler.h b/source/lib/sysdep/compiler.h index f0130de46f..b4da001ff6 100644 --- a/source/lib/sysdep/compiler.h +++ b/source/lib/sysdep/compiler.h @@ -172,4 +172,10 @@ # define EXTERN_C extern #endif +#if MSC_VERSION +# define INLINE __forceinline +#else +# define INLINE inline +#endif + #endif // #ifndef INCLUDED_COMPILER diff --git a/source/lib/sysdep/ia32/ia32.cpp b/source/lib/sysdep/ia32/ia32.cpp index 4ebcf3e0ad..1132dd309f 100644 --- a/source/lib/sysdep/ia32/ia32.cpp +++ b/source/lib/sysdep/ia32/ia32.cpp @@ -315,7 +315,7 @@ public: } }; -// note: this function uses timer.cpp!get_time, which is implemented via +// note: this function uses timer.cpp!timer_Time, which is implemented via // whrt.cpp on Windows, which again calls ia32_Init. be careful that // this function isn't called from there as well, else WHRT will be used // before its init completes. @@ -341,7 +341,7 @@ double ia32_ClockFrequency() // if clock is low-res, do less samples so it doesn't take too long. // balance measuring time (~ 10 ms) and accuracy (< 1 0/00 error - // ok for using the TSC as a time reference) - if(timer_res() >= 1e-3) + if(timer_Resolution() >= 1e-3) num_samples = 8; std::vector samples(num_samples); @@ -353,24 +353,24 @@ double ia32_ClockFrequency() // count # of clocks in max{1 tick, 1 ms}: // .. wait for start of tick. - const double t0 = get_time(); + const double t0 = timer_Time(); u64 c1; double t1; do { - // note: get_time effectively has a long delay (up to 5 us) + // note: timer_Time effectively has a long delay (up to 5 us) // before returning the time. we call it before ia32_rdtsc to // minimize the delay between actually sampling time / TSC, // thus decreasing the chance for interference. // (if unavoidable background activity, e.g. interrupts, // delays the second reading, inaccuracy is introduced). - t1 = get_time(); + t1 = timer_Time(); c1 = ia32_rdtsc(); } while(t1 == t0); // .. wait until start of next tick and at least 1 ms elapsed. do { - const double t2 = get_time(); + const double t2 = timer_Time(); const u64 c2 = ia32_rdtsc(); dc = (i64)(c2 - c1); dt = t2 - t1; diff --git a/source/lib/sysdep/ia32/ia32.h b/source/lib/sysdep/ia32/ia32.h index 84547596e7..966f03437e 100644 --- a/source/lib/sysdep/ia32/ia32.h +++ b/source/lib/sysdep/ia32/ia32.h @@ -30,14 +30,14 @@ enum Ia32Vendor IA32_VENDOR_AMD, }; -extern Ia32Vendor ia32_Vendor(); +LIB_API Ia32Vendor ia32_Vendor(); /** * @return the colloquial processor generation * (5 = Pentium, 6 = Pentium Pro/II/III / K6, 7 = Pentium4 / Athlon, 8 = Core / Opteron) **/ -extern uint ia32_Generation(); +LIB_API uint ia32_Generation(); /** @@ -73,7 +73,7 @@ enum IA32Cap /** * @return whether the CPU supports the indicated IA32Cap / feature flag. **/ -extern bool ia32_cap(IA32Cap cap); +LIB_API bool ia32_cap(IA32Cap cap); //----------------------------------------------------------------------------- @@ -88,7 +88,7 @@ extern bool ia32_cap(IA32Cap cap); * reliably on WinXP. also, the OS already has the APIC registers mapped and * in constant use, and we don't want to interfere. **/ -extern uint ia32_ApicId(); +LIB_API uint ia32_ApicId(); /** @@ -101,18 +101,18 @@ extern uint ia32_ApicId(); * * this function is used for walking the call stack. **/ -extern LibError ia32_GetCallTarget(void* ret_addr, void** target); +LIB_API LibError ia32_GetCallTarget(void* ret_addr, void** target); /// safe but slow inline-asm version -extern u64 ia32_rdtsc_safe(void); +LIB_API u64 ia32_rdtsc_safe(void); /** * @return the current value of the TimeStampCounter (a counter of * CPU cycles since power-on, which is useful for high-resolution timing * but potentially differs between multiple CPUs) **/ -extern u64 ia32_rdtsc(); // only for CppDoc's benefit +LIB_API u64 ia32_rdtsc(); // only for CppDoc's benefit #if CONFIG_RETURN64_EDX_EAX # define ia32_rdtsc ia32_asm_rdtsc_edx_eax #else @@ -122,7 +122,7 @@ extern u64 ia32_rdtsc(); // only for CppDoc's benefit /** * trigger a breakpoint inside this function when it is called. **/ -extern void ia32_DebugBreak(void); +LIB_API void ia32_DebugBreak(void); diff --git a/source/lib/sysdep/win/wdbg_sym.cpp b/source/lib/sysdep/win/wdbg_sym.cpp index c93aadef45..1b70bc0cbd 100644 --- a/source/lib/sysdep/win/wdbg_sym.cpp +++ b/source/lib/sysdep/win/wdbg_sym.cpp @@ -20,6 +20,7 @@ #include "lib/debug_stl.h" #include "lib/app_hooks.h" +#include "lib/os_path.h" #include "lib/path_util.h" #if ARCH_IA32 # include "lib/sysdep/ia32/ia32.h" @@ -1894,12 +1895,8 @@ void wdbg_sym_write_minidump(EXCEPTION_POINTERS* exception_pointers) { lock(); - // note: we go through some gyrations here (strcpy+strcat) to avoid - // dependency on file code (path_append). - char N_path[PATH_MAX]; - strcpy_s(N_path, ARRAY_SIZE(N_path), ah_get_log_dir()); - strcat_s(N_path, ARRAY_SIZE(N_path), "crashlog.dmp"); - HANDLE hFile = CreateFile(N_path, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0); + OsPath path = OsPath(ah_get_log_dir())/"crashlog.dmp"; + HANDLE hFile = CreateFile(path.string().c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0); if(hFile == INVALID_HANDLE_VALUE) goto fail; diff --git a/source/lib/sysdep/win/wdll_ver.cpp b/source/lib/sysdep/win/wdll_ver.cpp index 4ebaf93e82..174c7f2adf 100644 --- a/source/lib/sysdep/win/wdll_ver.cpp +++ b/source/lib/sysdep/win/wdll_ver.cpp @@ -17,7 +17,7 @@ #include "win.h" #include "wutil.h" -#include "lib/file/io/io.h" // io_Allocate +#include "lib/allocators/shared_ptr.h" #if MSC_VERSION #pragma comment(lib, "version.lib") // DLL version @@ -47,7 +47,7 @@ static LibError ReadVersionString(const OsPath& modulePathname_, char* out_ver, if(!ver_size) WARN_RETURN(ERR::FAIL); - shared_ptr mem = io_Allocate(ver_size); + shared_ptr mem = Allocate(ver_size); if(!GetFileVersionInfo(modulePathname.c_str(), 0, ver_size, mem.get())) WARN_RETURN(ERR::FAIL); diff --git a/source/lib/sysdep/win/wposix/wdlfcn.cpp b/source/lib/sysdep/win/wposix/wdlfcn.cpp index 76a66ee70b..3bc5a77437 100644 --- a/source/lib/sysdep/win/wposix/wdlfcn.cpp +++ b/source/lib/sysdep/win/wposix/wdlfcn.cpp @@ -1,7 +1,7 @@ #include "precompiled.h" #include "wdlfcn.h" -#include "lib/path_util.h" +#include "lib/os_path.h" #include "wposix_internal.h" @@ -34,16 +34,8 @@ void* dlopen(const char* so_name, int flags) { debug_assert(!(flags & RTLD_GLOBAL)); - // if present, strip .so extension; add .dll extension - char dll_name[MAX_PATH]; - strcpy_s(dll_name, ARRAY_SIZE(dll_name)-5, so_name); - char* ext = (char*)path_extension(dll_name); - if(ext[0] == '\0') // no extension - strcat(dll_name, ".dll"); // safe - else // need to replace extension - SAFE_STRCPY(ext, "dll"); - - HMODULE hModule = LoadLibrary(dll_name); + OsPath path = fs::change_extension(so_name, ".dll"); + HMODULE hModule = LoadLibrary(path.external_directory_string().c_str()); debug_assert(hModule); return void_from_HMODULE(hModule); } diff --git a/source/lib/tests/test_lockfree.h b/source/lib/tests/test_lockfree.h index b35b1b4274..39bbf7b9da 100644 --- a/source/lib/tests/test_lockfree.h +++ b/source/lib/tests/test_lockfree.h @@ -217,7 +217,7 @@ public: srand(1); static const double TEST_LENGTH = 30.; // [seconds] - const double end_time = get_time() + TEST_LENGTH; + const double end_time = timer_Time() + TEST_LENGTH; is_complete = false; TS_ASSERT_OK(lfl_init(&list)); @@ -234,7 +234,7 @@ public: } // wait until time interval elapsed (if we get that far, all is well). - while(get_time() < end_time) + while(timer_Time() < end_time) usleep(10*1000); // signal and wait for all threads to complete (poor man's barrier - diff --git a/source/lib/tests/test_path_util.h b/source/lib/tests/test_path_util.h index 1cce95040f..90d3dd0b5c 100644 --- a/source/lib/tests/test_path_util.h +++ b/source/lib/tests/test_path_util.h @@ -17,12 +17,6 @@ TS_ASSERT_STR_EQUALS(result, correct_result); \ } -#define TEST_LAST_COMPONENT(path, correct_result) \ -{ \ - const char* result = path_last_component(path); \ - TS_ASSERT_STR_EQUALS(result, correct_result); \ -} - #define TEST_STRIP_FN(path_readonly, correct_result) \ { \ char path[PATH_MAX]; \ @@ -33,16 +27,6 @@ class TestPathUtil : public CxxTest::TestSuite { - // if correct_ret is ERR::FAIL, ignore correct_result. - void TEST_REPLACE(const char* src, const char* remove, const char* replace, - LibError correct_ret, const char* correct_result) - { - char dst[PATH_MAX] = {0}; - TS_ASSERT_EQUALS(path_replace(dst, src, remove, replace), correct_ret); - if(correct_ret != ERR::FAIL) - TS_ASSERT_STR_EQUALS(dst, correct_result); - } - void TEST_PATH_EXT(const char* path, const char* correct_result) { const char* result = path_extension(path); @@ -96,22 +80,6 @@ public: TEST_APPEND("abc/", "def/", PATH_APPEND_SLASH, "abc/def/"); } - void test_replace() - { - // no match - TEST_REPLACE("abc/def", "/def", "xx", ERR::FAIL, 0); - // normal case: match and remove - TEST_REPLACE("abc/def", "abc", "ok", INFO::OK, "ok/def"); - // caller also stripping / - TEST_REPLACE("abc/def", "abc/", "ok", INFO::OK, "ok/def"); - // empty remove - TEST_REPLACE("abc/def", "", "ok", INFO::OK, "ok/abc/def"); - // empty replace - TEST_REPLACE("abc/def", "abc", "", INFO::OK, "def"); - // remove entire string - TEST_REPLACE("abc/def", "abc/def", "", INFO::OK, ""); - } - void test_name_only() { // path with filename @@ -128,33 +96,6 @@ public: TEST_NAME_ONLY("", ""); } - void test_last_component() - { - // path with filename - TEST_LAST_COMPONENT("abc/def", "def"); - // nonportable path with filename - TEST_LAST_COMPONENT("abc\\def\\ghi", "ghi"); - // mixed path with filename - TEST_LAST_COMPONENT("abc/def\\ghi", "ghi"); - // mixed path with filename (2) - TEST_LAST_COMPONENT("abc\\def/ghi", "ghi"); - // filename only - TEST_LAST_COMPONENT("abc", "abc"); - // empty - TEST_LAST_COMPONENT("", ""); - - // now paths (mostly copied from above test series) - - // path - TEST_LAST_COMPONENT("abc/def/", "def/"); - // nonportable path - TEST_LAST_COMPONENT("abc\\def\\ghi\\", "ghi\\"); - // mixed path - TEST_LAST_COMPONENT("abc/def\\ghi/", "ghi/"); - // mixed path (2) - TEST_LAST_COMPONENT("abc\\def/ghi\\", "ghi\\"); - } - void test_strip_fn() { // path with filename @@ -175,9 +116,6 @@ public: TEST_STRIP_FN("abc\\def\\ghi\\", "abc\\def\\ghi\\"); } - // note: no need to test path_dir_only - it is implemented exactly as - // done in TEST_STRIP_FN. - void test_path_ext() { TEST_PATH_EXT("a/b/c.bmp", "bmp"); diff --git a/source/lib/tests/test_secure_crt.h b/source/lib/tests/test_secure_crt.h index f12b5b7470..26f0f1d8d8 100644 --- a/source/lib/tests/test_secure_crt.h +++ b/source/lib/tests/test_secure_crt.h @@ -106,7 +106,7 @@ public: // this is still preferable to completely disabling the self-test. void test_param_validation() { - #if !HAVE_SECURE_CRT + #if EMULATE_SECURE_CRT debug_skip_next_err(ERR::INVALID_PARAM); TEST_CPY(0 ,0,0 , EINVAL,""); // all invalid debug_skip_next_err(ERR::INVALID_PARAM); diff --git a/source/lib/tex/tex.cpp b/source/lib/tex/tex.cpp index aaa7ed38f7..b45dae3956 100644 --- a/source/lib/tex/tex.cpp +++ b/source/lib/tex/tex.cpp @@ -17,6 +17,7 @@ #include "lib/timer.h" #include "lib/bits.h" +#include "lib/sysdep/cpu.h" #include "tex_codec.h" @@ -571,11 +572,11 @@ size_t tex_img_size(const Tex* t) // buffer that will hold the image, allocate this much extra and // pass the pointer as base+hdr_size. this allows writing the header // directly into the output buffer and makes for zero-copy IO. -size_t tex_hdr_size(const char* fn) +size_t tex_hdr_size(const VfsPath& filename) { const TexCodecVTbl* c; - const std::string extension = fs::extension(fn); + const std::string extension = fs::extension(filename); CHECK_ERR(tex_codec_for_filename(extension, &c)); return c->hdr_size(0); } diff --git a/source/lib/tex/tex.h b/source/lib/tex/tex.h index d40b68c1ed..376638a142 100644 --- a/source/lib/tex/tex.h +++ b/source/lib/tex/tex.h @@ -426,6 +426,6 @@ extern bool tex_is_known_extension(const char* filename); * case-insensitive. * @return size [bytes] or 0 on error (i.e. no codec found). **/ -extern size_t tex_hdr_size(const char* fn); +extern size_t tex_hdr_size(const VfsPath& filename); #endif // INCLUDED_TEX diff --git a/source/lib/timer.cpp b/source/lib/timer.cpp index ea819c0f78..017779eb58 100644 --- a/source/lib/timer.cpp +++ b/source/lib/timer.cpp @@ -2,8 +2,7 @@ * ========================================================================= * File : timer.cpp * Project : 0 A.D. - * Description : platform-independent high resolution timer and - * : FPS measuring code. + * Description : platform-independent high resolution timer * ========================================================================= */ @@ -18,8 +17,6 @@ #include #include "lib/posix/posix_time.h" -#include "adts.h" -#include "module_init.h" #include "lib/sysdep/cpu.h" #if OS_WIN #include "lib/sysdep/win/whrt/whrt.h" @@ -31,7 +28,6 @@ # include "lib/sysdep/ia32/ia32.h" // ia32_rdtsc #endif - #if OS_UNIX || OS_WIN # define HAVE_GETTIMEOFDAY 1 #else @@ -55,7 +51,11 @@ static struct timespec start; static struct timeval start; #endif -static void LatchStartTime() + +//----------------------------------------------------------------------------- +// timer API + +void timer_LatchStartTime() { #if HAVE_CLOCK_GETTIME (void)clock_gettime(CLOCK_REALTIME, &start); @@ -64,7 +64,8 @@ static void LatchStartTime() #endif } -double get_time() + +double timer_Time() { double t; @@ -79,7 +80,7 @@ double get_time() gettimeofday(&cur, 0); t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6; #else -# error "get_time: add timer implementation for this platform!" +# error "timer_Time: add timer implementation for this platform!" #endif // make sure time is monotonic (never goes backwards) @@ -92,9 +93,7 @@ double get_time() } -// return resolution (expressed in [s]) of the time source underlying -// get_time. -double timer_res() +double timer_Resolution() { // may take a while to determine, so cache it static double cached_res = 0.0; @@ -110,10 +109,10 @@ double timer_res() #elif OS_WIN res = whrt_Resolution(); #else - const double t0 = get_time(); + const double t0 = timer_Time(); double t1, t2; - do t1 = get_time(); while(t1 == t0); - do t2 = get_time(); while(t2 == t1); + do t1 = timer_Time(); while(t1 == t0); + do t2 = timer_Time(); while(t2 == t1); res = t2-t1; #endif @@ -124,9 +123,109 @@ double timer_res() //----------------------------------------------------------------------------- -// cumulative timer API, useful for profiling. -// this supplements in-game profiling by providing low-overhead, -// high resolution time accounting. +ScopeTimer::ScopeTimer(const char* description) + : m_t0(timer_Time()), m_description(description) +{ +} + + +ScopeTimer::~ScopeTimer() +{ + double t1 = timer_Time(); + double dt = t1-m_t0; + + // determine scale factor for pretty display + double scale = 1e6; + const char* unit = "us"; + if(dt > 1.0) + scale = 1, unit = "s"; + else if(dt > 1e-3) + scale = 1e3, unit = "ms"; + + debug_printf("TIMER| %s: %g %s\n", m_description, dt*scale, unit); +} + + +//----------------------------------------------------------------------------- +// TimerUnit + +// since TIMER_ACCRUE et al. are called so often, we try to keep +// overhead to an absolute minimum. storing raw tick counts (e.g. CPU cycles +// returned by ia32_rdtsc) instead of absolute time has two benefits: +// - no need to convert from raw->time on every call +// (instead, it's only done once when displaying the totals) +// - possibly less overhead to querying the time itself +// (timer_Time may be using slower time sources with ~3us overhead) +// +// however, the cycle count is not necessarily a measure of wall-clock time +// (see http://www.gamedev.net/reference/programming/features/timing). +// therefore, on systems with SpeedStep active, measurements of I/O or other +// non-CPU bound activity may be skewed. this is ok because the timer is +// only used for profiling; just be aware of the issue. +// if this is a problem, disable CONFIG_TIMER_ALLOW_RDTSC. +// +// note that overflow isn't an issue either way (63 bit cycle counts +// at 10 GHz cover intervals of 29 years). + +#if ARCH_IA32 && CONFIG_TIMER_ALLOW_RDTSC + +void TimerUnit::SetToZero() +{ + m_ticks = 0; +} + +void TimerUnit::SetFromTimer() +{ + m_ticks = ia32_rdtsc(); +} + +void TimerUnit::AddDifference(TimerUnit t0, TimerUnit t1) +{ + m_ticks += t1.m_ticks - t0.m_ticks; +} + +void TimerUnit::Subtract(TimerUnit t) +{ + m_ticks -= t.m_ticks; +} + +double TimerUnit::Seconds() const +{ + return m_ticks / cpu_ClockFrequency(); +} + +#else + +void TimerUnit::SetToZero() +{ + m_seconds = 0.0; +} + +void TimerUnit::SetFromTimer() +{ + m_seconds = timer_Time(); +} + +void TimerUnit::AddDifference(TimerUnit t0, TimerUnit t1) +{ + m_seconds += t1.m_seconds - t0.m_seconds; +} + +void TimerUnit::Subtract(TimerUnit t) +{ + m_seconds -= t.m_seconds; +} + +double TimerUnit::Seconds() const +{ + return m_seconds; +} + +#endif + + +//----------------------------------------------------------------------------- +// client API // intrusive linked-list of all clients. a fixed-size limit would be // acceptable (since timers are added manually), but the list is easy @@ -138,18 +237,10 @@ static uint num_clients; static TimerClient* clients; -// make the given TimerClient (usually instantiated as static data) -// ready for use. returns its address for TIMER_ADD_CLIENT's convenience. -// this client's total (added to by timer_bill_client) will be -// displayed by timer_display_client_totals. -// notes: -// - may be called at any time; -// - always succeeds (there's no fixed limit); -// - free() is not needed nor possible. -// - description must remain valid until exit; a string literal is safest. -TimerClient* timer_add_client(TimerClient* tc, const char* description) +TimerClient* timer_AddClient(TimerClient* tc, const char* description) { - tc->sum = 0.0; + tc->sum.SetToZero(); + tc->description = description; // insert at front of list @@ -161,17 +252,14 @@ TimerClient* timer_add_client(TimerClient* tc, const char* description) } -// add
to the client's total. -void timer_bill_client(TimerClient* tc, TimerUnit dt) +void timer_BillClient(TimerClient* tc, TimerUnit t0, TimerUnit t1) { - tc->sum += dt; + tc->sum.AddDifference(t0, t1); tc->num_calls++; } -// display all clients' totals; does not reset them. -// typically called at exit. -void timer_display_client_totals() +void timer_DisplayClientTotals() { debug_printf("TIMER TOTALS (%d clients)\n", num_clients); debug_printf("-----------------------------------------------------\n"); @@ -184,7 +272,7 @@ void timer_display_client_totals() clients = tc->next; num_clients--; - const double sum = Timer::ToSeconds(tc->sum); + const double sum = tc->sum.Seconds(); // determine scale factor for pretty display double scale = 1e6; @@ -201,32 +289,16 @@ void timer_display_client_totals() } -#if ARCH_IA32 && CONFIG_TIMER_ALLOW_RDTSC - -TimerRdtsc::unit TimerRdtsc::get_timestamp() const +ScopeTimerAccrue::ScopeTimerAccrue(TimerClient* tc) + : m_tc(tc) { - return ia32_rdtsc(); + m_t0.SetFromTimer(); } -#endif - -//----------------------------------------------------------------------------- - -static ModuleInitState initState; - -void timer_Init() +ScopeTimerAccrue::~ScopeTimerAccrue() { - if(!ModuleShouldInitialize(&initState)) - return; - - LatchStartTime(); -} - -void timer_Shutdown() -{ - if(!ModuleShouldShutdown(&initState)) - return; - - // nothing to do + TimerUnit t1; + t1.SetFromTimer(); + timer_BillClient(m_tc, m_t0, t1); } diff --git a/source/lib/timer.h b/source/lib/timer.h index 5dba6b8d74..db79b712fb 100644 --- a/source/lib/timer.h +++ b/source/lib/timer.h @@ -2,8 +2,7 @@ * ========================================================================= * File : timer.h * Project : 0 A.D. - * Description : platform-independent high resolution timer and - * : FPS measuring code. + * Description : platform-independent high resolution timer * ========================================================================= */ @@ -12,88 +11,99 @@ #ifndef INCLUDED_TIMER #define INCLUDED_TIMER -#include +/** + * timer_Time will subsequently return values relative to the current time. + **/ +LIB_API void timer_LatchStartTime(); -#include "debug.h" // debug_printf -#include "lib/sysdep/cpu.h" +/** + * @return high resolution (> 1 us) timestamp [s]. + **/ +LIB_API double timer_Time(void); - -LIB_API void timer_Init(); -LIB_API void timer_Shutdown(); - -// high resolution (> 1 us) timestamp [s], starting at or near 0 s. -LIB_API double get_time(void); - -// return resolution (expressed in [s]) of the time source underlying -// get_time. -LIB_API double timer_res(void); +/** + * @return resolution [s] of the timer. + **/ +LIB_API double timer_Resolution(void); //----------------------------------------------------------------------------- -// timestamp sources +// scope timing -// since TIMER_ACCRUE et al. are called so often, we try to keep -// overhead to an absolute minimum. storing raw tick counts (e.g. CPU cycles -// returned by ia32_rdtsc) instead of absolute time has two benefits: -// - no need to convert from raw->time on every call -// (instead, it's only done once when displaying the totals) -// - possibly less overhead to querying the time itself -// (get_time may be using slower time sources with ~3us overhead) -// -// however, the cycle count is not necessarily a measure of wall-clock time. -// therefore, on systems with SpeedStep active, measurements of I/O or other -// non-CPU bound activity may be skewed. this is ok because the timer is -// only used for profiling; just be aware of the issue. -// if this is a problem, disable CONFIG_TIMER_ALLOW_RDTSC. -// -// note that overflow isn't an issue either way (63 bit cycle counts -// at 10 GHz cover intervals of 29 years). - -#if ARCH_IA32 && CONFIG_TIMER_ALLOW_RDTSC - -// fast, IA-32 specific, not usable as wall-clock -// (see http://www.gamedev.net/reference/programming/features/timing) -class TimerRdtsc +/// used by TIMER +class ScopeTimer : boost::noncopyable { public: - typedef i64 unit; - unit get_timestamp() const; - static double ToSeconds(unit value) - { - return value / cpu_ClockFrequency(); - } + LIB_API ScopeTimer(const char* description); + LIB_API ~ScopeTimer(); + +private: + double m_t0; + const char* m_description; }; -#endif +/** + * Measures the time taken to execute code up until end of the current scope; + * displays it via debug_printf. Can safely be nested. + * Useful for measuring time spent in a function or basic block. + * must remain valid over the lifetime of this object; + * a string literal is safest. + * + * Example usage: + * void func() + * { + * TIMER("description"); + * // code to be measured + * } + **/ +#define TIMER(description) ScopeTimer UID__(description) -class TimerSafe -{ -public: - typedef double unit; - unit get_timestamp() const - { - return get_time(); - } - static double ToSeconds(unit value) - { - return value; - } -}; - -#if ARCH_IA32 && CONFIG_TIMER_ALLOW_RDTSC -typedef TimerRdtsc Timer; -#else -typedef TimerSafe Timer; -#endif - -typedef Timer::unit TimerUnit; // convenience +/** + * Measures the time taken to execute code between BEGIN and END markers; + * displays it via debug_printf. Can safely be nested. + * Useful for measuring several pieces of code within the same function/block. + * must remain valid over the lifetime of this object; + * a string literal is safest. + * + * Caveats: + * - this wraps the code to be measured in a basic block, so any + * variables defined there are invisible to surrounding code. + * - the description passed to END isn't inspected; you are responsible for + * ensuring correct nesting! + * + * Example usage: + * void func2() + * { + * // uninteresting code + * TIMER_BEGIN("description2"); + * // code to be measured + * TIMER_END("description2"); + * // uninteresting code + * } + **/ +#define TIMER_BEGIN(description) { ScopeTimer UID__(description) +#define TIMER_END(description) } //----------------------------------------------------------------------------- // cumulative timer API // this supplements in-game profiling by providing low-overhead, -// high resolution time accounting. +// high resolution time accounting of specific areas. + +union LIB_API TimerUnit +{ +public: + void SetToZero(); + void SetFromTimer(); + void AddDifference(TimerUnit t0, TimerUnit t1); + void Subtract(TimerUnit t); + double Seconds() const; + +private: + u64 m_ticks; + double m_seconds; +}; // opaque - do not access its fields! // note: must be defined here because clients instantiate them; @@ -107,165 +117,78 @@ struct TimerClient TimerClient* next; - // how often timer_bill_client was called (helps measure relative + // how often timer_BillClient was called (helps measure relative // performance of something that is done indeterminately often). uint num_calls; }; +/** + * make the given TimerClient (usually instantiated as static data) + * ready for use. returns its address for TIMER_ADD_CLIENT's convenience. + * this client's total (added to by timer_BillClient) will be + * displayed by timer_DisplayClientTotals. + * notes: + * - may be called at any time; + * - always succeeds (there's no fixed limit); + * - free() is not needed nor possible. + * - description must remain valid until exit; a string literal is safest. + **/ +LIB_API TimerClient* timer_AddClient(TimerClient* tc, const char* description); -// make the given TimerClient (usually instantiated as static data) -// ready for use. returns its address for TIMER_ADD_CLIENT's convenience. -// this client's total (added to by timer_bill_client) will be -// displayed by timer_display_client_totals. -// notes: -// - may be called at any time; -// - always succeeds (there's no fixed limit); -// - free() is not needed nor possible. -// - description must remain valid until exit; a string literal is safest. -LIB_API TimerClient* timer_add_client(TimerClient* tc, const char* description); - -// add
to the client's total. -LIB_API void timer_bill_client(TimerClient* tc, TimerUnit dt); - -// display all clients' totals; does not reset them. -// typically called at exit. -LIB_API void timer_display_client_totals(); - - -//----------------------------------------------------------------------------- -// scoped-based timers - -// used via TIMER* macros below. -class ScopeTimer -{ - double t0; - const char* description; - -public: - ScopeTimer(const char* _description) - { - t0 = get_time(); - description = _description; - } - - ~ScopeTimer() - { - double t1 = get_time(); - double dt = t1-t0; - - // determine scale factor for pretty display - double scale = 1e6; - const char* unit = "us"; - if(dt > 1.0) - scale = 1, unit = "s"; - else if(dt > 1e-3) - scale = 1e3, unit = "ms"; - - debug_printf("TIMER| %s: %g %s\n", description, dt*scale, unit); - } - - // disallow copying (makes no sense) -private: - ScopeTimer& operator=(const ScopeTimer&); -}; - -/* -Measure the time taken to execute code up until end of the current scope; -display it via debug_printf. Can safely be nested. -Useful for measuring time spent in a function or basic block. - must remain valid over the lifetime of this object; -a string literal is safest. - -Example usage: - void func() - { - TIMER("description"); - // code to be measured - } -*/ -#define TIMER(description) ScopeTimer UID__(description) - -/* -Measure the time taken to execute code between BEGIN and END markers; -display it via debug_printf. Can safely be nested. -Useful for measuring several pieces of code within the same function/block. - must remain valid over the lifetime of this object; -a string literal is safest. - -Caveats: -- this wraps the code to be measured in a basic block, so any - variables defined there are invisible to surrounding code. -- the description passed to END isn't inspected; you are responsible for - ensuring correct nesting! - -Example usage: - void func2() - { - // uninteresting code - TIMER_BEGIN("description2"); - // code to be measured - TIMER_END("description2"); - // uninteresting code - } -*/ -#define TIMER_BEGIN(description) { ScopeTimer UID__(description) -#define TIMER_END(description) } - - -// used via TIMER_ACCRUE -template class ScopeTimerAccrue -{ - TimerImpl impl; - typename TimerImpl::unit t0; - TimerClient* tc; - -public: - ScopeTimerAccrue(TimerClient* tc_) - { - t0 = impl.get_timestamp(); - tc = tc_; - } - ~ScopeTimerAccrue() - { - typename TimerImpl::unit dt = impl.get_timestamp() - t0; - timer_bill_client(tc, dt); - } - - // disallow copying (makes no sense) -private: - ScopeTimerAccrue& operator=(const ScopeTimerAccrue&); -}; - - -// "allocate" a new TimerClient that will keep track of the total time -// billed to it, along with a description string. These are displayed when -// timer_display_client_totals is called. -// Invoke this at file or function scope; a (static) TimerClient pointer of -// name will be defined, which should be passed to TIMER_ACCRUE. +/** + * "allocate" a new TimerClient that will keep track of the total time + * billed to it, along with a description string. These are displayed when + * timer_DisplayClientTotals is called. + * Invoke this at file or function scope; a (static) TimerClient pointer of + * name will be defined, which should be passed to TIMER_ACCRUE. + **/ #define TIMER_ADD_CLIENT(id)\ static TimerClient UID__;\ - static TimerClient* id = timer_add_client(&UID__, #id); + static TimerClient* id = timer_AddClient(&UID__, #id); -/* -Measure the time taken to execute code up until end of the current scope; -bill it to the given TimerClient object. Can safely be nested. -Useful for measuring total time spent in a function or basic block over the -entire program. - must remain valid over the lifetime of this object; -a string literal is safest. +/** + * bill the difference between t0 and t1 to the client's total. + **/ +LIB_API void timer_BillClient(TimerClient* tc, TimerUnit t0, TimerUnit t1); -Example usage: - TIMER_ADD_CLIENT(identifier) +/** + * display all clients' totals; does not reset them. + * typically called at exit. + **/ +LIB_API void timer_DisplayClientTotals(); - void func() - { - TIMER_ACCRUE(name_of_pointer_to_client); - // code to be measured - } +/// used by TIMER_ACCRUE +class ScopeTimerAccrue +{ +public: + LIB_API ScopeTimerAccrue(TimerClient* tc); + LIB_API ~ScopeTimerAccrue(); - [at exit] - timer_display_client_totals(); -*/ -#define TIMER_ACCRUE(client) ScopeTimerAccrue<> UID__(client) +private: + TimerUnit m_t0; + TimerClient* m_tc; +}; + +/** + * Measure the time taken to execute code up until end of the current scope; + * bill it to the given TimerClient object. Can safely be nested. + * Useful for measuring total time spent in a function or basic block over the + * entire program. + * must remain valid over the lifetime of this object; + * a string literal is safest. + * + * Example usage: + * TIMER_ADD_CLIENT(identifier) + * + * void func() + * { + * TIMER_ACCRUE(name_of_pointer_to_client); + * // code to be measured + * } + * + * [at exit] + * timer_DisplayClientTotals(); + **/ +#define TIMER_ACCRUE(client) ScopeTimerAccrue UID__(client) #endif // #ifndef INCLUDED_TIMER diff --git a/source/main.cpp b/source/main.cpp index 425a8cdd47..e2a8cb7977 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -165,12 +165,12 @@ static void Frame() ogl_WarnIfError(); // get elapsed time - const double time = get_time(); + const double time = timer_Time(); g_frequencyFilter->Update(time); // .. old method - "exact" but contains jumps #if 0 static double last_time; - const double time = get_time(); + const double time = timer_Time(); const float TimeSinceLastFrame = (float)(time-last_time); last_time = time; ONCE(return); // first call: set last_time and return @@ -363,7 +363,7 @@ static void RunGameOrAtlas(int argc, char* argv[]) // run the game Init(args, 0); - g_frequencyFilter = CreateFrequencyFilter(timer_res(), 30.0); + g_frequencyFilter = CreateFrequencyFilter(timer_Resolution(), 30.0); MainControllerInit(); while(!quit) Frame(); diff --git a/source/network/NetLog.cpp b/source/network/NetLog.cpp index 0abbfa0150..0e91ab1621 100644 --- a/source/network/NetLog.cpp +++ b/source/network/NetLog.cpp @@ -1039,7 +1039,7 @@ void CNetLogger::GetStringTime( CStr& str ) void CNetLogger::GetStringTimeStamp( CStr& str ) { char buffer[ 128 ] = { 0 }; - sprintf( buffer, "[%3u.%03u]", ( uint )get_time(), ( ( uint )( get_time() * 1000 ) % 1000 ) ); + sprintf( buffer, "[%3u.%03u]", ( uint )timer_Time(), ( ( uint )( timer_Time() * 1000 ) % 1000 ) ); str = buffer; } diff --git a/source/network/NetLog.h b/source/network/NetLog.h index 063f30be89..6aa1f7c1bf 100644 --- a/source/network/NetLog.h +++ b/source/network/NetLog.h @@ -67,7 +67,7 @@ public: * * @return Event time */ - inline Timer GetTimeStamp( void ) const { return m_TimeStamp; } + inline TimerUnit GetTimeStamp( void ) const { return m_TimeStamp; } /** * Returns the name of the logger which logged the event @@ -94,7 +94,7 @@ private: LogLevel m_Level; // Current level CStr m_LoggerName; // Logger which processed the event CStr m_Message; // Application message for the event - Timer m_TimeStamp; // Event logging time + TimerUnit m_TimeStamp; // Event logging time }; /* diff --git a/source/ps/CConsole.cpp b/source/ps/CConsole.cpp index 78b83c5de2..449b3d7585 100644 --- a/source/ps/CConsole.cpp +++ b/source/ps/CConsole.cpp @@ -599,7 +599,7 @@ void CConsole::SetBuffer(const wchar_t* szMessage, ...) m_iBufferPos = std::min(oldBufferPos, m_iBufferLength); } -void CConsole::UseHistoryFile(const CStr& filename, int max_history_lines) +void CConsole::UseHistoryFile(const VfsPath& filename, int max_history_lines) { m_MaxHistoryLines = max_history_lines; diff --git a/source/ps/CConsole.h b/source/ps/CConsole.h index 1d0e9b15b2..db6adcd9ad 100644 --- a/source/ps/CConsole.h +++ b/source/ps/CConsole.h @@ -14,6 +14,7 @@ #include "CStr.h" #include "lib/input.h" +#include "lib/file/vfs/vfs_path.h" #ifndef INCLUDED_CCONSOLE #define INCLUDED_CCONSOLE @@ -27,6 +28,43 @@ typedef void(*fptr)(void); class CConsole { +public: + CConsole(); + ~CConsole(); + + void SetSize(float X = 300, float Y = 0, float W = 800, float H = 600); + void UpdateScreenSize(int w, int h); + + void ToggleVisible(); + void SetVisible( bool visible ); + + void Update(float DeltaTime); + + void Render(); + + void InsertMessage(const wchar_t* szMessage, ...); + void InsertChar(const int szChar, const wchar_t cooked); + + void SendChatMessage(const wchar_t *szMessage); + void ReceivedChatMessage(const wchar_t *pSender, const wchar_t *szMessage); + + void SetBuffer(const wchar_t* szMessage, ...); + + void UseHistoryFile( const VfsPath& filename, int historysize ); + + // Only returns a pointer to the buffer; copy out of here if you want to keep it. + const wchar_t* GetBuffer(); + void FlushBuffer(); + + void RegisterFunc(fptr F, const wchar_t* szName); + + bool IsActive() { return m_bVisible; } + + int m_iFontHeight; + int m_iFontWidth; + int m_iFontOffset; // distance to move up before drawing + size_t m_charsPerPage; + private: float m_fX; float m_fY; @@ -50,7 +88,7 @@ private: int m_iBufferPos; int m_iBufferLength; - CStr m_sHistoryFile; + VfsPath m_sHistoryFile; int m_MaxHistoryLines; bool m_bFocus; @@ -81,43 +119,6 @@ private: void SaveHistory(); JSObject* m_ScriptObject; // to run scripts in, so they can define variables that are retained between commands - -public: - CConsole(); - ~CConsole(); - - void SetSize(float X = 300, float Y = 0, float W = 800, float H = 600); - void UpdateScreenSize(int w, int h); - - void ToggleVisible(); - void SetVisible( bool visible ); - - void Update(float DeltaTime); - - void Render(); - - void InsertMessage(const wchar_t* szMessage, ...); - void InsertChar(const int szChar, const wchar_t cooked); - - void SendChatMessage(const wchar_t *szMessage); - void ReceivedChatMessage(const wchar_t *pSender, const wchar_t *szMessage); - - void SetBuffer(const wchar_t* szMessage, ...); - - void UseHistoryFile( const CStr& filename, int historysize ); - - // Only returns a pointer to the buffer; copy out of here if you want to keep it. - const wchar_t* GetBuffer(); - void FlushBuffer(); - - void RegisterFunc(fptr F, const wchar_t* szName); - - bool IsActive() { return m_bVisible; } - - int m_iFontHeight; - int m_iFontWidth; - int m_iFontOffset; // distance to move up before drawing - size_t m_charsPerPage; }; extern CConsole* g_Console; diff --git a/source/ps/FilePacker.cpp b/source/ps/FilePacker.cpp index 1684744e0f..295d5585b3 100644 --- a/source/ps/FilePacker.cpp +++ b/source/ps/FilePacker.cpp @@ -31,7 +31,7 @@ CFilePacker::CFilePacker(u32 version, const char magicstr[4]) //////////////////////////////////////////////////////////////////////////////////////// // Write: write out to file all packed data added so far -void CFilePacker::Write(const char* filename) +void CFilePacker::Write(const VfsPath& filename) { const u32 size_le = to_le32(u32_from_larger(m_writeBuffer.Size())); m_writeBuffer.Overwrite(&size_le, sizeof(size_le), 8); diff --git a/source/ps/FilePacker.h b/source/ps/FilePacker.h index a94e53e93c..da410bda91 100644 --- a/source/ps/FilePacker.h +++ b/source/ps/FilePacker.h @@ -11,6 +11,7 @@ #include #include "CStr.h" +#include "lib/file/vfs/vfs_path.h" #include "ps/Errors.h" #include "ps/Filesystem.h" // WriteBuffer @@ -37,7 +38,7 @@ public: CFilePacker(u32 version, const char magicstr[4]); // Write: write out to file all packed data added so far - void Write(const char* filename); + void Write(const VfsPath& filename); // PackRaw: pack given number of bytes onto the end of the data stream void PackRaw(const void* rawdata, size_t rawdatalen); diff --git a/source/ps/FileUnpacker.cpp b/source/ps/FileUnpacker.cpp index e8b07ef2be..c25e24a15d 100644 --- a/source/ps/FileUnpacker.cpp +++ b/source/ps/FileUnpacker.cpp @@ -29,7 +29,7 @@ CFileUnpacker::~CFileUnpacker() //////////////////////////////////////////////////////////////////////////////////////// // Read: open and read in given file, check magic bits against those given; throw // variety of exceptions for missing files etc -void CFileUnpacker::Read(const char* filename, const char magicstr[4]) +void CFileUnpacker::Read(const VfsPath& filename, const char magicstr[4]) { // avoid vfs_load complaining about missing data files (which happens // too often). better to check here than squelch internal VFS error diff --git a/source/ps/FileUnpacker.h b/source/ps/FileUnpacker.h index 3a8a3bf1f3..3f96aa9cdf 100644 --- a/source/ps/FileUnpacker.h +++ b/source/ps/FileUnpacker.h @@ -11,6 +11,7 @@ #include +#include "lib/file/vfs/vfs_path.h" class CStr8; #include "ps/Errors.h" @@ -37,7 +38,7 @@ public: // Read: open and read in given file, check magic bits against those given; throw // variety of exceptions for missing files etc - void Read(const char* filename, const char magicstr[4]); + void Read(const VfsPath& filename, const char magicstr[4]); // GetVersion: return stored file version u32 GetVersion() const { return m_Version; } diff --git a/source/ps/Filesystem.cpp b/source/ps/Filesystem.cpp index 885e3f5d8f..429f7cccd7 100644 --- a/source/ps/Filesystem.cpp +++ b/source/ps/Filesystem.cpp @@ -13,6 +13,12 @@ bool FileExists(const char* pathname) return g_VFS->GetFileInfo(pathname, 0) == INFO::OK; } +bool FileExists(const VfsPath& pathname) +{ + return g_VFS->GetFileInfo(pathname, 0) == INFO::OK; +} + + CVFSFile::CVFSFile() { @@ -22,7 +28,7 @@ CVFSFile::~CVFSFile() { } -PSRETURN CVFSFile::Load(const char* filename) +PSRETURN CVFSFile::Load(const VfsPath& filename) { // Load should never be called more than once, so complain if (m_Buffer) @@ -34,7 +40,7 @@ PSRETURN CVFSFile::Load(const char* filename) LibError ret = g_VFS->LoadFile(filename, m_Buffer, m_BufferSize); if (ret != INFO::OK) { - LOG(CLogger::Error, LOG_CATEGORY, "CVFSFile: file %s couldn't be opened (vfs_load: %d)", filename, ret); + LOG(CLogger::Error, LOG_CATEGORY, "CVFSFile: file %s couldn't be opened (vfs_load: %d)", filename.string().c_str(), ret); return PSRETURN_CVFSFile_LoadFailed; } diff --git a/source/ps/Filesystem.h b/source/ps/Filesystem.h index 1e14640b9c..43a5241365 100644 --- a/source/ps/Filesystem.h +++ b/source/ps/Filesystem.h @@ -14,6 +14,7 @@ extern PIVFS g_VFS; extern bool FileExists(const char* pathname); +extern bool FileExists(const VfsPath& pathname); ERROR_GROUP(CVFSFile); ERROR_TYPE(CVFSFile, LoadFailed); @@ -29,7 +30,7 @@ public: // Returns either PSRETURN_OK or PSRETURN_CVFSFile_LoadFailed. // Dies if a file has already been successfully loaded. - PSRETURN Load(const char* filename); + PSRETURN Load(const VfsPath& filename); // These die if called when no file has been successfully loaded. const u8* GetBuffer() const; diff --git a/source/ps/GameSetup/Config.cpp b/source/ps/GameSetup/Config.cpp index e7d532b4cf..a59784b8bf 100644 --- a/source/ps/GameSetup/Config.cpp +++ b/source/ps/GameSetup/Config.cpp @@ -56,13 +56,15 @@ CStr g_AutostartMap = ""; static void LoadProfile( const CStr& profile ) { - CStr base = CStr( "profiles/" ) + profile; - g_ConfigDB.SetConfigFile(CFG_USER, true, base + "/settings/user.cfg"); + VfsPath path = VfsPath("profiles") / profile; + + VfsPath configFilename = path / "settings/user.cfg"; + g_ConfigDB.SetConfigFile(CFG_USER, true, configFilename.string().c_str()); g_ConfigDB.Reload(CFG_USER); int max_history_lines = 200; CFG_GET_USER_VAL("console.history.size", Int, max_history_lines); - g_Console->UseHistoryFile(base+"/settings/history", max_history_lines); + g_Console->UseHistoryFile(path / "settings/history", max_history_lines); } diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 596c9bf865..5582b9f413 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -855,7 +855,7 @@ void Shutdown(uint flags) TIMER_END("resource modules"); TIMER_BEGIN("shutdown misc"); - timer_display_client_totals(); + timer_DisplayClientTotals(); // should be last, since the above use them debug_shutdown(); @@ -863,7 +863,6 @@ void Shutdown(uint flags) delete &g_Profiler; delete &g_ProfileViewer; - timer_Shutdown(); lockfree_Shutdown(); TIMER_END("shutdown misc"); } @@ -883,7 +882,7 @@ void EarlyInit() cpu_ConfigureFloatingPoint(); - timer_Init(); + timer_LatchStartTime(); // Initialise the low-quality rand function srand(time(NULL)); // NOTE: this rand should *not* be used for simulation! diff --git a/source/ps/Interact.cpp b/source/ps/Interact.cpp index 1831c3df71..2ee3b80445 100644 --- a/source/ps/Interact.cpp +++ b/source/ps/Interact.cpp @@ -1264,7 +1264,7 @@ InReaction InteractInputHandler( const SDL_Event_* ev ) // arrays above. if (button >= 0 && button < SDL_BUTTON_INDEX_COUNT) { - double time = get_time(); + double time = timer_Time(); // Reset clicks counter if too slow or if the cursor's // hovering over something else now. @@ -1293,7 +1293,7 @@ InReaction InteractInputHandler( const SDL_Event_* ev ) button_down = true; button_down_x = ev->ev.button.x; button_down_y = ev->ev.button.y; - button_down_time = get_time(); + button_down_time = timer_Time(); if( g_BuildingPlacer.m_active ) { g_BuildingPlacer.MousePressed(); diff --git a/source/ps/Loader.cpp b/source/ps/Loader.cpp index bb96fc5d95..23306b2444 100644 --- a/source/ps/Loader.cpp +++ b/source/ps/Loader.cpp @@ -227,10 +227,10 @@ LibError LDR_ProgressiveLoad(double time_budget, wchar_t* description, size_t ma } // call this task's function and bill elapsed time. - const double t0 = get_time(); + const double t0 = timer_Time(); int status = lr.func(lr.param, time_left); const bool timed_out = ldr_was_interrupted(status); - const double elapsed_time = get_time() - t0; + const double elapsed_time = timer_Time() - t0; time_left -= elapsed_time; task_elapsed_time += elapsed_time; diff --git a/source/ps/Loader.h b/source/ps/Loader.h index 0272832340..40d5c3db36 100644 --- a/source/ps/Loader.h +++ b/source/ps/Loader.h @@ -151,9 +151,9 @@ extern LibError LDR_NonprogressiveLoad(); // boilerplate check-if-timed-out and return-progress-percent code. // completed_jobs and total_jobs are ints and must be updated by caller. // assumes presence of a local variable (double) -// (as returned by get_time()) that indicates the time at which to abort. +// (as returned by timer_Time()) that indicates the time at which to abort. #define LDR_CHECK_TIMEOUT(completed_jobs, total_jobs)\ - if(get_time() > end_time)\ + if(timer_Time() > end_time)\ {\ int progress_percent = ((completed_jobs)*100 / (total_jobs));\ /* 0 means "finished", so don't return that! */\ diff --git a/source/ps/Profile.cpp b/source/ps/Profile.cpp index 48443164ef..8d4368a6d4 100644 --- a/source/ps/Profile.cpp +++ b/source/ps/Profile.cpp @@ -443,7 +443,7 @@ void CProfileNode::Call() calls_frame_current++; if( recursion++ == 0 ) { - start = get_time(); + start = timer_Time(); start_mallocs = get_memory_alloc_count(); } } @@ -454,7 +454,7 @@ bool CProfileNode::Return() if( ( --recursion == 0 ) && ( calls_frame_current != 0 ) ) { - time_frame_current += ( get_time() - start ); + time_frame_current += ( timer_Time() - start ); mallocs_frame_current += ( get_memory_alloc_count() - start_mallocs ); } return( recursion == 0 ); @@ -527,7 +527,7 @@ void CProfileManager::Stop() void CProfileManager::Reset() { root->Reset(); - start = frame_start = get_time(); + start = frame_start = timer_Time(); start_mallocs = frame_start_mallocs = get_memory_alloc_count(); } @@ -537,11 +537,11 @@ void CProfileManager::Frame() ONCE(malloc_initialize_hook()); #endif - root->time_frame_current = ( get_time() - frame_start ); + root->time_frame_current = ( timer_Time() - frame_start ); root->mallocs_frame_current = ( get_memory_alloc_count() - frame_start_mallocs ); root->Frame(); - frame_start = get_time(); + frame_start = timer_Time(); frame_start_mallocs = get_memory_alloc_count(); } diff --git a/source/ps/Util.cpp b/source/ps/Util.cpp index dc631a267d..2d9db9060d 100644 --- a/source/ps/Util.cpp +++ b/source/ps/Util.cpp @@ -5,7 +5,7 @@ #include "lib/ogl.h" #include "lib/timer.h" #include "lib/bits.h" // round_up -#include "lib/allocators/allocators.h" +#include "lib/allocators/shared_ptr.h" #include "lib/sysdep/gfx.h" #include "lib/sysdep/snd.h" #include "lib/sysdep/cpu.h" @@ -170,9 +170,9 @@ 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) +static LibError tex_write(Tex* t, const VfsPath& filename) { - const std::string extension = fs::extension(fn); + const std::string extension = fs::extension(filename); DynArray da; RETURN_ERR(tex_encode(t, extension, &da)); @@ -182,7 +182,7 @@ static LibError tex_write(Tex* t, const char* fn) { (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); + const ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos); if(bytes_written > 0) debug_assert(bytes_written == (ssize_t)da.pos); else @@ -202,13 +202,11 @@ static unsigned s_nextScreenshotNumber; void WriteScreenshot(const char* extension) { // get next available numbered filename - // .. bake extension into format string. // note: %04d -> always 4 digits, so sorting by filename works correctly. - char file_format_string[PATH_MAX]; - snprintf(file_format_string, PATH_MAX, "screenshots/screenshot%%04d.%s", extension); - file_format_string[PATH_MAX-1] = '\0'; - char fn[PATH_MAX]; - fs_NextNumberedFilename(g_VFS, file_format_string, s_nextScreenshotNumber, fn); + const VfsPath basenameFormat("screenshots/screenshot%%04d"); + const VfsPath filenameFormat = fs::change_extension(basenameFormat, extension); + VfsPath filename; + fs_NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename); const int w = g_xres, h = g_yres; const int bpp = 24; @@ -223,14 +221,14 @@ void WriteScreenshot(const char* extension) } const size_t img_size = w * h * bpp/8; - const size_t hdr_size = tex_hdr_size(fn); + const size_t hdr_size = tex_hdr_size(filename); shared_ptr buf = io_Allocate(hdr_size+img_size); GLvoid* img = buf.get() + hdr_size; Tex t; if(tex_wrap(w, h, bpp, flags, buf, hdr_size, &t) < 0) return; glReadPixels(0, 0, w, h, fmt, GL_UNSIGNED_BYTE, img); - (void)tex_write(&t, fn); + (void)tex_write(&t, filename); tex_free(&t); } @@ -240,13 +238,11 @@ void WriteScreenshot(const char* extension) void WriteBigScreenshot(const char* extension, int tiles) { // get next available numbered filename - // .. bake extension into format string. // note: %04d -> always 4 digits, so sorting by filename works correctly. - char file_format_string[PATH_MAX]; - snprintf(file_format_string, PATH_MAX, "screenshots/screenshot%%04d.%s", extension); - file_format_string[PATH_MAX-1] = '\0'; - char fn[PATH_MAX]; - fs_NextNumberedFilename(g_VFS, file_format_string, s_nextScreenshotNumber, fn); + const VfsPath basenameFormat("screenshots/screenshot%%04d"); + const VfsPath filenameFormat = fs::change_extension(basenameFormat, extension); + VfsPath filename; + fs_NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename); // Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and // hope the screen is actually large enough for that. @@ -267,7 +263,7 @@ void WriteBigScreenshot(const char* extension, int tiles) const size_t img_size = img_w * img_h * bpp/8; const size_t tile_size = tile_w * tile_h * bpp/8; - const size_t hdr_size = tex_hdr_size(fn); + const size_t hdr_size = tex_hdr_size(filename); void* tile_data = malloc(tile_size); if(!tile_data) WARN_ERR_RETURN(ERR::NO_MEM); @@ -333,7 +329,7 @@ void WriteBigScreenshot(const char* extension, int tiles) g_Game->GetView()->GetCamera()->SetProjectionTile(1, 0, 0); } - (void)tex_write(&t, fn); + (void)tex_write(&t, filename); tex_free(&t); free(tile_data); } diff --git a/source/ps/XML/XML.h b/source/ps/XML/XML.h index d8f1c57802..b843ae3b0b 100644 --- a/source/ps/XML/XML.h +++ b/source/ps/XML/XML.h @@ -56,6 +56,7 @@ #include "XercesErrorHandler.h" #include "ps/CStr.h" +#include "lib/file/vfs/vfs_path.h" XERCES_CPP_NAMESPACE_USE @@ -85,7 +86,7 @@ public: // Open a VFS path for XML parsing // returns 0 if successful, -1 on failure - int OpenFile(const char *path); + int OpenFile(const VfsPath& path); virtual BinInputStream *makeStream() const; }; diff --git a/source/ps/XML/XMLUtils.cpp b/source/ps/XML/XMLUtils.cpp index dfb2422af3..f8f5eeeb0f 100644 --- a/source/ps/XML/XMLUtils.cpp +++ b/source/ps/XML/XMLUtils.cpp @@ -46,7 +46,7 @@ XMLCh *XMLTranscode(const char *str) return XMLString::transcode(str); } -int CVFSInputSource::OpenFile(const char *path) +int CVFSInputSource::OpenFile(const VfsPath& path) { LibError ret = g_VFS->LoadFile(path, m_pBuffer, m_BufferSize); if(ret != INFO::OK) @@ -55,7 +55,7 @@ int CVFSInputSource::OpenFile(const char *path) return -1; } - XMLCh *sysId=XMLString::transcode(path); + XMLCh *sysId=XMLString::transcode(path.string().c_str()); setSystemId(sysId); XMLString::release(&sysId); diff --git a/source/ps/XML/Xeromyces.cpp b/source/ps/XML/Xeromyces.cpp index 62304e7efd..c806e6a515 100644 --- a/source/ps/XML/Xeromyces.cpp +++ b/source/ps/XML/Xeromyces.cpp @@ -98,8 +98,7 @@ void CXeromyces::Terminate() // Find out write location of the XMB file corresponding to xmlFilename -void CXeromyces::GetXMBPath(const char* xmlFilename, const char* xmbFilename, - char* xmbPath) +void CXeromyces::GetXMBPath(PIVFS vfs, const VfsPath& xmlFilename, const VfsPath& xmbFilename, VfsPath& xmbActualPath) { // rationale: // - it is necessary to write out XMB files into a subdirectory @@ -114,21 +113,20 @@ void CXeromyces::GetXMBPath(const char* xmlFilename, const char* xmbFilename, // a subdirectory (which would make manually deleting all harder). // get real path of XML file (e.g. mods/official/entities/...) - char P_XMBRealPath[PATH_MAX]; - g_VFS->GetRealPath(xmlFilename, P_XMBRealPath); + Path P_XMBRealPath; + vfs->GetRealPath(xmlFilename, P_XMBRealPath); // extract mod name from that char modName[PATH_MAX]; // .. NOTE: can't use %s, of course (keeps going beyond '/') - int matches = sscanf(P_XMBRealPath, "mods/%[^/]", modName); + int matches = sscanf(P_XMBRealPath.string().c_str(), "mods/%[^/]", modName); debug_assert(matches == 1); // build full name: cache, then mod name, XMB subdir, original XMB path - snprintf(xmbPath, PATH_MAX, "cache/mods/%s/xmb/%s", modName, xmbFilename); - xmbPath[PATH_MAX-1] = '\0'; + xmbActualPath = VfsPath("cache/mods") / modName / "xmb" / xmbFilename; } -PSRETURN CXeromyces::Load(const char* filename) +PSRETURN CXeromyces::Load(const VfsPath& filename) { // Make sure the .xml actually exists if (! FileExists(filename)) @@ -165,26 +163,14 @@ PSRETURN CXeromyces::Load(const char* filename) // _.xmb // with mtime/size as 8-digit hex, where mtime's lowest bit is // zeroed because zip files only have 2 second resolution. + const int suffixLength = 22; + char suffix[suffixLength+1]; + int ret = sprintf(suffix, "_%08x%08xB.xmb", (int)(fileInfo.MTime() & ~1), (int)fileInfo.Size()); + debug_assert(ret == suffixLength); + VfsPath xmbFilename = change_extension(filename, suffix); - CStr xmbFilename = filename; - - // Strip the .xml suffix - int pos; - if ((pos = xmbFilename.FindInsensitive(".xml")) != -1) - xmbFilename = xmbFilename.Left(pos); - - const int bufLen = 22; - char buf[bufLen+1]; - if (sprintf(buf, "_%08x%08xB.xmb", (int)(fileInfo.MTime() & ~1), (int)fileInfo.Size()) != bufLen) - { - debug_assert(0); // Failed to create filename (?!) - return PSRETURN_Xeromyces_XMLOpenFailed; - } - xmbFilename += buf; - - char xmbPath[PATH_MAX]; - GetXMBPath(filename, xmbFilename, xmbPath); - + VfsPath xmbPath; + GetXMBPath(g_VFS, filename, xmbFilename, xmbPath); // If the file exists, use it if (FileExists(xmbPath)) @@ -226,7 +212,7 @@ PSRETURN CXeromyces::Load(const char* filename) CXercesErrorHandler errorHandler; Parser->setErrorHandler(&errorHandler); - CVFSEntityResolver entityResolver(filename); + CVFSEntityResolver entityResolver(filename.string().c_str()); Parser->setEntityResolver(&entityResolver); // Build a tree inside handler @@ -262,7 +248,7 @@ PSRETURN CXeromyces::Load(const char* filename) return PSRETURN_OK; } -bool CXeromyces::ReadXMBFile(const char* filename) +bool CXeromyces::ReadXMBFile(const VfsPath& filename) { size_t size; if(g_VFS->LoadFile(filename, XMBBuffer, size) < 0) diff --git a/source/ps/XML/Xeromyces.h b/source/ps/XML/Xeromyces.h index dca5a2e953..f816125dec 100644 --- a/source/ps/XML/Xeromyces.h +++ b/source/ps/XML/Xeromyces.h @@ -13,6 +13,7 @@ ERROR_TYPE(Xeromyces, XMLOpenFailed); ERROR_TYPE(Xeromyces, XMLParseError); #include "XeroXMB.h" +#include "ps/Filesystem.h" class CXeromyces : public XMBFile { @@ -22,7 +23,7 @@ public: ~CXeromyces(); // Load from an XML file (with invisible XMB caching). - PSRETURN Load(const char* filename); + PSRETURN Load(const VfsPath& filename); // Call once when shutting down the program, to unload Xerces. static void Terminate(); @@ -30,9 +31,9 @@ public: private: // Find out write location of the XMB file corresponding to xmlFilename - static void GetXMBPath(const char* xmlFilename, const char* xmbFilename, char* xmbPath); + static void GetXMBPath(PIVFS vfs, const VfsPath& xmlFilename, const VfsPath& xmbFilename, VfsPath& xmbActualPath); - bool ReadXMBFile(const char* filename); + bool ReadXMBFile(const VfsPath& filename); shared_ptr XMBBuffer; diff --git a/source/ps/XML/tests/test_Xeromyces.h b/source/ps/XML/tests/test_Xeromyces.h index 34a308b56d..8831518712 100644 --- a/source/ps/XML/tests/test_Xeromyces.h +++ b/source/ps/XML/tests/test_Xeromyces.h @@ -14,13 +14,13 @@ public: TS_ASSERT_OK(vfs->Mount("", "mods/_test.xero")); - char xmbPath[PATH_MAX]; + VfsPath xmbPath; - CXeromyces::GetXMBPath("test1.xml", "test1.xmb", xmbPath); - TS_ASSERT_STR_EQUALS(xmbPath, "cache/mods/_test.xero/xmb/test1.xmb"); + CXeromyces::GetXMBPath(vfs, "test1.xml", "test1.xmb", xmbPath); + TS_ASSERT_STR_EQUALS(xmbPath.string(), "cache/mods/_test.xero/xmb/test1.xmb"); - CXeromyces::GetXMBPath("a/b/test1.xml", "a/b/test1.xmb", xmbPath); - TS_ASSERT_STR_EQUALS(xmbPath, "cache/mods/_test.xero/xmb/a/b/test1.xmb"); + CXeromyces::GetXMBPath(vfs, "a/b/test1.xml", "a/b/test1.xmb", xmbPath); + TS_ASSERT_STR_EQUALS(xmbPath.string(), "cache/mods/_test.xero/xmb/a/b/test1.xmb"); path_ResetRootDir(); } diff --git a/source/renderer/SkyManager.cpp b/source/renderer/SkyManager.cpp index 04c9953f1c..5b24f2d933 100644 --- a/source/renderer/SkyManager.cpp +++ b/source/renderer/SkyManager.cpp @@ -77,7 +77,7 @@ int SkyManager::LoadSkyTextures() // yield after this time is reached. balances increased progress bar // smoothness vs. slowing down loading. - const double end_time = get_time() + 100e-3; + const double end_time = timer_Time() + 100e-3; while (cur_loading_tex < num_textures) { diff --git a/source/renderer/WaterManager.cpp b/source/renderer/WaterManager.cpp index 3983c482c9..e89c614e29 100644 --- a/source/renderer/WaterManager.cpp +++ b/source/renderer/WaterManager.cpp @@ -89,7 +89,7 @@ int WaterManager::LoadWaterTextures() // yield after this time is reached. balances increased progress bar // smoothness vs. slowing down loading. - const double end_time = get_time() + 100e-3; + const double end_time = timer_Time() + 100e-3; char filename[PATH_MAX]; diff --git a/source/scripting/DOMEvent.cpp b/source/scripting/DOMEvent.cpp index 2cfd61f857..9b819a69c1 100644 --- a/source/scripting/DOMEvent.cpp +++ b/source/scripting/DOMEvent.cpp @@ -158,7 +158,7 @@ CScriptEvent::CScriptEvent( const CStrW& Type, unsigned int TypeCode, bool Cance m_Type = Type; m_TypeCode = TypeCode; m_Cancelable = Cancelable; m_Cancelled = false; m_Blockable = Blockable; m_Blocked = false; - m_Timestamp = (long)( get_time() * 1000.0 ); + m_Timestamp = (long)( timer_Time() * 1000.0 ); } void CScriptEvent::ScriptingInit() diff --git a/source/scripting/ScriptGlue.cpp b/source/scripting/ScriptGlue.cpp index df0d79f763..0b554c61a6 100644 --- a/source/scripting/ScriptGlue.cpp +++ b/source/scripting/ScriptGlue.cpp @@ -613,7 +613,6 @@ JSBool SimRandInt(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval) // when the game exits. static const uint MAX_JS_TIMERS = 20; -static Timer js_timer; static TimerUnit js_start_times[MAX_JS_TIMERS]; static TimerUnit js_timer_overhead; static TimerClient js_timer_clients[MAX_JS_TIMERS]; @@ -626,7 +625,7 @@ static void InitJsTimers() { const char* description = pos; pos += sprintf(pos, "js_timer %d", i)+1; - timer_add_client(&js_timer_clients[i], description); + timer_AddClient(&js_timer_clients[i], description); } // call several times to get a good approximation of 'hot' performance. @@ -634,17 +633,16 @@ static void InitJsTimers() // overhead from another: that causes worse results (probably some // caching effects inside JS, but I don't entirely understand why). static const char* calibration_script = - "startXTimer(0);\n" - "stopXTimer (0);\n" - "startXTimer(0);\n" - "stopXTimer (0);\n" - "startXTimer(0);\n" - "stopXTimer (0);\n" "startXTimer(0);\n" "stopXTimer (0);\n" "\n"; g_ScriptingHost.RunMemScript(calibration_script, strlen(calibration_script)); - js_timer_overhead = js_timer_clients[0].sum/4; + // slight hack: call RunMemScript twice because we can't average several + // TimerUnit values because there's no operator/. this way is better anyway + // because it hopefully avoids the one-time JS init overhead. + g_ScriptingHost.RunMemScript(calibration_script, strlen(calibration_script)); + js_timer_overhead = js_timer_clients[0].sum; + js_timer_clients[0].sum.SetToZero(); } JSBool StartJsTimer(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval) @@ -655,9 +653,9 @@ JSBool StartJsTimer(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rva uint slot = ToPrimitive(argv[0]); if (slot >= MAX_JS_TIMERS) return JS_FALSE; + debug_assert(js_start_times[slot].Seconds() == 0.0); - debug_assert(js_start_times[slot] == 0); - js_start_times[slot] = js_timer.get_timestamp(); + js_start_times[slot].SetFromTimer(); return JS_TRUE; } @@ -668,11 +666,13 @@ JSBool StopJsTimer(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval uint slot = ToPrimitive(argv[0]); if (slot >= MAX_JS_TIMERS) return JS_FALSE; + debug_assert(js_start_times[slot].Seconds() != 0.0); - debug_assert(js_start_times[slot] != 0); - TimerUnit dt = js_timer.get_timestamp() - js_start_times[slot] - js_timer_overhead; - js_start_times[slot] = 0; - timer_bill_client(&js_timer_clients[slot], dt); + TimerUnit now; + now.SetFromTimer(); + now.Subtract(js_timer_overhead); + timer_BillClient(&js_timer_clients[slot], js_start_times[slot], now); + js_start_times[slot].SetToZero(); return JS_TRUE; } @@ -855,9 +855,9 @@ JSBool ForceGarbageCollection(JSContext* cx, JSObject* UNUSED(obj), uint argc, j { JSU_REQUIRE_NO_PARAMS(); - double time = get_time(); + double time = timer_Time(); JS_GC(cx); - time = get_time() - time; + time = timer_Time() - time; g_Console->InsertMessage(L"Garbage collection completed in: %f", time); *rval = JSVAL_TRUE; return JS_TRUE ; diff --git a/source/simulation/EntityTemplateCollection.cpp b/source/simulation/EntityTemplateCollection.cpp index 2305146008..d65d904a52 100644 --- a/source/simulation/EntityTemplateCollection.cpp +++ b/source/simulation/EntityTemplateCollection.cpp @@ -19,7 +19,7 @@ void CEntityTemplateCollection::LoadFile( const VfsPath& pathname ) // 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. - const CStrW basename(fs::basename((const fs::path&)pathname)); + const CStrW basename(fs::basename(pathname)); m_templateFilenames[basename] = pathname.string(); } diff --git a/source/simulation/FormationCollection.cpp b/source/simulation/FormationCollection.cpp index 7126bf0d08..ef9fb88770 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. - const CStrW basename(fs::basename((const fs::path&)pathname)); + const CStrW basename(fs::basename(pathname)); m_templateFilenames[basename] = pathname.string(); } diff --git a/source/simulation/TechnologyCollection.cpp b/source/simulation/TechnologyCollection.cpp index 1a8bafe2a4..61c29efc20 100644 --- a/source/simulation/TechnologyCollection.cpp +++ b/source/simulation/TechnologyCollection.cpp @@ -8,7 +8,7 @@ void CTechnologyCollection::LoadFile( const VfsPath& pathname ) { - const CStrW basename(fs::basename((const fs::path&)pathname)); + const CStrW basename(fs::basename(pathname)); m_techFilenames[basename] = pathname.string(); } diff --git a/source/tools/atlas/GameInterface/GameLoop.cpp b/source/tools/atlas/GameInterface/GameLoop.cpp index 4d83180bae..10082a03ee 100644 --- a/source/tools/atlas/GameInterface/GameLoop.cpp +++ b/source/tools/atlas/GameInterface/GameLoop.cpp @@ -122,7 +122,7 @@ bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll) state.view = View::GetView_None(); state.glCanvas = NULL; - double last_activity = get_time(); + double last_activity = timer_Time(); while (state.running) { @@ -134,7 +134,7 @@ bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll) // Calculate frame length { - double time = get_time(); + double time = timer_Time(); static double last_time = time; float length = (float)(time-last_time); last_time = time; @@ -212,7 +212,7 @@ bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll) g_Profiler.Frame(); - double time = get_time(); + double time = timer_Time(); if (recent_activity) last_activity = time; @@ -239,7 +239,7 @@ bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll) SDL_Delay(50); if (!msgPasser.IsEmpty()) break; - time = get_time(); + time = timer_Time(); } } else diff --git a/source/tools/atlas/GameInterface/MessagePasserImpl.cpp b/source/tools/atlas/GameInterface/MessagePasserImpl.cpp index a2f2317510..99a28164aa 100644 --- a/source/tools/atlas/GameInterface/MessagePasserImpl.cpp +++ b/source/tools/atlas/GameInterface/MessagePasserImpl.cpp @@ -64,7 +64,7 @@ void MessagePasserImpl::Add(IMessage* msg) debug_assert(msg->GetType() == IMessage::Message); if (m_Trace) - debug_printf("%8.3f add message: %s\n", get_time(), msg->GetName()); + debug_printf("%8.3f add message: %s\n", timer_Time(), msg->GetName()); m_Mutex.Lock(); m_Queue.push(msg); @@ -89,7 +89,7 @@ IMessage* MessagePasserImpl::Retrieve() m_Mutex.Unlock(); if (m_Trace && msg) - debug_printf("%8.3f retrieved message: %s\n", get_time(), msg->GetName()); + debug_printf("%8.3f retrieved message: %s\n", timer_Time(), msg->GetName()); return msg; } @@ -100,7 +100,7 @@ void MessagePasserImpl::Query(QueryMessage* qry, void(* UNUSED(timeoutCallback) debug_assert(qry->GetType() == IMessage::Query); if (m_Trace) - debug_printf("%8.3f add query: %s\n", get_time(), qry->GetName()); + debug_printf("%8.3f add query: %s\n", timer_Time(), qry->GetName()); // Set the semaphore, so we can block until the query has been handled qry->m_Semaphore = static_cast(m_Semaphore); diff --git a/source/tools/atlas/GameInterface/View.cpp b/source/tools/atlas/GameInterface/View.cpp index c517b56e0f..3413669302 100644 --- a/source/tools/atlas/GameInterface/View.cpp +++ b/source/tools/atlas/GameInterface/View.cpp @@ -156,8 +156,8 @@ void ViewGame::Update(float frameLength) // Whoops, we're trying to go faster than the simulation can manage. // It's probably better to run at the right sim rate, at the expense // of framerate, so let's try simulating a few more times. - double t = get_time(); - while (!ok && get_time() < t + 0.1) // don't go much worse than 10fps + double t = timer_Time(); + while (!ok && timer_Time() < t + 0.1) // don't go much worse than 10fps { ok = g_Game->Update(0.0, false); // don't add on any extra sim time }