200 lines
5.5 KiB
C++
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
|