From 77b348f1f4a200a36753cad62e697871da5a2197 Mon Sep 17 00:00:00 2001 From: Christoph Heiss Date: Fri, 15 May 2020 17:35:57 +0200 Subject: [PATCH] rwcore/LoaderIMG: Switch to fstream and avoid case-insensitive string comparision. --- rwcore/loaders/LoaderIMG.cpp | 146 ++++++++++++++++------------ rwcore/loaders/LoaderIMG.hpp | 18 +++- rwcore/platform/FileIndex.cpp | 17 ++-- rwcore/platform/FileIndex.hpp | 14 ++- rwviewer/models/IMGArchiveModel.hpp | 2 +- 5 files changed, 116 insertions(+), 81 deletions(-) diff --git a/rwcore/loaders/LoaderIMG.cpp b/rwcore/loaders/LoaderIMG.cpp index 34e73806..374ae13a 100644 --- a/rwcore/loaders/LoaderIMG.cpp +++ b/rwcore/loaders/LoaderIMG.cpp @@ -1,53 +1,87 @@ -#include "loaders/LoaderIMG.hpp" +#include -#include -#include +#include +#include +#include +#include -#include "rw/debug.hpp" +#include + +namespace { + +constexpr size_t kAssetRecordSize{2048}; + +void to_lowercase_inplace(char* name) { + size_t len = std::strlen(name); + + std::transform( + name, name+len, name, + [](char ch) -> char { return std::tolower(ch); } + ); +} // namespace + +} bool LoaderIMG::load(const rwfs::path& filepath) { + assert(m_archive.empty()); + m_archive = filepath; + auto dirPath = filepath; dirPath.replace_extension(".dir"); - FILE* fp = fopen(dirPath.string().c_str(), "rb"); - if (fp) { - fseek(fp, 0, SEEK_END); - unsigned long fileSize = ftell(fp); - fseek(fp, 0, SEEK_SET); - - std::size_t expectedCount = fileSize / 32; - m_assets.resize(expectedCount); - std::size_t actualCount = fread(&m_assets[0], sizeof(LoaderIMGFile), - expectedCount, fp); - - if (expectedCount != actualCount) { - m_assets.resize(actualCount); - RW_ERROR("Error reading records in IMG archive"); - } - - fclose(fp); - auto imgPath = filepath; - imgPath.replace_extension(".img"); - m_archive = imgPath; - return true; - } else { + std::ifstream file(dirPath.string(), std::ios::binary); + if (!file.is_open()) { + RW_ERROR("Failed to open " + dirPath.string()); return false; } + + file.seekg(0, std::ios::end); + auto fileSize = file.tellg(); + file.seekg(0); + + std::size_t expectedCount = fileSize / sizeof(LoaderIMGFile); + m_assets.resize(expectedCount); + + file.read(reinterpret_cast(m_assets.data()), expectedCount * sizeof(LoaderIMGFile)); + + if (file.fail() || file.gcount() != fileSize) {\ + m_assets.resize(file.gcount() / sizeof(LoaderIMGFile)); + RW_ERROR("Error reading records in IMG archive"); + } + + for (auto& asset : m_assets) { + to_lowercase_inplace(asset.name); + } + + auto imgPath = filepath; + imgPath.replace_extension(".img"); + + m_archive_stream.open(imgPath.string(), std::ios::binary); + if (!m_archive_stream.is_open()) { + RW_ERROR("Failed to open " << imgPath.string()); + } + + return true; } /// Get the information of a asset in the examining archive bool LoaderIMG::findAssetInfo(const std::string& assetname, LoaderIMGFile& out) { - for (auto &asset : m_assets) { - if (boost::iequals(asset.name, assetname)) { + for (const auto& asset : m_assets) { + if (assetname.compare(asset.name) == 0) { out = asset; return true; } } + return false; } std::unique_ptr LoaderIMG::loadToMemory(const std::string& assetname) { + if (!m_archive_stream.is_open()) { + return nullptr; + } + LoaderIMGFile assetInfo; bool found = findAssetInfo(assetname, assetInfo); @@ -56,50 +90,38 @@ std::unique_ptr LoaderIMG::loadToMemory(const std::string& assetname) { return nullptr; } - auto imgName = m_archive; + std::streamsize asset_size = assetInfo.size * kAssetRecordSize; + auto raw_data = std::make_unique(asset_size); + m_archive_stream.seekg(assetInfo.offset * kAssetRecordSize); + m_archive_stream.read(raw_data.get(), asset_size); - FILE* fp = fopen(imgName.string().c_str(), "rb"); - if (fp) { - auto raw_data = std::make_unique(assetInfo.size * 2048); + if (m_archive_stream.gcount() != asset_size) { + RW_ERROR("Error reading asset " << assetInfo.name); + } - fseek(fp, assetInfo.offset * 2048, SEEK_SET); - if (fread(raw_data.get(), 2048, assetInfo.size, fp) != assetInfo.size) { - RW_ERROR("Error reading asset " << assetInfo.name); - } - - fclose(fp); - return raw_data; - } else - return nullptr; + return raw_data; } /// Writes the contents of assetname to filename bool LoaderIMG::saveAsset(const std::string& assetname, const std::string& filename) { auto raw_data = loadToMemory(assetname); - if (!raw_data) return false; - - FILE* dumpFile = fopen(filename.c_str(), "wb"); - if (dumpFile) { - LoaderIMGFile asset; - if (findAssetInfo(assetname, asset)) { - fwrite(raw_data.get(), 2048, asset.size, dumpFile); - printf("=> IMG: Saved %s to disk with filename %s\n", - assetname.c_str(), filename.c_str()); - } - fclose(dumpFile); - - return true; - } else { + if (!raw_data) { return false; } -} -/// Get the information of an asset by its index -const LoaderIMGFile& LoaderIMG::getAssetInfoByIndex(size_t index) const { - return m_assets[index]; -} + LoaderIMGFile asset; + if (!findAssetInfo(assetname, asset)) { + return false; + } -std::size_t LoaderIMG::getAssetCount() const { - return m_assets.size(); + std::ofstream dump_file(filename, std::ios::binary); + if (!dump_file.is_open()) { + return false; + } + + dump_file.write(raw_data.get(), kAssetRecordSize * asset.size); + RW_MESSAGE("Saved " << assetname << " to disk with filename " << filename); + + return true; } diff --git a/rwcore/loaders/LoaderIMG.hpp b/rwcore/loaders/LoaderIMG.hpp index e3f45143..3a8aeda8 100644 --- a/rwcore/loaders/LoaderIMG.hpp +++ b/rwcore/loaders/LoaderIMG.hpp @@ -2,9 +2,10 @@ #define _LIBRW_LOADERIMG_HPP_ #include -#include #include #include +#include +#include #include @@ -32,11 +33,13 @@ public: /// Construct LoaderIMG() = default; + LoaderIMG(const LoaderIMG&) = delete; + LoaderIMG(LoaderIMG&&) noexcept = default; /// Load the structure of the archive /// Omit the extension in filename so both .dir and .img are loaded when /// appropriate - bool load(const rwfs::path& filename); + bool load(const rwfs::path& filepath); /// Load a file from the archive to memory and pass a pointer to it /// Warning: Returns nullptr if by any reason it can't load the file @@ -49,10 +52,14 @@ public: bool findAssetInfo(const std::string& assetname, LoaderIMGFile& out); /// Get the information of an asset by its index - const LoaderIMGFile& getAssetInfoByIndex(size_t index) const; + const LoaderIMGFile& getAssetInfoByIndex(size_t index) const { + return m_assets[index]; + } /// Returns the number of asset files in the archive - std::size_t getAssetCount() const; + std::size_t getAssetCount() const { + return m_assets.size(); + } Version getVersion() const { return m_version; @@ -61,8 +68,9 @@ public: private: Version m_version = GTAIIIVC; ///< Version of this IMG archive rwfs::path m_archive; ///< Path to the archive being used (no extension) + std::ifstream m_archive_stream; ///< File stream for archive - std::vector m_assets; ///< Asset info of the archive + std::vector m_assets; ///< Asset info of the archive }; #endif // LoaderIMG_h__ diff --git a/rwcore/platform/FileIndex.cpp b/rwcore/platform/FileIndex.cpp index 2aff9c2f..dd397f17 100644 --- a/rwcore/platform/FileIndex.cpp +++ b/rwcore/platform/FileIndex.cpp @@ -3,12 +3,9 @@ #include #include #include -#include #include #include "platform/FileHandle.hpp" -#include "loaders/LoaderIMG.hpp" - #include "rw/debug.hpp" std::string FileIndex::normalizeFilePath(const std::string &filePath) { @@ -75,7 +72,7 @@ FileContentsInfo FileIndex::openFileRaw(const std::string &filePath) const { void FileIndex::indexArchive(const std::string &archive) { rwfs::path path = findFilePath(archive); - LoaderIMG img; + LoaderIMG& img = loaders_[path.string()]; if (!img.load(path.string())) { throw std::runtime_error("Failed to load IMG archive: " + path.string()); } @@ -105,17 +102,17 @@ FileContentsInfo FileIndex::openFile(const std::string &filePath) { size_t length = 0; if (indexedData.type == IndexedDataType::ARCHIVE) { - LoaderIMG img; - - if (!img.load(indexedData.path)) { - throw std::runtime_error("Failed to load IMG archive: " + indexedData.path); + auto loaderPos = loaders_.find(indexedData.path); + if (loaderPos == loaders_.end()) { + throw std::runtime_error("IMG archive not indexed: " + indexedData.path); } + auto& loader = loaderPos->second; LoaderIMGFile file; auto filename = rwfs::path(indexedData.assetData).filename().string(); - if (img.findAssetInfo(filename, file)) { + if (loader.findAssetInfo(filename, file)) { length = file.size * 2048; - data = img.loadToMemory(filename); + data = loader.loadToMemory(filename); } } else { std::ifstream dfile(indexedData.path, std::ios::binary); diff --git a/rwcore/platform/FileIndex.hpp b/rwcore/platform/FileIndex.hpp index 790bf284..186d9167 100644 --- a/rwcore/platform/FileIndex.hpp +++ b/rwcore/platform/FileIndex.hpp @@ -1,10 +1,13 @@ #ifndef _LIBRW_FILEINDEX_HPP_ #define _LIBRW_FILEINDEX_HPP_ -#include "rw/filesystem.hpp" -#include "rw/forward.hpp" - #include +#include + +#include +#include +#include + class FileIndex { public: @@ -91,6 +94,11 @@ private: * @throws If this FileIndex has not indexed filePath */ const IndexedData *getIndexedDataAt(const std::string &filePath) const; + + /** + * @brief loaders_ Maps .img filepaths to its respective loader + */ + std::unordered_map loaders_; }; #endif diff --git a/rwviewer/models/IMGArchiveModel.hpp b/rwviewer/models/IMGArchiveModel.hpp index 350f9068..78677227 100644 --- a/rwviewer/models/IMGArchiveModel.hpp +++ b/rwviewer/models/IMGArchiveModel.hpp @@ -6,7 +6,7 @@ class IMGArchiveModel : public QAbstractListModel { Q_OBJECT - LoaderIMG archive; + const LoaderIMG& archive; public: IMGArchiveModel(const LoaderIMG& archive, QObject* parent = 0)