1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-07-19 02:54:44 +02:00

rwcore/LoaderIMG: Switch to fstream and avoid case-insensitive string comparision.

This commit is contained in:
Christoph Heiss 2020-05-15 17:35:57 +02:00 committed by Anonymous Maarten
parent edaab68525
commit 77b348f1f4
5 changed files with 116 additions and 81 deletions

View File

@ -1,53 +1,87 @@
#include "loaders/LoaderIMG.hpp" #include <loaders/LoaderIMG.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <cassert>
#include <cstdio> #include <cctype>
#include <cstring>
#include <algorithm>
#include "rw/debug.hpp" #include <rw/debug.hpp>
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) { bool LoaderIMG::load(const rwfs::path& filepath) {
assert(m_archive.empty());
m_archive = filepath;
auto dirPath = filepath; auto dirPath = filepath;
dirPath.replace_extension(".dir"); dirPath.replace_extension(".dir");
FILE* fp = fopen(dirPath.string().c_str(), "rb"); std::ifstream file(dirPath.string(), std::ios::binary);
if (fp) { if (!file.is_open()) {
fseek(fp, 0, SEEK_END); RW_ERROR("Failed to open " + dirPath.string());
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 {
return false; 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<char*>(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 /// Get the information of a asset in the examining archive
bool LoaderIMG::findAssetInfo(const std::string& assetname, bool LoaderIMG::findAssetInfo(const std::string& assetname,
LoaderIMGFile& out) { LoaderIMGFile& out) {
for (auto &asset : m_assets) { for (const auto& asset : m_assets) {
if (boost::iequals(asset.name, assetname)) { if (assetname.compare(asset.name) == 0) {
out = asset; out = asset;
return true; return true;
} }
} }
return false; return false;
} }
std::unique_ptr<char[]> LoaderIMG::loadToMemory(const std::string& assetname) { std::unique_ptr<char[]> LoaderIMG::loadToMemory(const std::string& assetname) {
if (!m_archive_stream.is_open()) {
return nullptr;
}
LoaderIMGFile assetInfo; LoaderIMGFile assetInfo;
bool found = findAssetInfo(assetname, assetInfo); bool found = findAssetInfo(assetname, assetInfo);
@ -56,50 +90,38 @@ std::unique_ptr<char[]> LoaderIMG::loadToMemory(const std::string& assetname) {
return nullptr; return nullptr;
} }
auto imgName = m_archive; std::streamsize asset_size = assetInfo.size * kAssetRecordSize;
auto raw_data = std::make_unique<char[]>(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 (m_archive_stream.gcount() != asset_size) {
if (fp) { RW_ERROR("Error reading asset " << assetInfo.name);
auto raw_data = std::make_unique<char[]>(assetInfo.size * 2048); }
fseek(fp, assetInfo.offset * 2048, SEEK_SET); return raw_data;
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;
} }
/// Writes the contents of assetname to filename /// Writes the contents of assetname to filename
bool LoaderIMG::saveAsset(const std::string& assetname, bool LoaderIMG::saveAsset(const std::string& assetname,
const std::string& filename) { const std::string& filename) {
auto raw_data = loadToMemory(assetname); auto raw_data = loadToMemory(assetname);
if (!raw_data) return false; if (!raw_data) {
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 {
return false; return false;
} }
}
/// Get the information of an asset by its index LoaderIMGFile asset;
const LoaderIMGFile& LoaderIMG::getAssetInfoByIndex(size_t index) const { if (!findAssetInfo(assetname, asset)) {
return m_assets[index]; return false;
} }
std::size_t LoaderIMG::getAssetCount() const { std::ofstream dump_file(filename, std::ios::binary);
return m_assets.size(); 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;
} }

View File

@ -2,9 +2,10 @@
#define _LIBRW_LOADERIMG_HPP_ #define _LIBRW_LOADERIMG_HPP_
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory>
#include <fstream>
#include <rw/filesystem.hpp> #include <rw/filesystem.hpp>
@ -32,11 +33,13 @@ public:
/// Construct /// Construct
LoaderIMG() = default; LoaderIMG() = default;
LoaderIMG(const LoaderIMG&) = delete;
LoaderIMG(LoaderIMG&&) noexcept = default;
/// Load the structure of the archive /// Load the structure of the archive
/// Omit the extension in filename so both .dir and .img are loaded when /// Omit the extension in filename so both .dir and .img are loaded when
/// appropriate /// 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 /// 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 /// 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); bool findAssetInfo(const std::string& assetname, LoaderIMGFile& out);
/// Get the information of an asset by its index /// 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 /// 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 { Version getVersion() const {
return m_version; return m_version;
@ -61,8 +68,9 @@ public:
private: private:
Version m_version = GTAIIIVC; ///< Version of this IMG archive Version m_version = GTAIIIVC; ///< Version of this IMG archive
rwfs::path m_archive; ///< Path to the archive being used (no extension) rwfs::path m_archive; ///< Path to the archive being used (no extension)
std::ifstream m_archive_stream; ///< File stream for archive
std::vector<LoaderIMGFile> m_assets; ///< Asset info of the archive std::vector<LoaderIMGFile> m_assets; ///< Asset info of the archive
}; };
#endif // LoaderIMG_h__ #endif // LoaderIMG_h__

View File

@ -3,12 +3,9 @@
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <fstream> #include <fstream>
#include <iterator>
#include <sstream> #include <sstream>
#include "platform/FileHandle.hpp" #include "platform/FileHandle.hpp"
#include "loaders/LoaderIMG.hpp"
#include "rw/debug.hpp" #include "rw/debug.hpp"
std::string FileIndex::normalizeFilePath(const std::string &filePath) { 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) { void FileIndex::indexArchive(const std::string &archive) {
rwfs::path path = findFilePath(archive); rwfs::path path = findFilePath(archive);
LoaderIMG img; LoaderIMG& img = loaders_[path.string()];
if (!img.load(path.string())) { if (!img.load(path.string())) {
throw std::runtime_error("Failed to load IMG archive: " + 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; size_t length = 0;
if (indexedData.type == IndexedDataType::ARCHIVE) { if (indexedData.type == IndexedDataType::ARCHIVE) {
LoaderIMG img; auto loaderPos = loaders_.find(indexedData.path);
if (loaderPos == loaders_.end()) {
if (!img.load(indexedData.path)) { throw std::runtime_error("IMG archive not indexed: " + indexedData.path);
throw std::runtime_error("Failed to load IMG archive: " + indexedData.path);
} }
auto& loader = loaderPos->second;
LoaderIMGFile file; LoaderIMGFile file;
auto filename = rwfs::path(indexedData.assetData).filename().string(); auto filename = rwfs::path(indexedData.assetData).filename().string();
if (img.findAssetInfo(filename, file)) { if (loader.findAssetInfo(filename, file)) {
length = file.size * 2048; length = file.size * 2048;
data = img.loadToMemory(filename); data = loader.loadToMemory(filename);
} }
} else { } else {
std::ifstream dfile(indexedData.path, std::ios::binary); std::ifstream dfile(indexedData.path, std::ios::binary);

View File

@ -1,10 +1,13 @@
#ifndef _LIBRW_FILEINDEX_HPP_ #ifndef _LIBRW_FILEINDEX_HPP_
#define _LIBRW_FILEINDEX_HPP_ #define _LIBRW_FILEINDEX_HPP_
#include "rw/filesystem.hpp"
#include "rw/forward.hpp"
#include <unordered_map> #include <unordered_map>
#include <memory>
#include <loaders/LoaderIMG.hpp>
#include <rw/filesystem.hpp>
#include <rw/forward.hpp>
class FileIndex { class FileIndex {
public: public:
@ -91,6 +94,11 @@ private:
* @throws If this FileIndex has not indexed filePath * @throws If this FileIndex has not indexed filePath
*/ */
const IndexedData *getIndexedDataAt(const std::string &filePath) const; const IndexedData *getIndexedDataAt(const std::string &filePath) const;
/**
* @brief loaders_ Maps .img filepaths to its respective loader
*/
std::unordered_map<std::string, LoaderIMG> loaders_;
}; };
#endif #endif

View File

@ -6,7 +6,7 @@
class IMGArchiveModel : public QAbstractListModel { class IMGArchiveModel : public QAbstractListModel {
Q_OBJECT Q_OBJECT
LoaderIMG archive; const LoaderIMG& archive;
public: public:
IMGArchiveModel(const LoaderIMG& archive, QObject* parent = 0) IMGArchiveModel(const LoaderIMG& archive, QObject* parent = 0)