diff --git a/Navmesher/esx_reader.cpp b/Navmesher/esx_reader.cpp index edd8db5..214ce6a 100644 --- a/Navmesher/esx_reader.cpp +++ b/Navmesher/esx_reader.cpp @@ -2,8 +2,104 @@ using namespace esxr; +Node read_unknown_record(std::istream &in); + [[nodiscard]] Header esxr::read_header(std::istream &in) { return read_header_impl(in); } +[[nodiscard]] std::size_t data_size(const RecordHeader& header) noexcept +{ + return header.size; +} + +[[nodiscard]] std::size_t data_size(const GroupHeader& header) noexcept +{ + return header.size - header_size; +} + +[[nodiscard]] std::streamsize data_ssize(const RecordHeader& header) noexcept +{ + return static_cast(data_size(header)); +} + +[[nodiscard]] std::streamsize data_ssize(const GroupHeader& header) noexcept +{ + return static_cast(data_size(header)); +} + +[[nodiscard]] std::size_t node_size(const RecordNode& node) noexcept +{ + return node.header.size + header_size; +} + +[[nodiscard]] std::size_t node_size(const GroupNode& node) noexcept +{ + return node.header.size; +} + +[[nodiscard]] std::streamsize node_ssize(const RecordNode& node) noexcept +{ + return static_cast(node_size(node)); +} + +[[nodiscard]] std::streamsize node_ssize(const GroupNode& node) noexcept +{ + return static_cast(node_size(node)); +} + +[[nodiscard]] std::streamsize node_ssize(const Node& node) +{ + return std::visit(utility::overloaded{ + [](const GroupNode &node) { return node_ssize(node); }, + [](const RecordNode &node) { return node_ssize(node); }, + }, node); +} + +Node read_record(RecordHeader header, std::istream &in) +{ + std::vector data(data_size(header)); + in.read(data.data(), data_ssize(header)); + return { RecordNode{ header, std::move(data) } }; +} + +Node read_group(GroupHeader header, std::istream &in) +{ + std::vector children{}; + auto remaining = data_ssize(header); + while (remaining > 0) { + children.emplace_back(read_unknown_record(in)); + remaining -= node_ssize(children.back()); + } + if (remaining < 0) + throw std::runtime_error("Read past end of group data."); + return { GroupNode{ header, std::move(children) } }; +} + +Node read_unknown_record(std::istream &in) +{ + auto header_variant = read_header(in); + struct visitor { + std::istream *in_ptr; + Node operator()(RecordHeader h) { + return read_record(h, *in_ptr); + } + Node operator()(GroupHeader h) { + return read_group(h, *in_ptr); + } + }; + return std::visit(visitor{ &in }, std::move(header_variant)); +} + +[[nodiscard]] RootNode esxr::read_esx(std::istream &in, std::size_t file_size) +{ + RootNode root{}; + auto remaining = static_cast(file_size); + while (remaining != 0) { + root.children.emplace_back(read_unknown_record(in)); + remaining -= node_ssize(root.children.back()); + } + return root; +} + diff --git a/Navmesher/esx_reader.hpp b/Navmesher/esx_reader.hpp index f845dc0..2b99ef6 100644 --- a/Navmesher/esx_reader.hpp +++ b/Navmesher/esx_reader.hpp @@ -18,8 +18,8 @@ namespace esxr { // Forward declarations // -class GroupNode; -class RecordNode; +struct RecordNode; +struct GroupNode; // // Enumerations @@ -135,16 +135,18 @@ struct RecordHeader { using Node = std::variant; using Header = std::variant; -class GroupNode { -private: - GroupHeader m_header; - std::vector m_children; +struct RootNode { + std::vector children; }; -class RecordNode { -private: - RecordHeader m_header; - std::vector data; +struct GroupNode { + GroupHeader header; + std::vector children; +}; + +struct RecordNode { + RecordHeader header; + std::vector data; }; // @@ -159,6 +161,7 @@ private: [[nodiscard]] std::optional refr_flag_to_description(RefrFlag refr_flag) noexcept; [[nodiscard]] Header read_header(std::istream &in); +[[nodiscard]] RootNode read_esx(std::istream &in, std::size_t file_size); // Convert a compatible C string to a FourCC (e.g. "LITR") static consteval FourCC fourcc_from_cstr(const char(&a)[5]) noexcept diff --git a/Navmesher/esx_reader_impl.hpp b/Navmesher/esx_reader_impl.hpp index 24ba0cc..e6773b0 100644 --- a/Navmesher/esx_reader_impl.hpp +++ b/Navmesher/esx_reader_impl.hpp @@ -10,8 +10,7 @@ namespace esxr { // static constexpr auto esx_endianness = std::endian::little; -static constexpr std::streamsize header_size = 24; -static constexpr bool same_endianness = std::endian::native == esx_endianness; +static constexpr std::size_t header_size = 24; template concept packed = sizeof(T) == N; @@ -19,8 +18,11 @@ concept packed = sizeof(T) == N; template concept packed_header = packed(header_size)>; +template +concept directly_serializable = (std::endian::native == e) && std::is_trivially_copyable_v && std::is_standard_layout_v; + template -concept directly_readable_header = packed_header && same_endianness; +concept directly_readable_header = packed_header && directly_serializable; constexpr bool directly_readable = directly_readable_header && directly_readable_header && directly_readable_header; diff --git a/Navmesher/main.cpp b/Navmesher/main.cpp index 1b768cc..f88034f 100644 --- a/Navmesher/main.cpp +++ b/Navmesher/main.cpp @@ -4,36 +4,28 @@ #include #include -static constexpr auto esm_name = "Skyrim.esm"; +static constexpr auto esx_name = "Skyrim.esm"; -/* Propagated Exceptions: - * - std::bad_alloc - * - std::filesystem::filesystem_error - * - std::ios_base::failure - */ -[[nodiscard]] static std::ifstream open_esm(void) + +[[nodiscard]] std::filesystem::path esm_path(void) { - std::filesystem::path cwd = std::filesystem::current_path(); - auto esm_path = cwd / esm_name; - auto esm_fs = std::ifstream(esm_path, std::ios::binary); + auto cwd = std::filesystem::current_path(); + return cwd / esx_name; +} + +[[nodiscard]] static std::ifstream open_esx(std::filesystem::path path) +{ + auto esm_fs = std::ifstream(path, std::ios::binary); if (esm_fs.fail()) throw std::ios_base::failure("Could not open the esm file for reading."); return esm_fs; } -void print_type(esxr::FourCC fcc) -{ - auto type = esxr::fourcc_to_record_type(fcc).value(); - auto name = esxr::record_type_to_name(type); - std::cout << name.value() << '\n'; -} - int main() { - auto esm_fs = open_esm(); - auto header_variant = esxr::read_header(esm_fs); - std::visit(utility::overloaded { - [](const esxr::RecordHeader &h) { print_type(h.type); }, - [](const esxr::GroupHeader& h) { print_type(h.grup); }, - }, header_variant); + auto path = esm_path(); + auto size = std::filesystem::file_size(path); + auto stream = open_esx(path); + auto root = esxr::read_esx(stream, size); + return 0; }