Compare commits

..

2 Commits

5 changed files with 136 additions and 43 deletions

View File

@@ -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<directly_readable>(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<std::streamsize>(data_size(header));
}
[[nodiscard]] std::streamsize data_ssize(const GroupHeader& header) noexcept
{
return static_cast<std::streamsize>(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<std::streamsize>(node_size(node));
}
[[nodiscard]] std::streamsize node_ssize(const GroupNode& node) noexcept
{
return static_cast<std::streamsize>(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<char> 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<Node> 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<std::streamsize>(file_size);
while (remaining != 0) {
root.children.emplace_back(read_unknown_record(in));
remaining -= node_ssize(root.children.back());
}
return root;
}

View File

@@ -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<GroupNode, RecordNode>;
using Header = std::variant<RecordHeader, GroupHeader>;
class GroupNode {
private:
GroupHeader m_header;
std::vector<Node> m_children;
struct RootNode {
std::vector<Node> children;
};
class RecordNode {
private:
RecordHeader m_header;
std::vector<std::byte> data;
struct GroupNode {
GroupHeader header;
std::vector<Node> children;
};
struct RecordNode {
RecordHeader header;
std::vector<char> data;
};
//
@@ -159,6 +161,14 @@ private:
[[nodiscard]] std::optional<std::string_view> 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
{
char temp[4] = { a[0], a[1], a[2], a[3] };
return std::bit_cast<FourCC, char[4]>(temp);
}
}

View File

@@ -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 <typename T, size_t N>
concept packed = sizeof(T) == N;
@@ -19,8 +18,11 @@ concept packed = sizeof(T) == N;
template <typename T>
concept packed_header = packed<T, static_cast<std::size_t>(header_size)>;
template <typename T, std::endian e>
concept directly_serializable = (std::endian::native == e) && std::is_trivially_copyable_v<T> && std::is_standard_layout_v<T>;
template <typename T>
concept directly_readable_header = packed_header<T> && same_endianness;
concept directly_readable_header = packed_header<T> && directly_serializable<T, esx_endianness>;
constexpr bool directly_readable = directly_readable_header<UnknownHeader> && directly_readable_header<GroupHeader> && directly_readable_header<RecordHeader>;

View File

@@ -15,13 +15,6 @@ static constexpr std::pair<GroupType, std::string_view> group_type_name_map_buil
{GroupType::CellTemporaryChildren , "Cell Temporary Children" },
};
// Convert a compatible C string to a FourCC (e.g. "LITR")
static consteval FourCC fourcc_from_cstr(const char(&a)[5]) noexcept
{
char temp[4] = { a[0], a[1], a[2], a[3] };
return std::bit_cast<FourCC, char[4]>(temp);
}
static constexpr std::pair<RecordType, FourCC> record_type_fourcc_map_builtin[] {
{RecordType::AACT, fourcc_from_cstr("AACT")},
{RecordType::ACHR, fourcc_from_cstr("ACHR")},

View File

@@ -4,36 +4,28 @@
#include <fstream>
#include <filesystem>
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;
}