1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-12-17 16:13:08 +00:00

Treat TES4+ BSA paths as VFS paths

This commit is contained in:
Evil Eye 2025-12-08 17:44:15 +01:00
parent d45f252f69
commit 82914be8b2
8 changed files with 54 additions and 96 deletions

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