1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2026-01-23 19:00:55 +00:00

Merge branch 'tovfsornottovfs' into 'master'

Address incorrect std::filesystem::path usage

Closes #8738

See merge request OpenMW/openmw!5033
This commit is contained in:
Alexei Kotov 2025-12-10 21:20:26 +03:00
commit d5c7a6c6db
16 changed files with 130 additions and 152 deletions

View file

@ -209,8 +209,9 @@ namespace MWGui
SDL_GL_GetDrawableSize(window, &dw, &dh);
mScalingFactor = Settings::gui().mScalingFactor * (dw / w);
constexpr VFS::Path::NormalizedView resourcePath("mygui");
mGuiPlatform = std::make_unique<MyGUIPlatform::Platform>(viewer, guiRoot, resourceSystem->getImageManager(),
resourceSystem->getVFS(), mScalingFactor, "mygui", logpath / "MyGUI.log");
resourceSystem->getVFS(), mScalingFactor, resourcePath, logpath / "MyGUI.log");
mGui = std::make_unique<MyGUI::Gui>();
mGui->initialise({});

View file

@ -3,7 +3,6 @@
#include <algorithm>
#include <cassert>
#include <cstring>
#include <filesystem>
#include <format>
#include <istream>
@ -11,9 +10,8 @@
#include <components/esm/fourcc.hpp>
#include <components/files/constrainedfilestream.hpp>
#include <components/files/conversion.hpp>
#include <components/files/utils.hpp>
#include <components/misc/strings/lower.hpp>
#include <components/vfs/pathutil.hpp>
#include "ba2file.hpp"
#include "memorystream.hpp"
@ -148,20 +146,10 @@ namespace Bsa
}
}
#ifdef _WIN32
const auto& path = str;
#else
// Force-convert the path into something UNIX can handle first
// to make sure std::filesystem::path doesn't think the entire path is the filename on Linux
// and subsequently purge it to determine the file folder.
std::string path(str);
std::replace(path.begin(), path.end(), '\\', '/');
#endif
const VFS::Path::Normalized path(str);
const auto p = std::filesystem::path{ path }; // Purposefully damage Unicode strings.
const auto fileName = Misc::StringUtils::lowerCase(p.stem().string());
const auto ext = Misc::StringUtils::lowerCase(p.extension().string()); // Purposefully damage Unicode strings.
const auto folder = Misc::StringUtils::lowerCase(p.parent_path().string());
const std::string_view fileName = path.stem();
const std::string_view folder = path.parent().value();
uint32_t folderHash = generateHash(folder);
auto it = mFolders.find(folderHash);
@ -169,7 +157,7 @@ namespace Bsa
return std::nullopt; // folder not found
uint32_t fileHash = generateHash(fileName);
uint32_t extHash = generateExtensionHash(ext);
uint32_t extHash = generateExtensionHash(path.filename());
auto iter = it->second.find({ fileHash, extHash });
if (iter == it->second.end())
return std::nullopt; // file not found
@ -229,13 +217,6 @@ namespace Bsa
fail("Add file is not implemented for compressed BSA: " + filename);
}
Files::IStreamPtr BA2DX10File::getFile(const char* file)
{
if (auto fileRec = getFileRecord(file); fileRec)
return getFile(*fileRec);
fail("File not found: " + std::string(file));
}
constexpr const uint32_t DDSD_CAPS = 0x00000001;
constexpr const uint32_t DDSD_HEIGHT = 0x00000002;
constexpr const uint32_t DDSD_WIDTH = 0x00000004;

View file

@ -59,7 +59,6 @@ namespace Bsa
/// Read header information from the input source
void readHeader(std::istream& stream) override;
Files::IStreamPtr getFile(const char* filePath);
Files::IStreamPtr getFile(const FileStruct* fileStruct);
void addFile(const std::string& filename, std::istream& file);
};

View file

