Initial Vectorail GC format library

This commit is contained in:
2026-08-02 17:05:27 +02:00
commit 8c4e1bcacb
28 changed files with 3426 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
#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;
}