From 9ceafe770d55499674fccbcdf111b194968a72e8 Mon Sep 17 00:00:00 2001 From: Project579 Date: Sat, 10 Sep 2022 19:38:29 +0200 Subject: [PATCH] Hard fail on loading BSA with records using unicode paths. --- apps/openmw/mwstate/character.cpp | 2 +- components/bsa/bsa_file.cpp | 2 +- components/bsa/bsa_file.hpp | 2 +- components/bsa/compressedbsafile.cpp | 23 +++++++++++++++-------- components/bsa/compressedbsafile.hpp | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 136adfec89..8e21325c47 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -83,7 +83,7 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile) } MWState::Character::Character (std::filesystem::path saves, const std::string& game) - : mPath (std::move(saves)) +: mPath (std::move(saves)) { if (!std::filesystem::is_directory (mPath)) { diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 4bd47a2c92..6a621d91a8 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -35,7 +35,7 @@ using namespace Bsa; /// Error handling -[[noreturn]] void BSAFile::fail(const std::string &msg) +[[noreturn]] void BSAFile::fail(const std::string &msg) const { throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + Files::pathToUnicodeString(mFilepath)); } diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index 0d1bda6134..b7f3b107fb 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -90,7 +90,7 @@ protected: std::filesystem::path mFilepath; /// Error handling - [[noreturn]] void fail(const std::string &msg); + [[noreturn]] void fail(const std::string &msg) const; /// Read header information from the input source virtual void readHeader(); diff --git a/components/bsa/compressedbsafile.cpp b/components/bsa/compressedbsafile.cpp index ee7204bd9e..ac50ef0c22 100644 --- a/components/bsa/compressedbsafile.cpp +++ b/components/bsa/compressedbsafile.cpp @@ -300,6 +300,13 @@ void CompressedBSAFile::readHeader() CompressedBSAFile::FileRecord CompressedBSAFile::getFileRecord(const std::string& str) const { + for (const auto c : str) + { + if (((static_cast(c) >> 7U) & 1U) != 0U) { + fail("File record " + str + " contains unicode characters, refusing to load."); + } + } + #ifdef _WIN32 const auto& path = str; #else @@ -310,9 +317,9 @@ CompressedBSAFile::FileRecord CompressedBSAFile::getFileRecord(const std::string std::replace(path.begin(), path.end(), '\\', '/'); #endif - auto p = Files::pathFromUnicodeString(path); + const auto p = std::filesystem::path{ path }; // Purposefully damage Unicode strings. const auto stem = p.stem(); - const auto ext = p.extension().u8string(); + const auto ext = p.extension().string(); // Purposefully damage Unicode strings. std::uint64_t folderHash = generateHash(p.parent_path(), {}); @@ -471,13 +478,13 @@ void CompressedBSAFile::convertCompressedSizesToUncompressed() } } -std::uint64_t CompressedBSAFile::generateHash(const std::filesystem::path& stem, std::u8string extension) +std::uint64_t CompressedBSAFile::generateHash(const std::filesystem::path& stem, std::string extension) { auto str = stem.u8string(); size_t len = str.length(); if (len == 0) return 0; - std::replace(str.begin(), str.end(), u8'/', u8'\\'); + std::replace(str.begin(), str.end(), '/', '\\'); Misc::StringUtils::lowerCaseInPlace(str); uint64_t result = str[len-1] | (len >= 3 ? (str[len-2] << 8) : 0) | (len << 16) | (str[0] << 24); if (len >= 4) @@ -490,10 +497,10 @@ std::uint64_t CompressedBSAFile::generateHash(const std::filesystem::path& stem, if (extension.empty()) return result; Misc::StringUtils::lowerCaseInPlace(extension); - if (extension == u8".kf") result |= 0x80; - else if (extension == u8".nif") result |= 0x8000; - else if (extension == u8".dds") result |= 0x8080; - else if (extension == u8".wav") result |= 0x80000000; + if (extension == ".kf") result |= 0x80; + else if (extension == ".nif") result |= 0x8000; + else if (extension == ".dds") result |= 0x8080; + else if (extension == ".wav") result |= 0x80000000; uint32_t hash = 0; for (const auto &c : extension) hash = hash * 0x1003f + c; diff --git a/components/bsa/compressedbsafile.hpp b/components/bsa/compressedbsafile.hpp index 71727bfade..bad8551f09 100644 --- a/components/bsa/compressedbsafile.hpp +++ b/components/bsa/compressedbsafile.hpp @@ -83,7 +83,7 @@ namespace Bsa //mFiles used by OpenMW will contain uncompressed file sizes void convertCompressedSizesToUncompressed(); /// \brief Normalizes given filename or folder and generates format-compatible hash. See https://en.uesp.net/wiki/Tes4Mod:Hash_Calculation. - static std::uint64_t generateHash(const std::filesystem::path& stem, std::u8string extension) ; + static std::uint64_t generateHash(const std::filesystem::path& stem, std::string extension) ; Files::IStreamPtr getFile(const FileRecord& fileRecord); public: using BSAFile::open;