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:
parent
edaab68525
commit
77b348f1f4
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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__
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user