Fix path handling for files in BSAs

This commit is contained in:
Evil Eye 2025-07-02 16:58:31 +02:00
parent 53feb29a5b
commit 206d38f3d7
26 changed files with 201 additions and 100 deletions

View File

@ -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);

View File

@ -113,7 +113,8 @@ namespace
void compile(const std::string& name)
{
mTechnique = std::make_unique<Technique>(*mVFS.get(), mImageManager, name, 1, 1, true, true);
mTechnique = std::make_unique<Technique>(
*mVFS.get(), mImageManager, Technique::makeFileName(name), name, 1, 1, true, true);
mTechnique->compile();
}
};

View File

@ -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);

View File

@ -113,7 +113,7 @@ bool isBSA(const std::filesystem::path& path)
std::unique_ptr<VFS::Archive> 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<VFS::FileSystemArchive>(path);
return nullptr;
@ -198,7 +198,7 @@ void readVFS(std::unique_ptr<VFS::Archive>&& 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)
{

View File

@ -143,7 +143,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
, mArchives(archives)
, mVFS(std::make_unique<VFS::Manager>())
{
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<ESM::RefId> 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 };

View File

@ -729,7 +729,7 @@ void OMW::Engine::prepareEngine()
mVFS = std::make_unique<VFS::Manager>();
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true, &mEncoder.get()->getStatelessEncoder());
mResourceSystem = std::make_unique<Resource::ResourceSystem>(
mVFS.get(), Settings::cells().mCacheExpiryDelay, &mEncoder.get()->getStatelessEncoder());

View File

@ -33,6 +33,14 @@
namespace MWGui
{
namespace
{
std::shared_ptr<fx::Technique>& getTechnique(const MyGUI::ListBox& list, size_t selected)
{
return *list.getItemDataAt<std::shared_ptr<fx::Technique>>(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<std::shared_ptr<fx::Technique>>(selected);
auto technique = getTechnique(*list, selected);
if (technique->getDynamic())
return;
@ -167,7 +175,7 @@ namespace MWGui
if (static_cast<size_t>(index) != selected)
{
auto technique = *mActiveList->getItemDataAt<std::shared_ptr<fx::Technique>>(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<std::string> techniques;
for (const auto& [name, _] : processor->getTechniqueMap())
techniques.push_back(name);
std::sort(techniques.begin(), techniques.end(), Misc::StringUtils::ciLess);
std::vector<VFS::Path::NormalizedView> 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);
}
}

View File

@ -6,6 +6,7 @@
#include <MyGUI_ListBox.h>
#include <components/settings/shadermanager.hpp>
#include <components/vfs/pathutil.hpp>
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);

View File

@ -12,6 +12,7 @@
#include <osg/Texture3D>
#include <components/files/conversion.hpp>
#include <components/misc/pathhelpers.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/lower.hpp>
#include <components/resource/scenemanager.hpp>
@ -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<fx::Technique> 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<fx::Technique> 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<fx::Technique>(*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();

View File

@ -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<fx::Technique> loadTechnique(VFS::Path::NormalizedView path, bool loadNextFrame = false);
std::shared_ptr<fx::Technique> loadTechnique(const std::string& name, bool loadNextFrame = false);
TechniqueList getChain();
@ -232,8 +233,7 @@ namespace MWRender
TechniqueList mQueuedTemplates;
TechniqueList mInternalTechniques;
std::unordered_map<std::string, std::filesystem::path, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>
mTechniqueFileMap;
std::unordered_set<VFS::Path::Normalized, VFS::Path::Hash, std::equal_to<>> mTechniqueFiles;
RenderingManager& mRendering;
osgViewer::Viewer* mViewer;

View File

@ -50,6 +50,7 @@ namespace Bsa
public:
using BSAFile::getFilename;
using BSAFile::getList;
using BSAFile::getPath;
using BSAFile::open;
BA2DX10File();

View File

@ -38,6 +38,7 @@ namespace Bsa
public:
using BSAFile::getFilename;
using BSAFile::getList;
using BSAFile::getPath;
using BSAFile::open;
BA2GNRLFile();

View File

@ -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<char> 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);
};

View File

@ -117,6 +117,7 @@ namespace Bsa
public:
using BSAFile::getFilename;
using BSAFile::getList;
using BSAFile::getPath;
using BSAFile::open;
CompressedBSAFile() = default;

View File

@ -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)

View File

@ -105,8 +105,8 @@ namespace fx
using UniformMap = std::vector<std::shared_ptr<Types::UniformBase>>;
using RenderTargetMap = std::unordered_map<std::string_view, Types::RenderTarget>;
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();

View File

@ -56,7 +56,9 @@ namespace TestingOpenMW
Files::IStreamPtr open() override { return std::make_unique<std::stringstream>(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;

View File

@ -10,45 +10,72 @@
#include <components/bsa/bsafile.hpp>
#include <components/bsa/compressedbsafile.hpp>
#include <components/toutf8/toutf8.hpp>
#include <algorithm>
#include <memory>
#include <stdexcept>
namespace VFS
{
template <typename BSAFileType>
class BsaArchive;
template <typename FileType>
class BsaArchiveFile : public File
{
public:
BsaArchiveFile(const Bsa::BSAFile::FileStruct* info, FileType* bsa)
BsaArchiveFile(const Bsa::BSAFile::FileStruct* info, const BsaArchive<FileType>* 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<FileType>* mFile;
};
template <typename BSAFileType>
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<BSAFileType>();
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<BSAFileType> mFile;
std::vector<BsaArchiveFile<BSAFileType>> mResources;
std::vector<VFS::Path::Normalized> mFiles;
const ToUTF8::StatelessUtf8Encoder* mEncoder;
};
inline std::unique_ptr<VFS::Archive> makeBsaArchive(const std::filesystem::path& path)
inline std::unique_ptr<VFS::Archive> 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<BsaArchive<Bsa::BSAFile>>(path);
return std::make_unique<BsaArchive<Bsa::BSAFile>>(path, encoder);
case Bsa::BsaVersion::Compressed:
return std::make_unique<BsaArchive<Bsa::CompressedBSAFile>>(path);
return std::make_unique<BsaArchive<Bsa::CompressedBSAFile>>(path, encoder);
case Bsa::BsaVersion::BA2GNRL:
return std::make_unique<BsaArchive<Bsa::BA2GNRLFile>>(path);
return std::make_unique<BsaArchive<Bsa::BA2GNRLFile>>(path, encoder);
case Bsa::BsaVersion::BA2DX10:
return std::make_unique<BsaArchive<Bsa::BA2DX10File>>(path);
return std::make_unique<BsaArchive<Bsa::BA2DX10File>>(path, encoder);
}
throw std::runtime_error("Unknown archive type '" + Files::pathToUnicodeString(path) + "'");

View File

@ -2,6 +2,7 @@
#define OPENMW_COMPONENTS_VFS_FILE_H
#include <filesystem>
#include <string>
#include <components/files/istreamptr.hpp>
@ -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;
};
}

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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

View File

@ -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<std::unique_ptr<Archive>> mArchives;

View File

@ -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;
};

View File

@ -14,7 +14,7 @@ namespace VFS
{
void registerArchives(VFS::Manager* vfs, const Files::Collections& collections,
const std::vector<std::string>& archives, bool useLooseFiles)
const std::vector<std::string>& 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
{

View File

@ -3,13 +3,18 @@
#include <components/files/collections.hpp>
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<std::string>& archives, bool useLooseFiles);
const std::vector<std::string>& archives, bool useLooseFiles, const ToUTF8::StatelessUtf8Encoder* encoder);
}
#endif