@ -1,5 +1,7 @@
#include "ba2file.hpp"
#include <components/misc/pathhelpers.hpp>
namespace Bsa
{
constexpr const uint32_t crc32table[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
@ -32,7 +34,7 @@ namespace Bsa
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e,
0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };
uint32_t generateHash(const std::string& name)
uint32_t generateHash(std::string_view name)
{
uint32_t result = 0;
for (auto c : name)
@ -46,8 +48,15 @@ namespace Bsa
return result;
}
uint32_t generateExtensionHash(std::string_view extension)
uint32_t generateExtensionHash(VFS::Path::NormalizedView file)
{
std::string_view extension;
if (const std::size_t pos = Misc::findExtension(file.value()); pos != std::string_view::npos)
{
// ext including .
extension = file.value();
extension.remove_prefix(pos);
}
uint32_t result = 0;
for (size_t i = 0; i < 4 && i < extension.size() - 1; i++)
result |= static_cast<uint8_t>(extension[i + 1]) << (8 * i);

View file

@ -4,10 +4,12 @@
#include <cstdint>
#include <string>
#include <components/vfs/pathutil.hpp>
namespace Bsa
{
uint32_t generateHash(const std::string& name);
uint32_t generateExtensionHash(std::string_view extension);
uint32_t generateHash(std::string_view name);
uint32_t generateExtensionHash(VFS::Path::NormalizedView file);
enum class BA2Version : std::uint32_t
{

View file

@ -2,7 +2,6 @@
#include <algorithm>
#include <cassert>
#include <filesystem>
#include <format>
#include <fstream>
@ -10,9 +9,8 @@
#include <components/esm/fourcc.hpp>
#include <components/files/constrainedfilestream.hpp>
#include <components/files/conversion.hpp>
#include <components/files/utils.hpp>
#include <components/misc/strings/lower.hpp>
#include <components/vfs/pathutil.hpp>
#include "ba2file.hpp"
#include "memorystream.hpp"
@ -139,20 +137,10 @@ namespace Bsa
}
}
#ifdef _WIN32
const auto& path = str;
#else
// Force-convert the path into something UNIX can handle first
// to make sure std::filesystem::path doesn't think the entire path is the filename on Linux
// and subsequently purge it to determine the file folder.
std::string path(str);
std::replace(path.begin(), path.end(), '\\', '/');
#endif
const VFS::Path::Normalized path(str);
const auto p = std::filesystem::path{ path }; // Purposefully damage Unicode strings.
const auto fileName = Misc::StringUtils::lowerCase(p.stem().string());
const auto ext = Misc::StringUtils::lowerCase(p.extension().string()); // Purposefully damage Unicode strings.
const auto folder = Misc::StringUtils::lowerCase(p.parent_path().string());
const std::string_view fileName = path.stem();
const std::string_view folder = path.parent().value();
uint32_t folderHash = generateHash(folder);
auto it = mFolders.find(folderHash);
@ -160,7 +148,7 @@ namespace Bsa
return FileRecord(); // folder not found, return default which has offset of sInvalidOffset
uint32_t fileHash = generateHash(fileName);
uint32_t extHash = generateExtensionHash(ext);
uint32_t extHash = generateExtensionHash(path.filename());
auto iter = it->second.find({ fileHash, extHash });
if (iter == it->second.end())
return FileRecord(); // file not found, return default which has offset of sInvalidOffset
@ -183,16 +171,6 @@ namespace Bsa
fail("Add file is not implemented for compressed BSA: " + filename);
}
Files::IStreamPtr BA2GNRLFile::getFile(const char* file)
{
FileRecord fileRec = getFileRecord(file);
if (!fileRec.isValid())
{
fail("File not found: " + std::string(file));
}
return getFile(fileRec);
}
Files::IStreamPtr BA2GNRLFile::getFile(const FileRecord& fileRecord)
{
const uint32_t inputSize = fileRecord.packedSize ? fileRecord.packedSize : fileRecord.size;

View file

@ -47,7 +47,6 @@ namespace Bsa
/// Read header information from the input source
void readHeader(std::istream& input) override;
Files::IStreamPtr getFile(const char* filePath);
Files::IStreamPtr getFile(const FileStruct* fileStruct);
void addFile(const std::string& filename, std::istream& file);
};

View file

@ -27,7 +27,6 @@
#include <algorithm>
#include <cassert>
#include <cerrno>
#include <filesystem>
#include <format>
#include <istream>
#include <system_error>
@ -38,7 +37,8 @@
#include <components/files/constrainedfilestream.hpp>
#include <components/files/conversion.hpp>
#include <components/files/utils.hpp>
#include <components/misc/strings/lower.hpp>
#include <components/misc/pathhelpers.hpp>
#include <components/vfs/pathutil.hpp>
#include "memorystream.hpp"
@ -218,26 +218,26 @@ namespace Bsa
}
}
#ifdef _WIN32
const auto& path = str;
#else
// Force-convert the path into something UNIX can handle first
// to make sure std::filesystem::path doesn't think the entire path is the filename on Linux
// and subsequently purge it to determine the file folder.
std::string path(str);
std::replace(path.begin(), path.end(), '\\', '/');
#endif
const VFS::Path::Normalized path(str);
const auto p = std::filesystem::path{ path }; // Purposefully damage Unicode strings.
const auto stem = p.stem();
const auto ext = p.extension().string(); // Purposefully damage Unicode strings.
const std::string_view stem = path.stem();
const std::string_view folder = path.parent().value();
std::uint64_t folderHash = generateHash(p.parent_path(), {});
std::uint64_t folderHash = generateHash(folder, {});
auto it = mFolders.find(folderHash);
if (it == mFolders.end())
return FileRecord();
const std::string_view file = path.filename().value();
std::string_view ext;
if (const std::size_t pos = Misc::findExtension(file); pos != std::string_view::npos)
{
// ext including .
ext = file;
ext.remove_prefix(pos);
}
std::uint64_t fileHash = generateHash(stem, ext);
auto iter = it->second.mFiles.find(fileHash);
if (iter == it->second.mFiles.end())
@ -262,16 +262,6 @@ namespace Bsa
fail("Add file is not implemented for compressed BSA: " + filename);
}
Files::IStreamPtr CompressedBSAFile::getFile(const char* file)
{
FileRecord fileRec = getFileRecord(file);
if (fileRec.mOffset == std::numeric_limits<uint32_t>::max())
{
fail("File not found: " + std::string(file));
}
return getFile(fileRec);
}
Files::IStreamPtr CompressedBSAFile::getFile(const FileRecord& fileRecord)
{
size_t size = fileRecord.mSize & (~FileSizeFlag_Compression);
@ -337,29 +327,31 @@ namespace Bsa
return std::make_unique<Files::StreamWithBuffer<MemoryInputStream>>(std::move(memoryStreamPtr));
}
std::uint64_t CompressedBSAFile::generateHash(const std::filesystem::path& stem, std::string extension)
std::uint64_t CompressedBSAFile::generateHash(std::string_view str, std::string_view extension)
{
auto str = stem.u8string();
size_t len = str.length();
if (len == 0)
if (str.empty())
return 0;
std::replace(str.begin(), str.end(), '/', '\\');
Misc::StringUtils::lowerCaseInPlace(str);
uint64_t result = str[len - 1];
const auto at = [&](std::size_t i) -> char {
const char c = str[i];
if (c == '/')
return '\\';
return c;
};
const size_t len = str.length();
uint64_t result = at(len - 1);
if (len >= 3)
result |= str[len - 2] << 8;
result |= at(len - 2) << 8;
result |= len << 16;
result |= static_cast<uint32_t>(str[0] << 24);
result |= static_cast<uint32_t>(at(0) << 24);
if (len >= 4)
{
uint32_t hash = 0;
for (size_t i = 1; i <= len - 3; ++i)
hash = hash * 0x1003f + str[i];
hash = hash * 0x1003f + at(i);
result += static_cast<uint64_t>(hash) << 32;
}
if (extension.empty())
return result;
Misc::StringUtils::lowerCaseInPlace(extension);
if (extension == ".kf")
result |= 0x80;
else if (extension == ".nif")

View file

@ -26,7 +26,6 @@
#ifndef OPENMW_COMPONENTS_BSA_COMPRESSEDBSAFILE_HPP
#define OPENMW_COMPONENTS_BSA_COMPRESSEDBSAFILE_HPP
#include <filesystem>
#include <limits>
#include <map>
@ -112,7 +111,7 @@ namespace Bsa
FileRecord getFileRecord(std::string_view str) const;
/// \brief Normalizes given filename or folder and generates format-compatible hash.
static std::uint64_t generateHash(const std::filesystem::path& stem, std::string extension);
static std::uint64_t generateHash(std::string_view stem, std::string_view extension);
Files::IStreamPtr getFile(const FileRecord& fileRecord);
public:
@ -127,7 +126,6 @@ namespace Bsa
/// Read header information from the input source
void readHeader(std::istream& input) override;
Files::IStreamPtr getFile(const char* filePath);
Files::IStreamPtr getFile(const FileStruct* fileStruct);
void addFile(const std::string& filename, std::istream& file);
};

