Skip to content

Commit

Permalink
archive: add unstable next and visit_objects APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
lmichaelis committed Jan 1, 2023
1 parent a327c9c commit 3079375
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 168 deletions.
45 changes: 44 additions & 1 deletion include/phoenix/archive.hh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <variant>

namespace phoenix {
enum class archive_format { binary, binsafe, ascii };
Expand Down Expand Up @@ -58,6 +60,32 @@ namespace phoenix {
std::uint32_t index;
};

enum class archive_entry_type : uint8_t {
string = 0x1,
int_ = 0x2,
float_ = 0x3,
byte = 0x4,
word = 0x5,
bool_ = 0x6,
vec3 = 0x7,
color = 0x8,
raw = 0x9,
raw_float = 0x10,
enum_ = 0x11,
hash = 0x12,
};

struct archive_entry {
archive_entry_type type;
std::string name;
std::variant<std::string, int, float, uint8_t, uint16_t, bool, glm::vec3, glm::u8vec4, buffer, uint32_t> value;
};

struct archive_object_end {};

using archive_visitor =
std::function<void(const std::optional<archive_object>&, const std::optional<archive_entry>&)>;

/// \brief A reader for ZenGin archives.
class archive_reader {
public:
Expand Down Expand Up @@ -173,6 +201,22 @@ namespace phoenix {
/// values until the current object closes.
virtual void print_structure(bool open_object);

/// \brief Get the next element in the archive.
///
/// Parses the next element, either an object begin, object end or entry from the archive and returns
/// its associated value.
///
/// \return The value of the next element in the archive.
/// \warning This API is unstable and may change at any time!
virtual std::variant<archive_object, archive_object_end, archive_entry> unstable__next() = 0;

/// \brief Walks the current or next object of the archive recursively, calling `cb` for each element.
/// \param open_object If `false`, walks out the next object in the reader, otherwise walks all
/// values until the current object closes.
/// \param cb A callback function to handle each element.
/// \warning This API is unstable and may change at any time!
void unstable__visit_objects(bool open_object, const archive_visitor& cb);

/// \return The header of the archive
[[nodiscard]] const archive_header& get_header() const noexcept {
return header;
Expand All @@ -189,7 +233,6 @@ namespace phoenix {

/// \brief Skips the next entry in the reader.
virtual void skip_entry() = 0;
virtual void print_entry() = 0;

protected:
archive_header header;
Expand Down
96 changes: 89 additions & 7 deletions source/archive.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@
#include <iostream>

namespace phoenix {
constexpr std::string_view type_names[] = {
"unknown", // ? = 0x00
"string", // bs_string = 0x01,
"int", // bs_int = 0x02,
"float", // bs_float = 0x03,
"byte", // bs_byte = 0x04,
"word", // bs_word = 0x05,
"bool", // bs_bool = 0x06,
"vec3", // bs_vec3 = 0x07,
"color", // bs_color = 0x08,
"raw", // bs_raw = 0x09,
"unknown", // ? = 0x0A
"unknown", // ? = 0x0B
"unknown", // ? = 0x0C
"unknown", // ? = 0x0D
"unknown", // ? = 0x0E
"unknown", // ? = 0x0F
"unknown", // bs_raw_float = 0x10,
"enum", // bs_enum = 0x11,
"hash", // bs_hash = 0x12,
};

archive_header archive_header::parse(buffer& in) {
try {
archive_header header {};
Expand Down Expand Up @@ -97,19 +119,79 @@ namespace phoenix {
}

void archive_reader::print_structure(bool open_object) {
archive_object tmp;
this->unstable__visit_objects(
open_object,
[](const std::optional<archive_object>& obj, const std::optional<archive_entry>& ent) {
if (obj) {
std::cout << "<object class=\"" << obj->class_name << "\" name=\"" << obj->object_name
<< "\" version=\"" << obj->version << "\" index=\"" << obj->index << "\">\n";
} else if (ent) {
std::cout << "<entry name=\"" << ent->name << "\" type=\""
<< type_names[static_cast<uint8_t>(ent->type)] << "\" ";

switch (ent->type) {
case archive_entry_type::string:
std::cout << "value=\"" << std::get<std::string>(ent->value) << "\" ";
break;
case archive_entry_type::int_:
std::cout << "value=\"" << std::get<int>(ent->value) << "\" ";
break;
case archive_entry_type::float_:
std::cout << "value=\"" << std::get<float>(ent->value) << "\" ";
break;
case archive_entry_type::byte:
std::cout << "value=\"" << static_cast<uint16_t>(std::get<uint8_t>(ent->value)) << "\" ";
break;
case archive_entry_type::word:
std::cout << "value=\"" << std::get<uint16_t>(ent->value) << "\" ";
break;
case archive_entry_type::bool_:
std::cout << "value=\"" << std::get<bool>(ent->value) << "\" ";
break;
case archive_entry_type::vec3: {
auto v = std::get<glm::vec3>(ent->value);
std::cout << "value=\"(" << v.x << ", " << v.y << ", " << v.z << ")\" ";
break;
}
case archive_entry_type::color: {
auto v = std::get<glm::u8vec4>(ent->value);
std::cout << "value=\"(" << v.r << ", " << v.g << ", " << v.b << ", " << v.a << ")\" ";
break;
}
case archive_entry_type::raw:
case archive_entry_type::raw_float:
std::cout << "length=\"" << std::get<buffer>(ent->value).remaining() << "\" ";
break;
case archive_entry_type::enum_:
std::cout << "value=\"" << std::get<uint32_t>(ent->value) << "\" ";
break;
case archive_entry_type::hash:
std::cout << "value=\"" << std::get<uint32_t>(ent->value) << "\" ";
break;
}

std::cout << "/>\n";
} else {
std::cout << "</object>\n";
}
});
}

void archive_reader::unstable__visit_objects(bool open_object, const archive_visitor& cb) {
std::variant<archive_object, archive_object_end, archive_entry> ent;
int32_t level = open_object ? 1 : 0;

do {
if (read_object_begin(tmp)) {
std::cout << "<object type=\"" << tmp.class_name << "\" name=\"" << tmp.object_name << "\" version=\""
<< tmp.version << "\" index=\"" << tmp.index << "\">\n";
ent = unstable__next();

if (std::holds_alternative<archive_object>(ent)) {
cb(std::get<archive_object>(ent), std::nullopt);
++level;
} else if (read_object_end()) {
std::cout << "</object>\n";
} else if (std::holds_alternative<archive_object_end>(ent)) {
cb(std::nullopt, std::nullopt);
--level;
} else {
print_entry();
cb(std::nullopt, std::get<archive_entry>(ent));
}
} while (level > 0);
}
Expand Down
78 changes: 71 additions & 7 deletions source/archive/archive_ascii.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@
#include <iostream>

namespace phoenix {
static const std::unordered_map<std::string, archive_entry_type> type_name_to_enum {
{"string", archive_entry_type::string},
{"int", archive_entry_type::int_},
{"float", archive_entry_type::float_},
{"byte", archive_entry_type::byte},
{"word", archive_entry_type::word},
{"bool_", archive_entry_type::bool_},
{"vec3", archive_entry_type::vec3},
{"color", archive_entry_type::color},
{"raw", archive_entry_type::raw},
{"rawFloat", archive_entry_type::raw_float},
{"enum", archive_entry_type::enum_},
{"hash", archive_entry_type::hash},
};

void archive_reader_ascii::read_header() {
{
std::string objects = input.get_line();
Expand Down Expand Up @@ -216,14 +231,63 @@ namespace phoenix {
return buffer::of(std::move(out));
}

void archive_reader_ascii::print_entry() {
auto line = input.get_line();
auto name = line.substr(line.find('='));
std::variant<archive_object, archive_object_end, archive_entry> archive_reader_ascii::unstable__next() {
static archive_object tmp {};
if (read_object_begin(tmp)) {
return tmp;
} else if (read_object_end()) {
return archive_object_end {};
} else {
input.mark();

line = line.substr(line.find('=') + 1);
auto colon = line.find(':');
archive_entry entry {};

auto line = input.get_line();
entry.name = line.substr(line.find('='));

line = line.substr(line.find('=') + 1);
entry.type = type_name_to_enum.at(line.substr(0, line.find(':')));

std::cout << "<field name=\"" << name << "\" type=\"" << line.substr(0, colon) << "\" value=\""
<< line.substr(colon + 1) << "\" />";
input.reset();

switch (entry.type) {
case archive_entry_type::string:
entry.value = read_string();
break;
case archive_entry_type::int_:
entry.value = read_int();
break;
case archive_entry_type::float_:
entry.value = read_float();
break;
case archive_entry_type::byte:
entry.value = read_byte();
break;
case archive_entry_type::word:
entry.value = read_word();
break;
case archive_entry_type::bool_:
entry.value = read_bool();
break;
case archive_entry_type::vec3:
entry.value = read_vec3();
break;
case archive_entry_type::color:
entry.value = read_color();
break;
case archive_entry_type::raw:
case archive_entry_type::raw_float:
entry.value = read_raw_bytes();
break;
case archive_entry_type::enum_:
entry.value = read_enum();
break;
case archive_entry_type::hash:
entry.value = 0u;
break;
}

return entry;
}
}
} // namespace phoenix
3 changes: 2 additions & 1 deletion source/archive/archive_ascii.hh
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ namespace phoenix {
buffer read_raw_bytes() override;
buffer read_raw_bytes(uint32_t size) override;

std::variant<archive_object, archive_object_end, archive_entry> unstable__next() override;

protected:
void read_header() override;
void skip_entry() override;
void print_entry() override;

std::string read_entry(std::string_view type);

Expand Down
30 changes: 15 additions & 15 deletions source/archive/archive_binary.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,6 @@ namespace phoenix {
return input.get_vec2();
}

void archive_reader_binary::skip_entry() {
throw parser_error {"archive_reader", "cannot skip entry in binary archive"};
}

void archive_reader_binary::skip_object(bool skip_current) {
if (skip_current) {
input.position(_m_object_end.top());
_m_object_end.pop();
} else {
input.skip(input.get_uint() - 4);
}
}

bounding_box archive_reader_binary::read_bbox() {
return bounding_box::parse(input);
}
Expand All @@ -116,7 +103,20 @@ namespace phoenix {
return input.extract(size);
}

void archive_reader_binary::print_entry() {
throw parser_error {"archive_reader", "cannot print entry in binary archive"};
void archive_reader_binary::skip_entry() {
throw parser_error {"archive_reader", "cannot skip entry in binary archive"};
}

void archive_reader_binary::skip_object(bool skip_current) {
if (skip_current) {
input.position(_m_object_end.top());
_m_object_end.pop();
} else {
input.skip(input.get_uint() - 4);
}
}

std::variant<archive_object, archive_object_end, archive_entry> archive_reader_binary::unstable__next() {
throw parser_error {"archive_reader", "next() doesn't work for binary archives"};
}
} // namespace phoenix
2 changes: 1 addition & 1 deletion source/archive/archive_binary.hh
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ namespace phoenix {
buffer read_raw_bytes(uint32_t size) override;

void skip_object(bool skip_current) override;
std::variant<archive_object, archive_object_end, archive_entry> unstable__next() override;

protected:
void read_header() override;
void skip_entry() override;
void print_entry() override;

private:
std::stack<uint64_t> _m_object_end {};
Expand Down
Loading

0 comments on commit 3079375

Please sign in to comment.