mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-07 11:22:45 +01:00
128 lines
3.2 KiB
C++
128 lines
3.2 KiB
C++
#include <loaders/LoaderIMG.hpp>
|
|
|
|
#include <cassert>
|
|
#include <cctype>
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
|
|
#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");
|
|
|
|
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 (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);
|
|
|
|
if (!found) {
|
|
RW_ERROR("Asset '" << assetname << "' not found!");
|
|
return nullptr;
|
|
}
|
|
|
|
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);
|
|
|
|
if (m_archive_stream.gcount() != asset_size) {
|
|
RW_ERROR("Error reading asset " << assetInfo.name);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
LoaderIMGFile asset;
|
|
if (!findAssetInfo(assetname, asset)) {
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|