#include "gc/MtxArchive.hpp" #include "gc/RvbScene.hpp" #include #include #include #include #include #include #include #include namespace fs = std::filesystem; namespace { bool readFile(const fs::path& path, std::vector* bytes) { std::ifstream file(path, std::ios::binary); if (!file) return false; bytes->assign(std::istreambuf_iterator(file), std::istreambuf_iterator()); 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::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::max(); number = number * 10 + static_cast(c - '0'); } return number == 0 ? std::numeric_limits::max() : number - 1; } } // namespace int main(int argc, char** argv) { if (argc < 3 || argc > 4) { std::cerr << "Usage: " << argv[0] << " [output-dir]\n"; return 2; } std::vector rvbBytes; std::vector 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 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(dds.data()), static_cast(dds.size())); if (!file) { std::cerr << "could not write extracted DDS\n"; return 1; } } } return 0; }