From 206d38f3d7b71ecd78f31d627e52d1d4ca9de8c1 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 2 Jul 2025 16:58:31 +0200 Subject: [PATCH] Fix path handling for files in BSAs --- apps/bulletobjecttool/main.cpp | 2 +- apps/components_tests/fx/technique.cpp | 3 +- apps/navmeshtool/main.cpp | 2 +- apps/niftest/niftest.cpp | 4 +- apps/opencs/model/world/data.cpp | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwgui/postprocessorhud.cpp | 38 ++++++++------ apps/openmw/mwgui/postprocessorhud.hpp | 3 +- apps/openmw/mwrender/postprocessor.cpp | 40 ++++++++------ apps/openmw/mwrender/postprocessor.hpp | 6 +-- components/bsa/ba2dx10file.hpp | 1 + components/bsa/ba2gnrlfile.hpp | 1 + components/bsa/bsafile.hpp | 16 +++--- components/bsa/compressedbsafile.hpp | 1 + components/fx/technique.cpp | 22 ++++---- components/fx/technique.hpp | 10 ++-- components/testing/util.hpp | 4 +- components/vfs/bsaarchive.hpp | 72 ++++++++++++++++++++------ components/vfs/file.hpp | 5 +- components/vfs/filesystemarchive.cpp | 10 ++++ components/vfs/filesystemarchive.hpp | 4 +- components/vfs/manager.cpp | 19 ++++--- components/vfs/manager.hpp | 7 ++- components/vfs/pathutil.hpp | 14 +++++ components/vfs/registerarchives.cpp | 4 +- components/vfs/registerarchives.hpp | 7 ++- 26 files changed, 201 insertions(+), 100 deletions(-) diff --git a/apps/bulletobjecttool/main.cpp b/apps/bulletobjecttool/main.cpp index 5cacedd07e..659d97b7f1 100644 --- a/apps/bulletobjecttool/main.cpp +++ b/apps/bulletobjecttool/main.cpp @@ -155,7 +155,7 @@ namespace VFS::Manager vfs; - VFS::registerArchives(&vfs, fileCollections, archives, true); + VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder()); Settings::Manager::load(config); diff --git a/apps/components_tests/fx/technique.cpp b/apps/components_tests/fx/technique.cpp index ad57074b18..2becf4da5b 100644 --- a/apps/components_tests/fx/technique.cpp +++ b/apps/components_tests/fx/technique.cpp @@ -113,7 +113,8 @@ namespace void compile(const std::string& name) { - mTechnique = std::make_unique(*mVFS.get(), mImageManager, name, 1, 1, true, true); + mTechnique = std::make_unique( + *mVFS.get(), mImageManager, Technique::makeFileName(name), name, 1, 1, true, true); mTechnique->compile(); } }; diff --git a/apps/navmeshtool/main.cpp b/apps/navmeshtool/main.cpp index 08ed10c3b3..6cfa7fc61d 100644 --- a/apps/navmeshtool/main.cpp +++ b/apps/navmeshtool/main.cpp @@ -189,7 +189,7 @@ namespace NavMeshTool VFS::Manager vfs; - VFS::registerArchives(&vfs, fileCollections, archives, true); + VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder()); Settings::Manager::load(config); diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 8634134665..7f19008bd1 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -113,7 +113,7 @@ bool isBSA(const std::filesystem::path& path) std::unique_ptr makeArchive(const std::filesystem::path& path) { if (isBSA(path)) - return VFS::makeBsaArchive(path); + return VFS::makeBsaArchive(path, nullptr); if (std::filesystem::is_directory(path)) return std::make_unique(path); return nullptr; @@ -198,7 +198,7 @@ void readVFS(std::unique_ptr&& archive, const std::filesystem::pat { try { - readVFS(VFS::makeBsaArchive(file.second), file.second, quiet); + readVFS(VFS::makeBsaArchive(file.second, nullptr), file.second, quiet); } catch (const std::exception& e) { diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 00e5fec7b0..fa9251b949 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -143,7 +143,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data , mArchives(archives) , mVFS(std::make_unique()) { - VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true); + VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true, &mEncoder.getStatelessEncoder()); mResourcesManager.setVFS(mVFS.get()); @@ -1465,7 +1465,7 @@ std::vector CSMWorld::Data::getIds(bool listDeleted) const void CSMWorld::Data::assetsChanged() { mVFS.get()->reset(); - VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true); + VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true, &mEncoder.getStatelessEncoder()); const UniversalId assetTableIds[] = { UniversalId::Type_Meshes, UniversalId::Type_Icons, UniversalId::Type_Musics, UniversalId::Type_SoundsRes, UniversalId::Type_Textures, UniversalId::Type_Videos }; diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 0f82e953c1..29cfb41071 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -729,7 +729,7 @@ void OMW::Engine::prepareEngine() mVFS = std::make_unique(); - VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true); + VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true, &mEncoder.get()->getStatelessEncoder()); mResourceSystem = std::make_unique( mVFS.get(), Settings::cells().mCacheExpiryDelay, &mEncoder.get()->getStatelessEncoder()); diff --git a/apps/openmw/mwgui/postprocessorhud.cpp b/apps/openmw/mwgui/postprocessorhud.cpp index 7712594c54..cc95203a0f 100644 --- a/apps/openmw/mwgui/postprocessorhud.cpp +++ b/apps/openmw/mwgui/postprocessorhud.cpp @@ -33,6 +33,14 @@ namespace MWGui { + namespace + { + std::shared_ptr& getTechnique(const MyGUI::ListBox& list, size_t selected) + { + return *list.getItemDataAt>(selected); + } + } + void PostProcessorHud::ListWrapper::onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char ch) { if (MyGUI::InputManager::getInstance().isShiftPressed() @@ -117,7 +125,7 @@ namespace MWGui if (index >= sender->getItemCount()) return; - updateConfigView(sender->getItemNameAt(index)); + updateConfigView(getTechnique(*sender, index)->getFileName()); } void PostProcessorHud::toggleTechnique(bool enabled) @@ -131,7 +139,7 @@ namespace MWGui auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor(); mOverrideHint = list->getItemNameAt(selected); - auto technique = *list->getItemDataAt>(selected); + auto technique = getTechnique(*list, selected); if (technique->getDynamic()) return; @@ -167,7 +175,7 @@ namespace MWGui if (static_cast(index) != selected) { - auto technique = *mActiveList->getItemDataAt>(selected); + auto technique = getTechnique(*mActiveList, selected); if (technique->getDynamic() || technique->getInternal()) return; @@ -290,16 +298,16 @@ namespace MWGui return; if (mInactiveList->getIndexSelected() != MyGUI::ITEM_NONE) - updateConfigView(mInactiveList->getItemNameAt(mInactiveList->getIndexSelected())); + updateConfigView(getTechnique(*mInactiveList, mInactiveList->getIndexSelected())->getFileName()); else if (mActiveList->getIndexSelected() != MyGUI::ITEM_NONE) - updateConfigView(mActiveList->getItemNameAt(mActiveList->getIndexSelected())); + updateConfigView(getTechnique(*mActiveList, mActiveList->getIndexSelected())->getFileName()); } - void PostProcessorHud::updateConfigView(const std::string& name) + void PostProcessorHud::updateConfigView(VFS::Path::NormalizedView path) { auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor(); - auto technique = processor->loadTechnique(name); + auto technique = processor->loadTechnique(path); if (technique->getStatus() == fx::Technique::Status::File_Not_exists) return; @@ -423,22 +431,22 @@ namespace MWGui auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor(); - std::vector techniques; - for (const auto& [name, _] : processor->getTechniqueMap()) - techniques.push_back(name); - std::sort(techniques.begin(), techniques.end(), Misc::StringUtils::ciLess); + std::vector techniques; + for (const auto& vfsPath : processor->getTechniqueFiles()) + techniques.emplace_back(vfsPath); + std::sort(techniques.begin(), techniques.end()); - for (const std::string& name : techniques) + for (VFS::Path::NormalizedView path : techniques) { - auto technique = processor->loadTechnique(name); + auto technique = processor->loadTechnique(path); if (!technique->getHidden() && !processor->isTechniqueEnabled(technique)) { - std::string lowerName = Utf8Stream::lowerCaseUtf8(name); + std::string lowerName = Utf8Stream::lowerCaseUtf8(technique->getName()); std::string lowerCaption = mFilter->getCaption(); lowerCaption = Utf8Stream::lowerCaseUtf8(lowerCaption); if (lowerName.find(lowerCaption) != std::string::npos) - mInactiveList->addItem(name, technique); + mInactiveList->addItem(technique->getName(), technique); } } diff --git a/apps/openmw/mwgui/postprocessorhud.hpp b/apps/openmw/mwgui/postprocessorhud.hpp index 20e27bac3a..0028999966 100644 --- a/apps/openmw/mwgui/postprocessorhud.hpp +++ b/apps/openmw/mwgui/postprocessorhud.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace MyGUI { @@ -48,7 +49,7 @@ namespace MWGui void notifyFilterChanged(MyGUI::EditBox* sender); - void updateConfigView(const std::string& name); + void updateConfigView(VFS::Path::NormalizedView path); void notifyResetButtonClicked(MyGUI::Widget* sender); diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index 1f1b7258b3..3a2d5218d5 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -250,14 +251,12 @@ namespace MWRender void PostProcessor::populateTechniqueFiles() { - for (const auto& name : mVFS->getRecursiveDirectoryIterator(fx::Technique::sSubdir)) + for (const auto& path : mVFS->getRecursiveDirectoryIterator(fx::Technique::sSubdir)) { - std::filesystem::path path = Files::pathFromUnicodeString(name); - std::string fileExt = Misc::StringUtils::lowerCase(Files::pathToUnicodeString(path.extension())); - if (!path.parent_path().has_parent_path() && fileExt == fx::Technique::sExt) + std::string_view fileExt = Misc::getFileExtension(path); + if (path.parent().parent().empty() && fileExt == fx::Technique::sExt) { - const auto absolutePath = mVFS->getAbsoluteFileName(path); - mTechniqueFileMap[Files::pathToUnicodeString(absolutePath.stem())] = absolutePath; + mTechniqueFiles.emplace(path); } } } @@ -351,7 +350,7 @@ namespace MWRender if (technique->getStatus() == fx::Technique::Status::File_Not_exists) continue; - const auto lastWriteTime = std::filesystem::last_write_time(mTechniqueFileMap[technique->getName()]); + const auto lastWriteTime = mVFS->getLastModified(technique->getFileName()); const bool isDirty = technique->setLastModificationTime(lastWriteTime); if (!isDirty) @@ -363,7 +362,7 @@ namespace MWRender std::this_thread::sleep_for(std::chrono::milliseconds(5)); if (technique->compile()) - Log(Debug::Info) << "Reloaded technique : " << mTechniqueFileMap[technique->getName()]; + Log(Debug::Info) << "Reloaded technique : " << technique->getFileName(); mReload = technique->isValid(); } @@ -750,27 +749,31 @@ namespace MWRender } std::shared_ptr PostProcessor::loadTechnique(const std::string& name, bool loadNextFrame) + { + VFS::Path::Normalized path = fx::Technique::makeFileName(name); + return loadTechnique(VFS::Path::NormalizedView(path), loadNextFrame); + } + + std::shared_ptr PostProcessor::loadTechnique(VFS::Path::NormalizedView path, bool loadNextFrame) { for (const auto& technique : mTemplates) - if (Misc::StringUtils::ciEqual(technique->getName(), name)) + if (technique->getFileName() == path) return technique; for (const auto& technique : mQueuedTemplates) - if (Misc::StringUtils::ciEqual(technique->getName(), name)) + if (technique->getFileName() == path) return technique; - std::string realName = name; - auto fileIter = mTechniqueFileMap.find(name); - if (fileIter != mTechniqueFileMap.end()) - realName = fileIter->first; + if (!mTechniqueFiles.contains(path)) + return {}; auto technique = std::make_shared(*mVFS, *mRendering.getResourceSystem()->getImageManager(), - std::move(realName), renderWidth(), renderHeight(), mUBO, mNormalsSupported); + path, mVFS->getStem(path), renderWidth(), renderHeight(), mUBO, mNormalsSupported); technique->compile(); if (technique->getStatus() != fx::Technique::Status::File_Not_exists) - technique->setLastModificationTime(std::filesystem::last_write_time(fileIter->second)); + technique->setLastModificationTime(mVFS->getLastModified(path)); if (loadNextFrame) { @@ -802,7 +805,10 @@ namespace MWRender if (techniqueName.empty()) continue; - mTechniques.push_back(loadTechnique(techniqueName)); + auto technique = loadTechnique(techniqueName); + if (!technique) + continue; + mTechniques.push_back(std::move(technique)); } dirtyTechniques(); diff --git a/apps/openmw/mwrender/postprocessor.hpp b/apps/openmw/mwrender/postprocessor.hpp index 6b1f4612f1..4b854cbc9c 100644 --- a/apps/openmw/mwrender/postprocessor.hpp +++ b/apps/openmw/mwrender/postprocessor.hpp @@ -128,7 +128,7 @@ namespace MWRender const TechniqueList& getTemplates() const { return mTemplates; } - const auto& getTechniqueMap() const { return mTechniqueFileMap; } + const auto& getTechniqueFiles() const { return mTechniqueFiles; } void resize(); @@ -176,6 +176,7 @@ namespace MWRender void toggleMode(); + std::shared_ptr loadTechnique(VFS::Path::NormalizedView path, bool loadNextFrame = false); std::shared_ptr loadTechnique(const std::string& name, bool loadNextFrame = false); TechniqueList getChain(); @@ -232,8 +233,7 @@ namespace MWRender TechniqueList mQueuedTemplates; TechniqueList mInternalTechniques; - std::unordered_map - mTechniqueFileMap; + std::unordered_set> mTechniqueFiles; RenderingManager& mRendering; osgViewer::Viewer* mViewer; diff --git a/components/bsa/ba2dx10file.hpp b/components/bsa/ba2dx10file.hpp index cd1f822179..c902a4ccb0 100644 --- a/components/bsa/ba2dx10file.hpp +++ b/components/bsa/ba2dx10file.hpp @@ -50,6 +50,7 @@ namespace Bsa public: using BSAFile::getFilename; using BSAFile::getList; + using BSAFile::getPath; using BSAFile::open; BA2DX10File(); diff --git a/components/bsa/ba2gnrlfile.hpp b/components/bsa/ba2gnrlfile.hpp index 0bc94eae0e..080ba3a8df 100644 --- a/components/bsa/ba2gnrlfile.hpp +++ b/components/bsa/ba2gnrlfile.hpp @@ -38,6 +38,7 @@ namespace Bsa public: using BSAFile::getFilename; using BSAFile::getList; + using BSAFile::getPath; using BSAFile::open; BA2GNRLFile(); diff --git a/components/bsa/bsafile.hpp b/components/bsa/bsafile.hpp index ad7acdad17..7b910208d8 100644 --- a/components/bsa/bsafile.hpp +++ b/components/bsa/bsafile.hpp @@ -84,15 +84,15 @@ namespace Bsa protected: bool mHasChanged = false; + /// True when an archive has been loaded + bool mIsLoaded = false; + /// Table of files in this archive FileList mFiles; /// Filename string buffer std::vector mStringBuf; - /// True when an archive has been loaded - bool mIsLoaded; - /// Used for error messages std::filesystem::path mFilepath; @@ -109,11 +109,6 @@ namespace Bsa * ----------------------------------- */ - BSAFile() - : mIsLoaded(false) - { - } - virtual ~BSAFile() { close(); @@ -148,6 +143,11 @@ namespace Bsa return Files::pathToUnicodeString(mFilepath); } + const std::filesystem::path& getPath() const + { + return mFilepath; + } + // checks version of BSA from file header static BsaVersion detectVersion(const std::filesystem::path& filePath); }; diff --git a/components/bsa/compressedbsafile.hpp b/components/bsa/compressedbsafile.hpp index 83620f11bc..1e359ea3fe 100644 --- a/components/bsa/compressedbsafile.hpp +++ b/components/bsa/compressedbsafile.hpp @@ -117,6 +117,7 @@ namespace Bsa public: using BSAFile::getFilename; using BSAFile::getList; + using BSAFile::getPath; using BSAFile::open; CompressedBSAFile() = default; diff --git a/components/fx/technique.cpp b/components/fx/technique.cpp index a8cd455dea..58ea55b2f6 100644 --- a/components/fx/technique.cpp +++ b/components/fx/technique.cpp @@ -37,22 +37,20 @@ namespace namespace fx { - namespace + VFS::Path::Normalized Technique::makeFileName(std::string_view name) { - VFS::Path::Normalized makeFilePath(std::string_view name) - { - std::string fileName(name); - fileName += Technique::sExt; - VFS::Path::Normalized result(Technique::sSubdir); - result /= fileName; - return result; - } + std::string fileName(name); + fileName += '.'; + fileName += Technique::sExt; + VFS::Path::Normalized result(Technique::sSubdir); + result /= fileName; + return result; } - Technique::Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, std::string name, int width, - int height, bool ubo, bool supportsNormals) + Technique::Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, + VFS::Path::NormalizedView fileName, std::string name, int width, int height, bool ubo, bool supportsNormals) : mName(std::move(name)) - , mFilePath(makeFilePath(mName)) + , mFilePath(fileName) , mLastModificationTime(std::filesystem::file_time_type::clock::now()) , mWidth(width) , mHeight(height) diff --git a/components/fx/technique.hpp b/components/fx/technique.hpp index ad5e876faa..00b87a86e2 100644 --- a/components/fx/technique.hpp +++ b/components/fx/technique.hpp @@ -105,8 +105,8 @@ namespace fx using UniformMap = std::vector>; using RenderTargetMap = std::unordered_map; - static constexpr std::string_view sExt = ".omwfx"; - static constexpr std::string_view sSubdir = "shaders"; + static constexpr std::string_view sExt = "omwfx"; + static constexpr VFS::Path::NormalizedView sSubdir{ "shaders" }; enum class Status { @@ -123,8 +123,10 @@ namespace fx static constexpr FlagsType Flag_Disable_SunGlare = (1 << 4); static constexpr FlagsType Flag_Hidden = (1 << 5); - Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, std::string name, int width, - int height, bool ubo, bool supportsNormals); + static VFS::Path::Normalized makeFileName(std::string_view name); + + Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, VFS::Path::NormalizedView fileName, + std::string name, int width, int height, bool ubo, bool supportsNormals); bool compile(); diff --git a/components/testing/util.hpp b/components/testing/util.hpp index 53331d6d37..e2183d5403 100644 --- a/components/testing/util.hpp +++ b/components/testing/util.hpp @@ -56,7 +56,9 @@ namespace TestingOpenMW Files::IStreamPtr open() override { return std::make_unique(mContent, std::ios_base::in); } - std::filesystem::path getPath() override { return "TestFile"; } + std::filesystem::file_time_type getLastModified() const override { return {}; } + + std::string getStem() const override { return "TestFile"; } private: const std::string mContent; diff --git a/components/vfs/bsaarchive.hpp b/components/vfs/bsaarchive.hpp index 2e6fac6558..f89e47d971 100644 --- a/components/vfs/bsaarchive.hpp +++ b/components/vfs/bsaarchive.hpp @@ -10,45 +10,72 @@ #include #include +#include + #include #include #include namespace VFS { + template + class BsaArchive; + template class BsaArchiveFile : public File { public: - BsaArchiveFile(const Bsa::BSAFile::FileStruct* info, FileType* bsa) + BsaArchiveFile(const Bsa::BSAFile::FileStruct* info, const BsaArchive* bsa) : mInfo(info) , mFile(bsa) { } - Files::IStreamPtr open() override { return mFile->getFile(mInfo); } + Files::IStreamPtr open() override { return mFile->getFile()->getFile(mInfo); } - std::filesystem::path getPath() override { return mInfo->name(); } + std::filesystem::file_time_type getLastModified() const override + { + return std::filesystem::last_write_time(mFile->getFile()->getPath()); + } + + std::string getStem() const override + { + std::string_view name = mInfo->name(); + auto index = name.find_last_of("\\/"); + if (index != std::string_view::npos) + name = name.substr(index + 1); + index = name.find_last_of('.'); + if (index != std::string_view::npos && index != 0) + name = name.substr(0, index); + std::string out; + std::string_view utf8 = mFile->getUtf8(name, out); + if (out.data() == utf8.data()) + out.resize(utf8.size()); + else + out = utf8; + return out; + } const Bsa::BSAFile::FileStruct* mInfo; - FileType* mFile; + const BsaArchive* mFile; }; template class BsaArchive : public Archive { public: - BsaArchive(const std::filesystem::path& filename) + BsaArchive(const std::filesystem::path& filename, const ToUTF8::StatelessUtf8Encoder* encoder) : Archive() + , mEncoder(encoder) { mFile = std::make_unique(); mFile->open(filename); - const Bsa::BSAFile::FileList& filelist = mFile->getList(); - for (Bsa::BSAFile::FileList::const_iterator it = filelist.begin(); it != filelist.end(); ++it) + std::string buffer; + for (const Bsa::BSAFile::FileStruct& file : mFile->getList()) { - mResources.emplace_back(&*it, mFile.get()); - mFiles.emplace_back(it->name()); + mResources.emplace_back(&file, this); + mFiles.emplace_back(getUtf8(file.name(), buffer)); } std::sort(mFiles.begin(), mFiles.end()); @@ -56,8 +83,12 @@ namespace VFS void listResources(FileMap& out) override { + std::string buffer; for (auto& resource : mResources) - out[VFS::Path::Normalized(resource.mInfo->name())] = &resource; + { + std::string_view path = getUtf8(resource.mInfo->name(), buffer); + out[VFS::Path::Normalized(path)] = &resource; + } } bool contains(Path::NormalizedView file) const override @@ -67,26 +98,37 @@ namespace VFS std::string getDescription() const override { return std::string{ "BSA: " } + mFile->getFilename(); } + BSAFileType* getFile() const { return mFile.get(); } + + std::string_view getUtf8(std::string_view input, std::string& buffer) const + { + if (mEncoder == nullptr) + return input; + return mEncoder->getUtf8(input, ToUTF8::BufferAllocationPolicy::UseGrowFactor, buffer); + } + private: std::unique_ptr mFile; std::vector> mResources; std::vector mFiles; + const ToUTF8::StatelessUtf8Encoder* mEncoder; }; - inline std::unique_ptr makeBsaArchive(const std::filesystem::path& path) + inline std::unique_ptr makeBsaArchive( + const std::filesystem::path& path, const ToUTF8::StatelessUtf8Encoder* encoder) { switch (Bsa::BSAFile::detectVersion(path)) { case Bsa::BsaVersion::Unknown: break; case Bsa::BsaVersion::Uncompressed: - return std::make_unique>(path); + return std::make_unique>(path, encoder); case Bsa::BsaVersion::Compressed: - return std::make_unique>(path); + return std::make_unique>(path, encoder); case Bsa::BsaVersion::BA2GNRL: - return std::make_unique>(path); + return std::make_unique>(path, encoder); case Bsa::BsaVersion::BA2DX10: - return std::make_unique>(path); + return std::make_unique>(path, encoder); } throw std::runtime_error("Unknown archive type '" + Files::pathToUnicodeString(path) + "'"); diff --git a/components/vfs/file.hpp b/components/vfs/file.hpp index f2dadb1162..7c65e3a1ba 100644 --- a/components/vfs/file.hpp +++ b/components/vfs/file.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_VFS_FILE_H #include +#include #include @@ -14,7 +15,9 @@ namespace VFS virtual Files::IStreamPtr open() = 0; - virtual std::filesystem::path getPath() = 0; + virtual std::filesystem::file_time_type getLastModified() const = 0; + + virtual std::string getStem() const = 0; }; } diff --git a/components/vfs/filesystemarchive.cpp b/components/vfs/filesystemarchive.cpp index 3303c6656c..0b67df45bc 100644 --- a/components/vfs/filesystemarchive.cpp +++ b/components/vfs/filesystemarchive.cpp @@ -81,4 +81,14 @@ namespace VFS return Files::openConstrainedFileStream(mPath); } + std::filesystem::file_time_type FileSystemArchiveFile::getLastModified() const + { + return std::filesystem::last_write_time(mPath); + } + + std::string FileSystemArchiveFile::getStem() const + { + return Files::pathToUnicodeString(mPath.stem()); + } + } diff --git a/components/vfs/filesystemarchive.hpp b/components/vfs/filesystemarchive.hpp index 215c443b58..f6b1a2ec5e 100644 --- a/components/vfs/filesystemarchive.hpp +++ b/components/vfs/filesystemarchive.hpp @@ -17,7 +17,9 @@ namespace VFS Files::IStreamPtr open() override; - std::filesystem::path getPath() override { return mPath; } + std::filesystem::file_time_type getLastModified() const override; + + std::string getStem() const override; private: std::filesystem::path mPath; diff --git a/components/vfs/manager.cpp b/components/vfs/manager.cpp index 12ef378017..ff25150f67 100644 --- a/components/vfs/manager.cpp +++ b/components/vfs/manager.cpp @@ -81,15 +81,20 @@ namespace VFS return {}; } - std::filesystem::path Manager::getAbsoluteFileName(const std::filesystem::path& name) const + std::filesystem::file_time_type Manager::getLastModified(VFS::Path::NormalizedView name) const { - std::string normalized = Files::pathToUnicodeString(name); - Path::normalizeFilenameInPlace(normalized); - - const auto found = mIndex.find(normalized); + const auto found = mIndex.find(name); if (found == mIndex.end()) - throw std::runtime_error("Resource '" + normalized + "' is not found"); - return found->second->getPath(); + throw std::runtime_error("Resource '" + std::string(name.value()) + "' not found"); + return found->second->getLastModified(); + } + + std::string Manager::getStem(VFS::Path::NormalizedView name) const + { + const auto found = mIndex.find(name); + if (found == mIndex.end()) + throw std::runtime_error("Resource '" + std::string(name.value()) + "' not found"); + return found->second->getStem(); } RecursiveDirectoryRange Manager::getRecursiveDirectoryIterator(std::string_view path) const diff --git a/components/vfs/manager.hpp b/components/vfs/manager.hpp index b6a9d796cc..3d10b3f355 100644 --- a/components/vfs/manager.hpp +++ b/components/vfs/manager.hpp @@ -72,10 +72,9 @@ namespace VFS RecursiveDirectoryRange getRecursiveDirectoryIterator() const; - /// Retrieve the absolute path to the file - /// @note Throws an exception if the file can not be found. - /// @note May be called from any thread once the index has been built. - std::filesystem::path getAbsoluteFileName(const std::filesystem::path& name) const; + std::filesystem::file_time_type getLastModified(VFS::Path::NormalizedView name) const; + // Equivalent to std::filesystem::path::stem. The result isn't normalized. + std::string getStem(VFS::Path::NormalizedView name) const; private: std::vector> mArchives; diff --git a/components/vfs/pathutil.hpp b/components/vfs/pathutil.hpp index f5393617d7..b4b4f6e278 100644 --- a/components/vfs/pathutil.hpp +++ b/components/vfs/pathutil.hpp @@ -127,6 +127,15 @@ namespace VFS::Path return stream << value.mValue; } + NormalizedView parent() const + { + NormalizedView p; + const std::size_t pos = mValue.find_last_of(separator); + if (pos != std::string_view::npos) + p.mValue = mValue.substr(0, pos); + return p; + } + private: std::string_view mValue; }; @@ -259,6 +268,11 @@ namespace VFS::Path return stream << value.mValue; } + NormalizedView parent() const + { + return NormalizedView(*this).parent(); + } + private: std::string mValue; }; diff --git a/components/vfs/registerarchives.cpp b/components/vfs/registerarchives.cpp index f017b5f73c..1d03766220 100644 --- a/components/vfs/registerarchives.cpp +++ b/components/vfs/registerarchives.cpp @@ -14,7 +14,7 @@ namespace VFS { void registerArchives(VFS::Manager* vfs, const Files::Collections& collections, - const std::vector& archives, bool useLooseFiles) + const std::vector& archives, bool useLooseFiles, const ToUTF8::StatelessUtf8Encoder* encoder) { const Files::PathContainer& dataDirs = collections.getPaths(); @@ -25,7 +25,7 @@ namespace VFS // Last BSA has the highest priority const auto archivePath = collections.getPath(*archive); Log(Debug::Info) << "Adding BSA archive " << archivePath; - vfs->addArchive(makeBsaArchive(archivePath)); + vfs->addArchive(makeBsaArchive(archivePath, encoder)); } else { diff --git a/components/vfs/registerarchives.hpp b/components/vfs/registerarchives.hpp index dac29f87a3..03247b74d3 100644 --- a/components/vfs/registerarchives.hpp +++ b/components/vfs/registerarchives.hpp @@ -3,13 +3,18 @@ #include +namespace ToUTF8 +{ + class StatelessUtf8Encoder; +} + namespace VFS { class Manager; /// @brief Register BSA and file system archives based on the given OpenMW configuration. void registerArchives(VFS::Manager* vfs, const Files::Collections& collections, - const std::vector& archives, bool useLooseFiles); + const std::vector& archives, bool useLooseFiles, const ToUTF8::StatelessUtf8Encoder* encoder); } #endif