#pragma once #include "IStreamReader.hpp" #include "IStreamWriter.hpp" #include "YAMLDocReader.hpp" #include "YAMLDocWriter.hpp" #include "ChecksumsLiterals.hpp" #include namespace athena::io { struct PropId { const char* name = nullptr; uint32_t rcrc32 = 0xffffffff; uint64_t crc64 = 0x0; template constexpr T opget() const; constexpr PropId() = default; constexpr PropId(const char* name, uint32_t rcrc32, uint64_t crc64) : name(name), rcrc32(rcrc32), crc64(crc64) {} constexpr PropId(const char* name) : name(name) , rcrc32(athena::checksums::literals::rcrc32_rec(0xFFFFFFFF, name)) , crc64(athena::checksums::literals::crc64_rec(0xFFFFFFFFFFFFFFFF, name)) {} constexpr PropId(const char* name, uint32_t rcrc32) : name(name), rcrc32(rcrc32), crc64(athena::checksums::literals::crc64_rec(0xFFFFFFFFFFFFFFFF, name)) {} }; template <> constexpr uint32_t PropId::opget() const { return rcrc32; } template <> constexpr uint64_t PropId::opget() const { return crc64; } namespace literals { constexpr PropId operator"" _propid(const char* s, size_t len) { return {s}; } } // namespace literals #define AT_PROP_CASE(...) case athena::io::PropId(__VA_ARGS__).opget() #if defined(__atdna__) #define AT_OVERRIDE_RCRC32(rcrc32) __attribute__((annotate("rcrc32=" #rcrc32))) #else #define AT_OVERRIDE_RCRC32(rcrc32) #endif #if defined(__atdna__) #define AT_SPECIALIZE_PARMS(...) __attribute__((annotate("specparms=" #__VA_ARGS__))) #else #define AT_SPECIALIZE_PARMS(...) #endif enum class PropType { None, CRC32, CRC64 }; template using __IsPODType = typename std::disjunction< std::is_arithmetic>, std::is_convertible&, atVec2f&>, std::is_convertible&, atVec3f&>, std::is_convertible&, atVec4f&>, std::is_convertible&, atVec2d&>, std::is_convertible&, atVec3d&>, std::is_convertible&, atVec4d&>>; template inline constexpr bool __IsPODType_v = __IsPODType::value; template using __CastPODType = typename std::conditional_t< std::is_convertible_v&, atVec2f&>, atVec2f, std::conditional_t< std::is_convertible_v&, atVec3f&>, atVec3f, std::conditional_t< std::is_convertible_v&, atVec4f&>, atVec4f, std::conditional_t< std::is_convertible_v&, atVec2d&>, atVec2d, std::conditional_t&, atVec3d&>, atVec3d, std::conditional_t&, atVec4d&>, atVec4d, std::remove_cv_t>>>>>>; template inline uint16_t __Read16(IStreamReader& r) { return DNAE == Endian::Big ? r.readUint16Big() : r.readUint16Little(); } template inline void __Write16(IStreamWriter& w, uint16_t v) { DNAE == Endian::Big ? w.writeUint16Big(v) : w.writeUint16Little(v); } template inline uint32_t __Read32(IStreamReader& r) { return DNAE == Endian::Big ? r.readUint32Big() : r.readUint32Little(); } template inline void __Write32(IStreamWriter& w, uint32_t v) { DNAE == Endian::Big ? w.writeUint32Big(v) : w.writeUint32Little(v); } template inline uint64_t __Read64(IStreamReader& r) { return DNAE == Endian::Big ? r.readUint64Big() : r.readUint64Little(); } template inline void __Write64(IStreamWriter& w, uint64_t v) { DNAE == Endian::Big ? w.writeUint64Big(v) : w.writeUint64Little(v); } template struct BinarySize { using PropT = std::conditional_t; using StreamT = size_t; template static typename std::enable_if_t> Do(const PropId& id, T& var, StreamT& s) { if (PropOp != PropType::None) { /* Accessed via Enumerate, header */ s += 6; } using PODType = std::underlying_type_t; BinarySize::Do(id, *reinterpret_cast(&var), s); } template static typename std::enable_if_t<__IsPODType_v> Do(const PropId& id, T& var, StreamT& s) { if (PropOp != PropType::None) { /* Accessed via Enumerate, header */ s += 6; } using CastT = __CastPODType; BinarySize::Do(id, static_cast(const_cast&>(var)), s); } template static typename std::enable_if_t<__IsDNARecord_v && PropOp != PropType::None> Do(const PropId& id, T& var, StreamT& s) { /* Accessed via Enumerate, header */ s += 6; var.template Enumerate>(s); } template static typename std::enable_if_t<__IsDNARecord_v && PropOp == PropType::None> Do(const PropId& id, T& var, StreamT& s) { var.template Enumerate>(s); } template static typename std::enable_if_t> Do(const PropId& id, T& var, StreamT& s) { for (auto& v : var) BinarySize::Do, DNAE>(id, v, s); } template static void DoSize(const PropId& id, T& var, StreamT& s) { BinarySize::Do(id, var, s); } template static typename std::enable_if_t> Do(const PropId& id, std::vector& vector, const S& count, StreamT& s) { for (T& v : vector) BinarySize::Do(id, v, s); } template static typename std::enable_if_t> Do(const PropId& id, std::vector& vector, const S& count, StreamT& s) { /* libc++ specializes vector as a bitstream */ s += vector.size(); } static void Do(const PropId& id, std::unique_ptr& buf, size_t count, StreamT& s) { if (buf) s += count; } template static typename std::enable_if_t> Do(const PropId& id, T& str, StreamT& s) { s += str.size() + 1; } static void Do(const PropId& id, std::string& str, atInt32 count, StreamT& s) { if (count < 0) s += str.size() + 1; else s += count; } template static typename std::enable_if_t> Do(const PropId& id, T& str, StreamT& s) { s += str.size() * 2 + 2; } template static void Do(const PropId& id, std::wstring& str, atInt32 count, StreamT& s) { if (count < 0) s += str.size() * 2 + 2; else s += count * 2; } static void DoSeek(atInt64 amount, SeekOrigin whence, StreamT& s) { switch (whence) { case SeekOrigin::Begin: s = amount; break; case SeekOrigin::Current: s += amount; break; default: break; } } static void DoAlign(atInt64 amount, StreamT& s) { s = (s + amount - 1) / amount * amount; } }; #define __BINARY_SIZE_S(type, endian) \ template <> \ template <> \ inline void BinarySize::Do(const PropId& id, type& var, BinarySize::StreamT& s) __BINARY_SIZE_S(bool, Endian::Big) { s += 1; } __BINARY_SIZE_S(atInt8, Endian::Big) { s += 1; } __BINARY_SIZE_S(atUint8, Endian::Big) { s += 1; } __BINARY_SIZE_S(atInt16, Endian::Big) { s += 2; } __BINARY_SIZE_S(atUint16, Endian::Big) { s += 2; } __BINARY_SIZE_S(atInt32, Endian::Big) { s += 4; } __BINARY_SIZE_S(atUint32, Endian::Big) { s += 4; } __BINARY_SIZE_S(atInt64, Endian::Big) { s += 8; } __BINARY_SIZE_S(atUint64, Endian::Big) { s += 8; } __BINARY_SIZE_S(float, Endian::Big) { s += 4; } __BINARY_SIZE_S(double, Endian::Big) { s += 8; } __BINARY_SIZE_S(atVec2f, Endian::Big) { s += 8; } __BINARY_SIZE_S(atVec2d, Endian::Big) { s += 16; } __BINARY_SIZE_S(atVec3f, Endian::Big) { s += 12; } __BINARY_SIZE_S(atVec3d, Endian::Big) { s += 24; } __BINARY_SIZE_S(atVec4f, Endian::Big) { s += 16; } __BINARY_SIZE_S(atVec4d, Endian::Big) { s += 32; } __BINARY_SIZE_S(bool, Endian::Little) { s += 1; } __BINARY_SIZE_S(atInt8, Endian::Little) { s += 1; } __BINARY_SIZE_S(atUint8, Endian::Little) { s += 1; } __BINARY_SIZE_S(atInt16, Endian::Little) { s += 2; } __BINARY_SIZE_S(atUint16, Endian::Little) { s += 2; } __BINARY_SIZE_S(atInt32, Endian::Little) { s += 4; } __BINARY_SIZE_S(atUint32, Endian::Little) { s += 4; } __BINARY_SIZE_S(atInt64, Endian::Little) { s += 8; } __BINARY_SIZE_S(atUint64, Endian::Little) { s += 8; } __BINARY_SIZE_S(float, Endian::Little) { s += 4; } __BINARY_SIZE_S(double, Endian::Little) { s += 8; } __BINARY_SIZE_S(atVec2f, Endian::Little) { s += 8; } __BINARY_SIZE_S(atVec2d, Endian::Little) { s += 16; } __BINARY_SIZE_S(atVec3f, Endian::Little) { s += 12; } __BINARY_SIZE_S(atVec3d, Endian::Little) { s += 24; } __BINARY_SIZE_S(atVec4f, Endian::Little) { s += 16; } __BINARY_SIZE_S(atVec4d, Endian::Little) { s += 32; } template struct PropCount { using PropT = std::conditional_t; using StreamT = size_t; template static void Do(const PropId& id, T& var, StreamT& s) { /* Only reports one level of properties */ s += 1; } template static void DoSize(const PropId& id, T& var, StreamT& s) { PropCount::Do(id, var, s); } template static void Do(const PropId& id, std::vector& vector, const S& count, StreamT& s) { /* Only reports one level of properties */ s += 1; } static void Do(const PropId& id, std::unique_ptr& buf, size_t count, StreamT& s) { /* Only reports one level of properties */ s += 1; } template static typename std::enable_if_t> Do(const PropId& id, T& str, StreamT& s) { /* Only reports one level of properties */ s += 1; } static void Do(const PropId& id, std::string& str, atInt32 count, StreamT& s) { /* Only reports one level of properties */ s += 1; } template static typename std::enable_if_t> Do(const PropId& id, T& str, StreamT& s) { /* Only reports one level of properties */ s += 1; } template static void Do(const PropId& id, std::wstring& str, atInt32 count, StreamT& s) { /* Only reports one level of properties */ s += 1; } static void DoSeek(atInt64 amount, SeekOrigin whence, StreamT& s) {} static void DoAlign(atInt64 amount, StreamT& s) {} }; template struct Read { using PropT = std::conditional_t; using StreamT = IStreamReader; template static typename std::enable_if_t> Do(const PropId& id, T& var, StreamT& r) { using PODType = std::underlying_type_t; Read::Do(id, *reinterpret_cast(&var), r); } template static typename std::enable_if_t<__IsPODType_v> Do(const PropId& id, T& var, StreamT& r) { using CastT = __CastPODType; Read::Do(id, static_cast(var), r); } template static typename std::enable_if_t<__IsDNARecord() && PropOp == PropType::None> Do(const PropId& id, T& var, StreamT& r) { var.template Enumerate>(r); } template static typename std::enable_if_t<__IsDNARecord() && PropOp != PropType::None> Do(const PropId& id, T& var, StreamT& r) { /* Accessed via Lookup, no header */ atUint16 propCount = __Read16(r); for (atUint32 i = 0; i < propCount; ++i) { atUint64 hash; if (PropOp == PropType::CRC64) hash = __Read64(r); else hash = __Read32(r); atInt64 size = __Read16(r); atInt64 start = r.position(); var.template Lookup>(hash, r); atInt64 actualRead = r.position() - start; if (actualRead != size) r.seek(size - actualRead); } } template static typename std::enable_if_t> Do(const PropId& id, T& var, StreamT& s) { for (auto& v : var) Read::Do, DNAE>(id, v, s); } template static void DoSize(const PropId& id, T& var, StreamT& s) { Read::Do(id, var, s); } template static typename std::enable_if_t> Do(const PropId& id, std::vector& vector, const S& count, StreamT& r) { vector.clear(); vector.reserve(count); for (size_t i = 0; i < static_cast(count); ++i) { vector.emplace_back(); Read::Do(id, vector.back(), r); } } template static typename std::enable_if_t> Do(const PropId& id, std::vector& vector, const S& count, StreamT& r) { /* libc++ specializes vector as a bitstream */ vector.clear(); vector.reserve(count); for (size_t i = 0; i < count; ++i) vector.push_back(r.readBool()); } static void Do(const PropId& id, std::unique_ptr& buf, size_t count, StreamT& r) { buf.reset(new atUint8[count]); r.readUBytesToBuf(buf.get(), count); } template static typename std::enable_if_t> Do(const PropId& id, T& str, StreamT& r) { str = r.readString(); } static void Do(const PropId& id, std::string& str, atInt32 count, StreamT& r) { str = r.readString(count); } template static typename std::enable_if_t> Do(const PropId& id, T& str, StreamT& r) { Read::Do(id, str, r); } template static void Do(const PropId& id, std::wstring& str, atInt32 count, StreamT& r) { Read::Do(id, str, count, r); } static void DoSeek(atInt64 amount, SeekOrigin whence, StreamT& r) { r.seek(amount, whence); } static void DoAlign(atInt64 amount, StreamT& r) { r.seek((r.position() + amount - 1) / amount * amount, athena::Begin); } }; #define __READ_S(type, endian) \ template <> \ template <> \ inline void Read::Do(const PropId& id, type& var, Read::StreamT& r) #define __READ_WSTR_S(endian) \ template <> \ template <> \ inline void Read::Do(const PropId& id, std::wstring& str, Read::StreamT& r) #define __READ_WSTRC_S(endian) \ template <> \ template <> \ inline void Read::Do(const PropId& id, std::wstring& str, atInt32 count, Read::StreamT& r) __READ_S(bool, Endian::Big) { var = r.readBool(); } __READ_S(atInt8, Endian::Big) { var = r.readByte(); } __READ_S(atUint8, Endian::Big) { var = r.readUByte(); } __READ_S(atInt16, Endian::Big) { var = r.readInt16Big(); } __READ_S(atUint16, Endian::Big) { var = r.readUint16Big(); } __READ_S(atInt32, Endian::Big) { var = r.readInt32Big(); } __READ_S(atUint32, Endian::Big) { var = r.readUint32Big(); } __READ_S(atInt64, Endian::Big) { var = r.readInt64Big(); } __READ_S(atUint64, Endian::Big) { var = r.readUint64Big(); } __READ_S(float, Endian::Big) { var = r.readFloatBig(); } __READ_S(double, Endian::Big) { var = r.readDoubleBig(); } __READ_S(atVec2f, Endian::Big) { var = r.readVec2fBig(); } __READ_S(atVec2d, Endian::Big) { var = r.readVec2dBig(); } __READ_S(atVec3f, Endian::Big) { var = r.readVec3fBig(); } __READ_S(atVec3d, Endian::Big) { var = r.readVec3dBig(); } __READ_S(atVec4f, Endian::Big) { var = r.readVec4fBig(); } __READ_S(atVec4d, Endian::Big) { var = r.readVec4dBig(); } __READ_WSTR_S(Endian::Big) { str = r.readWStringBig(); } __READ_WSTRC_S(Endian::Big) { str = r.readWStringBig(count); } __READ_S(bool, Endian::Little) { var = r.readBool(); } __READ_S(atInt8, Endian::Little) { var = r.readByte(); } __READ_S(atUint8, Endian::Little) { var = r.readUByte(); } __READ_S(atInt16, Endian::Little) { var = r.readInt16Little(); } __READ_S(atUint16, Endian::Little) { var = r.readUint16Little(); } __READ_S(atInt32, Endian::Little) { var = r.readInt32Little(); } __READ_S(atUint32, Endian::Little) { var = r.readUint32Little(); } __READ_S(atInt64, Endian::Little) { var = r.readInt64Little(); } __READ_S(atUint64, Endian::Little) { var = r.readUint64Little(); } __READ_S(float, Endian::Little) { var = r.readFloatLittle(); } __READ_S(double, Endian::Little) { var = r.readDoubleLittle(); } __READ_S(atVec2f, Endian::Little) { var = r.readVec2fLittle(); } __READ_S(atVec2d, Endian::Little) { var = r.readVec2dLittle(); } __READ_S(atVec3f, Endian::Little) { var = r.readVec3fLittle(); } __READ_S(atVec3d, Endian::Little) { var = r.readVec3dLittle(); } __READ_S(atVec4f, Endian::Little) { var = r.readVec4fLittle(); } __READ_S(atVec4d, Endian::Little) { var = r.readVec4dLittle(); } __READ_WSTR_S(Endian::Little) { str = r.readWStringLittle(); } __READ_WSTRC_S(Endian::Little) { str = r.readWStringLittle(count); } template struct Write { using PropT = std::conditional_t; using StreamT = IStreamWriter; template static typename std::enable_if_t> Do(const PropId& id, T& var, StreamT& w) { if (PropOp != PropType::None) { /* Accessed via Enumerate, header */ if (PropOp == PropType::CRC64) __Write64(w, id.crc64); else __Write32(w, id.rcrc32); size_t binarySize = 0; BinarySize::Do(id, var, binarySize); DNAE == Endian::Big ? w.writeUint16Big(atUint16(binarySize)) : w.writeUint16Little(atUint16(binarySize)); } using PODType = std::underlying_type_t; Write::Do(id, *reinterpret_cast(&var), w); } template static typename std::enable_if_t<__IsPODType_v> Do(const PropId& id, T& var, StreamT& w) { using CastT = __CastPODType; if (PropOp != PropType::None) { /* Accessed via Enumerate, header */ if (PropOp == PropType::CRC64) __Write64(w, id.crc64); else __Write32(w, id.rcrc32); size_t binarySize = 0; BinarySize::Do(id, static_cast(const_cast&>(var)), binarySize); __Write16(w, atUint16(binarySize)); } Write::Do(id, static_cast(const_cast&>(var)), w); } template static typename std::enable_if_t<__IsDNARecord() && PropOp != PropType::None> Do(const PropId& id, T& var, StreamT& w) { /* Accessed via Enumerate, header */ if (PropOp == PropType::CRC64) __Write64(w, id.crc64); else __Write32(w, id.rcrc32); size_t binarySize = 0; var.template Enumerate>(binarySize); __Write16(w, atUint16(binarySize)); size_t propCount = 0; var.template Enumerate>(propCount); __Write16(w, atUint16(propCount)); var.template Enumerate>(w); } template static typename std::enable_if_t<__IsDNARecord() && PropOp == PropType::None> Do(const PropId& id, T& var, StreamT& w) { var.template Enumerate>(w); } template static typename std::enable_if_t> Do(const PropId& id, T& var, StreamT& s) { for (auto& v : var) Write::Do, DNAE>(id, v, s); } template static void DoSize(const PropId& id, T& var, StreamT& s) { Write::Do(id, var, s); } template static typename std::enable_if_t> Do(const PropId& id, std::vector& vector, const S& count, StreamT& w) { for (T& v : vector) Write::Do(id, v, w); } template static typename std::enable_if_t> Do(const PropId& id, std::vector& vector, const S& count, StreamT& w) { /* libc++ specializes vector as a bitstream */ for (const T& v : vector) w.writeBool(v); } static void Do(const PropId& id, std::unique_ptr& buf, size_t count, StreamT& w) { if (buf) w.writeUBytes(buf.get(), count); } template static typename std::enable_if_t> Do(const PropId& id, std::string& str, StreamT& w) { w.writeString(str); } static void Do(const PropId& id, std::string& str, atInt32 count, StreamT& w) { w.writeString(str, count); } template static typename std::enable_if_t> Do(const PropId& id, std::wstring& str, StreamT& w) { Write::Do(id, str, w); } template static void Do(const PropId& id, std::wstring& str, atInt32 count, StreamT& w) { Write::Do(id, str, count, w); } static void DoSeek(atInt64 amount, SeekOrigin whence, StreamT& w) { w.seek(amount, whence); } static void DoAlign(atInt64 amount, StreamT& w) { w.writeZeroTo((w.position() + amount - 1) / amount * amount); } }; #define __WRITE_S(type, endian) \ template <> \ template <> \ inline void Write::Do(const PropId& id, type& var, Write::StreamT& w) #define __WRITE_WSTR_S(endian) \ template <> \ template <> \ inline void Write::Do(const PropId& id, std::wstring& str, Write::StreamT& w) #define __WRITE_WSTRC_S(endian) \ template <> \ template <> \ inline void Write::Do(const PropId& id, std::wstring& str, atInt32 count, Write::StreamT& w) __WRITE_S(bool, Endian::Big) { w.writeBool(var); } __WRITE_S(atInt8, Endian::Big) { w.writeByte(var); } __WRITE_S(atUint8, Endian::Big) { w.writeUByte(var); } __WRITE_S(atInt16, Endian::Big) { w.writeInt16Big(var); } __WRITE_S(atUint16, Endian::Big) { w.writeUint16Big(var); } __WRITE_S(atInt32, Endian::Big) { w.writeInt32Big(var); } __WRITE_S(atUint32, Endian::Big) { w.writeUint32Big(var); } __WRITE_S(atInt64, Endian::Big) { w.writeInt64Big(var); } __WRITE_S(atUint64, Endian::Big) { w.writeUint64Big(var); } __WRITE_S(float, Endian::Big) { w.writeFloatBig(var); } __WRITE_S(double, Endian::Big) { w.writeDoubleBig(var); } __WRITE_S(atVec2f, Endian::Big) { w.writeVec2fBig(var); } __WRITE_S(atVec2d, Endian::Big) { w.writeVec2dBig(var); } __WRITE_S(atVec3f, Endian::Big) { w.writeVec3fBig(var); } __WRITE_S(atVec3d, Endian::Big) { w.writeVec3dBig(var); } __WRITE_S(atVec4f, Endian::Big) { w.writeVec4fBig(var); } __WRITE_S(atVec4d, Endian::Big) { w.writeVec4dBig(var); } __WRITE_WSTR_S(Endian::Big) { w.writeWStringBig(str); } __WRITE_WSTRC_S(Endian::Big) { w.writeWStringBig(str, count); } __WRITE_S(bool, Endian::Little) { w.writeBool(var); } __WRITE_S(atInt8, Endian::Little) { w.writeByte(var); } __WRITE_S(atUint8, Endian::Little) { w.writeUByte(var); } __WRITE_S(atInt16, Endian::Little) { w.writeInt16Little(var); } __WRITE_S(atUint16, Endian::Little) { w.writeUint16Little(var); } __WRITE_S(atInt32, Endian::Little) { w.writeInt32Little(var); } __WRITE_S(atUint32, Endian::Little) { w.writeUint32Little(var); } __WRITE_S(atInt64, Endian::Little) { w.writeInt64Little(var); } __WRITE_S(atUint64, Endian::Little) { w.writeUint64Little(var); } __WRITE_S(float, Endian::Little) { w.writeFloatLittle(var); } __WRITE_S(double, Endian::Little) { w.writeDoubleLittle(var); } __WRITE_S(atVec2f, Endian::Little) { w.writeVec2fLittle(var); } __WRITE_S(atVec2d, Endian::Little) { w.writeVec2dLittle(var); } __WRITE_S(atVec3f, Endian::Little) { w.writeVec3fLittle(var); } __WRITE_S(atVec3d, Endian::Little) { w.writeVec3dLittle(var); } __WRITE_S(atVec4f, Endian::Little) { w.writeVec4fLittle(var); } __WRITE_S(atVec4d, Endian::Little) { w.writeVec4dLittle(var); } __WRITE_WSTR_S(Endian::Little) { w.writeWStringLittle(str); } __WRITE_WSTRC_S(Endian::Little) { w.writeWStringLittle(str, count); } template struct ReadYaml { using PropT = std::conditional_t; using StreamT = YAMLDocReader; template static typename std::enable_if_t> Do(const PropId& id, T& var, StreamT& r) { using PODType = std::underlying_type_t; ReadYaml::Do(id, *reinterpret_cast(&var), r); } template static typename std::enable_if_t<__IsPODType_v> Do(const PropId& id, T& var, StreamT& r) { using CastT = __CastPODType; ReadYaml::Do(id, static_cast(var), r); } template static typename std::enable_if_t<__IsDNARecord_v> Do(const PropId& id, T& var, StreamT& r) { if (auto rec = r.enterSubRecord(id.name)) var.template Enumerate>(r); } template static typename std::enable_if_t> Do(const PropId& id, T& var, StreamT& r) { size_t _count; if (auto __v = r.enterSubVector(id.name, _count)) for (size_t i = 0; i < _count && i < std::extent_v; ++i) ReadYaml::Do, DNAE>({}, var[i], r); } template static void DoSize(const PropId& id, T& var, StreamT& s) { /* Squelch size field access */ } template static typename std::enable_if_t> Do(const PropId& id, std::vector& vector, const S& count, StreamT& r) { size_t _count; vector.clear(); if (auto __v = r.enterSubVector(id.name, _count)) { vector.reserve(_count); for (size_t i = 0; i < _count; ++i) { vector.emplace_back(); ReadYaml::Do({}, vector.back(), r); } } /* Horrible reference abuse (but it works) */ const_cast(count) = vector.size(); } template static typename std::enable_if_t> Do(const PropId& id, std::vector& vector, const S& count, StreamT& r) { /* libc++ specializes vector as a bitstream */ size_t _count; vector.clear(); if (auto __v = r.enterSubVector(id.name, _count)) { vector.reserve(_count); for (size_t i = 0; i < _count; ++i) vector.push_back(r.readBool(nullptr)); } /* Horrible reference abuse (but it works) */ const_cast(count) = vector.size(); } static void Do(const PropId& id, std::unique_ptr& buf, size_t count, StreamT& r) { buf = r.readUBytes(id.name); } template static typename std::enable_if_t> Do(const PropId& id, T& str, StreamT& r) { str = r.readString(id.name); } static void Do(const PropId& id, std::string& str, atInt32 count, StreamT& r) { str = r.readString(id.name); } template static typename std::enable_if_t> Do(const PropId& id, T& str, StreamT& r) { str = r.readWString(id.name); } template static void Do(const PropId& id, std::wstring& str, atInt32 count, StreamT& r) { str = r.readWString(id.name); } static void DoSeek(atInt64 amount, SeekOrigin whence, StreamT& r) {} static void DoAlign(atInt64 amount, StreamT& r) {} }; #define __READ_YAML_S(type, endian) \ template <> \ template <> \ inline void ReadYaml::Do(const PropId& id, type& var, ReadYaml::StreamT& r) __READ_YAML_S(bool, Endian::Big) { var = r.readBool(id.name); } __READ_YAML_S(atInt8, Endian::Big) { var = r.readByte(id.name); } __READ_YAML_S(atUint8, Endian::Big) { var = r.readUByte(id.name); } __READ_YAML_S(atInt16, Endian::Big) { var = r.readInt16(id.name); } __READ_YAML_S(atUint16, Endian::Big) { var = r.readUint16(id.name); } __READ_YAML_S(atInt32, Endian::Big) { var = r.readInt32(id.name); } __READ_YAML_S(atUint32, Endian::Big) { var = r.readUint32(id.name); } __READ_YAML_S(atInt64, Endian::Big) { var = r.readInt64(id.name); } __READ_YAML_S(atUint64, Endian::Big) { var = r.readUint64(id.name); } __READ_YAML_S(float, Endian::Big) { var = r.readFloat(id.name); } __READ_YAML_S(double, Endian::Big) { var = r.readDouble(id.name); } __READ_YAML_S(atVec2f, Endian::Big) { var = r.readVec2f(id.name); } __READ_YAML_S(atVec2d, Endian::Big) { var = r.readVec2d(id.name); } __READ_YAML_S(atVec3f, Endian::Big) { var = r.readVec3f(id.name); } __READ_YAML_S(atVec3d, Endian::Big) { var = r.readVec3d(id.name); } __READ_YAML_S(atVec4f, Endian::Big) { var = r.readVec4f(id.name); } __READ_YAML_S(atVec4d, Endian::Big) { var = r.readVec4d(id.name); } __READ_YAML_S(bool, Endian::Little) { var = r.readBool(id.name); } __READ_YAML_S(atInt8, Endian::Little) { var = r.readByte(id.name); } __READ_YAML_S(atUint8, Endian::Little) { var = r.readUByte(id.name); } __READ_YAML_S(atInt16, Endian::Little) { var = r.readInt16(id.name); } __READ_YAML_S(atUint16, Endian::Little) { var = r.readUint16(id.name); } __READ_YAML_S(atInt32, Endian::Little) { var = r.readInt32(id.name); } __READ_YAML_S(atUint32, Endian::Little) { var = r.readUint32(id.name); } __READ_YAML_S(atInt64, Endian::Little) { var = r.readInt64(id.name); } __READ_YAML_S(atUint64, Endian::Little) { var = r.readUint64(id.name); } __READ_YAML_S(float, Endian::Little) { var = r.readFloat(id.name); } __READ_YAML_S(double, Endian::Little) { var = r.readDouble(id.name); } __READ_YAML_S(atVec2f, Endian::Little) { var = r.readVec2f(id.name); } __READ_YAML_S(atVec2d, Endian::Little) { var = r.readVec2d(id.name); } __READ_YAML_S(atVec3f, Endian::Little) { var = r.readVec3f(id.name); } __READ_YAML_S(atVec3d, Endian::Little) { var = r.readVec3d(id.name); } __READ_YAML_S(atVec4f, Endian::Little) { var = r.readVec4f(id.name); } __READ_YAML_S(atVec4d, Endian::Little) { var = r.readVec4d(id.name); } template struct WriteYaml { using PropT = std::conditional_t; using StreamT = YAMLDocWriter; template static typename std::enable_if_t> Do(const PropId& id, T& var, StreamT& w) { using PODType = std::underlying_type_t; WriteYaml::Do(id, *reinterpret_cast(&var), w); } template static typename std::enable_if_t<__IsPODType_v> Do(const PropId& id, T& var, StreamT& w) { using CastT = __CastPODType; WriteYaml::Do(id, static_cast(const_cast&>(var)), w); } template static typename std::enable_if_t<__IsDNARecord_v> Do(const PropId& id, T& var, StreamT& w) { if (auto rec = w.enterSubRecord(id.name)) var.template Enumerate>(w); } template static typename std::enable_if_t> Do(const PropId& id, T& var, StreamT& w) { if (auto __v = w.enterSubVector(id.name)) for (auto& v : var) WriteYaml::Do, DNAE>({}, v, w); } template static void DoSize(const PropId& id, T& var, StreamT& s) { /* Squelch size field access */ } template static typename std::enable_if_t> Do(const PropId& id, std::vector& vector, const S& count, StreamT& w) { if (auto __v = w.enterSubVector(id.name)) for (T& v : vector) WriteYaml::Do(id, v, w); } template static typename std::enable_if_t> Do(const PropId& id, std::vector& vector, const S& count, StreamT& w) { /* libc++ specializes vector as a bitstream */ if (auto __v = w.enterSubVector(id.name)) for (const T& v : vector) w.writeBool(nullptr, v); } static void Do(const PropId& id, std::unique_ptr& buf, size_t count, StreamT& w) { w.writeUBytes(id.name, buf, count); } template static typename std::enable_if_t> Do(const PropId& id, T& str, StreamT& w) { w.writeString(id.name, str); } static void Do(const PropId& id, std::string& str, atInt32 count, StreamT& w) { w.writeString(id.name, str); } template static typename std::enable_if_t> Do(const PropId& id, T& str, StreamT& w) { w.writeWString(id.name, str); } template static void Do(const PropId& id, std::wstring& str, atInt32 count, StreamT& w) { w.writeWString(id.name, str); } static void DoSeek(atInt64 amount, SeekOrigin whence, StreamT& w) {} static void DoAlign(atInt64 amount, StreamT& w) {} }; #define __WRITE_YAML_S(type, endian) \ template <> \ template <> \ inline void WriteYaml::Do(const PropId& id, type& var, WriteYaml::StreamT& w) __WRITE_YAML_S(bool, Endian::Big) { w.writeBool(id.name, var); } __WRITE_YAML_S(atInt8, Endian::Big) { w.writeByte(id.name, var); } __WRITE_YAML_S(atUint8, Endian::Big) { w.writeUByte(id.name, var); } __WRITE_YAML_S(atInt16, Endian::Big) { w.writeInt16(id.name, var); } __WRITE_YAML_S(atUint16, Endian::Big) { w.writeUint16(id.name, var); } __WRITE_YAML_S(atInt32, Endian::Big) { w.writeInt32(id.name, var); } __WRITE_YAML_S(atUint32, Endian::Big) { w.writeUint32(id.name, var); } __WRITE_YAML_S(atInt64, Endian::Big) { w.writeInt64(id.name, var); } __WRITE_YAML_S(atUint64, Endian::Big) { w.writeUint64(id.name, var); } __WRITE_YAML_S(float, Endian::Big) { w.writeFloat(id.name, var); } __WRITE_YAML_S(double, Endian::Big) { w.writeDouble(id.name, var); } __WRITE_YAML_S(atVec2f, Endian::Big) { w.writeVec2f(id.name, var); } __WRITE_YAML_S(atVec2d, Endian::Big) { w.writeVec2d(id.name, var); } __WRITE_YAML_S(atVec3f, Endian::Big) { w.writeVec3f(id.name, var); } __WRITE_YAML_S(atVec3d, Endian::Big) { w.writeVec3d(id.name, var); } __WRITE_YAML_S(atVec4f, Endian::Big) { w.writeVec4f(id.name, var); } __WRITE_YAML_S(atVec4d, Endian::Big) { w.writeVec4d(id.name, var); } __WRITE_YAML_S(bool, Endian::Little) { w.writeBool(id.name, var); } __WRITE_YAML_S(atInt8, Endian::Little) { w.writeByte(id.name, var); } __WRITE_YAML_S(atUint8, Endian::Little) { w.writeUByte(id.name, var); } __WRITE_YAML_S(atInt16, Endian::Little) { w.writeInt16(id.name, var); } __WRITE_YAML_S(atUint16, Endian::Little) { w.writeUint16(id.name, var); } __WRITE_YAML_S(atInt32, Endian::Little) { w.writeInt32(id.name, var); } __WRITE_YAML_S(atUint32, Endian::Little) { w.writeUint32(id.name, var); } __WRITE_YAML_S(atInt64, Endian::Little) { w.writeInt64(id.name, var); } __WRITE_YAML_S(atUint64, Endian::Little) { w.writeUint64(id.name, var); } __WRITE_YAML_S(float, Endian::Little) { w.writeFloat(id.name, var); } __WRITE_YAML_S(double, Endian::Little) { w.writeDouble(id.name, var); } __WRITE_YAML_S(atVec2f, Endian::Little) { w.writeVec2f(id.name, var); } __WRITE_YAML_S(atVec2d, Endian::Little) { w.writeVec2d(id.name, var); } __WRITE_YAML_S(atVec3f, Endian::Little) { w.writeVec3f(id.name, var); } __WRITE_YAML_S(atVec3d, Endian::Little) { w.writeVec3d(id.name, var); } __WRITE_YAML_S(atVec4f, Endian::Little) { w.writeVec4f(id.name, var); } __WRITE_YAML_S(atVec4d, Endian::Little) { w.writeVec4d(id.name, var); } template inline void __Do(const PropId& id, T& var, typename Op::StreamT& s) { Op::template Do(id, var, s); } template inline void __DoSize(const PropId& id, T& var, typename Op::StreamT& s) { Op::template DoSize(id, var, s); } template inline void __Do(const PropId& id, std::vector& vector, const S& count, typename Op::StreamT& s) { Op::template Do(id, vector, count, s); } template inline void __Do(const PropId& id, std::unique_ptr& buf, size_t count, typename Op::StreamT& s) { Op::Do(id, buf, count, s); } template inline void __Do(const PropId& id, std::string& str, atInt32 count, typename Op::StreamT& s) { Op::Do(id, str, count, s); } template inline void __Do(const PropId& id, std::wstring& str, atInt32 count, typename Op::StreamT& s) { Op::template Do(id, str, count, s); } template inline void __DoSeek(atInt64 delta, athena::SeekOrigin whence, typename Op::StreamT& s) { Op::DoSeek(delta, whence, s); } template inline void __DoAlign(atInt64 amount, typename Op::StreamT& s) { Op::DoAlign(amount, s); } template inline void __Read(T& obj, athena::io::IStreamReader& r) { __Do, T, T::DNAEndian>({}, obj, r); } template inline void __Write(const T& obj, athena::io::IStreamWriter& w) { __Do, T, T::DNAEndian>({}, const_cast(obj), w); } template inline void __BinarySize(const T& obj, size_t& s) { __Do, T, T::DNAEndian>({}, const_cast(obj), s); } template inline void __PropCount(const T& obj, size_t& s) { const_cast(obj).template Enumerate>(s); } template inline void __ReadYaml(T& obj, athena::io::YAMLDocReader& r) { obj.template Enumerate>(r); } template inline void __WriteYaml(const T& obj, athena::io::YAMLDocWriter& w) { const_cast(obj).template Enumerate>(w); } template inline void __ReadProp(T& obj, athena::io::IStreamReader& r) { /* Read root 0xffffffff hash (hashed empty string) */ T::DNAEndian == Endian::Big ? r.readUint32Big() : r.readUint32Little(); atInt64 size = T::DNAEndian == Endian::Big ? r.readUint16Big() : r.readUint16Little(); atInt64 start = r.position(); __Do, T, T::DNAEndian>({}, obj, r); atInt64 actualRead = r.position() - start; if (actualRead != size) r.seek(size - actualRead); } template inline void __WriteProp(const T& obj, athena::io::IStreamWriter& w) { __Do, T, T::DNAEndian>({}, const_cast(obj), w); } template inline void __BinarySizeProp(const T& obj, size_t& s) { __Do, T, T::DNAEndian>({}, const_cast(obj), s); } template inline void __ReadProp64(T& obj, athena::io::IStreamReader& r) { /* Read root 0x0 hash (hashed empty string) */ T::DNAEndian == Endian::Big ? r.readUint64Big() : r.readUint64Little(); atInt64 size = T::DNAEndian == Endian::Big ? r.readUint16Big() : r.readUint16Little(); atInt64 start = r.position(); __Do, T, T::DNAEndian>({}, obj, r); atInt64 actualRead = r.position() - start; if (actualRead != size) r.seek(size - actualRead); } template inline void __WriteProp64(const T& obj, athena::io::IStreamWriter& w) { __Do, T, T::DNAEndian>({}, const_cast(obj), w); } template inline void __BinarySizeProp64(const T& obj, size_t& s) { __Do, T, T::DNAEndian>({}, const_cast(obj), s); } } // namespace athena::io #define AT_DECL_DNA \ template \ void Do(const athena::io::PropId& _id, T& var, typename Op::StreamT& s) { \ athena::io::__Do(_id, var, s); \ } \ template \ void DoSize(const athena::io::PropId& _id, T& var, typename Op::StreamT& s) { \ athena::io::__DoSize(_id, var, s); \ } \ template \ void Do(const athena::io::PropId& _id, std::vector& var, const S& count, typename Op::StreamT& s) { \ athena::io::__Do(_id, var, count, s); \ } \ template \ void Do(const athena::io::PropId& _id, std::unique_ptr& buf, size_t count, typename Op::StreamT& s) { \ athena::io::__Do(_id, buf, count, s); \ } \ template \ void Do(const athena::io::PropId& _id, std::string& str, atInt32 count, typename Op::StreamT& s) { \ athena::io::__Do(_id, str, count, s); \ } \ template \ void Do(const athena::io::PropId& _id, std::wstring& str, atInt32 count, typename Op::StreamT& s) { \ athena::io::__Do(_id, str, count, s); \ } \ template \ void DoSeek(atInt64 delta, athena::SeekOrigin whence, typename Op::StreamT& s) { \ athena::io::__DoSeek(delta, whence, s); \ } \ template \ void DoAlign(atInt64 amount, typename Op::StreamT& s) { \ athena::io::__DoAlign(amount, s); \ } \ template \ void Enumerate(typename Op::StreamT& s); \ static const char* DNAType(); \ void read(athena::io::IStreamReader& r) { athena::io::__Read(*this, r); } \ void write(athena::io::IStreamWriter& w) const { athena::io::__Write(*this, w); } \ void binarySize(size_t& s) const { athena::io::__BinarySize(*this, s); } #define AT_DECL_DNA_YAML \ AT_DECL_DNA \ void read(athena::io::YAMLDocReader& r) { athena::io::__ReadYaml(*this, r); } \ void write(athena::io::YAMLDocWriter& w) const { athena::io::__WriteYaml(*this, w); } #define AT_DECL_EXPLICIT_DNA \ AT_DECL_DNA \ Delete __d; #define AT_DECL_EXPLICIT_DNA_YAML \ AT_DECL_DNA_YAML \ Delete __d; #define AT_DECL_DNAV \ const char* DNATypeV() const { return DNAType(); } #define AT_SPECIALIZE_DNA(...) \ template void __VA_ARGS__::Enumerate>( \ athena::io::Read::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::Write::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::BinarySize::StreamT & s); #define AT_SPECIALIZE_DNA_YAML(...) \ AT_SPECIALIZE_DNA(__VA_ARGS__) \ template void __VA_ARGS__::Enumerate>( \ athena::io::ReadYaml::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::WriteYaml::StreamT & s); #define AT_DECL_PROPDNA \ template \ void Do(const athena::io::PropId& _id, T& var, typename Op::StreamT& s) { \ athena::io::__Do(_id, var, s); \ } \ template \ void DoSize(const athena::io::PropId& _id, T& var, typename Op::StreamT& s) { \ athena::io::__DoSize(_id, var, s); \ } \ template \ void Do(const athena::io::PropId& _id, std::vector& var, const S& count, typename Op::StreamT& s) { \ athena::io::__Do(_id, var, count, s); \ } \ template \ void Do(const athena::io::PropId& _id, std::unique_ptr& buf, size_t count, typename Op::StreamT& s) { \ athena::io::__Do(_id, buf, count, s); \ } \ template \ void Do(const athena::io::PropId& _id, std::string& str, atInt32 count, typename Op::StreamT& s) { \ athena::io::__Do(_id, str, count, s); \ } \ template \ void Do(const athena::io::PropId& _id, std::wstring& str, atInt32 count, typename Op::StreamT& s) { \ athena::io::__Do(_id, str, count, s); \ } \ template \ void DoSeek(atInt64 delta, athena::SeekOrigin whence, typename Op::StreamT& s) { \ athena::io::__DoSeek(delta, whence, s); \ } \ template \ void DoAlign(atInt64 amount, typename Op::StreamT& s) { \ athena::io::__DoAlign(amount, s); \ } \ template \ void Enumerate(typename Op::StreamT& s); \ template \ bool Lookup(uint64_t hash, typename Op::StreamT& s); \ static const char* DNAType(); \ void read(athena::io::IStreamReader& r) { athena::io::__Read(*this, r); } \ void write(athena::io::IStreamWriter& w) const { athena::io::__Write(*this, w); } \ void binarySize(size_t& s) const { athena::io::__BinarySize(*this, s); } \ void read(athena::io::YAMLDocReader& r) { athena::io::__ReadYaml(*this, r); } \ void write(athena::io::YAMLDocWriter& w) const { athena::io::__WriteYaml(*this, w); } \ void readProp(athena::io::IStreamReader& r) { athena::io::__ReadProp(*this, r); } \ void writeProp(athena::io::IStreamWriter& w) const { athena::io::__WriteProp(*this, w); } \ void binarySizeProp(size_t& s) const { athena::io::__BinarySizeProp(*this, s); } \ void propCount(size_t& s) const { athena::io::__PropCount(*this, s); } \ void readProp64(athena::io::IStreamReader& r) { athena::io::__ReadProp64(*this, r); } \ void writeProp64(athena::io::IStreamWriter& w) const { athena::io::__WriteProp64(*this, w); } \ void binarySizeProp64(size_t& s) const { athena::io::__BinarySizeProp64(*this, s); } #define AT_DECL_EXPLICIT_PROPDNA \ AT_DECL_PROPDNA \ Delete __d; #define AT_SPECIALIZE_PROPDNA(...) \ template void __VA_ARGS__::Enumerate>( \ athena::io::Read::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::Write::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::BinarySize::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::ReadYaml::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::WriteYaml::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::Read::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::Read::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::Write::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::Write::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::BinarySize::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::BinarySize::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::PropCount::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::PropCount::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::ReadYaml::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::ReadYaml::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::WriteYaml::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::WriteYaml::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::Read::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::Read::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::Write::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::Write::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::BinarySize::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::BinarySize::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::PropCount::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::PropCount::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::ReadYaml::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::ReadYaml::StreamT & s); \ template bool __VA_ARGS__::Lookup>( \ uint64_t hash, athena::io::WriteYaml::StreamT & s); \ template void __VA_ARGS__::Enumerate>( \ athena::io::WriteYaml::StreamT & s); #define AT_SUBDECL_DNA \ void _read(athena::io::IStreamReader& r); \ void _write(athena::io::IStreamWriter& w) const; \ void _binarySize(size_t& s) const; \ void _read(athena::io::YAMLDocReader& r); \ void _write(athena::io::YAMLDocWriter& w) const; #define AT_SUBDECL_DNA_YAML \ AT_SUBDECL_DNA \ void _read(athena::io::YAMLDocReader& r); \ void _write(athena::io::YAMLDocWriter& w) const; #define AT_SUBSPECIALIZE_DNA(...) \ template <> \ template <> \ void __VA_ARGS__::Enumerate::BinarySize>(typename BinarySize::StreamT & s) { \ _binarySize(s); \ } \ template <> \ template <> \ void __VA_ARGS__::Enumerate::Read>(typename Read::StreamT & r) { \ _read(r); \ } \ template <> \ template <> \ void __VA_ARGS__::Enumerate::Write>(typename Write::StreamT & w) { \ _write(w); \ } #define AT_SUBSPECIALIZE_DNA_YAML(...) \ template <> \ template <> \ void __VA_ARGS__::Enumerate::ReadYaml>(typename ReadYaml::StreamT & r) { \ _read(r); \ } \ template <> \ template <> \ void __VA_ARGS__::Enumerate::WriteYaml>(typename WriteYaml::StreamT & w) { \ _write(w); \ } \ AT_SUBSPECIALIZE_DNA(__VA_ARGS__)