diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/abc/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/abc/abc.cpp | 912 | ||||
| -rw-r--r-- | src/abc/abc.h | 177 | ||||
| -rw-r--r-- | src/dtx/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/dtx/dtx.cpp | 207 | ||||
| -rw-r--r-- | src/dtx/dtx.h | 37 | ||||
| -rw-r--r-- | src/main.cpp | 99 | ||||
| -rw-r--r-- | src/rez/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | src/rez/dir.cpp | 117 | ||||
| -rw-r--r-- | src/rez/entry.cpp | 10 | ||||
| -rw-r--r-- | src/rez/file.cpp | 100 | ||||
| -rw-r--r-- | src/rez/io.cpp | 174 | ||||
| -rw-r--r-- | src/rez/resource.cpp | 46 | ||||
| -rw-r--r-- | src/rez/rez.cpp | 124 | ||||
| -rw-r--r-- | src/rez/rez.h | 46 | ||||
| -rw-r--r-- | src/rez/rez/dir.h | 34 | ||||
| -rw-r--r-- | src/rez/rez/entry.h | 31 | ||||
| -rw-r--r-- | src/rez/rez/file.h | 34 | ||||
| -rw-r--r-- | src/rez/rez/io.h | 34 | ||||
| -rw-r--r-- | src/rez/rez/resource.h | 30 |
21 files changed, 2233 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..e8e3894 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${PROJECT_NAME} PRIVATE main.cpp) +add_subdirectory(abc) +add_subdirectory(dtx) +add_subdirectory(rez) diff --git a/src/abc/CMakeLists.txt b/src/abc/CMakeLists.txt new file mode 100644 index 0000000..52460e3 --- /dev/null +++ b/src/abc/CMakeLists.txt @@ -0,0 +1,4 @@ +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +target_sources(${PROJECT_NAME} PRIVATE + abc.cpp +) 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) +{ +} diff --git a/src/abc/abc.h b/src/abc/abc.h new file mode 100644 index 0000000..b86f13c --- /dev/null +++ b/src/abc/abc.h @@ -0,0 +1,177 @@ +#ifndef ABC_H +#define ABC_H + +#include <rez/file.h> +#include <cstdint> +#include <list> +#include <memory> +#include <vector> + +class abc +{ +public: + abc(); + int parse(rez::file &f); + +private: + + struct vector + { + float x, y, z; + }; + + struct rotation + { + vector v; + float w; + }; + + struct weight + { + uint32_t node_index; + vector location; + float bias; + }; + + struct vertex + { + std::vector<weight> weights; + vector vertex, normal; + }; + + struct triangle + { + float a, b; + uint16_t trifs; + }; + + struct piece + { + uint16_t tex_index; + uint32_t specular_power, specular_scale, lod_weight, n_tris; + std::string name; + std::vector<triangle> triangles; + std::vector<vertex> vertices; + }; + + struct set + { + std::string name; + std::vector<float> weights; + }; + + struct node + { + std::string name; + uint16_t transform_index; + uint8_t flags; + rotation matrix[4]; + std::list<node> children; + }; + + struct header + { + uint32_t version, n_keyframe, n_anim, n_nodes, n_pieces, n_children, + n_triangles, n_vertices, n_vertweights, n_lod, n_socket, + n_weightsets, n_strings, n_strlen; + }; + + struct item + { + vector pos; + rotation rot; + }; + + struct childmodel + { + std::string name; + std::vector<item> items; + }; + + struct keyframe + { + std::string cmd; + uint32_t time; + }; + + struct animitem + { + abc::item item; + uint32_t unknown, unknown2; + }; + + struct nodeanim + { + std::vector<animitem> animitems; + }; + + struct animation + { + std::string name; + vector dimensions; + uint32_t compression_type, interp_time; + std::vector<keyframe> keyframes; + std::vector<nodeanim> nodeanims; + }; + + struct socket + { + std::string name; + uint32_t node_index; + rotation quat; + vector pos; + }; + + struct childanim + { + std::string name; + vector dimensions, translation; + }; + + struct animbinding + { + std::vector<childanim> childanims; + }; + + int parse_header(rez::file &f); + int parse_pieces(rez::file &f); + int parse_nodes(rez::file &f); + int parse_sets(rez::file &f); + int parse_childmodels(rez::file &f); + int parse_animation(rez::file &f); + int parse_sockets(rez::file &f); + int parse_animbindings(rez::file &f); + int parse_hitgroups(rez::file &f); + int parse(rez::file &f, std::string &name) const; + int parse(rez::file &f, node &parent); + int parse(rez::file &f, piece &piece) const; + int parse(rez::file &f, triangle &triangle) const; + int parse(rez::file &f, vertex &vertex) const; + int parse(rez::file &f, weight &weight) const; + int parse(rez::file &f, vector &vector) const; + int parse(rez::file &f, rotation &rot) const; + int parse(rez::file &f, set &set) const; + int parse(rez::file &f, childmodel &cm, bool self = false) const; + int parse(rez::file &f, item &item) const; + int parse(rez::file &f, animation &animation) const; + int parse(rez::file &f, keyframe &keyframe) const; + int parse(rez::file &f, nodeanim &nodeanim, uint32_t n_keyframes) const; + int parse(rez::file &f, animitem &animitem) const; + int parse(rez::file &f, socket &socket) const; + int parse(rez::file &f, animbinding &animbinding) const; + int parse(rez::file &f, childanim &childanim) const; + int print(const node &node, unsigned level) const; + size_t count(const node &node) const; + + node root; + header header; + std::vector<piece> pieces; + std::vector<abc::set> sets; + std::vector<childmodel> childmodels; + std::vector<animation> animations; + std::vector<socket> sockets; + std::vector<animbinding> animbindings; + float int_radius; +}; + +#endif diff --git a/src/dtx/CMakeLists.txt b/src/dtx/CMakeLists.txt new file mode 100644 index 0000000..40a8c1f --- /dev/null +++ b/src/dtx/CMakeLists.txt @@ -0,0 +1,4 @@ +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +target_sources(${PROJECT_NAME} PRIVATE + dtx.cpp +) diff --git a/src/dtx/dtx.cpp b/src/dtx/dtx.cpp new file mode 100644 index 0000000..463bb1d --- /dev/null +++ b/src/dtx/dtx.cpp @@ -0,0 +1,207 @@ +#include <dtx.h> +#include <rez/file.h> +#include <GL/gl.h> +#include <GL/glext.h> +#include <cstdint> +#include <cstdio> +#include <iostream> + +int dtx::draw() const +{ + int ret = 0; + GLenum error; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + + if (bpp == bpp_s3tc_dxt5) + { + glEnable(GL_BLEND); + glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); + } + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); glVertex2f(-0.5f, 0.5f); + glTexCoord2f(0.0f, 1.0f); glVertex2f(0.5f, 0.5f); + glTexCoord2f(1.0f, 1.0f); glVertex2f(0.5f, -0.5f); + glTexCoord2f(1.0f, 0.0f); glVertex2f(-0.5f, -0.5f); + glEnd(); + + glFlush(); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + + while ((error = glGetError()) != GL_NO_ERROR) + { + ret = -1; + fprintf(stderr, "%s:%d: glGetError returned %x\n", __FILE__, + __LINE__, error); + } + + return ret; +} + +int dtx::parse(rez::file &f) +{ + uint32_t restype, uversion; + + if (f.read_le(restype) || f.read_le(uversion)) + return -1; + + const uint32_t exp_type = 0; + const int32_t exp_version = -5; + int32_t version = uversion; + + if (restype != exp_type) + { + std::cerr << f.path() << ": invalid resource type " << restype + << ", expected " << exp_type << '\n'; + return -1; + } + else if (version != exp_version) + { + std::cerr << f.path() << ": invalid or unsupported version " << version + << ", expected " << exp_version << '\n'; + return -1; + } + + const uint16_t max_mipmaps = 8; + uint16_t w, h, n_mipmaps, n_sections, tex_angle; + uint32_t iflags, userflags, tex_scale; + unsigned char tex_group, n_rtmipmaps, bpp, mipmap_off_nc, mipmap_off, + tex_prio; + char cmd[128]; + + if (f.read_le(w) + || f.read_le(h) + || f.read_le(n_mipmaps) + || f.read_le(n_sections)) + { + std::cerr << f.path() << ": failed to read header\n"; + return -1; + } + else if (!n_mipmaps || n_mipmaps > max_mipmaps) + { + std::cerr << f.path() << ": invalid number of mipmaps: " + << n_mipmaps << '\n'; + return -1; + } + else if (f.read_le(iflags) || f.read_le(userflags)) + { + std::cerr << f.path() << ": failed to read flags\n"; + return -1; + } + else if (f.read(tex_group) + || f.read(n_rtmipmaps) + || f.read(bpp) + || f.read(mipmap_off_nc) + || f.read(mipmap_off) + || f.read(tex_prio) + || f.read_le(tex_scale) + || f.read_le(tex_angle)) + { + std::cerr << f.path() << ": failed to read extra data\n"; + return -1; + } + else if (bpp != bpp::bpp_32 + && bpp != bpp::bpp_s3tc_dxt1 + && bpp != bpp::bpp_s3tc_dxt5) + { + std::cerr << f.path() << ": invalid or unsupported bpp " + << std::to_string(bpp) << ", expected " << bpp::bpp_s3tc_dxt1 + << ", " << bpp::bpp_32 << " or " << bpp::bpp_s3tc_dxt5 << '\n'; + return -1; + } + else if (f.read(cmd, sizeof cmd)) + { + std::cerr << f.path() << ": failed to read cmd\n"; + return -1; + } + + this->bpp = static_cast<enum bpp>(bpp); + this->w = w; + this->h = h; + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + GLint level = 0; + + for (uint32_t i = 0; i < n_mipmaps; i++) + { + GLenum format; + unsigned long n_pix = (w >> i) * (h >> i); + pixdata mipmap; + + switch (this->bpp) + { + case bpp_32: + format = GL_BGRA; + n_pix *= sizeof (uint32_t); + break; + + case bpp_s3tc_dxt1: + format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + n_pix >>= 1; + break; + + case bpp_s3tc_dxt5: + format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + + default: + return -1; + } + + mipmap.reserve(n_pix); + + while (n_pix) + { + unsigned char buf[BUFSIZ]; + size_t n = n_pix > sizeof buf ? sizeof buf : n_pix; + + if (f.read(buf, n)) + return -1; + + mipmap.insert(mipmap.end(), buf, &buf[n]); + n_pix -= n; + } + + GLenum error; + int ret = 0; + + if (bpp == bpp::bpp_s3tc_dxt1 || bpp == bpp::bpp_s3tc_dxt5) + glCompressedTexImage2D(GL_TEXTURE_2D, level++, format, + w >> i, h >> i, 0, mipmap.size(), mipmap.data()); + else if (bpp == bpp::bpp_32) + glTexImage2D(GL_TEXTURE_2D, level++, format, w >> i, h >> i, 0, + format, GL_UNSIGNED_BYTE, mipmap.data()); + + while ((error = glGetError()) != GL_NO_ERROR) + { + fprintf(stderr, "%s:%d: glGetError returned %x\n", __FILE__, + __LINE__, error); + ret = -1; + } + + if (ret) + return ret; + } + + return 0; +} + +dtx::~dtx() +{ + if (texture) + glDeleteTextures(1, &texture); +} + +dtx::dtx() : + texture(0) +{ +} diff --git a/src/dtx/dtx.h b/src/dtx/dtx.h new file mode 100644 index 0000000..24a7a7b --- /dev/null +++ b/src/dtx/dtx.h @@ -0,0 +1,37 @@ +#ifndef DTX_H +#define DTX_H + +#include <rez/file.h> +#include <GL/gl.h> + +class dtx +{ +public: + typedef std::vector<unsigned char> pixdata; + + dtx(); + ~dtx(); + int parse(rez::file &f); + int draw() const; + +private: + + enum bpp + { + bpp_8_pal, + bpp_8, + bpp_16, + bpp_32, + bpp_s3tc_dxt1, + bpp_s3tc_dxt3, + bpp_s3tc_dxt5, + bpp_32_pal, + + n_bpp + } bpp; + + GLuint texture; + unsigned w, h; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..620e97e --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,99 @@ +#define SDL_MAIN_HANDLED + +#include <SDL2/SDL.h> +#include <SDL2/SDL_video.h> +#include <abc.h> +#include <dtx.h> +#include <rez.h> +#include <cstdlib> +#include <cstdio> +#include <iostream> +#include <GL/gl.h> + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + rez::ball b("globalops.rez"); + std::unique_ptr<rez::file> f, abcf; + SDL_Window *window = nullptr; + SDL_GLContext glcontext = nullptr; + static const char extension[] = "GL_EXT_texture_compression_s3tc"; + abc abc; + dtx dtx; + + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + std::cerr << "SDL_Init failed: " << SDL_GetError() << '\n'; + goto end; + } + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + + if (!(window = SDL_CreateWindow("GlobalOps", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, 640, 480, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE))) + { + std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << '\n'; + goto end; + } + else if (!(glcontext = SDL_GL_CreateContext(window))) + { + std::cerr << "SDL_GL_CreateContext failed: " << SDL_GetError() << '\n'; + goto end; + } + else if (!SDL_GL_ExtensionSupported(extension)) + { + std::cerr << "OpenGL extension " << extension << " not supported\n"; + goto end; + } + else if (SDL_GL_MakeCurrent(window, glcontext)) + { + std::cerr << "SDL_GL_MakeCurrent failed: " << SDL_GetError() + << std::endl; + goto end; + } + else if (b.parse() + || !(f = b.open("interface/blueprints/antarctica_tacmap.dtx")) + || dtx.parse(*f) + || !(f = b.open("models/grenades/v_frag.abc")) + || abc.parse(*f)) + goto end; + + for (;;) + { + SDL_Event event; + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + ret = EXIT_SUCCESS; + goto end; + } + } + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + if (dtx.draw()) + goto end; + + SDL_GL_SwapWindow(window); + SDL_Delay(1.0f / 60.f * 1000.0f); + } + +end: + + if (glcontext) + SDL_GL_DeleteContext(glcontext); + + if (window) + SDL_DestroyWindow(window); + + if (SDL_WasInit(SDL_INIT_VIDEO)) + SDL_QuitSubSystem(SDL_INIT_VIDEO); + + return ret; +} diff --git a/src/rez/CMakeLists.txt b/src/rez/CMakeLists.txt new file mode 100644 index 0000000..7a767fc --- /dev/null +++ b/src/rez/CMakeLists.txt @@ -0,0 +1,9 @@ +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +target_sources(${PROJECT_NAME} PRIVATE + dir.cpp + entry.cpp + file.cpp + io.cpp + resource.cpp + rez.cpp +) diff --git a/src/rez/dir.cpp b/src/rez/dir.cpp new file mode 100644 index 0000000..62e37fb --- /dev/null +++ b/src/rez/dir.cpp @@ -0,0 +1,117 @@ +#include <rez/dir.h> +#include <rez/io.h> +#include <iostream> + +const rez::dir::entrymap &rez::dir::entries() const +{ + return m_entries; +} + +const std::string &rez::dir::name() const +{ + return m_name; +} + +int rez::dir::read(const struct entry::cfg &cfg, rez::io &io, + std::string &name, direntry &de, unsigned level) +{ + rez::entry entry; + + if (entry.read(io)) + return -1; + + switch (entry.type) + { + case entry::type::dir: + { + long offset; + dir subdir; + + if (io.read(subdir.m_name, cfg.maxdirlen) + || (offset = io.tell()) < 0) + return -1; + + struct entry::cfg scfg = cfg; + +#if 0 + for (unsigned i = 0; i < level + 1; i++) + std::cout << "|\t"; + + std::cout << "name: " << subdir.m_name << std::endl; +#endif + + subdir.entry = entry; + scfg.size = entry.size; + scfg.offset = entry.pos; + + if (subdir.read(scfg, io, level + 1) || io.seek(offset)) + return -1; + + name = subdir.m_name; + de = subdir; + } + break; + + case entry::type::resource: + { + resource resource; + + resource.entry = entry; + + if (resource.read(cfg, io)) + return -1; + +#if 0 + for (unsigned i = 0; i < level + 1; i++) + std::cout << "|\t"; + + std::cout << "name: " << resource.name() << std::endl; +#endif + + name = resource.name(); + de = resource; + } + break; + + default: + std::cerr << "Invalid entry type " << entry.type << std::endl; + return -1; + } + + return 0; +} + +int rez::dir::read(const struct entry::cfg &cfg, rez::io &io, unsigned level) +{ + unsigned long size = cfg.size; + long before = cfg.offset; + + if (io.seek(cfg.offset)) + return -1; + + while (size) + { + std::string name; + direntry de; + long after; + + if (read(cfg, io, name, de, level) + || (after = io.tell()) < 0) + return -1; + + unsigned long r = after - before; + + if (r > size) + { + std::cerr << m_name << ": exceeded maximum direntry length " + "(" << cfg.size << ")" << std::endl; + return -1; + } + + m_entries[name] = de; + before = after; + size -= r; + } + + return 0; +} diff --git a/src/rez/entry.cpp b/src/rez/entry.cpp new file mode 100644 index 0000000..2e2f73e --- /dev/null +++ b/src/rez/entry.cpp @@ -0,0 +1,10 @@ +#include <rez/entry.h> +#include <rez/io.h> + +int rez::entry::read(rez::io &io) +{ + return io.read_le(type) + || io.read_le(pos) + || io.read_le(size) + || io.read_le(time); +} diff --git a/src/rez/file.cpp b/src/rez/file.cpp new file mode 100644 index 0000000..9a6a1f3 --- /dev/null +++ b/src/rez/file.cpp @@ -0,0 +1,100 @@ +#include <rez/file.h> +#include <rez/entry.h> +#include <rez/resource.h> +#include <cstddef> +#include <iostream> + +std::string rez::file::path() const +{ + return resource.name(); +} + +long rez::file::tell() +{ + return offset; +} + +int rez::file::seek(long offset) +{ + if (offset < 0 || offset > resource.entry.size) + return -1; + else if (io.seek(resource.entry.pos + offset)) + return -1; + + this->offset = offset; + return 0; +} + +int rez::file::eof() const +{ + return offset >= resource.entry.size; +} + +int rez::file::read(void *b, size_t n) +{ + const rez::entry &entry = resource.entry; + unsigned long roffset = offset + entry.pos, + end = entry.pos + entry.size, remaining = end - roffset; + + if (n > remaining) + { + std::cerr << path() << ": requested " << n << " bytes, only " + << remaining << " remaining" << std::endl; + return -1; + } + + if (seek(offset) || io.read(b, n)) + return -1; + + offset += n; + return 0; +} + +int rez::file::read(unsigned char &b) +{ + return read(&b, sizeof b); +} + +int rez::file::read(std::string &s, size_t len) +{ + for (size_t i = 0; i < len; i++) + { + char b; + + if (read(&b, sizeof b)) + return -1; + + s += b; + } + + return 0; +} + +int rez::file::read_le(uint16_t &w) +{ + unsigned char b[sizeof w]; + + if (read(b, sizeof b)) + return -1; + + w = b[0] | (b[1] << 8); + return 0; +} + +int rez::file::read_le(uint32_t &w) +{ + unsigned char b[sizeof w]; + + if (read(b, sizeof b)) + return -1; + + w = b[0] | (b[1] << 8u) | (b[2] << 16u) | (b[3] << 24u); + return 0; +} + +rez::file::file(const rez::resource &resource, rez::io &io) : + resource(resource), + offset(0), + io(io) +{ +} diff --git a/src/rez/io.cpp b/src/rez/io.cpp new file mode 100644 index 0000000..019fdab --- /dev/null +++ b/src/rez/io.cpp @@ -0,0 +1,174 @@ +#include <rez/io.h> +#include <cerrno> +#include <cstring> +#include <fstream> +#include <iostream> + +int rez::io::read(unsigned char &b) +{ + std::ifstream::pos_type off = f.tellg(); + + f.read((char *)&b, sizeof b); + + if (!f || f.eof()) + { + std::cerr << m_path << ": failed to read at offset " << off + << ", eof=" << f.eof() << std::endl; + return -1; + } + + return 0; +} + +int rez::io::check(char byte) +{ + char b; + std::ifstream::pos_type off = f.tellg(); + + f.read(&b, sizeof b); + + if (!f || f.eof()) + { + std::cerr << m_path << ": failed to read at offset " << off + << ", eof=" << f.eof() << std::endl; + return -1; + } + else if (b != byte) + { + std::cerr << m_path << ": expected " << byte << " at offset: " << off + << ", got " << b << std::endl; + return -1; + } + + return 0; +} + +int rez::io::check(const char *s) +{ + while (*s) + if (check(*s++)) + return -1; + + return 0; +} + +int rez::io::read_le(uint32_t &w) +{ + std::ifstream::pos_type off = f.tellg(); + unsigned char buf[sizeof w]; + + f.read((char *)buf, sizeof buf); + + if (!f || f.eof()) + { + std::cerr << m_path << ": failed to read word at offset " << off + << ", eof=" << f.eof() << std::endl; + return -1; + } + + w = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + return 0; +} + +int rez::io::read(std::string &s, unsigned maxlen) +{ + unsigned i = 0; + + while (i++ < maxlen) + { + unsigned char b; + + if (read(b)) + return -1; + else if (!b) + return 0; + + s += b; + } + + std::cerr << m_path << ": exceeded maximum string length (" << maxlen + << "): \"" << s << "\"" << std::endl; + return -1; +} + +int rez::io::read(void *b, size_t n) +{ + std::ifstream::pos_type off = f.tellg(); + + f.read((char *)(b), n); + + if (!f) + { + std::cerr << m_path << ": failed to read " << n << " bytes at offset " + << off << ", eof=" << f.eof() << std::endl; + return -1; + } + + return 0; +} + +long rez::io::tell() +{ + std::ifstream::pos_type offset = f.tellg(); + + if (!f) + { + std::cerr << m_path << ": failed to retrieve offset" << std::endl; + return -1; + } + + return offset; +} + +int rez::io::seek(long offset) +{ + long cur = f.tellg(); + + if (!f) + { + std::cerr << m_path << ": failed to tell offset " << std::endl; + return -1; + } + else if (cur != offset) + { + f.seekg(static_cast<std::ifstream::pos_type>(offset)); + + if (!f) + { + std::cerr << m_path << ": failed to seek to offset " << offset + << std::endl; + return -1; + } + } + + return 0; +} + +int rez::io::open() +{ + if (!f.is_open()) + { + const char *error; + +#ifdef _WIN32 + error = GetLastError(); +#else + error = strerror(errno); +#endif + std::cerr << "Failed to open " << m_path << ": " << error << std::endl; + return -1; + } + + return 0; +} + +const std::string &rez::io::path() const +{ + return m_path; +} + +rez::io::io(const char *path) : + f(path, std::ios::binary), + m_path(path) +{ +} diff --git a/src/rez/resource.cpp b/src/rez/resource.cpp new file mode 100644 index 0000000..3fc94a7 --- /dev/null +++ b/src/rez/resource.cpp @@ -0,0 +1,46 @@ +#include <rez/resource.h> +#include <rez/io.h> + +std::string rez::resource::name() const +{ + std::string ret = m_name; + + if (*type) + ret += std::string(".") + type; + + return ret; +} + +const std::string &rez::resource::description() const +{ + return descr; +} + +int rez::resource::read(const struct entry::cfg &cfg, rez::io &io) +{ + char letype[sizeof type]; + uint32_t n_keys; + + if (io.read_le(id) + || io.read(letype, sizeof letype) + || io.read_le(n_keys) + || io.read(m_name, cfg.maxreslen) + || io.read(descr, sizeof ".XYZ")) + return -1; + + for (uint32_t i = 0; i < n_keys; i++) + { + uint32_t key; + + if (io.read_le(key)) + return -1; + + keys.push_back(key); + } + + type[0] = letype[2]; + type[1] = letype[1]; + type[2] = letype[0]; + type[3] = '\0'; + return 0; +} diff --git a/src/rez/rez.cpp b/src/rez/rez.cpp new file mode 100644 index 0000000..2351c05 --- /dev/null +++ b/src/rez/rez.cpp @@ -0,0 +1,124 @@ +#include <rez.h> +#include <rez/dir.h> +#include <rez/io.h> +#include <cctype> +#include <iostream> +#include <memory> +#include <sstream> +#include <variant> + +std::string rez::ball::toupper(const std::string &s) const +{ + std::string ret; + + // Assume ASCII encoding. + for (const auto &c: s) + ret += std::toupper(c); + + return ret; +} + +std::unique_ptr<rez::file> rez::ball::open(const char *path) +{ + std::istringstream stream(path); + std::string token; + const rez::dir *dir = &root_dir; + const rez::resource *resource = nullptr; + + while (std::getline(stream, token, '/')) + { + std::string uctoken = toupper(token); + const auto &map = dir->entries(); + auto it = map.find(uctoken); + + if (it == map.end()) + return nullptr; + + const rez::dir::direntry &de = it->second; + + if (std::holds_alternative<rez::dir>(de)) + dir = &std::get<rez::dir>(de); + else if (std::holds_alternative<rez::resource>(de)) + { + dir = nullptr; + resource = &std::get<rez::resource>(de); + } + } + + if (dir) + { + std::cerr << path << " is a directory\n"; + return nullptr; + } + else if (!resource) + { + std::cerr << io.path() << ": could not find " << path << '\n'; + return nullptr; + } + + return std::make_unique<rez::file>(*resource, io); +} + +int rez::ball::parse() +{ + static const char title[63] = + "\r\nRezMgr Version 1 Copyright (C) 1995 MONOLITH INC. ", + subtitle[65] = + "\r\nLithTech Resource File \r\n"; + + if (io.open()) + return -1; + else if (io.check(title)) + { + std::cerr << path << ": wrong title\n"; + return -1; + } + else if (io.check(subtitle)) + { + std::cerr << path << ": wrong subtitle\n"; + return -1; + } + else if (io.check(0x1a)) + { + std::cerr << path << ": wrong EOF\n"; + return -1; + } + + struct + { + uint32_t w[N_WORDS]; + uint8_t is_sorted; + } header; + + for (auto &w : header.w) + if (io.read_le(w)) + return -1; + + if (header.w[FILE_FORMAT_VERSION] != 1) + { + std::cerr << path << ": expected file format version 1, got " + << header.w[FILE_FORMAT_VERSION] << '\n'; + return -1; + } + else if (io.read(header.is_sorted)) + { + std::cerr << path << ": could not read sorted flag" << '\n'; + return -1; + } + + struct rez::entry::cfg cfg = {0}; + + cfg.size = header.w[ROOT_DIR_SIZE]; + cfg.offset = header.w[ROOT_DIR_POS]; + cfg.maxcomlen = header.w[MAX_COMMENT_SIZE]; + cfg.maxdirlen = header.w[MAX_DIR_NAME_SIZE]; + cfg.maxreslen = header.w[MAX_REZ_NAME_SIZE]; + root_dir.read(cfg, io, 0); + return 0; +} + +rez::ball::ball(const char *path) : + path(path), + io(path) +{ +} diff --git a/src/rez/rez.h b/src/rez/rez.h new file mode 100644 index 0000000..5bbe206 --- /dev/null +++ b/src/rez/rez.h @@ -0,0 +1,46 @@ +#ifndef REZ_H +#define REZ_H + +#include <rez/dir.h> +#include <rez/file.h> +#include <rez/io.h> +#include <memory> +#include <string> + +namespace rez +{ + +class ball +{ +public: + ball(const char *path); + int parse(); + std::unique_ptr<rez::file> open(const char *path); + +private: + std::string toupper(const std::string &s) const; + + enum + { + FILE_FORMAT_VERSION, + ROOT_DIR_POS, + ROOT_DIR_SIZE, + ROOT_DIR_TIME, + NEXT_WRITE_POS, + TIME, + MAX_KEY_ARRAY, + MAX_DIR_NAME_SIZE, + MAX_REZ_NAME_SIZE, + MAX_COMMENT_SIZE, + + N_WORDS + }; + + const std::string path; + rez::io io; + rez::dir root_dir; +}; + +} + +#endif diff --git a/src/rez/rez/dir.h b/src/rez/rez/dir.h new file mode 100644 index 0000000..5485052 --- /dev/null +++ b/src/rez/rez/dir.h @@ -0,0 +1,34 @@ +#ifndef REZ_DIR_H +#define REZ_DIR_H + +#include <rez/io.h> +#include <rez/entry.h> +#include <rez/resource.h> +#include <map> +#include <string> +#include <variant> + +namespace rez +{ + +class dir +{ +public: + typedef std::variant<resource, rez::dir> direntry; + typedef std::map<std::string, direntry> entrymap; + int read(const struct entry::cfg &cfg, rez::io &io, unsigned level); + const std::string &name() const; + const entrymap &entries() const; + +private: + int read(const struct entry::cfg &cfg, rez::io &io, std::string &name, + direntry &de, unsigned level); + + rez::entry entry; + std::string m_name; + entrymap m_entries; +}; + +} + +#endif diff --git a/src/rez/rez/entry.h b/src/rez/rez/entry.h new file mode 100644 index 0000000..0e76321 --- /dev/null +++ b/src/rez/rez/entry.h @@ -0,0 +1,31 @@ +#ifndef REZ_ENTRY_H +#define REZ_ENTRY_H + +#include <rez/io.h> +#include <cstdint> + +namespace rez +{ + +struct entry +{ + int read(rez::io &io); + + struct cfg + { + long offset; + unsigned long size, maxdirlen, maxreslen, maxcomlen; + }; + + enum type + { + resource, + dir + }; + + uint32_t type, pos, size, time; +}; + +} + +#endif diff --git a/src/rez/rez/file.h b/src/rez/rez/file.h new file mode 100644 index 0000000..20b2310 --- /dev/null +++ b/src/rez/rez/file.h @@ -0,0 +1,34 @@ +#ifndef REZ_FILE_H +#define REZ_FILE_H + +#include <rez/io.h> +#include <rez/resource.h> +#include <cstddef> +#include <cstdint> + +namespace rez +{ + +class file +{ +public: + file(const rez::resource &resource, rez::io &io); + std::string path() const; + int read(void *b, size_t n); + int read(unsigned char &b); + int read(std::string &s, size_t len); + int read_le(uint16_t &w); + int read_le(uint32_t &w); + long tell(); + int seek(long offset); + int eof() const; + +private: + const rez::resource &resource; + long offset; + rez::io &io; +}; + +} + +#endif diff --git a/src/rez/rez/io.h b/src/rez/rez/io.h new file mode 100644 index 0000000..238a925 --- /dev/null +++ b/src/rez/rez/io.h @@ -0,0 +1,34 @@ +#ifndef REZ_IO_H +#define REZ_IO_H + +#include <cstddef> +#include <cstdint> +#include <fstream> +#include <string> + +namespace rez +{ + +class io +{ +public: + io(const char *path); + int open(); + int read(unsigned char &b); + int read(std::string &s, unsigned maxlen); + int read(void *b, size_t n); + int check(char byte); + int check(const char *s); + int read_le(uint32_t &w); + long tell(); + int seek(long offset); + const std::string &path() const; + +private: + std::ifstream f; + const std::string m_path; +}; + +} + +#endif diff --git a/src/rez/rez/resource.h b/src/rez/rez/resource.h new file mode 100644 index 0000000..4daafa0 --- /dev/null +++ b/src/rez/rez/resource.h @@ -0,0 +1,30 @@ +#ifndef REZ_RESOURCE_H +#define REZ_RESOURCE_H + +#include <rez/entry.h> +#include <rez/io.h> +#include <cstdint> +#include <string> +#include <vector> + +namespace rez +{ + +class resource +{ +public: + int read(const struct entry::cfg &cfg, rez::io &io); + const std::string &description() const; + std::string name() const; + rez::entry entry; + +private: + uint32_t id; + char type[sizeof "xyz"]; + std::string m_name, descr; + std::vector<uint32_t> keys; +}; + +} + +#endif |
