100 lines
3.6 KiB
C++
100 lines
3.6 KiB
C++
#include "gc/MtxArchive.hpp"
|
|
#include "gc/RvbScene.hpp"
|
|
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace {
|
|
|
|
bool readFile(const fs::path& path, std::vector<uint8_t>* bytes) {
|
|
std::ifstream file(path, std::ios::binary);
|
|
if (!file) return false;
|
|
bytes->assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
|
return true;
|
|
}
|
|
|
|
size_t textureIndex(const gc::RvbImageResource& image) {
|
|
static constexpr char prefix[] = "Image";
|
|
if (image.symbolName.rfind(prefix, 0) != 0 || image.symbolName.size() <= 5) {
|
|
return std::numeric_limits<size_t>::max();
|
|
}
|
|
size_t number = 0;
|
|
for (size_t i = 5; i < image.symbolName.size(); ++i) {
|
|
const char c = image.symbolName[i];
|
|
if (c < '0' || c > '9') return std::numeric_limits<size_t>::max();
|
|
number = number * 10 + static_cast<size_t>(c - '0');
|
|
}
|
|
return number == 0 ? std::numeric_limits<size_t>::max() : number - 1;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc < 3 || argc > 4) {
|
|
std::cerr << "Usage: " << argv[0] << " <scene.rvb> <textures.mtx> [output-dir]\n";
|
|
return 2;
|
|
}
|
|
std::vector<uint8_t> rvbBytes;
|
|
std::vector<uint8_t> mtxBytes;
|
|
if (!readFile(argv[1], &rvbBytes) || !readFile(argv[2], &mtxBytes)) {
|
|
std::cerr << "could not read RVB/MTX input\n";
|
|
return 1;
|
|
}
|
|
gc::RvbScene scene;
|
|
gc::MtxArchive archive;
|
|
std::string error;
|
|
if (!gc::ParseRvbScene(rvbBytes, &scene, &error) ||
|
|
!gc::ParseMtxArchive(mtxBytes, &archive, &error)) {
|
|
std::cerr << "parse failed: " << error << '\n';
|
|
return 1;
|
|
}
|
|
if (scene.images.size() != archive.textures.size()) {
|
|
std::cerr << "resource mismatch: RVB images=" << scene.images.size()
|
|
<< " MTX textures=" << archive.textures.size() << '\n';
|
|
return 1;
|
|
}
|
|
|
|
const bool extract = argc == 4;
|
|
const fs::path output = extract ? fs::path(argv[3]) : fs::path();
|
|
if (extract) fs::create_directories(output);
|
|
for (const gc::RvbImageResource& image : scene.images) {
|
|
const size_t index = textureIndex(image);
|
|
if (index >= archive.textures.size()) {
|
|
std::cerr << "invalid MTX symbol index: " << image.symbolName << '\n';
|
|
return 1;
|
|
}
|
|
const gc::MtxTexture& texture = archive.textures[index];
|
|
if (image.width != texture.width || image.height != texture.height) {
|
|
std::cerr << "dimension mismatch at " << index << ": " << image.symbolName
|
|
<< " RVB=" << image.width << 'x' << image.height
|
|
<< " MTX=" << texture.width << 'x' << texture.height << '\n';
|
|
return 1;
|
|
}
|
|
std::cout << index << ' ' << image.symbolName << ' ' << texture.width << 'x'
|
|
<< texture.height << " bytes=" << texture.size << '\n';
|
|
if (extract) {
|
|
std::vector<uint8_t> dds;
|
|
if (!gc::ExtractMtxTextureDds(mtxBytes, texture, &dds, &error)) {
|
|
std::cerr << "extract failed: " << error << '\n';
|
|
return 1;
|
|
}
|
|
std::ofstream file(output / (image.symbolName + ".dds"), std::ios::binary);
|
|
file.write(reinterpret_cast<const char*>(dds.data()),
|
|
static_cast<std::streamsize>(dds.size()));
|
|
if (!file) {
|
|
std::cerr << "could not write extracted DDS\n";
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|