mirror of
https://github.com/OpenMW/openmw.git
synced 2025-12-13 00:43:05 +00:00
Merge branch 'bsa_archive' into 'master'
Cleanup and optimize BSA archives code See merge request OpenMW/openmw!3876
This commit is contained in:
commit
518f34c869
8 changed files with 72 additions and 113 deletions
|
|
@ -329,17 +329,19 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
switch (bsaVersion)
|
switch (bsaVersion)
|
||||||
{
|
{
|
||||||
case Bsa::BSAVER_COMPRESSED:
|
case Bsa::BsaVersion::Unknown:
|
||||||
return call<Bsa::CompressedBSAFile>(info);
|
break;
|
||||||
case Bsa::BSAVER_BA2_GNRL:
|
case Bsa::BsaVersion::Uncompressed:
|
||||||
return call<Bsa::BA2GNRLFile>(info);
|
|
||||||
case Bsa::BSAVER_BA2_DX10:
|
|
||||||
return call<Bsa::BA2DX10File>(info);
|
|
||||||
case Bsa::BSAVER_UNCOMPRESSED:
|
|
||||||
return call<Bsa::BSAFile>(info);
|
return call<Bsa::BSAFile>(info);
|
||||||
default:
|
case Bsa::BsaVersion::Compressed:
|
||||||
throw std::runtime_error("Unrecognised BSA archive");
|
return call<Bsa::CompressedBSAFile>(info);
|
||||||
|
case Bsa::BsaVersion::BA2GNRL:
|
||||||
|
return call<Bsa::BA2GNRLFile>(info);
|
||||||
|
case Bsa::BsaVersion::BA2DX10:
|
||||||
|
return call<Bsa::BA2DX10File>(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Unrecognised BSA archive");
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -819,7 +819,7 @@ void Launcher::DataFilesPage::addArchivesFromDir(const QString& path)
|
||||||
for (const auto& fileinfo : dir.entryInfoList(archiveFilter))
|
for (const auto& fileinfo : dir.entryInfoList(archiveFilter))
|
||||||
{
|
{
|
||||||
const auto absPath = fileinfo.absoluteFilePath();
|
const auto absPath = fileinfo.absoluteFilePath();
|
||||||
if (Bsa::BSAFile::detectVersion(Files::pathFromQString(absPath)) == Bsa::BSAVER_UNKNOWN)
|
if (Bsa::BSAFile::detectVersion(Files::pathFromQString(absPath)) == Bsa::BsaVersion::Unknown)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto fileName = fileinfo.fileName();
|
const auto fileName = fileinfo.fileName();
|
||||||
|
|
|
||||||
|
|
@ -42,29 +42,10 @@ bool isBSA(const std::filesystem::path& filename)
|
||||||
return hasExtension(filename, ".bsa") || hasExtension(filename, ".ba2");
|
return hasExtension(filename, ".bsa") || hasExtension(filename, ".ba2");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<VFS::Archive> makeBsaArchive(const std::filesystem::path& path)
|
|
||||||
{
|
|
||||||
switch (Bsa::BSAFile::detectVersion(path))
|
|
||||||
{
|
|
||||||
case Bsa::BSAVER_COMPRESSED:
|
|
||||||
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_COMPRESSED>::type>(path);
|
|
||||||
case Bsa::BSAVER_BA2_GNRL:
|
|
||||||
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_BA2_GNRL>::type>(path);
|
|
||||||
case Bsa::BSAVER_BA2_DX10:
|
|
||||||
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_BA2_DX10>::type>(path);
|
|
||||||
case Bsa::BSAVER_UNCOMPRESSED:
|
|
||||||
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_UNCOMPRESSED>::type>(path);
|
|
||||||
case Bsa::BSAVER_UNKNOWN:
|
|
||||||
default:
|
|
||||||
std::cerr << "'" << Files::pathToUnicodeString(path) << "' is not a recognized BSA archive" << std::endl;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
|
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
|
||||||
{
|
{
|
||||||
if (isBSA(path))
|
if (isBSA(path))
|
||||||
return makeBsaArchive(path);
|
return VFS::makeBsaArchive(path);
|
||||||
if (std::filesystem::is_directory(path))
|
if (std::filesystem::is_directory(path))
|
||||||
return std::make_unique<VFS::FileSystemArchive>(path);
|
return std::make_unique<VFS::FileSystemArchive>(path);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -124,17 +105,23 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
|
||||||
|
|
||||||
if (!archivePath.empty() && !isBSA(archivePath))
|
if (!archivePath.empty() && !isBSA(archivePath))
|
||||||
{
|
{
|
||||||
Files::PathContainer dataDirs = { archivePath };
|
const Files::Collections fileCollections({ archivePath });
|
||||||
const Files::Collections fileCollections = Files::Collections(dataDirs);
|
|
||||||
const Files::MultiDirCollection& bsaCol = fileCollections.getCollection(".bsa");
|
const Files::MultiDirCollection& bsaCol = fileCollections.getCollection(".bsa");
|
||||||
const Files::MultiDirCollection& ba2Col = fileCollections.getCollection(".ba2");
|
const Files::MultiDirCollection& ba2Col = fileCollections.getCollection(".ba2");
|
||||||
for (auto& file : bsaCol)
|
for (const Files::MultiDirCollection& collection : { bsaCol, ba2Col })
|
||||||
{
|
{
|
||||||
readVFS(makeBsaArchive(file.second), file.second, quiet);
|
for (auto& file : collection)
|
||||||
}
|
{
|
||||||
for (auto& file : ba2Col)
|
try
|
||||||
{
|
{
|
||||||
readVFS(makeBsaArchive(file.second), file.second, quiet);
|
readVFS(VFS::makeBsaArchive(file.second), file.second, quiet);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to read archive file '" << Files::pathToUnicodeString(file.second)
|
||||||
|
<< "': " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -325,43 +325,33 @@ BsaVersion Bsa::BSAFile::detectVersion(const std::filesystem::path& filePath)
|
||||||
{
|
{
|
||||||
std::ifstream input(filePath, std::ios_base::binary);
|
std::ifstream input(filePath, std::ios_base::binary);
|
||||||
|
|
||||||
// Total archive size
|
|
||||||
std::streamoff fsize = 0;
|
|
||||||
if (input.seekg(0, std::ios_base::end))
|
|
||||||
{
|
|
||||||
fsize = input.tellg();
|
|
||||||
input.seekg(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fsize < 12)
|
|
||||||
{
|
|
||||||
return BSAVER_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get essential header numbers
|
// Get essential header numbers
|
||||||
|
|
||||||
// First 12 bytes
|
// First 12 bytes
|
||||||
uint32_t head[3];
|
uint32_t head[3];
|
||||||
|
|
||||||
input.read(reinterpret_cast<char*>(head), 12);
|
input.read(reinterpret_cast<char*>(head), sizeof(head));
|
||||||
|
|
||||||
if (head[0] == static_cast<uint32_t>(BSAVER_UNCOMPRESSED))
|
if (input.gcount() != sizeof(head))
|
||||||
|
return BsaVersion::Unknown;
|
||||||
|
|
||||||
|
if (head[0] == static_cast<uint32_t>(BsaVersion::Uncompressed))
|
||||||
{
|
{
|
||||||
return BSAVER_UNCOMPRESSED;
|
return BsaVersion::Uncompressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (head[0] == static_cast<uint32_t>(BSAVER_COMPRESSED) || head[0] == ESM::fourCC("BTDX"))
|
if (head[0] == static_cast<uint32_t>(BsaVersion::Compressed) || head[0] == ESM::fourCC("BTDX"))
|
||||||
{
|
{
|
||||||
if (head[1] == static_cast<uint32_t>(0x01))
|
if (head[1] == static_cast<uint32_t>(0x01))
|
||||||
{
|
{
|
||||||
if (head[2] == ESM::fourCC("GNRL"))
|
if (head[2] == ESM::fourCC("GNRL"))
|
||||||
return BSAVER_BA2_GNRL;
|
return BsaVersion::BA2GNRL;
|
||||||
if (head[2] == ESM::fourCC("DX10"))
|
if (head[2] == ESM::fourCC("DX10"))
|
||||||
return BSAVER_BA2_DX10;
|
return BsaVersion::BA2DX10;
|
||||||
return BSAVER_UNKNOWN;
|
return BsaVersion::Unknown;
|
||||||
}
|
}
|
||||||
return BSAVER_COMPRESSED;
|
return BsaVersion::Compressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return BSAVER_UNKNOWN;
|
return BsaVersion::Unknown;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@
|
||||||
namespace Bsa
|
namespace Bsa
|
||||||
{
|
{
|
||||||
|
|
||||||
enum BsaVersion
|
enum class BsaVersion : std::uint32_t
|
||||||
{
|
{
|
||||||
BSAVER_UNKNOWN = 0x0,
|
Unknown = 0x0,
|
||||||
BSAVER_UNCOMPRESSED = 0x100,
|
Uncompressed = 0x100,
|
||||||
BSAVER_COMPRESSED = 0x415342, // B, S, A,
|
Compressed = 0x415342, // B, S, A,
|
||||||
BSAVER_BA2_GNRL, // used by FO4, BSA which contains files
|
BA2GNRL, // used by FO4, BSA which contains files
|
||||||
BSAVER_BA2_DX10 // used by FO4, BSA which contains textures
|
BA2DX10 // used by FO4, BSA which contains textures
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ namespace Bsa
|
||||||
|
|
||||||
input.read(reinterpret_cast<char*>(&mHeader), sizeof(mHeader));
|
input.read(reinterpret_cast<char*>(&mHeader), sizeof(mHeader));
|
||||||
|
|
||||||
if (mHeader.mFormat != BSAVER_COMPRESSED) // BSA
|
if (mHeader.mFormat != static_cast<std::uint32_t>(BsaVersion::Compressed)) // BSA
|
||||||
fail("Unrecognized compressed BSA format");
|
fail("Unrecognized compressed BSA format");
|
||||||
if (mHeader.mVersion != Version_TES4 && mHeader.mVersion != Version_FO3 && mHeader.mVersion != Version_SSE)
|
if (mHeader.mVersion != Version_TES4 && mHeader.mVersion != Version_FO3 && mHeader.mVersion != Version_SSE)
|
||||||
fail("Unrecognized compressed BSA version");
|
fail("Unrecognized compressed BSA version");
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@
|
||||||
#include <components/bsa/bsa_file.hpp>
|
#include <components/bsa/bsa_file.hpp>
|
||||||
#include <components/bsa/compressedbsafile.hpp>
|
#include <components/bsa/compressedbsafile.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace VFS
|
namespace VFS
|
||||||
{
|
{
|
||||||
template <typename FileType>
|
template <typename FileType>
|
||||||
|
|
@ -44,10 +48,11 @@ namespace VFS
|
||||||
for (Bsa::BSAFile::FileList::const_iterator it = filelist.begin(); it != filelist.end(); ++it)
|
for (Bsa::BSAFile::FileList::const_iterator it = filelist.begin(); it != filelist.end(); ++it)
|
||||||
{
|
{
|
||||||
mResources.emplace_back(&*it, mFile.get());
|
mResources.emplace_back(&*it, mFile.get());
|
||||||
|
mFiles.emplace_back(it->name());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~BsaArchive() {}
|
std::sort(mFiles.begin(), mFiles.end());
|
||||||
|
}
|
||||||
|
|
||||||
void listResources(FileMap& out) override
|
void listResources(FileMap& out) override
|
||||||
{
|
{
|
||||||
|
|
@ -57,12 +62,7 @@ namespace VFS
|
||||||
|
|
||||||
bool contains(Path::NormalizedView file) const override
|
bool contains(Path::NormalizedView file) const override
|
||||||
{
|
{
|
||||||
for (const auto& it : mResources)
|
return std::binary_search(mFiles.begin(), mFiles.end(), file);
|
||||||
{
|
|
||||||
if (Path::pathEqual(file.value(), it.mInfo->name()))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getDescription() const override { return std::string{ "BSA: " } + mFile->getFilename(); }
|
std::string getDescription() const override { return std::string{ "BSA: " } + mFile->getFilename(); }
|
||||||
|
|
@ -70,36 +70,27 @@ namespace VFS
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<BSAFileType> mFile;
|
std::unique_ptr<BSAFileType> mFile;
|
||||||
std::vector<BsaArchiveFile<BSAFileType>> mResources;
|
std::vector<BsaArchiveFile<BSAFileType>> mResources;
|
||||||
|
std::vector<VFS::Path::Normalized> mFiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <Bsa::BsaVersion>
|
inline std::unique_ptr<VFS::Archive> makeBsaArchive(const std::filesystem::path& path)
|
||||||
struct ArchiveSelector
|
|
||||||
{
|
{
|
||||||
};
|
switch (Bsa::BSAFile::detectVersion(path))
|
||||||
|
{
|
||||||
|
case Bsa::BsaVersion::Unknown:
|
||||||
|
break;
|
||||||
|
case Bsa::BsaVersion::Uncompressed:
|
||||||
|
return std::make_unique<BsaArchive<Bsa::BSAFile>>(path);
|
||||||
|
case Bsa::BsaVersion::Compressed:
|
||||||
|
return std::make_unique<BsaArchive<Bsa::CompressedBSAFile>>(path);
|
||||||
|
case Bsa::BsaVersion::BA2GNRL:
|
||||||
|
return std::make_unique<BsaArchive<Bsa::BA2GNRLFile>>(path);
|
||||||
|
case Bsa::BsaVersion::BA2DX10:
|
||||||
|
return std::make_unique<BsaArchive<Bsa::BA2DX10File>>(path);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
throw std::runtime_error("Unknown archive type '" + Files::pathToUnicodeString(path) + "'");
|
||||||
struct ArchiveSelector<Bsa::BSAVER_UNCOMPRESSED>
|
}
|
||||||
{
|
|
||||||
using type = BsaArchive<Bsa::BSAFile>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct ArchiveSelector<Bsa::BSAVER_COMPRESSED>
|
|
||||||
{
|
|
||||||
using type = BsaArchive<Bsa::CompressedBSAFile>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct ArchiveSelector<Bsa::BSAVER_BA2_GNRL>
|
|
||||||
{
|
|
||||||
using type = BsaArchive<Bsa::BA2GNRLFile>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct ArchiveSelector<Bsa::BSAVER_BA2_DX10>
|
|
||||||
{
|
|
||||||
using type = BsaArchive<Bsa::BA2DX10File>;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -25,18 +25,7 @@ namespace VFS
|
||||||
// Last BSA has the highest priority
|
// Last BSA has the highest priority
|
||||||
const auto archivePath = collections.getPath(*archive);
|
const auto archivePath = collections.getPath(*archive);
|
||||||
Log(Debug::Info) << "Adding BSA archive " << archivePath;
|
Log(Debug::Info) << "Adding BSA archive " << archivePath;
|
||||||
Bsa::BsaVersion bsaVersion = Bsa::BSAFile::detectVersion(archivePath);
|
vfs->addArchive(makeBsaArchive(archivePath));
|
||||||
|
|
||||||
if (bsaVersion == Bsa::BSAVER_COMPRESSED)
|
|
||||||
vfs->addArchive(std::make_unique<ArchiveSelector<Bsa::BSAVER_COMPRESSED>::type>(archivePath));
|
|
||||||
else if (bsaVersion == Bsa::BSAVER_BA2_GNRL)
|
|
||||||
vfs->addArchive(std::make_unique<ArchiveSelector<Bsa::BSAVER_BA2_GNRL>::type>(archivePath));
|
|
||||||
else if (bsaVersion == Bsa::BSAVER_BA2_DX10)
|
|
||||||
vfs->addArchive(std::make_unique<ArchiveSelector<Bsa::BSAVER_BA2_DX10>::type>(archivePath));
|
|
||||||
else if (bsaVersion == Bsa::BSAVER_UNCOMPRESSED)
|
|
||||||
vfs->addArchive(std::make_unique<ArchiveSelector<Bsa::BSAVER_UNCOMPRESSED>::type>(archivePath));
|
|
||||||
else
|
|
||||||
throw std::runtime_error("Unknown archive type '" + *archive + "'");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue