diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 2e81885c75..cb6205ef5c 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -52,11 +52,8 @@ void readVFS(VFS::Archive* anArchive,std::string archivePath = "") myManager.addArchive(anArchive); myManager.buildIndex(); - std::map files=myManager.getIndex(); - for(auto it=files.begin(); it!=files.end(); ++it) + for(const auto& name : myManager.getRecursiveDirectoryIterator("")) { - std::string name = it->first; - try{ if(isNIF(name)) { diff --git a/apps/opencs/model/world/resources.cpp b/apps/opencs/model/world/resources.cpp index 2544886f3e..cd9f58e848 100644 --- a/apps/opencs/model/world/resources.cpp +++ b/apps/opencs/model/world/resources.cpp @@ -23,10 +23,8 @@ void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char * const * size_t baseSize = mBaseDirectory.size(); - const std::map& index = vfs->getIndex(); - for (std::map::const_iterator it = index.begin(); it != index.end(); ++it) + for (const auto& filepath : vfs->getRecursiveDirectoryIterator("")) { - std::string filepath = it->first; if (filepath.size() #include +#include #include #include #include @@ -66,35 +67,15 @@ namespace MWGui void LoadingScreen::findSplashScreens() { - const std::map& index = mResourceSystem->getVFS()->getIndex(); - std::string pattern = "Splash/"; - mResourceSystem->getVFS()->normalizeFilename(pattern); + auto isSupportedExtension = [](const std::string_view& ext) { + static const std::array supported_extensions{ {"tga", "dds", "ktx", "png", "bmp", "jpeg", "jpg"} }; + return !ext.empty() && std::find(supported_extensions.begin(), supported_extensions.end(), ext) != supported_extensions.end(); + }; - /* priority given to the left */ - const std::array supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}}; - - auto found = index.lower_bound(pattern); - while (found != index.end()) + for (const auto& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator("Splash/")) { - const std::string& name = found->first; - if (name.size() >= pattern.size() && name.substr(0, pattern.size()) == pattern) - { - size_t pos = name.find_last_of('.'); - if (pos != std::string::npos) - { - for(auto const& extension: supported_extensions) - { - if (name.compare(pos, name.size() - pos, extension) == 0) - { - mSplashScreens.push_back(found->first); - break; /* based on priority */ - } - } - } - } - else - break; - ++found; + if (isSupportedExtension(Misc::getFileExtension(name))) + mSplashScreens.push_back(name); } if (mSplashScreens.empty()) Log(Debug::Warning) << "Warning: no splash screens found!"; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a04724c70a..1dc42db47c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -591,8 +592,6 @@ namespace MWRender void Animation::loadAllAnimationsInFolder(const std::string &model, const std::string &baseModel) { - const std::map& index = mResourceSystem->getVFS()->getIndex(); - std::string animationPath = model; if (animationPath.find("meshes") == 0) { @@ -600,21 +599,10 @@ namespace MWRender } animationPath.replace(animationPath.size()-3, 3, "/"); - mResourceSystem->getVFS()->normalizeFilename(animationPath); - - std::map::const_iterator found = index.lower_bound(animationPath); - while (found != index.end()) + for (const auto& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator(animationPath)) { - const std::string& name = found->first; - if (name.size() >= animationPath.size() && name.substr(0, animationPath.size()) == animationPath) - { - size_t pos = name.find_last_of('.'); - if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".kf") == 0) - addSingleAnimSource(name, baseModel); - } - else - break; - ++found; + if (Misc::getFileExtension(name) == "kf") + addSingleAnimSource(name, baseModel); } } @@ -1295,8 +1283,6 @@ namespace MWRender if (model.empty()) return; - const std::map& index = resourceSystem->getVFS()->getIndex(); - std::string animationPath = model; if (animationPath.find("meshes") == 0) { @@ -1304,21 +1290,10 @@ namespace MWRender } animationPath.replace(animationPath.size()-4, 4, "/"); - resourceSystem->getVFS()->normalizeFilename(animationPath); - - std::map::const_iterator found = index.lower_bound(animationPath); - while (found != index.end()) + for (const auto& name : resourceSystem->getVFS()->getRecursiveDirectoryIterator(animationPath)) { - const std::string& name = found->first; - if (name.size() >= animationPath.size() && name.substr(0, animationPath.size()) == animationPath) - { - size_t pos = name.find_last_of('.'); - if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".nif") == 0) - loadBonesFromFile(node, name, resourceSystem); - } - else - break; - ++found; + if (Misc::getFileExtension(name) == "nif") + loadBonesFromFile(node, name, resourceSystem); } } diff --git a/apps/openmw/mwsound/sound_buffer.cpp b/apps/openmw/mwsound/sound_buffer.cpp index cb71cb56d2..e64e89d775 100644 --- a/apps/openmw/mwsound/sound_buffer.cpp +++ b/apps/openmw/mwsound/sound_buffer.cpp @@ -131,7 +131,7 @@ namespace MWSound max = std::max(min, max); Sound_Buffer& sfx = mSoundBuffers.emplace_back("Sound/" + sound.mSound, volume, min, max); - mVfs->normalizeFilename(sfx.mResourceName); + sfx.mResourceName = mVfs->normalizeFilename(sfx.mResourceName); mBufferNameMap.emplace(soundId, &sfx); return &sfx; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index fc9735d576..d2422870d7 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -294,20 +294,9 @@ namespace MWSound if (mMusicFiles.find(playlist) == mMusicFiles.end()) { std::vector filelist; - const std::map& index = mVFS->getIndex(); - std::string pattern = "Music/" + playlist; - mVFS->normalizeFilename(pattern); - - std::map::const_iterator found = index.lower_bound(pattern); - while (found != index.end()) - { - if (found->first.size() >= pattern.size() && found->first.substr(0, pattern.size()) == pattern) - filelist.push_back(found->first); - else - break; - ++found; - } + for (const auto& name : mVFS->getRecursiveDirectoryIterator("Music/" + playlist)) + filelist.push_back(name); mMusicFiles[playlist] = filelist; } @@ -327,13 +316,11 @@ namespace MWSound if (mMusicFiles.find("Title") == mMusicFiles.end()) { std::vector filelist; - const std::map& index = mVFS->getIndex(); // Is there an ini setting for this filename or something? std::string filename = "music/special/morrowind title.mp3"; - auto found = index.find(filename); - if (found != index.end()) + if (mVFS->exists(filename)) { - filelist.emplace_back(found->first); + filelist.emplace_back(filename); mMusicFiles["Title"] = filelist; } else @@ -355,10 +342,7 @@ namespace MWSound if(!mOutput->isInitialized()) return; - std::string voicefile = "Sound/"+filename; - - mVFS->normalizeFilename(voicefile); - DecoderPtr decoder = loadVoice(voicefile); + DecoderPtr decoder = loadVoice(mVFS->normalizeFilename("Sound/" + filename)); if (!decoder) return; @@ -389,10 +373,7 @@ namespace MWSound if(!mOutput->isInitialized()) return; - std::string voicefile = "Sound/"+filename; - - mVFS->normalizeFilename(voicefile); - DecoderPtr decoder = loadVoice(voicefile); + DecoderPtr decoder = loadVoice(mVFS->normalizeFilename("Sound/" + filename)); if (!decoder) return; diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 487b7bd70b..da43cc38ec 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -17,6 +17,7 @@ #include +#include #include #include @@ -191,24 +192,10 @@ namespace Gui void FontLoader::loadBitmapFonts(bool exportToFile) { - const std::map& index = mVFS->getIndex(); - - std::string pattern = "Fonts/"; - mVFS->normalizeFilename(pattern); - - std::map::const_iterator found = index.lower_bound(pattern); - while (found != index.end()) + for (const auto& name : mVFS->getRecursiveDirectoryIterator("Fonts/")) { - const std::string& name = found->first; - if (name.size() >= pattern.size() && name.substr(0, pattern.size()) == pattern) - { - size_t pos = name.find_last_of('.'); - if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".fnt") == 0) - loadBitmapFont(name, exportToFile); - } - else - break; - ++found; + if (Misc::getFileExtension(name) == "fnt") + loadBitmapFont(name, exportToFile); } } diff --git a/components/misc/pathhelpers.hpp b/components/misc/pathhelpers.hpp new file mode 100644 index 0000000000..88913a1f7b --- /dev/null +++ b/components/misc/pathhelpers.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_COMPONENTS_MISC_PATHHELPERS_H +#define OPENMW_COMPONENTS_MISC_PATHHELPERS_H + +#include + +namespace Misc +{ + inline std::string_view getFileExtension(std::string_view file) + { + if (auto extPos = file.find_last_of('.'); extPos != std::string::npos) + { + file.remove_prefix(extPos + 1); + return file; + } + return {}; + } +} + +#endif diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index b1b0f74176..0d0f81962b 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -121,8 +122,7 @@ BulletShapeManager::~BulletShapeManager() osg::ref_ptr BulletShapeManager::getShape(const std::string &name) { - std::string normalized = name; - mVFS->normalizeFilename(normalized); + const std::string normalized = mVFS->normalizeFilename(name); osg::ref_ptr shape; osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); @@ -130,12 +130,7 @@ osg::ref_ptr BulletShapeManager::getShape(const std::string & shape = osg::ref_ptr(static_cast(obj.get())); else { - size_t extPos = normalized.find_last_of('.'); - std::string ext; - if (extPos != std::string::npos && extPos+1 < normalized.size()) - ext = normalized.substr(extPos+1); - - if (ext == "nif") + if (Misc::getFileExtension(normalized) == "nif") { NifBullet::BulletNifLoader loader; shape = loader.load(*mNifFileManager->get(normalized)); @@ -180,8 +175,7 @@ osg::ref_ptr BulletShapeManager::getShape(const std::string & osg::ref_ptr BulletShapeManager::cacheInstance(const std::string &name) { - std::string normalized = name; - mVFS->normalizeFilename(normalized); + const std::string normalized = mVFS->normalizeFilename(name); osg::ref_ptr instance = createInstance(normalized); if (instance) @@ -191,8 +185,7 @@ osg::ref_ptr BulletShapeManager::cacheInstance(const std::s osg::ref_ptr BulletShapeManager::getInstance(const std::string &name) { - std::string normalized = name; - mVFS->normalizeFilename(normalized); + const std::string normalized = mVFS->normalizeFilename(name); osg::ref_ptr obj = mInstanceCache->takeFromObjectCache(normalized); if (obj.get()) diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp index 37e76359ff..a544b7b621 100644 --- a/components/resource/imagemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "objectcache.hpp" @@ -83,8 +84,7 @@ namespace Resource osg::ref_ptr ImageManager::getImage(const std::string &filename) { - std::string normalized = filename; - mVFS->normalizeFilename(normalized); + const std::string normalized = mVFS->normalizeFilename(filename); osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); if (obj) @@ -103,10 +103,7 @@ namespace Resource return mWarningImage; } - size_t extPos = normalized.find_last_of('.'); - std::string ext; - if (extPos != std::string::npos && extPos+1 < normalized.size()) - ext = normalized.substr(extPos+1); + const std::string ext(Misc::getFileExtension(normalized)); osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); if (!reader) { diff --git a/components/resource/keyframemanager.cpp b/components/resource/keyframemanager.cpp index 444d2bd7aa..01a8639aa2 100644 --- a/components/resource/keyframemanager.cpp +++ b/components/resource/keyframemanager.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "animation.hpp" @@ -133,8 +134,7 @@ namespace Resource osg::ref_ptr KeyframeManager::get(const std::string &name) { - std::string normalized = name; - mVFS->normalizeFilename(normalized); + const std::string normalized = mVFS->normalizeFilename(name); osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); if (obj) @@ -142,8 +142,7 @@ namespace Resource else { osg::ref_ptr loaded (new SceneUtil::KeyframeHolder); - std::string ext = Resource::getFileExtension(normalized); - if (ext == "kf") + if (Misc::getFileExtension(normalized) == "kf") { NifOsg::Loader::loadKf(Nif::NIFFilePtr(new Nif::NIFFile(mVFS->getNormalized(normalized), normalized)), *loaded.get()); } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 33a9ab99a1..76e73da384 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -358,10 +359,7 @@ namespace Resource bool SceneManager::checkLoaded(const std::string &name, double timeStamp) { - std::string normalized = name; - mVFS->normalizeFilename(normalized); - - return mCache->checkInObjectCache(normalized, timeStamp); + return mCache->checkInObjectCache(mVFS->normalizeFilename(name), timeStamp); } /// @brief Callback to read image files from the VFS. @@ -391,12 +389,12 @@ namespace Resource osg::ref_ptr load (const std::string& normalizedFilename, const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) { - std::string ext = Resource::getFileExtension(normalizedFilename); + auto ext = Misc::getFileExtension(normalizedFilename); if (ext == "nif") return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), imageManager); else { - osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); + osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(std::string(ext)); if (!reader) { std::stringstream errormsg; @@ -533,8 +531,7 @@ namespace Resource osg::ref_ptr SceneManager::getTemplate(const std::string &name, bool compile) { - std::string normalized = name; - mVFS->normalizeFilename(normalized); + std::string normalized = mVFS->normalizeFilename(name); osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); if (obj) @@ -603,8 +600,7 @@ namespace Resource osg::ref_ptr SceneManager::cacheInstance(const std::string &name) { - std::string normalized = name; - mVFS->normalizeFilename(normalized); + const std::string normalized = mVFS->normalizeFilename(name); osg::ref_ptr node = createInstance(normalized); @@ -642,8 +638,7 @@ namespace Resource osg::ref_ptr SceneManager::getInstance(const std::string &name) { - std::string normalized = name; - mVFS->normalizeFilename(normalized); + const std::string normalized = mVFS->normalizeFilename(name); osg::ref_ptr obj = mInstanceCache->takeFromObjectCache(normalized); if (obj.get()) @@ -822,12 +817,4 @@ namespace Resource shaderVisitor->setTranslucentFramebuffer(translucentFramebuffer); return shaderVisitor; } - - std::string getFileExtension(const std::string& file) - { - size_t extPos = file.find_last_of('.'); - if (extPos != std::string::npos && extPos+1 < file.size()) - return file.substr(extPos+1); - return std::string(); - } } diff --git a/components/vfs/manager.cpp b/components/vfs/manager.cpp index 045fe3cf5c..1cb8745497 100644 --- a/components/vfs/manager.cpp +++ b/components/vfs/manager.cpp @@ -86,14 +86,11 @@ namespace VFS return mIndex.find(normalized) != mIndex.end(); } - const std::map& Manager::getIndex() const + std::string Manager::normalizeFilename(const std::string& name) const { - return mIndex; - } - - void Manager::normalizeFilename(std::string &name) const - { - normalize_path(name, mStrict); + std::string result = name; + normalize_path(result, mStrict); + return result; } std::string Manager::getArchive(const std::string& name) const @@ -107,4 +104,9 @@ namespace VFS } return {}; } + + RecursiveDirectoryIterator Manager::getRecursiveDirectoryIterator(const std::string& path) const + { + return RecursiveDirectoryIterator(mIndex, normalizeFilename(path)); + } } diff --git a/components/vfs/manager.hpp b/components/vfs/manager.hpp index 5a09a995eb..b98d8021d4 100644 --- a/components/vfs/manager.hpp +++ b/components/vfs/manager.hpp @@ -12,6 +12,51 @@ namespace VFS class Archive; class File; + class RecursiveDirectoryIterator; + RecursiveDirectoryIterator end(const RecursiveDirectoryIterator& iter); + + class RecursiveDirectoryIterator + { + public: + RecursiveDirectoryIterator(const std::map& index, const std::string& path) + : mPath(path) + , mIndex(&index) + , mIt(index.lower_bound(path)) + {} + + RecursiveDirectoryIterator(const RecursiveDirectoryIterator&) = default; + + const std::string& operator*() const { return mIt->first; } + const std::string* operator->() const { return &mIt->first; } + + bool operator!=(const RecursiveDirectoryIterator& other) { return mPath != other.mPath || mIt != other.mIt; } + + RecursiveDirectoryIterator& operator++() + { + if (++mIt == mIndex->end() || !starts_with(mIt->first, mPath)) + *this = end(*this); + return *this; + } + + friend RecursiveDirectoryIterator end(const RecursiveDirectoryIterator& iter); + + private: + static bool starts_with(const std::string& text, const std::string& start) { return text.rfind(start, 0) == 0; } + + std::string mPath; + const std::map* mIndex; + std::map::const_iterator mIt; + }; + + inline RecursiveDirectoryIterator begin(RecursiveDirectoryIterator iter) { return iter; } + + inline RecursiveDirectoryIterator end(const RecursiveDirectoryIterator& iter) + { + RecursiveDirectoryIterator result(iter); + result.mIt = result.mIndex->end(); + return result; + } + /// @brief The main class responsible for loading files from a virtual file system. /// @par Various archive types (e.g. directories on the filesystem, or compressed archives) /// can be registered, and will be merged into a single file tree. If the same filename is @@ -40,13 +85,9 @@ namespace VFS /// @note May be called from any thread once the index has been built. bool exists(const std::string& name) const; - /// Get a complete list of files from all archives - /// @note May be called from any thread once the index has been built. - const std::map& getIndex() const; - /// Normalize the given filename, making slashes/backslashes consistent, and lower-casing if mStrict is false. /// @note May be called from any thread once the index has been built. - void normalizeFilename(std::string& name) const; + [[nodiscard]] std::string normalizeFilename(const std::string& name) const; /// Retrieve a file by name. /// @note Throws an exception if the file can not be found. @@ -59,6 +100,13 @@ namespace VFS Files::IStreamPtr getNormalized(const std::string& normalizedName) const; std::string getArchive(const std::string& name) const; + + /// Recursivly iterate over the elements of the given path + /// In practice it return all files of the VFS starting with the given path + /// @note the path is normalized + /// @note May be called from any thread once the index has been built. + RecursiveDirectoryIterator getRecursiveDirectoryIterator(const std::string& path) const; + private: bool mStrict;