#include #include #include #include #include #include int abc::parse(rez::file &f, std::string &name) const { uint16_t len; if (f.read_le(len)) { std::cerr << f.path() << ": failed to read name length\n"; return -1; } else if (len && f.read(name, len)) { std::cerr << f.path() << ": failed to read name\n"; return -1; } return 0; } int abc::parse_header(rez::file &f) { struct header &h = this->header; if (f.read_le(h.version) || f.read_le(h.n_keyframe) || f.read_le(h.n_anim) || f.read_le(h.n_nodes) || f.read_le(h.n_pieces) || f.read_le(h.n_children) || f.read_le(h.n_triangles) || f.read_le(h.n_vertices) || f.read_le(h.n_vertweights) || f.read_le(h.n_lod) || f.read_le(h.n_socket) || f.read_le(h.n_weightsets) || f.read_le(h.n_strings) || f.read_le(h.n_strlen)) { std::cerr << f.path() << ": failed to read header\n"; return -1; } // Apparently, versions are numbered from 0x6a to 0x6c, as opposed // to other titles with the same engine, that used 0xa to 0xc instead. const uint32_t min_version = 0x6a, max_version = 0x6c; if (h.version < min_version || h.version > max_version) { std::cerr << f.path() << ": unsupported version " << h.version << ", expected {" << min_version << ", " << max_version << "}\n"; return -1; } uint16_t cmdlen; std::string cmd; uint32_t n_loddist; if (f.read_le(cmdlen)) { std::cerr << f.path() << ": failed to read command length\n"; return -1; } else if (cmdlen && f.read(cmd, cmdlen)) { std::cerr << f.path() << ": failed to read command\n"; return -1; } // These do not appear on the ABC v13 specification. // Maybe they are game-specific? uint32_t unknown[3], uint_radius; for (auto &w : unknown) if (f.read_le(w)) return -1; if (f.read_le(uint_radius)) { std::cerr << f.path() << ": failed to read internal radius\n"; return -1; } else if (f.read_le(n_loddist)) { std::cerr << f.path() << ": failed to read LoD distance count\n"; return -1; } long cur = f.tell(); if (cur < 0) { std::cerr << f.path() << ": failed to tell offset\n"; return -1; } else if (f.seek(cur + 60)) { std::cerr << f.path() << ": failed to skip padding bytes\n"; return -1; } std::vector lod_distances; for (uint32_t i = 0; i < n_loddist; i++) { uint32_t v; if (f.read_le(v)) { std::cerr << f.path() << ": failed to read LoD number " << i << std::endl; return -1; } lod_distances.push_back(v); } memcpy(&int_radius, &uint_radius, sizeof uint_radius); return 0; } int abc::parse(rez::file &f, triangle &triangle) const { uint16_t trifs; uint32_t a, b; if (f.read_le(a) || f.read_le(b) || f.read_le(trifs)) { std::cerr << f.path() << ": failed to read triangle coords\n"; return -1; } memcpy(&triangle.a, &a, sizeof a); memcpy(&triangle.b, &b, sizeof b); triangle.trifs = trifs; return 0; } int abc::parse(rez::file &f, vector &vector) const { uint32_t x, y, z; if (f.read_le(x) || f.read_le(y) || f.read_le(z)) return -1; memcpy(&vector.x, &x, sizeof x); memcpy(&vector.y, &y, sizeof y); memcpy(&vector.z, &z, sizeof z); return 0; } int abc::parse(rez::file &f, weight &weight) const { uint32_t bias; if (f.read_le(weight.node_index)) { std::cerr << f.path() << ": failed to read node index\n"; return -1; } else if (parse(f, weight.location)) { std::cerr << f.path() << ": failed to read location\n"; return -1; } else if (f.read_le(bias)) { std::cerr << f.path() << ": failed to read bias\n"; return -1; } memcpy(&weight.bias, &bias, sizeof bias); return 0; } int abc::parse(rez::file &f, vertex &vertex) const { uint16_t n_weights, unknown; if (f.read_le(n_weights)) { std::cerr << f.path() << ": failed to read number of weights\n"; return -1; } else if (f.read_le(unknown)) { std::cerr << f.path() << ": failed to read reserved halfword\n"; return -1; } for (uint16_t i = 0; i < n_weights; i++) { weight weight; if (parse(f, weight)) return -1; vertex.weights.push_back(weight); } if (parse(f, vertex.vertex)) { std::cerr << f.path() << ": failed to read vertex\n"; return -1; } else if (parse(f, vertex.normal)) { std::cerr << f.path() << ": failed to read normal\n"; return -1; } return 0; } int abc::parse(rez::file &f, piece &piece) const { long offset; if (f.read_le(piece.tex_index)) { std::cerr << f.path() << ": failed to read texture index\n"; return -1; } else if (f.read_le(piece.specular_power)) { std::cerr << f.path() << ": failed to read specular power\n"; return -1; } else if (f.read_le(piece.specular_scale)) { std::cerr << f.path() << ": failed to read specular scale\n"; return -1; } else if (f.read_le(piece.lod_weight)) { std::cerr << f.path() << ": failed to read LoD weight\n"; return -1; } else if ((offset = f.tell()) < 0) { std::cerr << f.path() << ": failed to get offset\n"; return -1; } else if (f.seek(offset + sizeof (uint64_t))) { std::cerr << f.path() << ": failed to seek\n"; return -1; } else if (parse(f, piece.name)) { std::cerr << f.path() << ": failed to read piece name\n"; return -1; } else if (f.read_le(piece.n_tris)) { std::cerr << f.path() << ": failed to read n. trianges\n"; return -1; } piece.triangles.reserve(piece.n_tris * 3); for (uint32_t i = 0; i < piece.n_tris * 3; i++) { struct triangle triangle; if (parse(f, triangle)) return -1; piece.triangles.push_back(triangle); } uint32_t n_vertices; if (f.read_le(n_vertices)) { std::cerr << f.path() << ": failed to read number of vertices\n"; return -1; } piece.vertices.reserve(n_vertices); for (uint32_t i = 0; i < n_vertices; i++) { struct vertex vertex; if (parse(f, vertex)) return -1; piece.vertices.push_back(vertex); } return 0; } int abc::parse_pieces(rez::file &f) { uint32_t n_weights, n_pieces; if (f.read_le(n_weights)) { std::cerr << f.path() << ": failed to get weight count\n"; return -1; } else if (f.read_le(n_pieces)) { std::cerr << f.path() << ": failed to get piece count\n"; return -1; } for (uint32_t i = 0; i < n_pieces; i++) { piece piece; if (parse(f, piece)) { std::cerr << f.path() << ": failed to read piece " << i << std::endl; return -1; } pieces.push_back(piece); } return 0; } int abc::parse(rez::file &f, set &set) const { uint32_t n_weights; if (parse(f, set.name)) { std::cerr << f.path() << ": failed to read set name\n"; return -1; } else if (f.read_le(n_weights)) { std::cerr << f.path() << ": failed to read number of weights\n"; return -1; } for (uint32_t i = 0; i < n_weights; i++) { uint32_t weight; float fweight; if (f.read_le(weight)) { std::cerr << f.path() << ": failed to read weight " << i << std::endl; return -1; } memcpy(&fweight, &weight, sizeof weight); set.weights.push_back(fweight); } return 0; } int abc::parse_sets(rez::file &f) { uint32_t n_sets; if (f.read_le(n_sets)) { std::cerr << f.path() << ": failed to read number of sets\n"; return -1; } std::cerr << "n_sets: " << n_sets << std::endl; for (uint32_t i = 0; i < n_sets; i++) { set set; if (parse(f, set)) return -1; sets.push_back(set); } return 0; } int abc::parse(rez::file &f, rotation &rot) const { uint32_t w; if (parse(f, rot.v) || f.read_le(w)) return -1; memcpy(&rot.w, &w, sizeof w); return 0; } int abc::parse(rez::file &f, node &parent) { uint32_t n_children; uint32_t unknown2; node node; if (parse(f, node.name)) { std::cerr << f.path() << ": failed to read node name\n"; return -1; } else if (f.read_le(node.transform_index)) { std::cerr << f.path() << ": failed to read transform index\n"; return -1; } else if (f.read(node.flags)) { std::cerr << f.path() << ": failed to read node flags\n"; return -1; } else if (f.read_le(unknown2)) { std::cerr << f.path() << ": failed to read unknown word\n"; return -1; } for (auto &rot : node.matrix) if (parse(f, rot)) { std::cerr << f.path() << ": failed to read rotation\n"; return -1; } if (f.read_le(n_children)) { std::cerr << f.path() << ": failed to read number of children\n"; return -1; } for (uint32_t i = 0; i < n_children; i++) if (parse(f, node)) return -1; parent.children.push_back(node); return 0; } int abc::print(const node &node, unsigned level) const { for (unsigned i = 0; i < level; i++) std::cout << "|\t"; std::cout << node.name << '\n'; for (const auto &child : node.children) print(child, level + 1); return 0; } size_t abc::count(const node &node) const { size_t ret = 0; for (const auto &child : node.children) ret += count(child); return ret + node.children.size(); } int abc::parse_nodes(rez::file &f) { uint32_t unknown, n_nodes; if (f.read_le(unknown)) { std::cerr << f.path() << ": failed to read unknown word\n"; return -1; } else if (parse(f, root)) { std::cerr << f.path() << ": failed to parse node\n"; return -1; } else if ((n_nodes = count(root)) != header.n_nodes) { std::cerr << f.path() << ": expected to read " << header.n_nodes << " nodes, got " << n_nodes << '\n'; return -1; } return 0; } int abc::parse(rez::file &f, item &item) const { if (parse(f, item.pos)) { std::cerr << f.path() << ": failed to read position\n"; return -1; } else if (parse(f, item.rot)) { std::cerr << f.path() << ": failed to read rotation\n"; return -1; } return 0; } int abc::parse(rez::file &f, childmodel &cm, bool self) const { uint32_t unknown; if (self) cm.name = "SELF"; else if (parse(f, cm.name)) return -1; if (f.read_le(unknown)) { std::cerr << f.path() << ": failed to read unknown word\n"; return -1; } cm.items.reserve(header.n_nodes); for (uint32_t i = 0; i < header.n_nodes; i++) { item item; if (parse(f, item)) return -1; cm.items.push_back(item); } return 0; } int abc::parse_childmodels(rez::file &f) { childmodel self; uint32_t n; if (f.read_le(n)) { std::cerr << f.path() << ": failed to read number of child models\n"; return -1; } else if (parse(f, self, true)) { std::cerr << f.path() << ": failed to read self child model\n"; return -1; } childmodels.reserve(n); childmodels.push_back(self); for (uint32_t i = 1; i < n; i++) { childmodel cm; if (parse(f, cm)) return -1; childmodels.push_back(cm); } return 0; } int abc::parse(rez::file &f, keyframe &keyframe) const { if (f.read_le(keyframe.time)) { std::cerr << f.path() << ": failed to read keyframe time\n"; return -1; } else if (parse(f, keyframe.cmd)) { std::cerr << f.path() << ": failed to read keyframe cmd\n"; return -1; } return 0; } int abc::parse(rez::file &f, animitem &animitem) const { if (parse(f, animitem.item)) return -1; else if (f.read_le(animitem.unknown)) { std::cerr << f.path() << ": failed to read unknown word 1\n"; return -1; } else if (f.read_le(animitem.unknown2)) { std::cerr << f.path() << ": failed to read unknown word 2\n"; return -1; } return 0; } int abc::parse(rez::file &f, nodeanim &nodeanim, uint32_t n_keyframes) const { nodeanim.animitems.reserve(n_keyframes); for (uint32_t i = 0; i < n_keyframes; i++) { animitem animitem; if (parse(f, animitem)) return -1; nodeanim.animitems.push_back(animitem); } return 0; } int abc::parse(rez::file &f, animation &animation) const { uint32_t n_keyframes; if (parse(f, animation.dimensions)) { std::cerr << f.path() << ": failed to read dimensions\n"; return -1; } else if (parse(f, animation.name)) { std::cerr << f.path() << ": failed to read animation name\n"; return -1; } else if (f.read_le(animation.compression_type)) { std::cerr << f.path() << ": failed to read compression type\n"; return -1; } else if (f.read_le(animation.interp_time)) { std::cerr << f.path() << ": failed to read interp time\n"; return -1; } else if (f.read_le(n_keyframes)) { std::cerr << f.path() << ": failed to read number of keyframes\n"; return -1; } animation.keyframes.reserve(n_keyframes); for (uint32_t i = 0; i < n_keyframes; i++) { keyframe keyframe; if (parse(f, keyframe)) return -1; animation.keyframes.push_back(keyframe); } animation.nodeanims.reserve(header.n_nodes); for (uint32_t i = 0; i < header.n_nodes; i++) { nodeanim nodeanim; uint32_t unknown; if (f.read_le(unknown)) { std::cerr << f.path() << ": failed to read unknown word\n"; return -1; } else if (parse(f, nodeanim, n_keyframes)) return -1; animation.nodeanims.push_back(nodeanim); } return 0; } int abc::parse_animation(rez::file &f) { uint32_t n; if (f.read_le(n)) { std::cerr << f.path() << ": failed to read number of animations\n"; return -1; } animations.reserve(n); for (uint32_t i = 0; i < n; i++) { animation animation; if (parse(f, animation)) return -1; animations.push_back(animation); } return 0; } int abc::parse(rez::file &f, socket &socket) const { if (f.read_le(socket.node_index)) { std::cerr << f.path() << ": failed to read node index\n"; return -1; } else if (parse(f, socket.name)) { std::cerr << f.path() << ": failed to read socket name\n"; return -1; } else if (parse(f, socket.quat)) { std::cerr << f.path() << ": failed to read socket quat\n"; return -1; } else if (parse(f, socket.pos)) { std::cerr << f.path() << ": failed to read socket position\n"; return -1; } return 0; } int abc::parse_sockets(rez::file &f) { uint32_t n; if (f.read_le(n)) { std::cerr << f.path() << ": failed to read number of sockets\n"; return -1; } for (uint32_t i = 0; i < n; i++) { socket socket; if (parse(f, socket)) return -1; } return 0; } int abc::parse(rez::file &f, childanim &childanim) const { if (parse(f, childanim.name)) { std::cerr << f.path() << ": failed to read childanim name\n"; return -1; } else if (parse(f, childanim.dimensions)) { std::cerr << f.path() << ": failed to read childanim dimensions\n"; return -1; } else if (parse(f, childanim.translation)) { std::cerr << f.path() << ": failed to read childanim translation\n"; return -1; } return 0; } int abc::parse(rez::file &f, animbinding &animbinding) const { uint32_t n; if (f.read_le(n)) { std::cerr << f.path() << ": failed to read number of animbindings\n"; return -1; } animbinding.childanims.reserve(n); for (uint32_t i = 0; i < n; i++) { childanim childanim; if (parse(f, childanim)) return -1; animbinding.childanims.push_back(childanim); } return 0; } int abc::parse_animbindings(rez::file &f) { animbindings.reserve(childmodels.size()); for (size_t i = 0; i < childmodels.size(); i++) { animbinding animbinding; if (parse(f, animbinding)) return -1; animbindings.push_back(animbinding); } return 0; } int abc::parse_hitgroups(rez::file &f) { uint32_t unknown, unknown2; if (f.read_le(unknown) || f.read_le(unknown2)) { std::cerr << f.path() << ": failed to read unknown words\n"; return -1; } return 0; } int abc::parse(rez::file &f) { int32_t offset; do { uint32_t uoffset; std::string chunk; if (parse(f, chunk) || f.read_le(uoffset)) return -1; offset = uoffset; static const struct { const char *chunk; int (abc::*fn)(rez::file &); } fns[] = { {"Header", &abc::parse_header}, {"Pieces", &abc::parse_pieces}, {"Nodes", &abc::parse_nodes}, {"ChildModels", &abc::parse_childmodels}, {"Animation", &abc::parse_animation}, {"Sockets", &abc::parse_sockets}, {"AnimBindings", &abc::parse_animbindings}, {"HitGroups", &abc::parse_hitgroups} }; bool found = false; for (const auto &fn : fns) { long before, after; if (chunk != fn.chunk) continue; else if ((before = f.tell()) < 0) { std::cerr << f.path() << ": failed to get \"before\" offset\n"; return -1; } else if (std::invoke(fn.fn, this, f)) return -1; else if ((after = f.tell()) < 0) { std::cerr << f.path() << ": failed to get offset\n"; return -1; } else if (offset > 0 && after != offset) { std::cerr << f.path() << ": expected file offset " << offset << " after reading section " << chunk << ", but current offset is " << after << '\n'; return -1; } found = true; break; } if (!found) { std::cerr << f.path() << ": unsupported section: " << chunk << '\n'; return -1; } } while (offset != -1); return 0; } abc::abc() : int_radius(0.0f) { }