Hard fail on loading BSA with records using unicode paths.

crashfix_debugdraw
Project579 2 years ago
parent a60cebd0f9
commit 9ceafe770d

@ -83,7 +83,7 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile)
} }
MWState::Character::Character (std::filesystem::path saves, const std::string& game) 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)) if (!std::filesystem::is_directory (mPath))
{ {

@ -35,7 +35,7 @@ using namespace Bsa;
/// Error handling /// 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)); throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + Files::pathToUnicodeString(mFilepath));
} }

@ -90,7 +90,7 @@ protected:
std::filesystem::path mFilepath; std::filesystem::path mFilepath;
/// Error handling /// Error handling
[[noreturn]] void fail(const std::string &msg); [[noreturn]] void fail(const std::string &msg) const;
/// Read header information from the input source /// Read header information from the input source
virtual void readHeader(); virtual void readHeader();

@ -300,6 +300,13 @@ void CompressedBSAFile::readHeader()
CompressedBSAFile::FileRecord CompressedBSAFile::getFileRecord(const std::string& str) const CompressedBSAFile::FileRecord CompressedBSAFile::getFileRecord(const std::string& str) const
{ {
for (const auto c : str)
{
if (((static_cast<unsigned>(c) >> 7U) & 1U) != 0U) {
fail("File record " + str + " contains unicode characters, refusing to load.");
}
}
#ifdef _WIN32 #ifdef _WIN32
const auto& path = str; const auto& path = str;
#else #else
@ -310,9 +317,9 @@ CompressedBSAFile::FileRecord CompressedBSAFile::getFileRecord(const std::string
std::replace(path.begin(), path.end(), '\\', '/'); std::replace(path.begin(), path.end(), '\\', '/');
#endif #endif
auto p = Files::pathFromUnicodeString(path); const auto p = std::filesystem::path{ path }; // Purposefully damage Unicode strings.
const auto stem = p.stem(); 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(), {}); 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(); auto str = stem.u8string();
size_t len = str.length(); size_t len = str.length();
if (len == 0) if (len == 0)
return 0; return 0;
std::replace(str.begin(), str.end(), u8'/', u8'\\'); std::replace(str.begin(), str.end(), '/', '\\');
Misc::StringUtils::lowerCaseInPlace(str); Misc::StringUtils::lowerCaseInPlace(str);
uint64_t result = str[len-1] | (len >= 3 ? (str[len-2] << 8) : 0) | (len << 16) | (str[0] << 24); uint64_t result = str[len-1] | (len >= 3 ? (str[len-2] << 8) : 0) | (len << 16) | (str[0] << 24);
if (len >= 4) if (len >= 4)
@ -490,10 +497,10 @@ std::uint64_t CompressedBSAFile::generateHash(const std::filesystem::path& stem,
if (extension.empty()) if (extension.empty())
return result; return result;
Misc::StringUtils::lowerCaseInPlace(extension); Misc::StringUtils::lowerCaseInPlace(extension);
if (extension == u8".kf") result |= 0x80; if (extension == ".kf") result |= 0x80;
else if (extension == u8".nif") result |= 0x8000; else if (extension == ".nif") result |= 0x8000;
else if (extension == u8".dds") result |= 0x8080; else if (extension == ".dds") result |= 0x8080;
else if (extension == u8".wav") result |= 0x80000000; else if (extension == ".wav") result |= 0x80000000;
uint32_t hash = 0; uint32_t hash = 0;
for (const auto &c : extension) for (const auto &c : extension)
hash = hash * 0x1003f + c; hash = hash * 0x1003f + c;

@ -83,7 +83,7 @@ namespace Bsa
//mFiles used by OpenMW will contain uncompressed file sizes //mFiles used by OpenMW will contain uncompressed file sizes
void convertCompressedSizesToUncompressed(); void convertCompressedSizesToUncompressed();
/// \brief Normalizes given filename or folder and generates format-compatible hash. See https://en.uesp.net/wiki/Tes4Mod:Hash_Calculation. /// \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); Files::IStreamPtr getFile(const FileRecord& fileRecord);
public: public:
using BSAFile::open; using BSAFile::open;

Loading…
Cancel
Save