diff options
Diffstat (limited to 'libs/bsatk')
| -rw-r--r-- | libs/bsatk/include/bsatk/bsaarchive.h | 21 | ||||
| -rw-r--r-- | libs/bsatk/src/bsaarchive.cpp | 102 |
2 files changed, 119 insertions, 4 deletions
diff --git a/libs/bsatk/include/bsatk/bsaarchive.h b/libs/bsatk/include/bsatk/bsaarchive.h index cd15f64..915b9fb 100644 --- a/libs/bsatk/include/bsatk/bsaarchive.h +++ b/libs/bsatk/include/bsatk/bsaarchive.h @@ -112,6 +112,23 @@ public: * @return ERROR_NONE on success or an error code */ EErrorCode extract(File::Ptr file, const char* outputDirectory) const; + + /** + * extract a file from the archive directly into an in-memory buffer. + * @param file descriptor of the file to extract + * @param result output buffer; cleared and filled with uncompressed file data + * @return ERROR_NONE on success or an error code + */ + EErrorCode extractToMemory(File::Ptr file, + std::vector<unsigned char>& result) const; + + /** + * find a file in the archive by its relative path. + * @param path relative path inside the archive (separators may be / or \, + * matching is case-insensitive). + * @return the file descriptor, or nullptr if no such file exists. + */ + File::Ptr findFile(const std::string& path) const; /** * @return archive flags */ @@ -211,8 +228,8 @@ private: void getDX10Header(DirectX::DDS_HEADER_DXT10& DX10Header, File::Ptr file, DirectX::DDS_HEADER DDSHeader) const; - EErrorCode extractDirect(File::Ptr file, std::ofstream& outFile) const; - EErrorCode extractCompressed(File::Ptr file, std::ofstream& outFile) const; + EErrorCode extractDirect(File::Ptr file, std::ostream& outFile) const; + EErrorCode extractCompressed(File::Ptr file, std::ostream& outFile) const; void createFolders(const std::string& targetDirectory, Folder::Ptr folder); diff --git a/libs/bsatk/src/bsaarchive.cpp b/libs/bsatk/src/bsaarchive.cpp index 172a0b2..0ec9e20 100644 --- a/libs/bsatk/src/bsaarchive.cpp +++ b/libs/bsatk/src/bsaarchive.cpp @@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include <boost/interprocess/sync/scoped_lock.hpp> #include <boost/shared_array.hpp> #include <boost/thread.hpp> +#include <cctype> #include <cstring> #include <fstream> #include <iostream> @@ -34,6 +35,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include <lz4frame.h> #include <memory> #include <queue> +#include <sstream> #include <sys/stat.h> #include <zlib.h> #ifdef _WIN32 @@ -621,7 +623,7 @@ void Archive::getDX10Header(DirectX::DDS_HEADER_DXT10& DX10Header, File::Ptr fil static const BSAULong CHUNK_SIZE = 128 * 1024; -EErrorCode Archive::extractDirect(File::Ptr file, std::ofstream& outFile) const +EErrorCode Archive::extractDirect(File::Ptr file, std::ostream& outFile) const { EErrorCode result = ERROR_NONE; if (file->m_FileSize == 0) { @@ -744,7 +746,7 @@ std::shared_ptr<unsigned char[]> Archive::decompress(unsigned char* inBuffer, } } -EErrorCode Archive::extractCompressed(File::Ptr file, std::ofstream& outFile) const +EErrorCode Archive::extractCompressed(File::Ptr file, std::ostream& outFile) const { EErrorCode result = ERROR_NONE; if (file->m_FileSize == 0) { @@ -881,6 +883,102 @@ EErrorCode Archive::extract(File::Ptr file, const char* outputDirectory) const return result; } +EErrorCode Archive::extractToMemory(File::Ptr file, + std::vector<unsigned char>& result) const +{ + result.clear(); + if (!file) { + return ERROR_INVALIDDATA; + } + + std::ostringstream stream(std::ios::out | std::ios::binary); + EErrorCode rc = ERROR_NONE; + if (compressed(file)) { + rc = extractCompressed(file, stream); + } else { + rc = extractDirect(file, stream); + } + + if (rc != ERROR_NONE) { + return rc; + } + + const std::string& data = stream.str(); + result.assign(reinterpret_cast<const unsigned char*>(data.data()), + reinterpret_cast<const unsigned char*>(data.data() + data.size())); + return ERROR_NONE; +} + +static bool iequals_bsa(const std::string& lhs, const std::string& rhs) +{ + if (lhs.size() != rhs.size()) { + return false; + } + for (std::size_t i = 0; i < lhs.size(); ++i) { + if (std::tolower(static_cast<unsigned char>(lhs[i])) != + std::tolower(static_cast<unsigned char>(rhs[i]))) { + return false; + } + } + return true; +} + +// Normalize a path for case-insensitive, separator-agnostic comparison: +// all forward slashes, all lowercase. Used by findFile only — doesn't mutate +// stored names. +static std::string normalize_bsa_path(const std::string& in) +{ + std::string out; + out.reserve(in.size()); + for (char ch : in) { + if (ch == '\\') { + ch = '/'; + } + out.push_back(static_cast<char>( + std::tolower(static_cast<unsigned char>(ch)))); + } + // strip any leading separator + std::size_t start = 0; + while (start < out.size() && out[start] == '/') { + ++start; + } + return out.substr(start); +} + +static void collect_files_recursive(Folder::Ptr folder, + std::vector<File::Ptr>& out) +{ + for (unsigned int i = 0; i < folder->getNumFiles(); ++i) { + out.push_back(folder->getFile(i)); + } + for (unsigned int i = 0; i < folder->getNumSubFolders(); ++i) { + collect_files_recursive(folder->getSubFolder(i), out); + } +} + +File::Ptr Archive::findFile(const std::string& path) const +{ + if (!m_RootFolder || path.empty()) { + return nullptr; + } + + const std::string want = normalize_bsa_path(path); + + // BSAs store flat folder names like "meshes\\actors\\character". On Linux + // std::filesystem::path doesn't treat '\\' as a separator, so bsatk builds + // a flat folder tree there. Rather than trying to reverse-engineer the + // structure, iterate every File in the archive and compare its full path + // (normalized) against the caller-supplied one. + std::vector<File::Ptr> allFiles; + collect_files_recursive(m_RootFolder, allFiles); + for (const File::Ptr& entry : allFiles) { + if (normalize_bsa_path(entry->getFilePath()) == want) { + return entry; + } + } + return nullptr; +} + void Archive::readFiles(std::queue<FileInfo>& queue, boost::mutex& mutex, boost::interprocess::interprocess_semaphore& bufferCount, boost::interprocess::interprocess_semaphore& queueFree, |