View file

@ -57,16 +57,16 @@ namespace ESM4
return header + ": code " + std::to_string(errorCode) + ", " + std::string(msg != nullptr ? msg : "(null)");
}
std::u8string_view getStringsSuffix(LocalizedStringType type)
std::string_view getStringsSuffix(LocalizedStringType type)
{
switch (type)
{
case LocalizedStringType::Strings:
return u8".STRINGS";
return ".STRINGS";
case LocalizedStringType::ILStrings:
return u8".ILSTRINGS";
return ".ILSTRINGS";
case LocalizedStringType::DLStrings:
return u8".DLSTRINGS";
return ".DLSTRINGS";
}
throw std::logic_error("Unsupported LocalizedStringType: " + std::to_string(static_cast<int>(type)));
@ -304,29 +304,35 @@ namespace ESM4
if ((mHeader.mFlags & Rec_ESM) == 0 || (mHeader.mFlags & Rec_Localized) == 0)
return;
const std::u8string prefix = mCtx.filename.stem().filename().u8string();
const std::string prefix = Files::pathToUnicodeString(mCtx.filename.stem().filename());
buildLStringIndex(LocalizedStringType::Strings, prefix);
buildLStringIndex(LocalizedStringType::ILStrings, prefix);
buildLStringIndex(LocalizedStringType::DLStrings, prefix);
}
void Reader::buildLStringIndex(LocalizedStringType stringType, const std::u8string& prefix)
void Reader::buildLStringIndex(LocalizedStringType stringType, std::string_view prefix)
{
static const std::filesystem::path strings("Strings");
const std::u8string language(u8"_En");
const std::u8string altLanguage(u8"_English");
const std::u8string suffix(getStringsSuffix(stringType));
std::filesystem::path path = strings / (prefix + language + suffix);
const std::string_view suffix = getStringsSuffix(stringType);
constexpr std::string_view language("_En");
constexpr std::string_view altLanguage("_English");
if (mVFS != nullptr)
{
VFS::Path::Normalized vfsPath(Files::pathToUnicodeString(path));
constexpr VFS::Path::NormalizedView strings("strings");
std::string fileName(prefix);
fileName += language;
fileName += suffix;
VFS::Path::Normalized vfsPath(strings);
vfsPath /= fileName;
Files::IStreamPtr stream = mVFS->find(vfsPath);
if (stream == nullptr)
{
path = strings / (prefix + altLanguage + suffix);
vfsPath = VFS::Path::Normalized(Files::pathToUnicodeString(path));
fileName = prefix;
fileName += altLanguage;
fileName += suffix;
vfsPath = strings;
vfsPath /= fileName;
stream = mVFS->find(vfsPath);
}
@ -337,28 +343,33 @@ namespace ESM4
}
if (mIgnoreMissingLocalizedStrings)
{
Log(Debug::Warning) << "Ignore missing VFS strings file: " << vfsPath;
}
else
{
static const std::filesystem::path strings("Strings");
std::string fileName(prefix);
fileName += language;
fileName += suffix;
std::filesystem::path fsPath = mCtx.filename.parent_path() / strings / fileName;
if (!std::filesystem::exists(fsPath))
{
fileName = prefix;
fileName += altLanguage;
fileName += suffix;
fsPath = mCtx.filename.parent_path() / strings / fileName;
}
if (std::filesystem::exists(fsPath))
{
const Files::IStreamPtr stream = Files::openConstrainedFileStream(fsPath);
buildLStringIndex(stringType, *stream);
return;
}
}
std::filesystem::path fsPath = mCtx.filename.parent_path() / path;
if (!std::filesystem::exists(fsPath))
{
path = strings / (prefix + altLanguage + suffix);
fsPath = mCtx.filename.parent_path() / path;
if (mIgnoreMissingLocalizedStrings)
Log(Debug::Warning) << "Ignore missing strings file: " << fsPath;
}
if (std::filesystem::exists(fsPath))
{
const Files::IStreamPtr stream = Files::openConstrainedFileStream(fsPath);
buildLStringIndex(stringType, *stream);
return;
}
if (mIgnoreMissingLocalizedStrings)
Log(Debug::Warning) << "Ignore missing strings file: " << fsPath;
}
void Reader::buildLStringIndex(LocalizedStringType stringType, std::istream& stream)

View file

@ -167,7 +167,7 @@ namespace ESM4
bool mIgnoreMissingLocalizedStrings = false;
void buildLStringIndex(LocalizedStringType stringType, const std::u8string& prefix);
void buildLStringIndex(LocalizedStringType stringType, std::string_view prefix);
void buildLStringIndex(LocalizedStringType stringType, std::istream& stream);

View file

@ -289,8 +289,9 @@ namespace Gui
MyGUI::IntSize bookSize = getBookSize(layersStream.get());
float bookScale = MyGUIPlatform::ScalingLayer::getScaleFactor(bookSize);
const auto oldDataPath = dataManager->getDataPath({});
dataManager->setResourcePath("fonts");
const VFS::Path::Normalized oldDataPath(dataManager->getResourcePath());
constexpr VFS::Path::NormalizedView fonts("fonts");
dataManager->setResourcePath(fonts);
std::unique_ptr<MyGUI::IDataStream> dataStream(dataManager->getData(fileName));
MyGUI::xml::Document xmlDocument;

View file

@ -5,7 +5,6 @@
#include <MyGUI_DataFileStream.h>
#include <components/files/conversion.hpp>
#include <components/vfs/manager.hpp>
namespace
@ -27,12 +26,17 @@ namespace
namespace MyGUIPlatform
{
void DataManager::setResourcePath(const std::filesystem::path& path)
void DataManager::setResourcePath(VFS::Path::NormalizedView path)
{
mResourcePath = path;
}
DataManager::DataManager(const std::string& resourcePath, const VFS::Manager* vfs)
VFS::Path::NormalizedView DataManager::getResourcePath() const
{
return mResourcePath;
}
DataManager::DataManager(VFS::Path::NormalizedView resourcePath, const VFS::Manager* vfs)
: mResourcePath(resourcePath)
, mVfs(vfs)
{
@ -40,7 +44,9 @@ namespace MyGUIPlatform
MyGUI::IDataStream* DataManager::getData(const std::string& name) const
{
return new DataStream(mVfs->get(Files::pathToUnicodeString(mResourcePath / name)));
VFS::Path::Normalized path(mResourcePath);
path /= name;
return new DataStream(mVfs->get(path));
}
void DataManager::freeData(MyGUI::IDataStream* data)
@ -50,7 +56,9 @@ namespace MyGUIPlatform
bool DataManager::isDataExist(const std::string& name) const
{
return mVfs->exists(Files::pathToUnicodeString(mResourcePath / name));
VFS::Path::Normalized path(mResourcePath);
path /= name;
return mVfs->exists(path);
}
const MyGUI::VectorString& DataManager::getDataListNames(const std::string& /*pattern*/) const
@ -60,15 +68,12 @@ namespace MyGUIPlatform
std::string DataManager::getDataPath(const std::string& name) const
{
if (name.empty())
{
return Files::pathToUnicodeString(mResourcePath);
}
if (!isDataExist(name))
VFS::Path::Normalized path(mResourcePath);
path /= name;
if (!mVfs->exists(path))
return {};
return Files::pathToUnicodeString(mResourcePath / name);
return path;
}
}

View file

@ -3,9 +3,10 @@
#include <MyGUI_DataManager.h>
#include <filesystem>
#include <string>
#include <components/vfs/pathutil.hpp>
namespace VFS
{
class Manager;
@ -17,9 +18,10 @@ namespace MyGUIPlatform
class DataManager : public MyGUI::DataManager
{
public:
explicit DataManager(const std::string& path, const VFS::Manager* vfs);
explicit DataManager(VFS::Path::NormalizedView path, const VFS::Manager* vfs);
void setResourcePath(const std::filesystem::path& path);
void setResourcePath(VFS::Path::NormalizedView path);
VFS::Path::NormalizedView getResourcePath() const;
/** Get data stream from specified resource name.
@param name Resource name (usually file name).
@ -48,7 +50,7 @@ namespace MyGUIPlatform
std::string getDataPath(const std::string& name) const override;
private:
std::filesystem::path mResourcePath;
VFS::Path::Normalized mResourcePath;
const VFS::Manager* mVfs;
};

View file

@ -4,17 +4,15 @@
#include "myguiloglistener.hpp"
#include "myguirendermanager.hpp"
#include "components/files/conversion.hpp"
namespace MyGUIPlatform
{
Platform::Platform(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ImageManager* imageManager,
const VFS::Manager* vfs, float uiScalingFactor, const std::filesystem::path& resourcePath,
const VFS::Manager* vfs, float uiScalingFactor, VFS::Path::NormalizedView resourcePath,
const std::filesystem::path& logName)
: mLogFacility(logName.empty() ? nullptr : std::make_unique<LogFacility>(logName, false))
, mLogManager(std::make_unique<MyGUI::LogManager>())
, mDataManager(std::make_unique<DataManager>(Files::pathToUnicodeString(resourcePath), vfs))
, mDataManager(std::make_unique<DataManager>(resourcePath, vfs))
, mRenderManager(std::make_unique<RenderManager>(viewer, guiRoot, imageManager, uiScalingFactor))
{
if (mLogFacility != nullptr)

View file

@ -5,6 +5,8 @@
#include <memory>
#include <string>
#include <components/vfs/pathutil.hpp>
namespace osgViewer
{
class Viewer;
@ -37,7 +39,7 @@ namespace MyGUIPlatform
{
public:
Platform(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ImageManager* imageManager,
const VFS::Manager* vfs, float uiScalingFactor, const std::filesystem::path& resourcePath,
const VFS::Manager* vfs, float uiScalingFactor, VFS::Path::NormalizedView resourcePath,
const std::filesystem::path& logName = "MyGUI.log");
~Platform();