1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-07-08 05:48:07 +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 <cstdio>
#include <cassert>
#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) {
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<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
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<char[]> 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<char[]> 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<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 (fp) {
auto raw_data = std::make_unique<char[]>(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;
}

View File

@ -2,9 +2,10 @@
#define _LIBRW_LOADERIMG_HPP_
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
#include <fstream>
#include <rw/filesystem.hpp>
@ -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<LoaderIMGFile> m_assets; ///< Asset info of the archive
std::vector<LoaderIMGFile> m_assets; ///< Asset info of the archive
};
#endif // LoaderIMG_h__

View File

@ -3,12 +3,9 @@
#include <algorithm>
#include <cctype>
#include <fstream>
#include <iterator>
#include <sstream>
#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);

View File

@ -1,10 +1,13 @@
#ifndef _LIBRW_FILEINDEX_HPP_
#define _LIBRW_FILEINDEX_HPP_
#include "rw/filesystem.hpp"
#include "rw/forward.hpp"
#include <unordered_map>
#include <memory>
#include <loaders/LoaderIMG.hpp>
#include <rw/filesystem.hpp>
#include <rw/forward.hpp>
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<std::string, LoaderIMG> loaders_;
};
#endif

View File

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