#include #include "ObjDecoder.hh" #include "exceptions.hh" #include "util.hh" ObjDecoder::ObjDecoder(uint8_t const* base, size_t length) : iff_(base, length) { auto& root = iff_.getRoot(); if (!root.isForm()) throw FormatException{"Root node not FORM"}; auto& rootForm = dynamic_cast(root); if (rootForm.getSubtype() != "REAL") throw FormatException{"Root form not REAL"}; for (auto realIt = rootForm.childrenBegin();realIt != rootForm.childrenEnd();++realIt) { if (realIt->getType() == "INFO") { name_ = *realIt; printf("Name: \"%s\"\n", name_.c_str()); } else if (realIt->isForm()) { auto& itForm = dynamic_cast(*realIt); if (itForm.getSubtype() == "OBJT") parseOBJT_(itForm); else if (itForm.getSubtype() == "APPR") parseAPPR_(itForm); else throw FormatException{"Unknown FORM type " + itForm.getSubtype()}; } else throw FormatException{"Unknown chunk type " + realIt->getType()}; } } ObjDecoder::Triangles ObjDecoder::getTriangles(unsigned lvl) const { Triangles ret; for (auto idx : detailLevels_.at(lvl)) { if (idx < triangles_.size()) ret.push_back(triangles_[idx]); } return ret; } ObjDecoder::Quads ObjDecoder::getQuads(unsigned lvl) const { Quads ret; for (auto idx : detailLevels_.at(lvl)) { if (idx >= triangles_.size()) ret.push_back(quads_[idx-triangles_.size()]); } return ret; } void ObjDecoder::parseOBJT_(IffFile::Form const& form) { } void ObjDecoder::parseAPPR_(IffFile::Form const& form) { if (form.getChildCount() != 1) throw FormatException{"Unexpected child count in APPR"}; auto& child = *form.childrenBegin(); if (!child.isForm()) throw FormatException{"APPR child not FORM"}; auto& childForm = dynamic_cast(child); if (childForm.getSubtype() != "POLY") throw FormatException{"APPR child form not POLY"}; parsePOLY_(childForm); } void ObjDecoder::parsePOLY_(IffFile::Form const& form) { for (auto it = form.childrenBegin();it != form.childrenEnd();++it) { if (it->getType() == "INFO") { } else if (it->getType() == "VERT") { if (it->getSize()%12 != 0) throw FormatException{"Unexpected VERT size"}; for (size_t i = 0;i < it->getSize();i+=12) { std::array vertex; for (size_t j = 0;j < 3;++j) vertex[j] = readU32LE(it->begin()+i+j*4); // printf("V: %d %d %d\n", vertex[0], vertex[1], vertex[2]); vertices_.push_back(vertex); } } else if (it->isForm()) { auto& itForm = dynamic_cast(*it); if (itForm.getSubtype() == "TRIS") { parseTRIS_(itForm); } else if (itForm.getSubtype() == "QUAD") { parseQUAD_(itForm); } else if (itForm.getSubtype() == "DETA") { parseDETA_(itForm); } else if (itForm.getSubtype() == "TXMS") { parseTXMS_(itForm); } else if (itForm.getSubtype() == "GRUP") { // NYI } else throw FormatException{"Unknown POLY subform " + itForm.getSubtype()}; } else if (it->getType() == "ATTR") { // NYI } else if (it->getType() == "XATR") { // NYI } else if (it->getType() == "SUPR") { // NYI } else throw FormatException{"Unknown POLY child " + it->getType()}; } } void ObjDecoder::parseTRIS_(IffFile::Form const& form) { if (form.getChildCount() != 2) throw FormatException{"Unexpected child count in TRIS"}; auto vertCount = vertices_.size(); printf("VC: %lu\n", vertCount); IffFile::Object const *face = nullptr, *maps = nullptr; for (auto it = form.childrenBegin();it != form.childrenEnd();++it) { if (it->getType() == "FACE") { if (face) throw FormatException{"Multiple FACE in TRIS"}; face = &*it; } else if (it->getType() == "MAPS") { if (maps) throw FormatException{"Multiple MAPS in TRIS"}; maps = &*it; } else throw FormatException{"Unknown TRIS child " + it->getType()}; } assert(face && maps); if (face->getSize()%8 != 0) throw FormatException{"Unexpected FACE size"}; auto numTriangles = face->getSize()/8; if (maps->getSize() != numTriangles*16) printf("WARNING: Unexpected MAPS size\n"); // throw FormatException{"Unexpected MAPS size"}; for (unsigned i = 0;i < numTriangles;++i) { Triangle tri; for (unsigned j = 0;j < 3;++j) tri.vertIdx[j] = readU16LE(face->begin()+i*8+2+j*2); tri.id = readU16LE(maps->begin()+i*16); tri.tex = readU16LE(maps->begin()+i*16+2); for (unsigned j = 0;j < 3;++j) { tri.texCoords[j][0] = readU16LE(maps->begin()+i*16+4+j*4); tri.texCoords[j][1] = readU16LE(maps->begin()+i*16+4+j*4+2); } // printf("T%u: (%hu (%hu %hu)), (%hu (%hu %hu)), (%hu (%hu %hu))\n", tri.id, // tri.vertIdx[0], tri.texCoords[0][0], tri.texCoords[0][1], // tri.vertIdx[1], tri.texCoords[1][0], tri.texCoords[1][1], // tri.vertIdx[2], tri.texCoords[2][0], tri.texCoords[2][1]); triangles_.push_back(tri); } printf("TC: %lu\n", triangles_.size()); } void ObjDecoder::parseQUAD_(IffFile::Form const& form) { if (form.getChildCount() != 2) throw FormatException{"Unexpected child count in QUAD"}; IffFile::Object const *face = nullptr, *maps = nullptr; for (auto it = form.childrenBegin();it != form.childrenEnd();++it) { if (it->getType() == "FACE") { if (face) throw FormatException{"Multiple FACE in QUAD"}; face = &*it; } else if (it->getType() == "MAPS") { if (maps) throw FormatException{"Multiple MAPS in QUAD"}; maps = &*it; } else throw FormatException{"Unknown TRIS child " + it->getType()}; } assert(face && maps); if (face->getSize()%10 != 0) throw FormatException{"Unexpected FACE size"}; auto numQuads = face->getSize()/10; if (maps->getSize() != numQuads*20) printf("WARNING: Unexpected MAPS size\n"); //throw FormatException{"Unexpected MAPS size"}; for (unsigned i = 0;i < numQuads;++i) { Quad quad; for (unsigned j = 0;j < 4;++j) quad.vertIdx[j] = readU16LE(face->begin()+i*10+2+j*2); quad.id = readU16LE(maps->begin()+i*20); quad.tex = readU16LE(maps->begin()+i*20+2); for (unsigned j = 0;j < 4;++j) { quad.texCoords[j][0] = readU16LE(maps->begin()+i*20+4+j*4); quad.texCoords[j][1] = readU16LE(maps->begin()+i*20+4+j*4+2); } // printf("Q%u: (%hu (%hu %hu)), (%hu (%hu %hu)), (%hu (%hu %hu)), (%hu (%hu %hu))\n", quad.id, // quad.vertIdx[0], quad.texCoords[0][0], quad.texCoords[0][1], // quad.vertIdx[1], quad.texCoords[1][0], quad.texCoords[1][1], // quad.vertIdx[2], quad.texCoords[2][0], quad.texCoords[2][1], // quad.vertIdx[3], quad.texCoords[3][0], quad.texCoords[3][1]); quads_.push_back(quad); } printf("QC: %lu\n", quads_.size()); } void ObjDecoder::parseDETA_(IffFile::Form const& form) { if (form.getChildCount() < 1) throw FormatException{"Unexpected child count in DETA"}; unsigned lvl = 0; for (auto it = form.childrenBegin();it != form.childrenEnd();++it) { if (it->getType().substr(0, 3) != "LVL") throw FormatException{"Unexpected child " + it->getType() + " in DETA"}; if (std::stoi(it->getType().substr(3)) != lvl) throw FormatException{"Unexpected detail level"}; if (it->getSize() < 4) throw FormatException{"DETA child too small"}; // uint32_t unkown = readU32LE(it->begin()); detailLevels_.emplace_back(); for (size_t pos = 4;pos < it->getSize()-1;pos += 2) detailLevels_.back().push_back(readU16LE(it->begin()+pos)); printf("LVL %u: %lu entries\n", lvl, detailLevels_.back().size()); ++lvl; } } void ObjDecoder::parseTXMS_(IffFile::Form const& form) { if ((textures_.size() != 0) || texAnims_.size() != 0) throw FormatException{"Multiple TXMS"}; if (form.getChildCount() < 1) throw FormatException{"Unexpected child count in TXMS"}; auto it = form.childrenBegin(); auto& info = *it++; if (info.getType() != "INFO") throw FormatException{"Missing INFO in TXMS"}; if (info.getSize() != 4) throw FormatException{"Unexpected INFO size in TXMS"}; uint16_t numTXMP, numTXMA; numTXMP = readU16LE(info.begin()); numTXMA = readU16LE(info.begin()+2); if (form.getChildCount() != 1u+numTXMP+numTXMA) throw FormatException{"Unexpected child count in TXMS"}; textures_.reserve(numTXMP); texAnims_.reserve(numTXMA); for (;it != form.childrenEnd();++it) { if (it->getType() == "TXMP") { if (it->getSize() < 12u) throw FormatException{"TXMP smaller than header"}; Texture tex; if (it->begin()[7] == '\0') tex.name = std::string(reinterpret_cast(it->begin())); else tex.name = std::string(reinterpret_cast(it->begin()), 8); printf("TXMP name \"%s\"\n", tex.name.c_str()); tex.width = readU16LE(it->begin()+8); tex.height = readU16LE(it->begin()+10); printf("TXMP size %hux%hu\n", tex.width, tex.height); if (it->getSize() < 12u+tex.width*tex.height) throw FormatException{"TXMP too small for pixel data"}; tex.pixels = std::vector(it->begin()+12, it->begin()+12+tex.width*tex.height); textures_.emplace_back(std::move(tex)); } else if (it->getType() == "TXMA") { if (it->getSize() < 14u) throw FormatException{"TXMA smaller than header"}; TextureAnimation tex; if (it->begin()[7] == '\0') tex.name = std::string(reinterpret_cast(it->begin())); else tex.name = std::string(reinterpret_cast(it->begin()), 8); printf("TXMA name \"%s\"\n", tex.name.c_str()); tex.width = readU16LE(it->begin()+8); tex.height = readU16LE(it->begin()+10); tex.frames = readU16LE(it->begin()+12); printf("TXMA size %hux%hu, %hu frames\n", tex.width, tex.height, tex.frames); if (it->getSize() < 14u+tex.width*tex.height*tex.frames) throw FormatException{"TXMA too small for pixel data"}; tex.pixels.reserve(tex.frames); uint8_t const* pos = it->begin()+14; for (unsigned i = 0;i < tex.frames;++i) { tex.pixels.emplace_back(pos, pos+tex.width*tex.height); pos += tex.width*tex.height; } texAnims_.emplace_back(std::move(tex)); } else throw FormatException{"Unkown TXMS child " + it->getType()}; } }