diff options
Diffstat (limited to 'src/abc/abc.cpp')
| -rw-r--r-- | src/abc/abc.cpp | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/src/abc/abc.cpp b/src/abc/abc.cpp new file mode 100644 index 0000000..2ad45f1 --- /dev/null +++ b/src/abc/abc.cpp @@ -0,0 +1,912 @@ +#include <abc.h> +#include <cstdint> +#include <cstring> +#include <functional> +#include <iostream> +#include <string> + +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<float> 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) +{ +} |
