power_play/src/pp/pp_transcode.c
2026-01-15 13:22:24 -06:00

214 lines
6.4 KiB
C

////////////////////////////////////////////////////////////
//~ World
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, "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_dummy)
{
result.len += PushString(arena, Lit(" dummy\n")).len;
}
if (ent->has_weapon)
{
result.len += PushString(arena, Lit(" has_weapon\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->control.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("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;
}
if (MatchString(prop->value, Lit("dummy")))
{
ent->is_dummy = 1;
}
if (MatchString(prop->value, Lit("has_weapon")))
{
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->control.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;
};
////////////////////////////////////////////////////////////
//~ Message
String P_PackMessage(Arena *arena, P_Msg *msg)
{
String result = Zi;
// TODO
return result;
}
P_Msg P_UnpackMsg(Arena *arena, String packed)
{
P_Msg result = Zi;
// TODO
return result;
}