Files
Navmesher/Navmesher/esx_reader.hpp

200 lines
5.5 KiB
C++

#ifndef ESX_READER_H
#define ESX_READER_H
#include <memory>
#include <variant>
#include <array>
#include <string_view>
#include <compare>
#include <vector>
#include <type_traits>
#include <fstream>
#include "utility.hpp"
namespace esxr {
//
// Forward declarations
//
class GroupNode;
class RecordNode;
//
// Constants
//
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;
//
// Enumerations
//
// This is defined by the file spec, do not change unless the file spec changes.
enum class GroupType : int32_t {
Top = 0,
WorldChildren = 1,
InteriorCellBlock = 2,
InteriorCellSubBlock = 3,
ExteriorCellBlock = 4,
ExteriorCellSubBlock = 5,
CellChildren = 6,
TopicChildren = 7,
CellPersistentChildren = 8,
CellTemporaryChildren = 9,
};
enum class RecordType {
AACT, ACHR, ACTI, ADDN, ALCH, AMMO, ANIO, APPA, ARMA, ARMO, ARTO, ASPC,
ASTP, AVIF, BOOK, BPTD, CAMS, CELL, CLAS, CLDC, CLFM, CLMT, COBJ, COLL,
CONT, CPTH, CSTY, DEBR, DIAL, DLBR, DLVW, DOBJ, DOOR, DUAL, ECZN, EFSH,
ENCH, EQUP, EXPL, EYES, FACT, FLOR, FLST, FSTP, FSTS, FURN, GLOB, GMST,
GRAS, GRUP, HAIR, HAZD, HDPT, IDLE, IDLM, IMAD, IMGS, INFO, INGR, IPCT,
IPDS, KEYM, KYWD, LAND, LCRT, LCTN, LENS, LGTM, LIGH, LSCR, LTEX, LVLI,
LVLN, LVSP, MATO, MATT, MESG, MGEF, MISC, MOVT, MSTT, MUSC, MUST, NAVI,
NAVM, NOTE, NPC_, OTFT, PACK, PARW, PBAR, PBEA, PCON, PERK, PFLA, PGRE,
PHZD, PLYR, PMIS, PROJ, PWAT, QUST, RACE, REFR, REGN, RELA, REVB, RFCT,
RGDL, SCEN, SCOL, SCPT, SCRL, SHOU, SLGM, SMBN, SMEN, SMQN, SNCT, SNDR,
SOPM, SOUN, SPEL, SPGD, STAT, TACT, TES4, TREE, TXST, VOLI, VTYP, WATR,
WEAP, WOOP, WRLD, WTHR,
};
//
// Type aliases
//
using Node = std::variant<GroupNode, RecordNode>;
//
// Aggregate types
//
#pragma warning( push )
#pragma warning( disable : 4514 )
struct FourCC : utility::Store<uint32_t> {
constexpr FourCC() noexcept = default;
explicit constexpr FourCC(const char(&str)[5]) noexcept : Store{ utility::fourcc_lit_to_val(str) } { }
};
#pragma warning( pop )
struct FormID : utility::Store<uint32_t> { };
struct Timestamp : utility::Store<uint16_t> { };
struct VCInfo : utility::Store<uint16_t> { };
struct Flag {
RecordType type;
unsigned bit;
friend constexpr auto operator<=>(const Flag &, const Flag &) noexcept = default;
};
struct RefrFlag {
RecordType refr_type;
unsigned bit;
friend constexpr auto operator<=>(const RefrFlag &, const RefrFlag &) noexcept = default;
};
struct GroupHeader {
struct Coord {
int16_t y, x;
};
struct TopType : utility::Store<FourCC> { };
struct ParentWorld : utility::Store<FormID> { };
struct BlockNumber : utility::Store<int32_t> { };
struct SubBlockNumber : utility::Store<int32_t> { };
struct BlockCoord : utility::Store<Coord> { };
struct SubBlockCoord : utility::Store<Coord> { };
struct ParentCell : utility::Store<FormID> { };
struct ParentDialogue : utility::Store<FormID> { };
union GroupLabel {
TopType top_type;
ParentWorld parent_world;
BlockNumber block_number;
SubBlockNumber sub_block_number;
BlockCoord block_coord;
SubBlockCoord sub_block_coord;
ParentCell parent_cell;
ParentDialogue parent_dialogue;
};
RecordType grup;
uint32_t size;
GroupLabel label;
GroupType type;
Timestamp timestamp;
VCInfo version_control_information;
uint32_t unknown;
};
struct RecordHeader {
struct Flags : utility::Store<uint32_t> { };
struct Version : utility::Store<uint16_t> { };
RecordType type;
uint32_t size;
Flags flags;
FormID formid;
Timestamp timestamp;
VCInfo version_control_information;
Version version;
uint16_t unknown;
};
//
// Concepts
//
template <typename T, size_t N>
concept packed = sizeof(T) == N;
template <typename T>
concept is_header = std::is_same_v<T, GroupHeader> || std::is_same_v<T, RecordHeader>;
template <typename T>
concept packed_header = is_header<T> && packed<T, static_cast<std::size_t>(header_size)>;
template <typename T>
concept directly_readable_header = packed_header<T> && same_endianness;
//
// Classes
//
class GroupNode {
private:
GroupHeader m_header;
std::vector<Node> m_children;
};
class RecordNode {
private:
RecordHeader m_header;
std::vector<std::byte> data;
};
//
// Free functions
//
[[nodiscard]] std::optional<std::string_view> group_type_to_name(GroupType group_type) noexcept;
[[nodiscard]] std::optional<FourCC> record_type_to_fourcc(RecordType record_type) noexcept;
[[nodiscard]] std::optional<RecordType> fourcc_to_record_type(FourCC fourcc) noexcept;
[[nodiscard]] std::optional<std::string_view> record_type_to_name(RecordType record_type) noexcept;
[[nodiscard]] std::optional<std::string_view> flag_to_description(Flag flag) noexcept;
[[nodiscard]] std::optional<std::string_view> refr_flag_to_description(RefrFlag refr_flag) noexcept;
void read_header(std::ifstream &in, directly_readable_header auto &out)
{
auto char_ptr = reinterpret_cast<char *>(&out);
in.read(char_ptr, header_size);
}
}
#endif