Last active
January 8, 2026 07:12
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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