aboutsummaryrefslogtreecommitdiff
path: root/src/abc/abc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/abc/abc.cpp')
-rw-r--r--src/abc/abc.cpp912
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)
+{
+}