mirror of
https://github.com/OpenMW/openmw.git
synced 2025-12-17 17:43:07 +00:00
Treat TES4+ BSA paths as VFS paths
This commit is contained in:
parent
d45f252f69
commit
82914be8b2
8 changed files with 54 additions and 96 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue