//////////////////////////////////////////////////////////// //~ Transcode String P_PackWorld(Arena *arena, P_World *src_world) { String result = Zi; result.text = ArenaNext(arena, u8); TempArena scratch = BeginScratch(arena); P_Frame *src_frame = src_world->last_frame; result.len += StringF(arena, "version: %F\n", FmtUint(P_Tv_Latest)).len; result.len += StringF(arena, "\n").len; result.len += StringF(arena, "seed: 0x%F\n", FmtHex(src_world->seed)).len; result.len += StringF(arena, "tick: %F\n", FmtSint(src_frame->tick)).len; result.len += StringF(arena, "time: %F\n", FmtSint(src_frame->time_ns)).len; // Pack entities // FIXME: Precision result.len += PushString(arena, Lit("\nentities:\n")).len; result.len += PushString(arena, Lit("{\n")).len; for (P_Ent *ent = P_FirstEnt(src_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) { // TODO: Pack bullets if (!ent->is_bullet) { result.len += StringF(arena, " 0x%F:\n", FmtHex(ent->key.v)).len; result.len += PushString(arena, Lit(" {\n")).len; { result.len += StringF(arena, " props: \n").len; result.len += StringF(arena, " {\n").len; { if (ent->is_player) { result.len += PushString(arena, Lit(" player\n")).len; } if (ent->is_bullet) { result.len += PushString(arena, Lit(" bullet\n")).len; } } result.len += StringF(arena, " }\n").len; result.len += StringF(arena, " pos: \"%F\"\n", FmtFloat2(ent->xf.og)).len; result.len += StringF(arena, " rot: \"%F\"\n", FmtFloat2(RightFromXform(ent->xf))).len; result.len += StringF(arena, " exists: \"%F\"\n", FmtFloat(ent->exists)).len; result.len += StringF(arena, " look: \"%F\"\n", FmtFloat2(ent->look)).len; } result.len += PushString(arena, Lit(" }\n")).len; } } result.len += PushString(arena, Lit("}\n")).len; // Pack tiles // TODO: Compress tile data result.len += PushString(arena, Lit("\ntiles:\n")).len; result.len += PushString(arena, Lit("{\n")).len; { String tiles_str = Base64FromString(scratch.arena, STRING(P_TilesCount, src_world->tiles)); u64 tile_chars_per_line = 128; u64 tile_char_pos = 0; while (tile_char_pos < tiles_str.len) { result.len += PushString(arena, Lit(" ")).len; String src = STRING(tile_chars_per_line, tiles_str.text + tile_char_pos); src.len = MinU64(tile_chars_per_line, tiles_str.len - tile_char_pos); result.len += PushString(arena, src).len; result.len += PushString(arena, Lit("\n")).len; tile_char_pos += tile_chars_per_line; } } result.len += PushString(arena, Lit("}\n")).len; EndScratch(scratch); return result; } P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed) { P_UnpackedWorld result = Zi; TempArena scratch = BeginScratch(arena); CR_Item *root = CR_ItemFromString(scratch.arena, packed); // Unpack version P_Tv version = 0; for (CR_Item *root_item = root->first; root_item; root_item = root_item->next) { if (MatchString(root_item->name, Lit("version"))) { version = CR_IntFromString(root_item->value); break; } } for (CR_Item *top_item = root->first; top_item; top_item = top_item->next) { // Unpack metadata if (MatchString(top_item->name, Lit("seed"))) { result.seed = CR_IntFromString(top_item->value); } if (MatchString(top_item->name, Lit("tick"))) { result.tick = CR_IntFromString(top_item->value); } if (MatchString(top_item->name, Lit("time"))) { result.time_ns = CR_IntFromString(top_item->value); } // Unpack entities if (MatchString(top_item->name, Lit("entities"))) { for (CR_Item *ent_item = top_item->first; ent_item; ent_item = ent_item->next) { P_Ent *ent = P_PushTempEnt(arena, &result.ents); ent->key = (P_Key) { .v = CR_IntFromString(ent_item->name) }; for (CR_Item *attr = ent_item->first; attr; attr = attr->next) { if (MatchString(attr->name, Lit("props"))) { for (CR_Item *prop = attr->first; prop; prop = prop->next) { if (MatchString(prop->value, Lit("player"))) { ent->is_player = 1; ent->has_weapon = 1; } if (MatchString(prop->value, Lit("bullet"))) { ent->is_bullet = 1; } } } if (MatchString(attr->name, Lit("pos"))) { Vec2 pos = CR_Vec2FromString(attr->value); ent->xf.og = pos; } if (MatchString(attr->name, Lit("rot"))) { Vec2 rot = CR_Vec2FromString(attr->value); ent->xf = XformWithWorldRotation(ent->xf, AngleFromVec2(rot)); } if (MatchString(attr->name, Lit("exists"))) { ent->exists = CR_FloatFromString(attr->value); } if (MatchString(attr->name, Lit("health"))) { ent->health = CR_FloatFromString(attr->value); } if (MatchString(attr->name, Lit("look"))) { Vec2 look = CR_Vec2FromString(attr->value); ent->look = look; } } } } // Parse tiles String tiles_base64 = Zi; tiles_base64.text = ArenaNext(scratch.arena, u8); for (CR_Item *tile_item = top_item->first; tile_item; tile_item = tile_item->next) { if (tile_item->name.len == 0) { tiles_base64.len += PushString(scratch.arena, tile_item->value).len; } } if (StringLenFromBase64Len(tiles_base64.len) == P_TilesCount) { result.tiles = StringFromBase64(arena, tiles_base64).text; } } if (!result.tiles) { result.tiles = PushStructs(arena, u8, P_TilesCount); } EndScratch(scratch); return result; };