Skip to content

Instantly share code, notes, and snippets.

@namandixit
Last active January 8, 2026 07:12
Show Gist options
  • Select an option

  • Save namandixit/9058f383ed92693f8903de8aaec74062 to your computer and use it in GitHub Desktop.

Select an option

Save namandixit/9058f383ed92693f8903de8aaec74062 to your computer and use it in GitHub Desktop.
Data Serialization and Archive Library for C23 (note: requires a separate code generator)
/*
* Creator: Naman Dixit
* Notice: © Copyright 2025 Naman Dixit
*/
#pragma once
#include "std.h"
#define M3D_USE_MATH_H
#include "math3d.h"
pragma_clang("clang diagnostic push");
pragma_clang("clang diagnostic ignored \"-Wcovered-switch-default\"");
pragma_clang("clang diagnostic ignored \"-Wpadded\"");
pragma_clang("clang diagnostic ignored \"-Wunsafe-buffer-usage\"");
#if defined(ENV_LANG_C)
pragma_clang("clang diagnostic ignored \"-Wpre-c23-compat\"");
pragma_clang("clang diagnostic ignored \"-Wdeclaration-after-statement\"");
pragma_clang("clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"");
pragma_clang("clang diagnostic ignored \"-Wcast-qual\""); // "drops const qualifier", we deal with the global metadata which is const
#elif defined(ENV_LANG_CXX)
pragma_clang("clang diagnostic ignored \"-Wc++98-compat-pedantic\"");
pragma_clang("clang diagnostic ignored \"-Wmissing-field-initializers\"");
#endif
pragma_msvc("warning ( push )");
pragma_msvc("warning ( disable: 4820 )"); // Padding
#if defined(ENV_LANG_C)
#elif defined(ENV_LANG_CXX)
#endif
typedef enum Roop_Type_Category {
Roop_Type_Category_BASIC, // primitives: Bool/Uint8/16/32/64, Sint8/16/32/64, Float32/64, Symbol
Roop_Type_Category_VALUE, // enums
Roop_Type_Category_CLUMP, // Binary blobs
Roop_Type_Category_TUPLE, // structs
} Roop_Type_Category;
///////////////////////////////////////////////////////////////////////////////////////////////
#if defined(ROOP_SCHEMA_HEADER)
# include ROOP_SCHEMA_HEADER
#else
// WARN(naman): Be very very VERY FUCKING CAREFUL before modifying these types and their metadata.
// The metadata for these types is derived manually and changing the types will make it invalid.
// =======>>>> IF IT AIN'T BROKE, DON'T FIX IT. <<<<=======
typedef enum Roop_Versions {
Roop_Versions_EMPTY = 0,
Roop_Versions_NEXT,
Roop_Versions_CURRENT = Roop_Versions_NEXT - 1,
} Roop_Versions;
static_assert(Roop_Versions_NEXT <= INT16_MAX);
typedef enum Roop_Type {
Roop_Type_Refer = 0, /* basic */
Roop_Type_Block = 1, /* basic */
Roop_Type_Bool, /* basic */
Roop_Type_Uint8, /* basic */
Roop_Type_Sint8, /* basic */
Roop_Type_Uint16, /* basic */
Roop_Type_Sint16, /* basic */
Roop_Type_Uint32, /* basic */
Roop_Type_Sint32, /* basic */
Roop_Type_Float32, /* basic */
Roop_Type_Uint64, /* basic */
Roop_Type_Sint64, /* basic */
Roop_Type_Float64, /* basic */
Roop_Type_Vec2f, /* basic */
Roop_Type_Vec3f, /* basic */
Roop_Type_Quat4f, /* basic */
Roop_Type_ColRGB, /* basic */
Roop_Type_ColRGBA, /* basic */
Roop_Type_Mat4f, /* basic */
Roop_Type_Symbol, /* basic */
Roop_Type_Schema_Type_Member, /* tuple */
Roop_Type_Schema_Type_Version_Patch, /* clump */
Roop_Type_Schema_Type, /* tuple */
Roop_Type_Schema_Version, /* tuple */
Roop_Type_Schema_Group, /* tuple */
Roop_Type_Schema, /* tuple, group */
} Roop_Type;
#define ROOP_TYPE_COUNT 26
typedef enum Roop_Group_ID {
Roop_Group_ID_schema = 0x32c8ba8a, /* Type: Roop_Schema */
} Roop_Group_ID;
#define ROOP_GROUP_COUNT 1
typedef struct Roop_Schema_Type_Member Roop_Schema_Type_Member;
typedef segarOf(Roop_Schema_Type_Member) Roop_Block_Schema_Type_Member;
typedef struct Roop_Schema_Type_Version_Patch Roop_Schema_Type_Version_Patch;
typedef segarOf(Roop_Schema_Type_Version_Patch) Roop_Block_Schema_Type_Version_Patch;
typedef struct Roop_Schema_Type Roop_Schema_Type;
typedef segarOf(Roop_Schema_Type) Roop_Block_Schema_Type;
typedef struct Roop_Schema_Version Roop_Schema_Version;
typedef segarOf(Roop_Schema_Version) Roop_Block_Schema_Version;
typedef struct Roop_Schema_Group Roop_Schema_Group;
typedef segarOf(Roop_Schema_Group) Roop_Block_Schema_Group;
typedef struct Roop_Schema Roop_Schema;
typedef segarOf(Roop_Schema) Roop_Block_Schema;
typedef struct Roop_Group_Header {
Memory_Push *allocator;
} Roop_Group_Header;
#define ROOP_GROUP_TYPE(_type) struct { Roop_Group_Header header; _type datum; }
/* Roop Refer ****************************************************************
* This has to be in the generated code since it depends on the Roop_Group_ID,
* and enums can't be forward declared in C (because C sucks!!!!).
*/
typedef struct Roop_Refer {
Label name;
Roop_Group_ID id;
} Roop_Refer;
typedef struct Roop_Schema_Type_Member {
Symbol name;
Sint32 type;
Sint32 type_base;
Uint32 data_offset;
Uint32 version_add;
Uint32 version_rem;
Sint32 enums_value;
} Roop_Schema_Type_Member;
typedef struct Roop_Schema_Type_Version_Patch {
Uint32 bytes;
Uint32 crc32;
void *data;
} Roop_Schema_Type_Version_Patch;
typedef struct Roop_Schema_Type {
Symbol name;
Uint64 size;
Uint64 size_old;
Sint32 type;
Sint32 category;
Sint32 group_id;
Uint8 group_checksum;
Uint32 group_index;
Roop_Block_Schema_Type_Member members;
Roop_Schema_Type_Version_Patch patch;
} Roop_Schema_Type;
typedef struct Roop_Schema_Version {
Symbol name;
} Roop_Schema_Version;
typedef struct Roop_Schema_Group {
Uint32 id;
Uint32 type;
} Roop_Schema_Group;
typedef struct Roop_Schema {
Uint16 version;
Roop_Block_Schema_Version versions;
Roop_Block_Schema_Type types;
Roop_Block_Schema_Group groups;
} Roop_Schema;
typedef ROOP_GROUP_TYPE(Roop_Schema) Roop_Group_Schema;
#undef ROOP_GROUP_TYPE
pragma_clang("clang diagnostic push");
pragma_clang("clang diagnostic ignored \"-Warray-bounds-pointer-arithmetic\"");
#define ROOP_SEGAR_FAKE_SEGMENTATION_NAME(_member_block) \
_member_block##_fake_segmentation_pointers
#define ROOP_SEGAR_FAKE_SEGMENTATION(_type, _member_block) \
global_variable _type *ROOP_SEGAR_FAKE_SEGMENTATION_NAME(_member_block)[] = { \
(_member_block) + ((1u << 0) - 1), \
(_member_block) + ((1u << 1) - 1), \
(_member_block) + ((1u << 2) - 1), \
(_member_block) + ((1u << 3) - 1), \
(_member_block) + ((1u << 4) - 1), \
(_member_block) + ((1u << 5) - 1), \
(_member_block) + ((1u << 6) - 1), \
(_member_block) + ((1u << 7) - 1), \
(_member_block) + ((1u << 8) - 1), \
(_member_block) + ((1u << 9) - 1), \
(_member_block) + ((1u << 10) - 1), \
(_member_block) + ((1u << 11) - 1), \
(_member_block) + ((1u << 12) - 1), \
(_member_block) + ((1u << 13) - 1), \
(_member_block) + ((1u << 14) - 1), \
(_member_block) + ((1u << 15) - 1), \
(_member_block) + ((1u << 16) - 1), \
(_member_block) + ((1u << 17) - 1), \
(_member_block) + ((1u << 18) - 1), \
(_member_block) + ((1u << 19) - 1), \
(_member_block) + ((1u << 20) - 1), \
(_member_block) + ((1u << 21) - 1), \
(_member_block) + ((1u << 22) - 1), \
(_member_block) + ((1u << 23) - 1), \
(_member_block) + ((1u << 24) - 1), \
(_member_block) + ((1u << 25) - 1), \
(_member_block) + ((1u << 26) - 1), \
(_member_block) + ((1u << 27) - 1), \
(_member_block) + ((1u << 28) - 1), \
(_member_block) + ((1u << 29) - 1), \
}
global_variable Roop_Schema_Version GLOBAL_roop_schema_versions[] = {
{.name = {.h32=0xac39834,.h16=0x8441,.h8=0xa6,.len=0x5, .str = "EMPTY", ._zero=0} },
};
ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Version, GLOBAL_roop_schema_versions);
global_variable Roop_Schema_Type_Member GLOBAL_roop_schema_type_members_Schema_Type_Member[] = {
{.name = {.h32=0xc08fee59,.h16=0x33dc,.h8=0x2a,.len=0x4, .str = "name", ._zero=0}, .type = Roop_Type_Symbol, .data_offset = offsetof(Roop_Schema_Type_Member, name), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0xd5ae7e48,.h16=0xb323,.h8=0x8d,.len=0x4, .str = "type", ._zero=0}, .type = Roop_Type_Sint32, .data_offset = offsetof(Roop_Schema_Type_Member, type), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0x561049f2,.h16=0x6d6,.h8=0xa2,.len=0x9, .str = "type_base", ._zero=0}, .type = Roop_Type_Sint32, .data_offset = offsetof(Roop_Schema_Type_Member, type_base), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0x6bd3607a,.h16=0x1f02,.h8=0x6c,.len=0xb, .str = "data_offset", ._zero=0}, .type = Roop_Type_Uint32, .data_offset = offsetof(Roop_Schema_Type_Member, data_offset), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0x6156fadc,.h16=0xd586,.h8=0xa9,.len=0xb, .str = "version_add", ._zero=0}, .type = Roop_Type_Uint32, .data_offset = offsetof(Roop_Schema_Type_Member, version_add), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0xe4225139,.h16=0x8594,.h8=0x27,.len=0xb, .str = "version_rem", ._zero=0}, .type = Roop_Type_Uint32, .data_offset = offsetof(Roop_Schema_Type_Member, version_rem), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0xd0dad82e,.h16=0x4f42,.h8=0x4c,.len=0xb, .str = "enums_value", ._zero=0}, .type = Roop_Type_Sint32, .data_offset = offsetof(Roop_Schema_Type_Member, enums_value), .version_add = Roop_Versions_EMPTY, },
};
ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Type_Member, GLOBAL_roop_schema_type_members_Schema_Type_Member);
global_variable Roop_Schema_Type_Member GLOBAL_roop_schema_type_members_Schema_Type[] = {
{.name = {.h32=0xc08fee59,.h16=0x33dc,.h8=0x2a,.len=0x4, .str = "name", ._zero=0}, .type = Roop_Type_Symbol, .data_offset = offsetof(Roop_Schema_Type, name), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0x1d13d42a,.h16=0xef73,.h8=0x63,.len=0x4, .str = "size", ._zero=0}, .type = Roop_Type_Uint64, .data_offset = offsetof(Roop_Schema_Type, size), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0xbe28a04f,.h16=0x74a8,.h8=0xd5,.len=0x8, .str = "size_old", ._zero=0}, .type = Roop_Type_Uint64, .data_offset = offsetof(Roop_Schema_Type, size_old), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0xd5ae7e48,.h16=0xb323,.h8=0x8d,.len=0x4, .str = "type", ._zero=0}, .type = Roop_Type_Sint32, .data_offset = offsetof(Roop_Schema_Type, type), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0x491ff89f,.h16=0x4c03,.h8=0xc1,.len=0x8, .str = "category", ._zero=0}, .type = Roop_Type_Sint32, .data_offset = offsetof(Roop_Schema_Type, category), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0xc6c51c4e,.h16=0x3033,.h8=0x81,.len=0x8, .str = "group_id", ._zero=0}, .type = Roop_Type_Sint32, .data_offset = offsetof(Roop_Schema_Type, group_id), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0x6c51b4df,.h16=0x3647,.h8=0xf3,.len=0xe, .str = "group_checksum", ._zero=0}, .type = Roop_Type_Uint8, .data_offset = offsetof(Roop_Schema_Type, group_checksum), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0x2541244f,.h16=0x2bfb,.h8=0x6e,.len=0xb, .str = "group_index", ._zero=0}, .type = Roop_Type_Uint32, .data_offset = offsetof(Roop_Schema_Type, group_index), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0x629bcc4a,.h16=0x9b1,.h8=0x5a,.len=0x7, .str = "members", ._zero=0}, .type = Roop_Type_Block, .type_base = Roop_Type_Schema_Type_Member, .data_offset = offsetof(Roop_Schema_Type, members), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0x33b15572,.h16=0xfc76,.h8=0xea,.len=0x5, .str = "patch", ._zero=0}, .type = Roop_Type_Schema_Type_Version_Patch, .data_offset = offsetof(Roop_Schema_Type, patch), .version_add = Roop_Versions_EMPTY, },
};
ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Type_Member, GLOBAL_roop_schema_type_members_Schema_Type);
global_variable Roop_Schema_Type_Member GLOBAL_roop_schema_type_members_Schema_Version[] = {
{.name = {.h32=0xc08fee59,.h16=0x33dc,.h8=0x2a,.len=0x4, .str = "name", ._zero=0}, .type = Roop_Type_Symbol, .data_offset = offsetof(Roop_Schema_Version, name), .version_add = Roop_Versions_EMPTY, },
};
ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Type_Member, GLOBAL_roop_schema_type_members_Schema_Version);
global_variable Roop_Schema_Type_Member GLOBAL_roop_schema_type_members_Schema_Group[] = {
{.name = {.h32=0x98768be1,.h16=0x1e3b,.h8=0xd9,.len=0x2, .str = "id", ._zero=0}, .type = Roop_Type_Uint32, .data_offset = offsetof(Roop_Schema_Group, id), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0xd5ae7e48,.h16=0xb323,.h8=0x8d,.len=0x4, .str = "type", ._zero=0}, .type = Roop_Type_Uint32, .data_offset = offsetof(Roop_Schema_Group, type), .version_add = Roop_Versions_EMPTY, },
};
ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Type_Member, GLOBAL_roop_schema_type_members_Schema_Group);
global_variable Roop_Schema_Type_Member GLOBAL_roop_schema_type_members_Schema[] = {
{.name = {.h32=0xe053f0d4,.h16=0xf389,.h8=0x49,.len=0x7, .str = "version", ._zero=0}, .type = Roop_Type_Uint16, .data_offset = offsetof(Roop_Schema, version), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0xf652027e,.h16=0xf2d0,.h8=0x55,.len=0x8, .str = "versions", ._zero=0}, .type = Roop_Type_Block, .type_base = Roop_Type_Schema_Version, .data_offset = offsetof(Roop_Schema, versions), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0x4baa70f5,.h16=0x40e7,.h8=0xfd,.len=0x5, .str = "types", ._zero=0}, .type = Roop_Type_Block, .type_base = Roop_Type_Schema_Type, .data_offset = offsetof(Roop_Schema, types), .version_add = Roop_Versions_EMPTY, },
{.name = {.h32=0xbabd3ff1,.h16=0xd41e,.h8=0x71,.len=0x6, .str = "groups", ._zero=0}, .type = Roop_Type_Block, .type_base = Roop_Type_Schema_Group, .data_offset = offsetof(Roop_Schema, groups), .version_add = Roop_Versions_EMPTY, },
};
ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Type_Member, GLOBAL_roop_schema_type_members_Schema);
global_variable Roop_Schema_Type GLOBAL_roop_schema_types[] = {
/*[Roop_Type_Refer] =*/ {
.type = Roop_Type_Refer,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Block] =*/ {
.type = Roop_Type_Block,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Bool] =*/ {
.name = {.h32=0x647cc508,.h16=0x613b,.h8=0xf6,.len=0x4, .str = "Bool", ._zero=0},
.size = sizeof(Bool),
.type = Roop_Type_Bool,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Uint8] =*/ {
.name = {.h32=0xef02b0c5,.h16=0x3b31,.h8=0x24,.len=0x5, .str = "Uint8", ._zero=0},
.size = sizeof(Uint8),
.type = Roop_Type_Uint8,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Sint8] =*/ {
.name = {.h32=0x2335a77e,.h16=0x4136,.h8=0x44,.len=0x5, .str = "Sint8", ._zero=0},
.size = sizeof(Sint8),
.type = Roop_Type_Sint8,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Uint16] =*/ {
.name = {.h32=0xdf5183ad,.h16=0x4b3,.h8=0x21,.len=0x6, .str = "Uint16", ._zero=0},
.size = sizeof(Uint16),
.type = Roop_Type_Uint16,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Sint16] =*/ {
.name = {.h32=0x4e89ee49,.h16=0x7eb4,.h8=0xb1,.len=0x6, .str = "Sint16", ._zero=0},
.size = sizeof(Sint16),
.type = Roop_Type_Sint16,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Uint32] =*/ {
.name = {.h32=0xe0d54964,.h16=0xaaf,.h8=0x81,.len=0x6, .str = "Uint32", ._zero=0},
.size = sizeof(Uint32),
.type = Roop_Type_Uint32,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Sint32] =*/ {
.name = {.h32=0x4bc0a677,.h16=0x70a8,.h8=0x11,.len=0x6, .str = "Sint32", ._zero=0},
.size = sizeof(Sint32),
.type = Roop_Type_Sint32,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Float32] =*/ {
.name = {.h32=0xb72405a8,.h16=0xca1f,.h8=0x89,.len=0x7, .str = "Float32", ._zero=0},
.size = sizeof(Float32),
.type = Roop_Type_Float32,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Uint64] =*/ {
.name = {.h32=0x90a3fd83,.h16=0x11bd,.h8=0xb1,.len=0x6, .str = "Uint64", ._zero=0},
.size = sizeof(Uint64),
.type = Roop_Type_Uint64,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Sint64] =*/ {
.name = {.h32=0x8745488a,.h16=0x6bba,.h8=0x21,.len=0x6, .str = "Sint64", ._zero=0},
.size = sizeof(Sint64),
.type = Roop_Type_Sint64,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Float64] =*/ {
.name = {.h32=0x64554ad1,.h16=0xd85e,.h8=0xb9,.len=0x7, .str = "Float64", ._zero=0},
.size = sizeof(Float64),
.type = Roop_Type_Float64,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Vec2f] =*/ {
.name = {.h32=0x24facc62,.h16=0xf2fc,.h8=0xef,.len=0x5, .str = "Vec2f", ._zero=0},
.size = sizeof(Vec2f),
.type = Roop_Type_Vec2f,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Vec3f] =*/ {
.name = {.h32=0xfbb7fddc,.h16=0xf2e9,.h8=0xaf,.len=0x5, .str = "Vec3f", ._zero=0},
.size = sizeof(Vec3f),
.type = Roop_Type_Vec3f,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Quat4f] =*/ {
.name = {.h32=0x658c48c8,.h16=0x7540,.h8=0x35,.len=0x6, .str = "Quat4f", ._zero=0},
.size = sizeof(Quat4f),
.type = Roop_Type_Quat4f,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_ColRGB] =*/ {
.name = {.h32=0x5e57176,.h16=0xa449,.h8=0xf0,.len=0x6, .str = "ColRGB", ._zero=0},
.size = sizeof(ColRGB),
.type = Roop_Type_ColRGB,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_ColRGBA] =*/ {
.name = {.h32=0x7be8f349,.h16=0xbaf1,.h8=0x1b,.len=0x7, .str = "ColRGBA", ._zero=0},
.size = sizeof(ColRGBA),
.type = Roop_Type_ColRGBA,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Mat4f] =*/ {
.name = {.h32=0x4b3feb13,.h16=0x9d32,.h8=0x9a,.len=0x5, .str = "Mat4f", ._zero=0},
.size = sizeof(Mat4f),
.type = Roop_Type_Mat4f,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Symbol] =*/ {
.name = {.h32=0xad90315e,.h16=0xddab,.h8=0x83,.len=0x6, .str = "Symbol", ._zero=0},
.size = sizeof(Symbol),
.type = Roop_Type_Symbol,
.category = Roop_Type_Category_BASIC,
},
/*[Roop_Type_Schema_Type_Member] =*/ {
.name = {.h32=0x7e252e4a,.h16=0x9c3c,.h8=0x5,.len=0x12, .str = "Schema_Type_Member", ._zero=0},
.size = sizeof(Roop_Schema_Type_Member),
.type = Roop_Type_Schema_Type_Member,
.category = Roop_Type_Category_TUPLE,
.members = {
.length = 7,
.segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_type_members_Schema_Type_Member),
},
},
/*[Roop_Type_Schema_Type_Version_Patch] =*/ {
.name = {.h32=0x51d4d061,.h16=0xf2dc,.h8=0x84,.len=0x19, .str = "Schema_Type_Version_Patch", ._zero=0},
.size = sizeof(Roop_Schema_Type_Version_Patch),
.type = Roop_Type_Schema_Type_Version_Patch,
.category = Roop_Type_Category_CLUMP,
},
/*[Roop_Type_Schema_Type] =*/ {
.name = {.h32=0x8f2313e1,.h16=0x1ed2,.h8=0xf0,.len=0xb, .str = "Schema_Type", ._zero=0},
.size = sizeof(Roop_Schema_Type),
.type = Roop_Type_Schema_Type,
.category = Roop_Type_Category_TUPLE,
.members = {
.length = 10,
.segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_type_members_Schema_Type),
},
},
/*[Roop_Type_Schema_Version] =*/ {
.name = {.h32=0xf4e94c68,.h16=0x3b81,.h8=0x9e,.len=0xe, .str = "Schema_Version", ._zero=0},
.size = sizeof(Roop_Schema_Version),
.type = Roop_Type_Schema_Version,
.category = Roop_Type_Category_TUPLE,
.members = {
.length = 1,
.segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_type_members_Schema_Version),
},
},
/*[Roop_Type_Schema_Group] =*/ {
.name = {.h32=0xbdf0876,.h16=0xff1c,.h8=0xf2,.len=0xc, .str = "Schema_Group", ._zero=0},
.size = sizeof(Roop_Schema_Group),
.type = Roop_Type_Schema_Group,
.category = Roop_Type_Category_TUPLE,
.members = {
.length = 2,
.segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_type_members_Schema_Group),
},
},
/*[Roop_Type_Schema] =*/ {
.name = {.h32=0xa1775a00,.h16=0x97dd,.h8=0x8c,.len=0x6, .str = "Schema", ._zero=0},
.size = sizeof(Roop_Schema),
.type = Roop_Type_Schema,
.category = Roop_Type_Category_TUPLE,
.group_id = 0x32c8ba8a, /* schema */
.group_checksum = 24, /* r */
.group_index = 0,
.members = {
.length = 4,
.segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_type_members_Schema),
},
},
};
ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Type, GLOBAL_roop_schema_types);
global_variable Roop_Schema_Group GLOBAL_roop_schema_groups[] = {
{.id = Roop_Group_ID_schema, .type = Roop_Type_Schema} /* Roop_Group_Schema , schema */,
};
ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Group, GLOBAL_roop_schema_groups);
#undef ROOP_SEGAR_FAKE_SEGMENTATION
pragma_clang("clang diagnostic pop");
global_immutable Roop_Group_Schema GLOBAL_roop_schema = {
.header = {/* No mempush, since this is read-only anyways */},
.datum = {
.version = Roop_Versions_CURRENT,
.versions = {
.length = Roop_Versions_NEXT,
.segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_versions),
},
.types = {
.length = ROOP_TYPE_COUNT,
.segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_types),
},
.groups = {
.length = ROOP_GROUP_COUNT,
.segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_groups),
},
},
};
#undef ROOP_SEGAR_FAKE_SEGMENTATION_NAME
#endif
///////////////////////////////////////////////////////////////////////////////////////////////
header_function
Roop_Refer roopReferMakeFromLabel (Label lbl, Roop_Group_ID id)
{
Roop_Refer pr = {
.name = lbl,
.id = id,
};
return pr;
}
header_function
Roop_Refer roopReferMakeFromStr (const Char *str, Roop_Group_ID id)
{
return roopReferMakeFromLabel(labelMake(str), id);
}
#if defined(ENV_LANG_C)
# define roopReferMake(_name, _id) \
_Generic(_name, \
const Char*: roopReferMakeFromStr, \
Char*: roopReferMakeFromStr, \
Label: roopReferMakeFromLabel \
)(_name, _id)
#elif defined(ENV_LANG_CXX)
header_function Roop_Refer roopReferMake (const Char *str, Roop_Group_ID id) { return roopReferMakeFromStr(str, id); }
header_function Roop_Refer roopReferMake (Char *str, Roop_Group_ID id) { return roopReferMakeFromStr(str, id); }
header_function Roop_Refer roopReferMake (Label lbl, Roop_Group_ID id) { return roopReferMakeFromLabel(lbl, id); }
#endif
header_function
void* roopGroupGetDatumPtr (Roop_Group_Header *header)
{
Roop_Group_Schema *group_ptr = cast_ptr(typeof(group_ptr), header); // Using Roop_Group_Schema since it'll always be available
void *datum = cast_ptr(void*, &group_ptr->datum);
return datum;
}
#define roopGetGroup(_dest_var, _header_ptr) containerof(_header_ptr, typeof(*(_dest_var)), header)
struct Roop_Group_ID_Str {
Char str[6];
Char _zero;
};
header_function
struct Roop_Group_ID_Str roop_GroupIDStrMake (const Char *str)
{
debugAssert(strlen(str) >= 6);
struct Roop_Group_ID_Str idstr = {.str = {str[0], str[1], str[2], str[3], str[4], str[5]}};
return idstr;
}
// Uses 30 bits
header_function
Roop_Group_ID roop_PackGroupIDStrIntoGroupID (struct Roop_Group_ID_Str idstr)
{
Uint32 id = 0;
id |= (cast_val(Uint32, b32DecodeChar(idstr.str[0])) & ~(0xFFFFFFFFu << 5)) << (30 - 5);
id |= (cast_val(Uint32, b32DecodeChar(idstr.str[1])) & ~(0xFFFFFFFFu << 5)) << (30 - 10);
id |= (cast_val(Uint32, b32DecodeChar(idstr.str[2])) & ~(0xFFFFFFFFu << 5)) << (30 - 15);
id |= (cast_val(Uint32, b32DecodeChar(idstr.str[3])) & ~(0xFFFFFFFFu << 5)) << (30 - 20);
id |= (cast_val(Uint32, b32DecodeChar(idstr.str[4])) & ~(0xFFFFFFFFu << 5)) << (30 - 25);
id |= (cast_val(Uint32, b32DecodeChar(idstr.str[5])) & ~(0xFFFFFFFFu << 5)) << (30 - 30);
return cast_val(Roop_Group_ID, id);
}
header_function
struct Roop_Group_ID_Str roop_UnpackGroupIDIntoGroupIDStr (Roop_Group_ID idnum)
{
Uint32 id = cast_val(Uint32, idnum);
struct Roop_Group_ID_Str idstr = {};
idstr.str[0] = b32EncodeChar(cast_val(Uint8, ((id >> (30- 5)) & ~(0xFFFFFFFFull << 5))));
idstr.str[1] = b32EncodeChar(cast_val(Uint8, ((id >> (30-10)) & ~(0xFFFFFFFFull << 5))));
idstr.str[2] = b32EncodeChar(cast_val(Uint8, ((id >> (30-15)) & ~(0xFFFFFFFFull << 5))));
idstr.str[3] = b32EncodeChar(cast_val(Uint8, ((id >> (30-20)) & ~(0xFFFFFFFFull << 5))));
idstr.str[4] = b32EncodeChar(cast_val(Uint8, ((id >> (30-25)) & ~(0xFFFFFFFFull << 5))));
idstr.str[5] = b32EncodeChar(cast_val(Uint8, ((id >> (30-30)) & ~(0xFFFFFFFFull << 5))));
return idstr;
}
header_function
Roop_Type roopTypeFromGroupID (Roop_Schema const*const schema, Roop_Group_ID id) {
Roop_Type type = cast_val(Roop_Type, -1);
for (Uint32 i = 0; i < segarLen(&schema->groups); i++) {
Roop_Schema_Group group = segarGet(&schema->groups, i);
if (cast_val(Roop_Group_ID, group.id) == id) {
type = cast_val(Roop_Type, group.type);
break;
}
}
debugAssert(type != cast_val(Roop_Type, -1));
return type;
}
header_function
const Char* roopGetGroupName (Roop_Schema const*const schema, Roop_Group_ID id)
{
Roop_Type type = roopTypeFromGroupID(schema, id);
const Roop_Schema_Type *details = segarGetPtr(&schema->types, cast_val(Uint32, type));
const Char *name = details->name.str;
return name;
}
header_function
Uint roopGetGroupIndex (Roop_Schema const*const schema, Roop_Group_ID id)
{
Roop_Type type = roopTypeFromGroupID(schema, id);
const Roop_Schema_Type *details = segarGetPtr(&schema->types, cast_val(Uint32, type));
Uint index = details->group_index;
return index;
}
header_function
Uint8 roopGetGroupChecksum (Roop_Schema const*const schema, Roop_Group_ID id)
{
Roop_Type type = roopTypeFromGroupID(schema, id);
const Roop_Schema_Type *details = segarGetPtr(&schema->types, cast_val(Uint32, type));
Uint8 checksum = details->group_checksum;
return checksum;
}
header_function
Bool roopEnumFromSymbol (Roop_Schema const*const schema, Roop_Type type, Sint *ptr, Symbol symbol)
{
const Roop_Schema_Type *details = segarGetPtr(&schema->types, cast_val(Uint32, type));
debugAssert(details->category == Roop_Type_Category_VALUE);
for (Uint i = 0; i < segarLen(&details->members); i++) {
Roop_Schema_Type_Member *elem = segarGetPtr(&details->members, i);
if (symbolIsEqual(symbol, elem->name)) {
*ptr = elem->enums_value;
return true;
}
}
return false;
}
struct Roop {
SRLZ_FUNC((*func));
void *func_data;
Memory_Push *mempush;
const Roop_Schema *schema;
Uint16 version;
Bool big_endian, is_dumping;
Byte _pad[4];
};
header_function Bool roopSerialize_Type (struct Roop *rp, Roop_Type type, Roop_Type base_type, void *datum_ptr);
header_function
Bool roopSerialize_TypeMembers (struct Roop *rp, Roop_Schema_Type const*const parent_details,
Byte *ptr_add, Byte *ptr_rem)
{
for (Uint32 j = 0; j < segarLen(&parent_details->members); j++) {
Roop_Schema_Type_Member *member_details = segarGetPtr(&parent_details->members, j);
if (rp->version >= member_details->version_add) { // File written after field added
if (member_details->version_rem == Roop_Versions_EMPTY) { // Field never removed
void *ptr = ptr_add + member_details->data_offset;
if (!roopSerialize_Type(rp, cast_val(Roop_Type, member_details->type),
cast_val(Roop_Type, member_details->type_base),
ptr)) {
return false;
}
} else if (rp->version < member_details->version_rem) { // File written before field removed
void *ptr_old = ptr_rem + member_details->data_offset;
if (!roopSerialize_Type(rp, cast_val(Roop_Type, member_details->type),
cast_val(Roop_Type, member_details->type_base),
ptr_old)) {
return false;
}
} else { // File written after field removed
// Do nothing
}
} else { // File written before field added
// Do nothing
}
}
return true;
}
header_function
Bool roopSerialize_Type (struct Roop *rp, Roop_Type type, Roop_Type base_type, void *datum_ptr)
{
const Roop_Schema_Type *type_details = segarGetPtr(&rp->schema->types, cast_val(Uint32, type));
switch (cast_val(Roop_Type_Category, type_details->category)) {
case Roop_Type_Category_BASIC: {
switch (type_details->type) {
case Roop_Type_Refer: {
Roop_Refer *datum = cast_ptr(typeof(datum), datum_ptr);
if (!srlzLabel(cast_ptr(Label*, &datum->name), rp->func, rp->func_data, rp->is_dumping, rp->big_endian)) {
return false;
}
if (!roopSerialize_Type(rp, Roop_Type_Sint32, cast_val(Roop_Type, -1), &datum->id)) {
return false;
}
return true;
} break;
case Roop_Type_Block: {
type_details = nullptr; // Not used for blocks, so NULL it to prevent accidental usage
debugAssert(base_type);
const Roop_Schema_Type *base_details = segarGetPtr(&rp->schema->types, cast_val(Uint32, base_type));
segarOf(void) *datum = cast_ptr(typeof(datum), datum_ptr);
if (!rp->is_dumping) {
datum->mempush = rp->mempush;
}
if (!roopSerialize_Type(rp, Roop_Type_Uint32, cast_val(Roop_Type, -1), &datum->length)) {
return false;
}
if (datum->length) {
void *datum_old = nullptr;
if (base_details->size_old) {
datum_old = memPushAllot(rp->mempush, base_details->size_old);
}
for (Uint32 i = 0; i < segarLen(datum); i++) {
if (datum_old) memset(datum_old, 0, base_details->size_old);
Uint32 segment_index = segar_CheckAndGetSegmentIndex(rp->mempush,
segarLen(datum), i,
cast_val(Uint32, base_details->size),
&datum->segments);
void *segment = datum->segments[segment_index];
Uint32 relative_index = segar_CheckAndGetRelativeIndex(segarLen(datum), i);
void *ptr = cast_ptr(Byte*, segment) + (relative_index * base_details->size);
if (!roopSerialize_TypeMembers(rp, base_details, cast_ptr(Byte*, ptr), cast_ptr(Byte*, datum_old))) {
return false;
}
if (datum_old) {
// TODO(naman): Call the patch function here, gets called once for each element
}
}
}
return true;
} break;
case Roop_Type_Bool: case Roop_Type_Uint8: {
Bool result = srlzUint8(cast_ptr(Uint8*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
case Roop_Type_Sint8: {
Bool result = srlzSint8(cast_ptr(Sint8*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
case Roop_Type_Uint16: {
Bool result = srlzUint16(cast_ptr(Uint16*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
case Roop_Type_Sint16: {
Bool result = srlzSint16(cast_ptr(Sint16*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
case Roop_Type_Uint32: {
Bool result = srlzUint32(cast_ptr(Uint32*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
case Roop_Type_Sint32: {
Bool result = srlzSint32(cast_ptr(Sint32*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
case Roop_Type_Float32: {
Bool result = srlzFloat32(cast_ptr(Float32*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
case Roop_Type_Uint64: {
Bool result = srlzUint64(cast_ptr(Uint64*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
case Roop_Type_Sint64: {
Bool result = srlzSint64(cast_ptr(Sint64*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
case Roop_Type_Float64: {
Bool result = srlzFloat64(cast_ptr(Float64*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
case Roop_Type_Vec2f: {
Float32x2 *datum = cast_ptr(typeof(datum), datum_ptr);
Bool result = true;
for (Uint i = 0; i < elemin(*datum); i++) {
result |= roopSerialize_Type(rp, Roop_Type_Float32, cast_val(Roop_Type, -1), &(*datum)[i]);
}
return result;
} break;
case Roop_Type_Vec3f: {
Float32x3 *datum = cast_ptr(typeof(datum), datum_ptr);
Bool result = true;
for (Uint i = 0; i < elemin(*datum); i++) {
result |= roopSerialize_Type(rp, Roop_Type_Float32, cast_val(Roop_Type, -1), &(*datum)[i]);
}
return result;
} break;
case Roop_Type_Quat4f: {
Float32x4 *datum = cast_ptr(typeof(datum), datum_ptr);
Bool result = true;
for (Uint i = 0; i < elemin(*datum); i++) {
result |= roopSerialize_Type(rp, Roop_Type_Float32, cast_val(Roop_Type, -1), &(*datum)[i]);
}
return result;
} break;
case Roop_Type_ColRGB: {
Float32x3 *datum = cast_ptr(typeof(datum), datum_ptr);
Bool result = true;
for (Uint i = 0; i < elemin(*datum); i++) {
result |= roopSerialize_Type(rp, Roop_Type_Float32, cast_val(Roop_Type, -1), &(*datum)[i]);
}
return result;
} break;
case Roop_Type_ColRGBA: {
Float32x4 *datum = cast_ptr(typeof(datum), datum_ptr);
Bool result = true;
for (Uint i = 0; i < elemin(*datum); i++) {
result |= roopSerialize_Type(rp, Roop_Type_Float32, cast_val(Roop_Type, -1), &(*datum)[i]);
}
return result;
} break;
case Roop_Type_Symbol: {
Bool result = srlzSymbol(cast_ptr(Symbol*, datum_ptr), rp->func, rp->func_data, rp->is_dumping, rp->big_endian);
return result;
} break;
default: debugBreak(); return false;
}
} break;
case Roop_Type_Category_VALUE: {
Sint32 *datum = cast_ptr(typeof(datum), datum_ptr);
if (!roopSerialize_Type(rp, Roop_Type_Sint32, cast_val(Roop_Type, -1), datum)) {
return false;
}
return true;
} break;
case Roop_Type_Category_CLUMP: {
Roop_Schema_Type_Version_Patch *datum = cast_ptr(typeof(datum), datum_ptr); // CLUMP type that is always available
if (!roopSerialize_Type(rp, Roop_Type_Uint32, cast_val(Roop_Type, -1), &datum->bytes)) {
return false;
}
if (datum->bytes) {
if (!rp->is_dumping) {
datum->data = cast_ptr(typeof(datum->data), memPushAllot(rp->mempush, datum->bytes));
}
rp->func(datum->data, datum->bytes, rp->func_data);
#if defined(BUILD_INTERNAL)
datum->crc32 = crc32(cast_ptr(Byte*, datum->data), datum->bytes);
#endif
}
return true;
} break;
case Roop_Type_Category_TUPLE: {
if (type_details->group_id) { // Group Header (added before every group)
Roop_Group_ID id = cast_val(typeof(id), type_details->group_id);
Char type_name[6] = {};
{ // Type_Name
struct Roop_Group_ID_Str idstr = roop_UnpackGroupIDIntoGroupIDStr(id);
if (rp->is_dumping) {
memcpy(type_name, idstr.str, 6);
}
if (!rp->func(type_name, 6, rp->func_data)) return false;
if (!rp->is_dumping) {
if (memcmp(type_name, idstr.str, 6) != 0) return false;
}
}
Uint8 checksum = 0;
if (rp->is_dumping) {
checksum = roopGetGroupChecksum(rp->schema, id);
}
if (!roopSerialize_Type(rp, Roop_Type_Uint8, cast_val(Roop_Type, -1), &checksum)) {
return false;
}
if (checksum != roopGetGroupChecksum(rp->schema, id)) {
return false;
}
Char checkchar = b32EncodeChecksumChar(checksum);
if (!roopSerialize_Type(rp, Roop_Type_Uint8, cast_val(Roop_Type, -1), cast_ptr(Uint8*, &checkchar))) {
return false;
}
if (!b32VerifyInputString(type_name, 6, checkchar)) {
return false;
}
}
void *datum_old = nullptr;
if (type_details->size_old) {
datum_old = memPushAllot(rp->mempush, type_details->size_old);
}
if (!roopSerialize_TypeMembers(rp, type_details, cast_ptr(Byte*, datum_ptr), cast_ptr(Byte*, datum_old))) {
return false;
}
if (datum_old) {
// TODO(naman): Call the patch function here
}
return true;
} break;
default: debugBreak(); return false;
}
}
typedef struct Roop_Serialize_Params_ { Bool is_dumping, big_endian; } Roop_Serialize_Params_;
#define roopSerialize(_s, _id, _h, _f, _fd, _m, ...) roop_Serialize((_s), (_id), (_h), (_f), (_fd), (_m), \
compound_init(Roop_Serialize_Params_, {__VA_ARGS__}))
header_function
Roop_Group_Header* roop_Serialize (Roop_Schema const*const schema,
Roop_Group_ID id, Roop_Group_Header *group_header,
SRLZ_FUNC((*func)), void *func_data,
Memory_Push *mempush,
Roop_Serialize_Params_ params)
{
struct Roop rp = {
.func = func,
.func_data = func_data,
.mempush = mempush,
.schema = schema,
.big_endian = params.big_endian,
.is_dumping = params.is_dumping,
};
{ // File Header
Byte magic[5] = "njrp";
Size magic_len = strlen(cast_ptr(Char*, magic));
debugAssert(magic_len == 4);
if (!rp.func(magic, magic_len, rp.func_data)) {
return nullptr;
}
if (memcmp(magic, "njrp", 4) != 0) {
return nullptr;
}
rp.version = schema->version;
srlzUint16(&rp.version, rp.func, rp.func_data, rp.is_dumping, rp.big_endian);
}
Roop_Type type = roopTypeFromGroupID(schema, id);
const Roop_Schema_Type *details = segarGetPtr(&rp.schema->types, cast_val(Uint32, type));
Size size = details->size;
if (!rp.is_dumping) {
debugAssert(group_header == nullptr);
group_header = cast_ptr(typeof(group_header), memPushAllot(rp.mempush, sizeof(*group_header) + size));
group_header->allocator = mempush;
}
Bool result = roopSerialize_Type(&rp, type, cast_val(Roop_Type, -1), roopGroupGetDatumPtr(group_header));
if (!result) {
return nullptr;
}
return group_header;
}
header_function void roopVinyDump_Type (Roop_Schema const*const schema, Viny_Dump_Tuple *vt,
Char *name, Roop_Type type, Roop_Type base_type, void *datum_ptr);
header_function
void roopVinyDump_TypeMembers (Roop_Schema const*const schema, Viny_Dump_Tuple *vt,
Roop_Schema_Type const*const parent_details, Byte *ptr)
{
for (Uint32 j = 0; j < segarLen(&parent_details->members); j++) {
Roop_Schema_Type_Member *member_details = segarGetPtr(&parent_details->members, j);
if (member_details->version_rem == Roop_Versions_EMPTY) { // Field never removed
void *p = ptr + member_details->data_offset;
roopVinyDump_Type(schema, vt, member_details->name.str,
cast_val(Roop_Type, member_details->type),
cast_val(Roop_Type, member_details->type_base),
p);
}
}
return;
}
header_function
void roopVinyDump_Type (Roop_Schema const*const schema, Viny_Dump_Tuple *vt, Char *name, Roop_Type type, Roop_Type base_type, void *datum_ptr)
{
const Roop_Schema_Type *type_details = segarGetPtr(&schema->types, cast_val(Uint32, type));
switch (cast_val(Roop_Type_Category, type_details->category)) {
case Roop_Type_Category_BASIC: {
switch (type_details->type) {
case Roop_Type_Refer: {
Roop_Refer *datum = cast_ptr(typeof(datum), datum_ptr);
Viny_Dump_Tuple *vt_refer = vdTupleBegin(vt, name);
vdString(vt_refer, "name", datum->name.str);
vdString(vt_refer, "id", roop_UnpackGroupIDIntoGroupIDStr(datum->id).str);
vdTupleEnd(&vt_refer);
} return;
case Roop_Type_Block: {
type_details = nullptr; // Not used for blocks, so NULL it to prevent accidental usage
const Roop_Schema_Type *base_details = segarGetPtr(&schema->types, cast_val(Uint32, base_type));
segarOf(void) *datum = cast_ptr(typeof(datum), datum_ptr);
Viny_Dump_Array *va = vdArrayBegin(vt, name);
for (Uint32 i = 0; i < segarLen(datum); i++) {
// WARN(naman): We pass NULL as `mempush` arg, since we don't expect there to be any memory
// allocations here when simply dumping stuff out.
Uint32 segment_index = segar_CheckAndGetSegmentIndex(nullptr,
segarLen(datum), i,
cast_val(Uint32, base_details->size),
&datum->segments);
void *segment = datum->segments[segment_index];
Uint32 relative_index = segar_CheckAndGetRelativeIndex(segarLen(datum), i);
Byte *ptr = cast_ptr(Byte*, segment) + (relative_index * base_details->size);
Viny_Dump_Tuple *vte = vdTupleBegin(va);
roopVinyDump_TypeMembers(schema, vte, base_details, ptr);
vdTupleEnd(&vte);
}
vdArrayEnd(&va);
} return;
case Roop_Type_Bool: case Roop_Type_Uint8: vdUint(vt, name, *cast_ptr(Uint8*, datum_ptr)); return;
case Roop_Type_Uint16: vdUint(vt, name, *cast_ptr(Uint16*, datum_ptr)); return;
case Roop_Type_Uint32: vdUint(vt, name, *cast_ptr(Uint32*, datum_ptr)); return;
case Roop_Type_Uint64: vdUint(vt, name, *cast_ptr(Uint64*, datum_ptr)); return;
case Roop_Type_Sint8: vdSint(vt, name, *cast_ptr(Sint8*, datum_ptr)); return;
case Roop_Type_Sint16: vdSint(vt, name, *cast_ptr(Sint16*, datum_ptr)); return;
case Roop_Type_Sint32: vdSint(vt, name, *cast_ptr(Sint32*, datum_ptr)); return;
case Roop_Type_Sint64: vdSint(vt, name, *cast_ptr(Sint64*, datum_ptr)); return;
case Roop_Type_Float32: vdFloat(vt, name, cast_val(Float64, (*cast_ptr(Float32*, datum_ptr)))); return;
case Roop_Type_Float64: vdFloat(vt, name, *cast_ptr(Float64*, datum_ptr)); return;
case Roop_Type_Vec2f: {
Float32x2 *datum = cast_ptr(typeof(datum), datum_ptr);
Viny_Dump_Tuple *vt_refer = vdTupleBegin(vt, name);
vdFloat(vt_refer, "x", cast_val(Float64, (*datum)[0]));
vdFloat(vt_refer, "y", cast_val(Float64, (*datum)[1]));
vdTupleEnd(&vt_refer);
} break;
case Roop_Type_Vec3f: {
Float32x3 *datum = cast_ptr(typeof(datum), datum_ptr);
Viny_Dump_Tuple *vt_refer = vdTupleBegin(vt, name);
vdFloat(vt_refer, "x", cast_val(Float64, (*datum)[0]));
vdFloat(vt_refer, "y", cast_val(Float64, (*datum)[1]));
vdFloat(vt_refer, "z", cast_val(Float64, (*datum)[2]));
vdTupleEnd(&vt_refer);
} break;
case Roop_Type_Quat4f: {
Float32x4 *datum = cast_ptr(typeof(datum), datum_ptr);
Viny_Dump_Tuple *vt_refer = vdTupleBegin(vt, name);
vdFloat(vt_refer, "i", cast_val(Float64, (*datum)[0]));
vdFloat(vt_refer, "j", cast_val(Float64, (*datum)[1]));
vdFloat(vt_refer, "k", cast_val(Float64, (*datum)[2]));
vdFloat(vt_refer, "r", cast_val(Float64, (*datum)[3]));
vdTupleEnd(&vt_refer);
} break;
case Roop_Type_ColRGB: {
Float32x3 *datum = cast_ptr(typeof(datum), datum_ptr);
Viny_Dump_Tuple *vt_refer = vdTupleBegin(vt, name);
vdFloat(vt_refer, "r", cast_val(Float64, (*datum)[0]));
vdFloat(vt_refer, "g", cast_val(Float64, (*datum)[1]));
vdFloat(vt_refer, "b", cast_val(Float64, (*datum)[2]));
vdTupleEnd(&vt_refer);
} break;
case Roop_Type_ColRGBA: {
Float32x4 *datum = cast_ptr(typeof(datum), datum_ptr);
Viny_Dump_Tuple *vt_refer = vdTupleBegin(vt, name);
vdFloat(vt_refer, "r", cast_val(Float64, (*datum)[0]));
vdFloat(vt_refer, "g", cast_val(Float64, (*datum)[1]));
vdFloat(vt_refer, "b", cast_val(Float64, (*datum)[2]));
vdFloat(vt_refer, "a", cast_val(Float64, (*datum)[3]));
vdTupleEnd(&vt_refer);
} break;
case Roop_Type_Symbol: vdString(vt, name, cast_ptr(Symbol*, datum_ptr)->str); return;
default: debugBreak(); return;
}
} return;
case Roop_Type_Category_VALUE: {
Sint32 *datum = cast_ptr(typeof(datum), datum_ptr);
for (Uint i = 0; i < segarLen(&type_details->members); i++) {
Roop_Schema_Type_Member *elem = segarGetPtr(&type_details->members, i);
if (elem->enums_value == *datum) {
vdSint(vt, name, *datum);
break;
}
}
} return;
case Roop_Type_Category_CLUMP: {
Roop_Schema_Type_Version_Patch *datum = cast_ptr(typeof(datum), datum_ptr); // Clump type that is always available
Viny_Dump_Tuple *vt_clump = vdTupleBegin(vt, name);
vdUint(vt_clump, "crc32", datum->crc32);
vdTupleEnd(&vt_clump);
} return;
case Roop_Type_Category_TUPLE: {
Viny_Dump_Tuple *vt_tuple = vdTupleBegin(vt, name);
roopVinyDump_TypeMembers(schema, vt_tuple, type_details, cast_ptr(Byte*, datum_ptr));
vdTupleEnd(&vt_tuple);
} return;
default: debugBreak(); return;
}
}
header_function
Txt roopVinyDump (Roop_Schema const*const schema, Roop_Group_ID id, Roop_Group_Header *group_data, Txt_Fmt_Pool *tfp, Memory_Push *mempush)
{
Roop_Type type = roopTypeFromGroupID(schema, id);
Viny_Dump v = vdCreate(tfp, mempush);
Viny_Dump_Tuple *vt_root = vdRootTuple(&v);
struct Roop_Group_ID_Str idstr = roop_UnpackGroupIDIntoGroupIDStr(id);
roopVinyDump_Type(schema, vt_root, idstr.str, type, cast_val(Roop_Type, -1), roopGroupGetDatumPtr(group_data));
Txt viny_test = vdFinish(&v);
return viny_test;
}
header_function
void roopHeader_TypeMembers (Roop_Schema const*const schema, Txt_Fmt *out, Roop_Schema_Type_Member *member)
{
Roop_Schema_Type *member_type = segarGetPtr(&schema->types, cast_val(Uint32, member->type));
if (member->type == Roop_Type_Refer) {
if (member->type_base) {
Roop_Schema_Type *dt = segarGetPtr(&schema->types, cast_val(Uint32, member->type_base));
if (dt->category == Roop_Type_Category_BASIC) {
txtfmtAppendF(out, " Roop_Refer %s; /* %s* */\n",
member->name.str, dt->name.str);
} else {
txtfmtAppendF(out, " Roop_Refer %s; /* Roop_%s* */\n",
member->name.str, dt->name.str);
}
} else {
txtfmtAppendF(out, " Roop_Refer %s; /* void* */\n",
member->name.str);
}
} else if (member->type == Roop_Type_Block) {
Roop_Schema_Type *dt = segarGetPtr(&schema->types, cast_val(Uint32, member->type_base));
txtfmtAppendF(out, " Roop_Block_%s %s;\n", dt->name.str, member->name.str);
} else if (member_type->category == Roop_Type_Category_BASIC) {
txtfmtAppendF(out, " %s %s;\n",
member_type->name.str, member->name.str);
} else {
txtfmtAppendF(out, " Roop_%s %s;\n",
member_type->name.str, member->name.str);
}
}
header_function
Txt roopHeader (Roop_Schema const*const schema, Txt_Fmt_Pool *tfp, Memory_Push *mempush)
{
Txt_Fmt fmt = txtfmtCreate(tfp);
txtfmtAppendF(&fmt, "/* THIS IS AN AUTO-GENERATED FILE */\n");
txtfmtAppendF(&fmt, "/* DO NOT EDIT */\n");
txtfmtAppendF(&fmt, "\n\n\n");
// Version
txtfmtAppendF(&fmt, "typedef enum Roop_Versions {\n");
txtfmtAppendF(&fmt, " Roop_Versions_EMPTY = 0,\n\n");
for (Uint i = 1; i < segarLen(&schema->versions); i++) {
Roop_Schema_Version *version = segarGetPtr(&schema->versions, i);
txtfmtAppendF(&fmt, " Roop_Versions_%s,\n", version->name.str);
}
txtfmtAppendF(&fmt, "\n");
txtfmtAppendF(&fmt, " Roop_Versions_NEXT,\n");
txtfmtAppendF(&fmt, " Roop_Versions_CURRENT = Roop_Versions_NEXT - 1,\n");
txtfmtAppendF(&fmt, "} Roop_Versions;\n");
txtfmtAppendF(&fmt, "static_assert(Roop_Versions_NEXT <= INT16_MAX);\n");
txtfmtAppendF(&fmt, "\n\n\n");
// Type
txtfmtAppendF(&fmt, "typedef enum Roop_Type {\n");
for (Uint i = 0; i < segarLen(&schema->types); i++) {
Roop_Schema_Type *type = segarGetPtr(&schema->types, i);
if (i == 0) {
txtfmtAppendF(&fmt, " Roop_Type_Refer = 0");
} else if (i == 1) {
txtfmtAppendF(&fmt, " Roop_Type_Block = 1");
} else {
txtfmtAppendF(&fmt, " Roop_Type_%s", type->name.str);
}
txtfmtAppendF(&fmt, ", /* ");
switch (cast_val(Roop_Type_Category, type->category)) {
case Roop_Type_Category_BASIC: txtfmtAppendF(&fmt, "basic"); break;
case Roop_Type_Category_VALUE: txtfmtAppendF(&fmt, "value"); break;
case Roop_Type_Category_CLUMP: txtfmtAppendF(&fmt, "clump"); break;
case Roop_Type_Category_TUPLE: txtfmtAppendF(&fmt, "tuple%s", type->group_id ? ", group" : ""); break;
default: debugBreak(); break;
}
txtfmtAppendF(&fmt, " */\n");
}
txtfmtAppendF(&fmt, "} Roop_Type;\n");
txtfmtAppendF(&fmt, "#define ROOP_TYPE_COUNT %u\n", segarLen(&schema->types));
txtfmtAppendF(&fmt, "\n\n\n");
// Group
Uint group_count = 0;
txtfmtAppendF(&fmt, "typedef enum Roop_Group_ID {\n");
for (Uint i = 0; i < segarLen(&schema->types); i++) {
Roop_Schema_Type *type = segarGetPtr(&schema->types, i);
if ((type->category == Roop_Type_Category_TUPLE) && type->group_id) {
group_count++;
struct Roop_Group_ID_Str idstr = roop_UnpackGroupIDIntoGroupIDStr(cast_val(Roop_Group_ID, type->group_id));
txtfmtAppendF(&fmt,
" Roop_Group_ID_%s = 0x%x, /* Type: Roop_%s */\n",
idstr.str, cast_val(Uint32, type->group_id), type->name.str);
}
}
txtfmtAppendF(&fmt, "} Roop_Group_ID;\n");
txtfmtAppendF(&fmt, "#define ROOP_GROUP_COUNT %u\n", group_count);
txtfmtAppendF(&fmt, "\n\n\n");
// Block
for (Uint i = 0; i < segarLen(&schema->types); i++) {
Roop_Schema_Type *type = segarGetPtr(&schema->types, i);
if ((type->category == Roop_Type_Category_TUPLE) || (type->category == Roop_Type_Category_CLUMP)) {
txtfmtAppendF(&fmt, "typedef struct Roop_%s Roop_%s;\n", type->name.str, type->name.str);
txtfmtAppendF(&fmt, "typedef segarOf(Roop_%s) Roop_Block_%s;\n", type->name.str, type->name.str);
}
}
txtfmtAppendF(&fmt, "\n\n");
// Types
txtfmtAppendC(&fmt, "typedef struct Roop_Group_Header {\n");
txtfmtAppendC(&fmt, " Memory_Push *allocator;\n");
txtfmtAppendC(&fmt, "} Roop_Group_Header;\n");
txtfmtAppendC(&fmt, "\n");
txtfmtAppendC(&fmt, "#define ROOP_GROUP_TYPE(_type) struct { Roop_Group_Header header; _type datum; }\n");
txtfmtAppendF(&fmt, "\n");
txtfmtAppendF(&fmt, "/* Roop Refer ****************************************************************\n");
txtfmtAppendF(&fmt, " * This has to be in the generated code since it depends on the Roop_Group_ID,\n");
txtfmtAppendF(&fmt, " * and enums can't be forward declared in C (because C sucks!!!!).\n");
txtfmtAppendF(&fmt, " */\n");
txtfmtAppendF(&fmt, "typedef struct Roop_Refer {\n");
txtfmtAppendF(&fmt, " Label name;\n");
txtfmtAppendF(&fmt, " Roop_Group_ID id;\n");
txtfmtAppendF(&fmt, "} Roop_Refer;\n");
txtfmtAppendF(&fmt, "\n");
Bool *type_has_removed_members = cast_ptr(typeof(type_has_removed_members),
memPushAllot(mempush, sizeof(*type_has_removed_members) * segarLen(&schema->types)));
for (Uint i = 0; i < segarLen(&schema->types); i++) {
Roop_Schema_Type *type = segarGetPtr(&schema->types, i);
switch (type->category) {
case Roop_Type_Category_BASIC: {
} break;
case Roop_Type_Category_VALUE: {
txtfmtAppendF(&fmt, "typedef enum Roop_%s {\n", type->name.str);
for (Uint j = 0; j < segarLen(&type->members); j++) {
Roop_Schema_Type_Member *member = segarGetPtr(&type->members, j);
txtfmtAppendF(&fmt, " Roop_%s_%s = %u,\n", type->name.str, member->name.str, j+1);
}
txtfmtAppendF(&fmt, "} Roop_%s;\n", type->name.str);
txtfmtAppendF(&fmt, "\n");
} break;
case Roop_Type_Category_CLUMP: {
txtfmtAppendF(&fmt, "typedef struct Roop_%s {\n", type->name.str);
txtfmtAppendF(&fmt, " Uint32 bytes;\n");
txtfmtAppendF(&fmt, " Uint32 crc32;\n");
txtfmtAppendF(&fmt, " void *data;\n");
txtfmtAppendF(&fmt, "} Roop_%s;\n", type->name.str);
txtfmtAppendF(&fmt, "\n");
} break;
case Roop_Type_Category_TUPLE: {
// Existing members
txtfmtAppendF(&fmt, "typedef struct Roop_%s {\n", type->name.str);
for (Uint j = 0; j < segarLen(&type->members); j++) {
Roop_Schema_Type_Member *member = segarGetPtr(&type->members, j);
if (member->version_rem == Roop_Versions_EMPTY) {
roopHeader_TypeMembers(schema, &fmt, member);
} else {
type_has_removed_members[i] = true;
}
}
txtfmtAppendF(&fmt, "} Roop_%s;\n", type->name.str);
if (type->group_id) {
txtfmtAppendF(&fmt, "typedef ROOP_GROUP_TYPE(Roop_%s) Roop_Group_%s;\n", type->name.str, type->name.str);
}
txtfmtAppendF(&fmt, "\n");
// Removed members
if (type_has_removed_members[i]) {
txtfmtAppendF(&fmt, "struct Roop_%s_OLD {\n", type->name.str);
for (Uint j = 0; j < segarLen(&type->members); j++) {
Roop_Schema_Type_Member *member = segarGetPtr(&type->members, j);
if (member->version_rem != Roop_Versions_EMPTY) {
roopHeader_TypeMembers(schema, &fmt, member);
}
}
for (Uint j = 0; j < segarLen(&type->members); j++) {
Roop_Schema_Type_Member *member = segarGetPtr(&type->members, j);
if (member->version_rem != Roop_Versions_EMPTY) {
txtfmtAppendF(&fmt, " Bool %s_exists;\n", member->name.str);
}
}
txtfmtAppendF(&fmt, "};\n");
txtfmtAppendF(&fmt, "\n");
}
} break;
default: debugBreak(); break;
}
}
txtfmtAppendC(&fmt, "#undef ROOP_GROUP_TYPE\n");
txtfmtAppendF(&fmt, "\n");
// Macros to deal with globally declared Segars in a cross-language (C & C++) manner
txtfmtAppendC(&fmt, "pragma_clang(\"clang diagnostic push\");\n");
txtfmtAppendC(&fmt, "pragma_clang(\"clang diagnostic ignored \\\"-Warray-bounds-pointer-arithmetic\\\"\");\n");
txtfmtAppendC(&fmt,
"#define ROOP_SEGAR_FAKE_SEGMENTATION_NAME(_member_block) \\\n"
" _member_block##_fake_segmentation_pointers\n\n"
"#define ROOP_SEGAR_FAKE_SEGMENTATION(_type, _member_block) \\\n"
" global_variable _type *ROOP_SEGAR_FAKE_SEGMENTATION_NAME(_member_block)[] = { \\\n"
" (_member_block) + ((1u << 0) - 1), \\\n"
" (_member_block) + ((1u << 1) - 1), \\\n"
" (_member_block) + ((1u << 2) - 1), \\\n"
" (_member_block) + ((1u << 3) - 1), \\\n"
" (_member_block) + ((1u << 4) - 1), \\\n"
" (_member_block) + ((1u << 5) - 1), \\\n"
" (_member_block) + ((1u << 6) - 1), \\\n"
" (_member_block) + ((1u << 7) - 1), \\\n"
" (_member_block) + ((1u << 8) - 1), \\\n"
" (_member_block) + ((1u << 9) - 1), \\\n"
" (_member_block) + ((1u << 10) - 1), \\\n"
" (_member_block) + ((1u << 11) - 1), \\\n"
" (_member_block) + ((1u << 12) - 1), \\\n"
" (_member_block) + ((1u << 13) - 1), \\\n"
" (_member_block) + ((1u << 14) - 1), \\\n"
" (_member_block) + ((1u << 15) - 1), \\\n"
" (_member_block) + ((1u << 16) - 1), \\\n"
" (_member_block) + ((1u << 17) - 1), \\\n"
" (_member_block) + ((1u << 18) - 1), \\\n"
" (_member_block) + ((1u << 19) - 1), \\\n"
" (_member_block) + ((1u << 20) - 1), \\\n"
" (_member_block) + ((1u << 21) - 1), \\\n"
" (_member_block) + ((1u << 22) - 1), \\\n"
" (_member_block) + ((1u << 23) - 1), \\\n"
" (_member_block) + ((1u << 24) - 1), \\\n"
" (_member_block) + ((1u << 25) - 1), \\\n"
" (_member_block) + ((1u << 26) - 1), \\\n"
" (_member_block) + ((1u << 27) - 1), \\\n"
" (_member_block) + ((1u << 28) - 1), \\\n"
" (_member_block) + ((1u << 29) - 1), \\\n"
" }\n\n\n"
);
// Metadata - Versions
txtfmtAppendF(&fmt, "global_variable Roop_Schema_Version GLOBAL_roop_schema_versions[] = {\n");
for (Uint i = 0; i < segarLen(&schema->versions); i++) {
Roop_Schema_Version *version = segarGetPtr(&schema->versions, i);
txtfmtAppendF(&fmt,
" {.name = {.h32=0x%x,.h16=0x%hx,.h8=0x%hhx,.len=0x%hhx, .str = \"%s\", ._zero=0} },\n",
version->name.h32, version->name.h16, version->name.h8, version->name.len, version->name.str);
}
txtfmtAppendF(&fmt, "};\n");
txtfmtAppendF(&fmt, "ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Version, GLOBAL_roop_schema_versions);\n");
txtfmtAppendF(&fmt, "\n\n");
// Metadata - Type Members
for (Uint i = 0; i < segarLen(&schema->types); i++) {
Roop_Schema_Type *type = segarGetPtr(&schema->types, i);
if ((type->category != Roop_Type_Category_BASIC) && segarLen(&type->members)) {
txtfmtAppendF(&fmt, "global_variable Roop_Schema_Type_Member GLOBAL_roop_schema_type_members_%s[] = {\n", type->name.str);
for (Uint j = 0; j < segarLen(&type->members); j++) {
Roop_Schema_Type_Member *member = segarGetPtr(&type->members, j);
txtfmtAppendF(&fmt, " {");
txtfmtAppendF(&fmt,
".name = {.h32=0x%x,.h16=0x%hx,.h8=0x%hhx,.len=0x%hhx, .str = \"%s\", ._zero=0}, ",
member->name.h32, member->name.h16, member->name.h8, member->name.len, member->name.str);
if (type->category != Roop_Type_Category_VALUE) {
if (member->type == Roop_Type_Refer) {
txtfmtAppendF(&fmt, ".type = Roop_Type_Refer, ");
if (member->type_base) {
Roop_Schema_Type *refer_type = segarGetPtr(&schema->types, cast_val(Uint32, member->type_base));
txtfmtAppendF(&fmt, ".type_base = Roop_Type_%s, ", refer_type->name.str);
}
} else if (member->type == Roop_Type_Block) {
txtfmtAppendF(&fmt, ".type = Roop_Type_Block, ");
Roop_Schema_Type *block_type = segarGetPtr(&schema->types, cast_val(Uint32, member->type_base));
txtfmtAppendF(&fmt, ".type_base = Roop_Type_%s, ", block_type->name.str);
} else {
Roop_Schema_Type *member_type = segarGetPtr(&schema->types, cast_val(Uint32, member->type));
txtfmtAppendF(&fmt, ".type = Roop_Type_%s, ", member_type->name.str);
}
if (member->version_rem) {
if (type->category == 0 /* Roop_Type_Category_BLOCK */) {
txtfmtAppendF(&fmt,
".data_offset = offsetof(struct Roop_%s_OLD, %s), ",
type->name.str, member->name.str);
} else {
txtfmtAppendF(&fmt,
".data_offset = offsetof(struct Roop_%s_OLD, %s), ",
type->name.str, member->name.str);
}
} else {
if (type->category == 0 /*Roop_Type_Category_BLOCK */) {
txtfmtAppendF(&fmt,
".data_offset = offsetof(Roop_%s, %s), ",
type->name.str, member->name.str);
} else {
txtfmtAppendF(&fmt,
".data_offset = offsetof(Roop_%s, %s), ",
type->name.str, member->name.str);
}
}
Roop_Schema_Version *version_add =segarGetPtr(&schema->versions, cast_val(Uint32, member->version_add));
txtfmtAppendF(&fmt, ".version_add = Roop_Versions_%s, ", version_add->name.str);
if (member->version_rem) {
Roop_Schema_Version *version_rem = segarGetPtr(&schema->versions,
cast_val(Uint32, member->version_rem));
txtfmtAppendF(&fmt, ".version_rem = Roop_Versions_%s, ", version_rem->name.str);
}
} else {
txtfmtAppendF(&fmt, ".enums_value = Roop_%s_%s, ", type->name.str, member->name.str);
}
txtfmtAppendF(&fmt, "},\n");
}
txtfmtAppendF(&fmt, "};\n");
txtfmtAppendF(&fmt, "ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Type_Member, GLOBAL_roop_schema_type_members_%s);\n", type->name.str);
txtfmtAppendF(&fmt, "\n");
}
}
txtfmtAppendF(&fmt, "\n\n");
// Metadata - Types
txtfmtAppendF(&fmt, "global_variable Roop_Schema_Type GLOBAL_roop_schema_types[] = {\n");
for (Uint i = 0; i < segarLen(&schema->types); i++) {
Roop_Schema_Type *type = segarGetPtr(&schema->types, i);
if (type->type == Roop_Type_Refer) {
txtfmtAppendF(&fmt, " /*[Roop_Type_Refer] =*/ {\n");
txtfmtAppendF(&fmt, " .type = Roop_Type_Refer,\n");
} else if (type->type == Roop_Type_Block) {
txtfmtAppendF(&fmt, " /*[Roop_Type_Block] =*/ {\n");
txtfmtAppendF(&fmt, " .type = Roop_Type_Block,\n");
} else {
txtfmtAppendF(&fmt, " /*[Roop_Type_%s] =*/ {\n", type->name.str);
txtfmtAppendF(&fmt, " .name = {.h32=0x%x,.h16=0x%hx,.h8=0x%hhx,.len=0x%hhx, .str = \"%s\", ._zero=0},\n",
type->name.h32, type->name.h16, type->name.h8, type->name.len, type->name.str);
if (type->category == Roop_Type_Category_BASIC) {
txtfmtAppendF(&fmt, " .size = sizeof(%s),\n", type->name.str);
} else {
txtfmtAppendF(&fmt, " .size = sizeof(Roop_%s),\n", type->name.str);
}
if (type_has_removed_members[i]) {
txtfmtAppendF(&fmt, " .size_old = sizeof(struct Roop_%s_OLD),\n", type->name.str);
}
txtfmtAppendF(&fmt, " .type = Roop_Type_%s,\n", type->name.str);
}
const Char *category;
switch (type->category) {
case Roop_Type_Category_BASIC: category = "BASIC"; break;
case Roop_Type_Category_VALUE: category = "VALUE"; break;
case Roop_Type_Category_CLUMP: category = "CLUMP"; break;
case Roop_Type_Category_TUPLE: category = "TUPLE"; break;
default: debugBreak(); category = nullptr;
}
txtfmtAppendF(&fmt, " .category = Roop_Type_Category_%s,\n", category);
if (type->group_id) {
struct Roop_Group_ID_Str idstr = roop_UnpackGroupIDIntoGroupIDStr(cast_val(Roop_Group_ID, type->group_id));
txtfmtAppendF(&fmt, " .group_id = 0x%x, /* %s */\n", cast_val(Uint32, type->group_id), idstr.str);
txtfmtAppendF(&fmt, " .group_checksum = %hhu, /* %c */\n",
type->group_checksum, b32EncodeChecksumChar(type->group_checksum));
txtfmtAppendF(&fmt, " .group_index = %u,\n", type->group_index);
}
if ((type->category != Roop_Type_Category_BASIC) && segarLen(&type->members)) {
txtfmtAppendC(&fmt, " .members = {\n");
txtfmtAppendF(&fmt, " .length = %u,\n", segarLen(&type->members));
txtfmtAppendF(&fmt, " .segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_type_members_%s),\n", type->name.str);
txtfmtAppendC(&fmt, " },\n");
}
txtfmtAppendF(&fmt, " },\n\n");
}
txtfmtAppendF(&fmt, "};\n");
txtfmtAppendF(&fmt, "ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Type, GLOBAL_roop_schema_types);\n");
txtfmtAppendF(&fmt, "\n\n\n");
txtfmtAppendF(&fmt, "global_variable Roop_Schema_Group GLOBAL_roop_schema_groups[] = {\n");
for (Uint i = 0; i < segarLen(&schema->types); i++) {
Roop_Schema_Type *type = segarGetPtr(&schema->types, i);
if ((type->category == Roop_Type_Category_TUPLE) && type->group_id) {
struct Roop_Group_ID_Str idstr = roop_UnpackGroupIDIntoGroupIDStr(cast_val(Roop_Group_ID, type->group_id));
txtfmtAppendF(&fmt, " {.id = Roop_Group_ID_%s, .type = Roop_Type_%s} /* Roop_Group_%s , %s */,\n",
idstr.str, type->name.str, type->name.str, idstr.str);
}
}
txtfmtAppendF(&fmt, "};\n");
txtfmtAppendF(&fmt, "ROOP_SEGAR_FAKE_SEGMENTATION(Roop_Schema_Group, GLOBAL_roop_schema_groups);\n");
txtfmtAppendF(&fmt, "\n\n");
txtfmtAppendF(&fmt, "#undef ROOP_SEGAR_FAKE_SEGMENTATION\n\n");
txtfmtAppendC(&fmt, "pragma_clang(\"clang diagnostic pop\");\n\n");
txtfmtAppendC(&fmt, "global_immutable Roop_Group_Schema GLOBAL_roop_schema = {\n");
txtfmtAppendC(&fmt, " .header = {/* No mempush, since this is read-only anyways */},\n");
txtfmtAppendC(&fmt, " .datum = {\n");
txtfmtAppendC(&fmt, " .version = Roop_Versions_CURRENT,\n");
txtfmtAppendC(&fmt, "\n");
txtfmtAppendC(&fmt, " .versions = {\n");
txtfmtAppendC(&fmt, " .length = Roop_Versions_NEXT,\n");
txtfmtAppendC(&fmt, " .segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_versions),\n");
txtfmtAppendC(&fmt, " },\n");
txtfmtAppendC(&fmt, "\n");
txtfmtAppendC(&fmt, " .types = {\n");
txtfmtAppendC(&fmt, " .length = ROOP_TYPE_COUNT,\n");
txtfmtAppendC(&fmt, " .segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_types),\n");
txtfmtAppendC(&fmt, " },\n");
txtfmtAppendC(&fmt, "\n");
txtfmtAppendC(&fmt, " .groups = {\n");
txtfmtAppendC(&fmt, " .length = ROOP_GROUP_COUNT,\n");
txtfmtAppendC(&fmt, " .segments = ROOP_SEGAR_FAKE_SEGMENTATION_NAME(GLOBAL_roop_schema_groups),\n");
txtfmtAppendC(&fmt, " },\n");
txtfmtAppendC(&fmt, " },\n");
txtfmtAppendC(&fmt, "};\n");
txtfmtAppendC(&fmt, "#undef ROOP_SEGAR_FAKE_SEGMENTATION_NAME\n");
txtfmtAppendC(&fmt, "\n\n\n");
// TODO(naman): Print patch functions
Txt header_txt = txtfmtFinish(&fmt, mempush);
return header_txt;
}
///////////////////////////////////////////////////////////////////////////////////////////
// NOTE(naman): All metadata is stored as big-endian, since it is easy to read in a hex editor.
enum Kosh_Version {
Kosh_Version_INITIAL = 1,
Kosh_Version_NEXT,
Kosh_Version_LAST = Kosh_Version_NEXT - 1,
};
struct Kosh_Writer_Header {
Uint64 timestamp;
Sint32 version;
Uint32 entry_count;
};
struct Kosh_Writer_Entry {
Label name;
Uint64 size;
Roop_Group_ID id;
Uint32 flags;
void *data;
Size offset;
struct Kosh_Writer_Entry *next;
};
header_function
Bool kosh_SrlzHeader (struct Kosh_Writer_Header *datum, SRLZ_FUNC((*func)), void *funcdata, Bool is_dumping)
{
Bool big_endian = true;
Uint header_size = 0; // debugAssert()ed later for sanity checking
{ // Magic: 9 bytes
Char magic[9] = "KoshRoop";
if (!func(magic, elemin(magic), funcdata)) {
return false;
}
if (!strleq(magic, elemin(magic), "KoshRoop")) {
return false;
}
header_size += sizeof(magic);
}
{ // Header Tag: 7 bytes
Char magic[7] = "Header";
if (!func(magic, elemin(magic), funcdata)) {
return false;
}
if (!strleq(magic, elemin(magic), "Header")) {
return false;
}
header_size += sizeof(magic);
}
{ // Timestamp: 18 bytes (nanoseconds since 01/01/1970 in UTC)
{ // Tag: 10 bytes
Char tag[10] = "Timestamp";
if (!func(tag, elemin(tag), funcdata)) {
return false;
}
if (!strleq(tag, elemin(tag), "Timestamp")) {
return false;
}
header_size += sizeof(tag);
} { // Timestamp: 8 bytes
if (!srlzUint64(&datum->timestamp, func, funcdata, is_dumping, big_endian)) {
return false;
}
header_size += sizeof(datum->timestamp);
}
}
{ // Version: 12 bytes
{ // Tag: 8 bytes
Char tag[8] = "Version";
if (!func(tag, elemin(tag), funcdata)) {
return false;
}
if (!strleq(tag, elemin(tag), "Version")) {
return false;
}
header_size += sizeof(tag);
} { // Element Count: 4 bytes
if (!srlzSint32(&datum->version, func, funcdata, is_dumping, big_endian)) {
return false;
}
header_size += sizeof(datum->version);
}
}
{ // Count: 10 bytes
{ // Tag: 6 bytes
Char tag[6] = "Count";
if (!func(tag, elemin(tag), funcdata)) {
return false;
}
if (!strleq(tag, elemin(tag), "Count")) {
return false;
}
header_size += sizeof(tag);
} { // Element Count: 4 bytes
if (!srlzUint32(&datum->entry_count, func, funcdata, is_dumping, big_endian)) {
return false;
}
header_size += sizeof(datum->entry_count);
}
}
{ // Pad: 8 bytes
Char tag[8] = "EndHead";
if (!func(tag, elemin(tag), funcdata)) {
return false;
}
if (!strleq(tag, elemin(tag), "EndHead")) {
return false;
}
header_size += sizeof(tag);
}
debugAssert(header_size == 64);
return true;
}
header_function
Bool kosh_SrlzEntry (struct Kosh_Writer_Entry *datum, SRLZ_FUNC((*func)), void *funcdata, Bool is_dumping)
{
Bool big_endian = true;
{ // Magic: 6 bytes
Char magic[6] = "Entry";
if (!func(magic, elemin(magic), funcdata)) {
return false;
}
if (!strleq(magic, elemin(magic), "Entry")) {
return false;
}
}
{ // Group ID: 20 bytes
{ // Tag: 8 bytes
Char tag[8] = "GroupID";
if (!func(tag, elemin(tag), funcdata)) {
return false;
}
if (!strleq(tag, elemin(tag), "GroupID")) {
return false;
}
} { // ID: 4 bytes
if (!srlzSint32(cast_ptr(Sint32*, &datum->id), func, funcdata, is_dumping, big_endian)) {
return false;
}
} { // ID str: 8 bytes
struct Roop_Group_ID_Str cidstr = roop_UnpackGroupIDIntoGroupIDStr(datum->id);
debugAssert(strlen(cidstr.str) == 6);
Char idstr[8] = {0, cidstr.str[0], cidstr.str[1], cidstr.str[2],
cidstr.str[3], cidstr.str[4], cidstr.str[5], 0};
if (!func(idstr, elemin(idstr), funcdata)) {
return false;
}
if (!strleq(cidstr.str, strlen(cidstr.str), idstr + 1)) {
return false;
}
}
}
{ // Data offset: 20 bytes
{ // Tag: 12 bytes
Char tag[12] = "DatumOffset";
if (!func(tag, elemin(tag), funcdata)) {
return false;
}
if (!strleq(tag, elemin(tag), "DatumOffset")) {
return false;
}
} { // Offset: 8 bytes
if (!srlzUint64(&datum->offset, func, funcdata, is_dumping, big_endian)) {
return false;
}
}
}
{ // Data size: 18 bytes
{ // Tag: 10 bytes
Char tag[10] = "DatumSize";
if (!func(tag, elemin(tag), funcdata)) {
return false;
}
if (!strleq(tag, elemin(tag), "DatumSize")) {
return false;
}
} { // Flags: 8 byte
if (!srlzUint64(&datum->size, func, funcdata, is_dumping, big_endian)) {
return false;
}
}
}
{ // Flags: 10 bytes
{ // Tag: 6 bytes
Char tag[6] = "Flags";
if (!func(tag, elemin(tag), funcdata)) {
return false;
}
if (!strleq(tag, elemin(tag), "Flags")) {
return false;
}
} { // Flags: 4 byte
if (!srlzUint32(&datum->flags, func, funcdata, is_dumping, big_endian)) {
return false;
}
}
}
{ // Name: 5+1+len
{ // Tag: 5 bytes
Char tag[5] = "Name";
if (!func(tag, elemin(tag), funcdata)) {
return false;
}
if (!strleq(tag, elemin(tag), "Name")) {
return false;
}
} {
// Name: 1+len
if (!srlzLabel(&datum->name, func, funcdata, is_dumping, big_endian)) {
return false;
}
}
}
{ // Tag: 4 bytes
Char tag[4] = "End";
if (!func(tag, elemin(tag), funcdata)) {
return false;
}
if (!strleq(tag, elemin(tag), "End")) {
return false;
}
}
// Total size: 84+len, where len is the length of name
return true;
}
typedef struct Kosh_Writer {
SRLZ_FUNC((*func));
void *funcdata;
Memory_Push *mempush;
Uint64 timestamp;
Sint32 version;
Uint32 entry_count;
Uint64 current_offset;
struct Kosh_Writer_Entry *entries; // Head of linked list
} Kosh_Writer;
header_function
Kosh_Writer* koshWriteCommence (Uint64 timestamp, SRLZ_FUNC((*func)), void *funcdata, Memory_Push *mempush)
{
Kosh_Writer *kw = cast_ptr(typeof(kw), memPushAllot(mempush, sizeof(*kw)));
kw->func = func;
kw->funcdata = funcdata;
kw->mempush = mempush;
kw->timestamp = timestamp;
kw->version = Kosh_Version_LAST;
return kw;
}
header_function
Bool koshWriteAddEntry (Kosh_Writer *kw, Roop_Group_ID id, Label name, void *data, Size size, Uint32 flags)
{
void *datacpy = memPushAllot(kw->mempush, size);
memcpy(datacpy, data, size);
struct Kosh_Writer_Entry *ke = cast_ptr(typeof(ke), memPushAllot(kw->mempush, sizeof(*ke)));
ke->name = name;
ke->size = size;
ke->id = id;
ke->flags = flags;
ke->data = datacpy;
ke->offset = kw->current_offset;
ke->next = kw->entries;
kw->entries = ke;
kw->entry_count++;
kw->current_offset += size;
return true;
}
header_function
Bool koshWriteConclude (Kosh_Writer *kw)
{
{ // Header
struct Kosh_Writer_Header header = {
.timestamp = kw->timestamp,
.version = kw->version,
.entry_count = kw->entry_count,
};
if (!kosh_SrlzHeader(&header, kw->func, kw->funcdata, true)) {
return false;
}
}
for (struct Kosh_Writer_Entry *ke = kw->entries; ke != nullptr; ke = ke->next) { // Index Entries
ke->offset = kw->current_offset - ke->offset - ke->size; // Since we are going in reverse direction
if (!kosh_SrlzEntry(ke, kw->func, kw->funcdata, true)) {
return false;
}
}
for (struct Kosh_Writer_Entry *ke = kw->entries; ke != nullptr; ke = ke->next) { // Actual Data
if (!kw->func(ke->data, ke->size, kw->funcdata)) {
return false;
}
}
if (!kw->func(cast_ptr(void*, cast_const(Char*, "EndKoshRoop")), 12, kw->funcdata)) {
return false;
}
return true;
}
typedef struct Kosh_Entry {
Roop_Refer refer;
Uint32 flags;
Uint64 data_size;
Size data_offset_from_data_beginning;
} Kosh_Entry;
typedef struct Kosh {
Uint64 timestamp;
Sint32 version;
Uint32 entry_count;
Kosh_Entry *entries;
} Kosh;
// NOTE(naman): When this function returns, make sure to store the read offset.
// The data offsets will be calculated from there.
header_function
Kosh* koshRead (SRLZ_FUNC((*func)), void *funcdata, Memory_Push *mempush)
{
Kosh *kosh = cast_ptr(typeof(kosh), memPushAllot(mempush, sizeof(*kosh)));
{ // Header
struct Kosh_Writer_Header header = {};
if (!kosh_SrlzHeader(&header, func, funcdata, false)) {
return nullptr;
}
kosh->timestamp = header.timestamp;
kosh->version = header.version;
kosh->entry_count = header.entry_count;
}
kosh->entries = cast_ptr(typeof(kosh->entries), memPushAllot(mempush, kosh->entry_count * sizeof(*(kosh->entries))));
for (Uint32 i = 0; i < kosh->entry_count; i++) {
struct Kosh_Writer_Entry ke = {};
if (!kosh_SrlzEntry(&ke, func, funcdata, false)) {
return nullptr;
}
kosh->entries[i].refer = roopReferMake(ke.name, ke.id);
kosh->entries[i].flags = ke.flags;
kosh->entries[i].data_size = ke.size;
kosh->entries[i].data_offset_from_data_beginning = ke.offset;
}
return kosh;
}
pragma_clang("clang diagnostic pop");
pragma_msvc("warning ( pop )");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment