store level files in textual format

This commit is contained in:
jacob 2026-01-09 06:46:52 -06:00
parent 1c042f1c11
commit a00fcf3fba
16 changed files with 904 additions and 294 deletions

Binary file not shown.

Binary file not shown.

View File

@ -787,8 +787,6 @@ i64 BB_IntFromTwosCompliment(u64 tc, u8 num_bits)
////////////////////////////////////////////////////////////
//~ Test
#if BITBUFF_TEST
void BB_Test(void)
{
TempArena scratch = BeginScratchNoConflict();
@ -937,5 +935,3 @@ void BB_Test(void)
EndScratch(scratch);
}
#endif

View File

@ -187,6 +187,4 @@ i64 BB_IntFromTwosCompliment(u64 tc, u8 num_bits);
////////////////////////////////////////////////////////////
//~ Test
#if BITBUFF_TEST
void BB_Test(void);
#endif

444
src/base/base_crum.c Normal file
View File

@ -0,0 +1,444 @@
////////////////////////////////////////////////////////////
//~ Value conversion utilities
i64 CR_IntFromString(String str)
{
i64 result = 0;
b32 ok = 1;
b32 is_hex = 0;
u64 pos = 0;
if (StringBeginsWith(str, Lit("0x")))
{
pos += 2;
is_hex = 1;
}
// Eat sign
i32 sign = 1;
if (ok && !is_hex)
{
if (pos < str.len)
{
if (str.text[pos] == '-')
{
sign = -1;
pos += 1;
}
else if (str.text[pos] == '+')
{
pos += 1;
}
}
}
// Parse number
while (ok && pos < str.len)
{
u8 c = str.text[pos];
u8 digit = 0;
if (c >= '0' && c <= '9')
{
digit = c - '0';
}
else if (is_hex)
{
if (c >= 'a' && c <= 'f')
{
digit = 10 + c - 'a';
}
else if (c >= 'A' && c <= 'F')
{
digit = 10 + c - 'A';
}
else
{
ok = 0;
}
}
else
{
ok = 0;
}
pos += 1;
result += digit * PowU64(10 + is_hex * 6, str.len - pos);
}
if (ok)
{
result *= sign;
}
else
{
result = 0;
}
return result;
}
// TODO: Handle hexadecimal
f64 CR_FloatFromString(String str)
{
f64 result = 0;
b32 ok = 1;
// Eat sign
u64 whole_start_idx = 0;
i32 sign = 1;
if (ok && str.len > 0)
{
if (whole_start_idx < str.len)
{
u8 c = str.text[whole_start_idx];
if (c == '-')
{
sign = -1;
whole_start_idx += 1;
}
else if (c == '+')
{
whole_start_idx += 1;
}
}
}
// Find decimal place
u64 frac_start_idx = whole_start_idx;
for (; ok && frac_start_idx < str.len; ++frac_start_idx)
{
u8 c = str.text[frac_start_idx];
if (c == '.')
{
break;
}
}
// Parse whole part
u64 whole_part = 0;
for (u64 char_idx = whole_start_idx; ok && char_idx < frac_start_idx; ++char_idx)
{
u8 c = str.text[char_idx];
if (c >= '0' && c <= '9')
{
u8 digit = c - '0';
whole_part += digit * PowU64(10, frac_start_idx - (char_idx + 1));
}
else
{
ok = 0;
}
}
// Parse frac part
u64 frac_part = 0;
for (u64 char_idx = frac_start_idx + 1; ok && char_idx < str.len; ++char_idx)
{
u8 c = str.text[char_idx];
if (c >= '0' && c <= '9')
{
u8 digit = c - '0';
frac_part += digit * PowU64(10, str.len - (char_idx + 1));
}
else
{
ok = 0;
}
}
if (ok)
{
if (frac_part != 0)
{
result = ((f64)whole_part + ((f64)frac_part / PowU64(10, str.len - (frac_start_idx + 1)))) * sign;
}
else
{
result = (f64)whole_part * sign;
}
}
else
{
result = 0;
}
return result;
}
b32 CR_BoolFromString(String str)
{
return MatchString(str, Lit("true"));
}
Vec4 CR_Vec4FromString(String str)
{
Vec4 result = Zi;
b32 ok = 1;
if (StringBeginsWith(str, Lit("(")))
{
str.text += 1;
str.len -= 1;
}
if (StringEndsWith(str, Lit(")")))
{
str.len -= 1;
}
u64 pos = 0;
u64 parts_found = 0;
String part = Zi;
part.text = str.text;
while (pos < str.len && parts_found < 4)
{
u8 c = str.text[pos];
if (c == ',' || c == ')' || pos + 1 >= str.len)
{
part.len = (str.text + pos) - part.text;
f64 part_float = CR_FloatFromString(part);
result.v[parts_found] = part_float;
parts_found += 1;
part.text = str.text + pos + 2;
pos += 2;
}
else
{
pos += 1;
}
}
return result;
}
Vec3 CR_Vec3FromString(String str)
{
Vec4 v4 = CR_Vec4FromString(str);
Vec3 result = Zi;
result.x = v4.x;
result.y = v4.y;
result.z = v4.z;
return result;
}
Vec2 CR_Vec2FromString(String str)
{
Vec4 v4 = CR_Vec4FromString(str);
Vec2 result = Zi;
result.x = v4.x;
result.y = v4.y;
return result;
}
////////////////////////////////////////////////////////////
//~ Parse
CR_Item *CR_ItemFromString(Arena *arena, String str)
{
CR_Item *result = PushStruct(arena, CR_Item);
TempArena scratch = BeginScratch(arena);
Enum(Mode)
{
Mode_None,
Mode_Text,
Mode_SingleLineComment,
Mode_MultiLineComment,
};
Mode mode = Mode_None;
Struct(ItemNode)
{
ItemNode *next;
CR_Item *item;
};
// TODO: Reuse these
ItemNode *top_item = PushStruct(scratch.arena, ItemNode);
top_item->item = result;
CR_Item *cur_item = 0;
i64 text_start = 0;
b32 text_is_string = 0;
b32 text_has_name = 0;
i64 pos = 0;
b32 is_escaped = 0;
while (pos < (i64)str.len)
{
u8 c = str.text[pos];
u8 next_c = 0;
if (pos + 1 < (i64)str.len)
{
next_c = str.text[pos + 1];
}
b32 is_eol = c == '\n' || c == '\r' || c == 0;
b32 is_whitespace = c == ' ' || c == '\t' || is_eol;
b32 next_is_eol = next_c == '\n' || next_c == '\r' || next_c == 0;
b32 next_is_whitespace = next_c == ' ' || next_c == '\t' || next_is_eol;
switch (mode)
{
case Mode_None:
{
if (c == '/' && next_c == '/')
{
mode = Mode_SingleLineComment;
pos += 2;
}
else if (c == '/' && next_c == '*')
{
mode = Mode_MultiLineComment;
pos += 2;
}
else if (!is_whitespace)
{
mode = Mode_Text;
text_is_string = 0;
text_start = pos;
}
else
{
pos += 1;
}
} break;
case Mode_Text:
{
i64 text_end = -1;
b32 is_name = 0;
if (is_escaped)
{
is_escaped = 0;
}
else
{
if (c == '\\')
{
is_escaped = 1;
}
else if (c == '\"')
{
if (pos == text_start)
{
text_is_string = 1;
text_start = pos + 1;
}
else
{
text_end = pos;
}
}
else if (next_c == ':')
{
text_end = pos + 1;
is_name = 1;
}
}
if (!text_is_string && next_is_whitespace)
{
text_end = pos + 1;
}
if (text_end != -1)
{
if (!cur_item)
{
cur_item = PushStruct(arena, CR_Item);
cur_item->parent = top_item->item;
DllQueuePush(cur_item->parent->first, cur_item->parent->last, cur_item);
cur_item->parent->count += 1;
}
is_escaped = 0;
text_is_string = 0;
String src_text = Zi;
if (text_end > text_start)
{
src_text.len = text_end - text_start;
src_text.text = str.text + text_start;
}
if (is_name)
{
}
// FIXME: Arrays always have an item even if empty
if (MatchString(src_text, Lit("{")))
{
ItemNode *n = PushStruct(scratch.arena, ItemNode);
n->item = cur_item;
SllStackPush(top_item, n);
pos += 1;
cur_item = 0;
}
else if (MatchString(src_text, Lit("}")))
{
SllStackPop(top_item);
pos += 1;
cur_item = 0;
}
else
{
// FIXME: Resolve escape sequences
String final_text = PushString(arena, src_text);
if (is_name)
{
cur_item->name = final_text;
}
else
{
cur_item->value = final_text;
cur_item = 0;
}
if (is_name)
{
pos += 1;
}
}
mode = Mode_None;
}
pos += 1;
} break;
case Mode_SingleLineComment:
{
if (is_eol)
{
mode = Mode_None;
}
pos += 1;
} break;
case Mode_MultiLineComment:
{
if (!is_escaped && c == '*' && next_c == '/')
{
mode = Mode_None;
pos += 2;
}
else
{
pos += 1;
}
} break;
}
}
EndScratch(scratch);
return result;
}

30
src/base/base_crum.h Normal file
View File

@ -0,0 +1,30 @@
////////////////////////////////////////////////////////////
//~ Item types
Struct(CR_Item)
{
CR_Item *parent;
CR_Item *next;
CR_Item *prev;
CR_Item *first;
CR_Item *last;
i64 count;
String name;
String value;
};
////////////////////////////////////////////////////////////
//~ Value conversion utilities
i64 CR_IntFromString(String str);
f64 CR_FloatFromString(String str);
b32 CR_BoolFromString(String str);
Vec4 CR_Vec4FromString(String str);
Vec3 CR_Vec3FromString(String str);
Vec2 CR_Vec2FromString(String str);
////////////////////////////////////////////////////////////
//~ Parse
CR_Item *CR_ItemFromString(Arena *arena, String str);

View File

@ -25,6 +25,7 @@
#include "base_resource.h"
#include "base_controller.h"
#include "base_async.h"
#include "base_crum.h"
#include "base_tweak.h"
#include "base_state.h"
#elif IsLanguageG
@ -47,6 +48,7 @@
#include "base_bitbuff.c"
#include "base_resource.c"
#include "base_controller.c"
#include "base_crum.c"
#include "base_async.c"
#include "base_state.c"
#else

View File

@ -20,7 +20,7 @@ String StringFromChar(Arena *arena, char c)
String StringFromUint(Arena *arena, u64 n, u64 base, u64 zfill)
{
// Base too large
Assert(base <= (countof(IntChars) - 1));
Assert(base <= (countof(Base16Chars) - 1));
String result = Zi;
TempArena scratch = BeginScratch(arena);
{
@ -28,7 +28,7 @@ String StringFromUint(Arena *arena, u64 n, u64 base, u64 zfill)
u8 *backwards_text = ArenaNext(scratch.arena, u8);
do
{
StringFromChar(scratch.arena, IntChars[n % base]);
StringFromChar(scratch.arena, Base16Chars[n % base]);
++result.len;
n /= base;
} while (n > 0);
@ -213,104 +213,6 @@ String StringFromUid(Arena *arena, Uid uid)
return result;
}
////////////////////////////////////////////////////////////
//~ Parsing helpers
b32 BoolFromString(String str)
{
b32 result = 0;
result = MatchString(str, Lit("true"));
return result;
}
f64 FloatFromString(String str)
{
f64 result = 0;
b32 ok = 1;
// Eat sign
u64 whole_start_idx = 0;
i32 sign = 1;
if (ok && str.len > 0)
{
if (whole_start_idx < str.len)
{
u8 c = str.text[whole_start_idx];
if (c == '-')
{
sign = -1;
whole_start_idx += 1;
}
else if (c == '+')
{
whole_start_idx += 1;
}
}
}
// Find decimal place
u64 frac_start_idx = whole_start_idx;
for (; ok && frac_start_idx < str.len; ++frac_start_idx)
{
u8 c = str.text[frac_start_idx];
if (c == '.')
{
break;
}
}
// Parse whole part
u64 whole_part = 0;
for (u64 char_idx = whole_start_idx; ok && char_idx < frac_start_idx; ++char_idx)
{
u8 c = str.text[char_idx];
if (c >= '0' && c <= '9')
{
u8 digit = c - '0';
whole_part += digit * PowU64(10, frac_start_idx - (char_idx + 1));
}
else
{
ok = 0;
}
}
// Parse frac part
u64 frac_part = 0;
for (u64 char_idx = frac_start_idx + 1; ok && char_idx < str.len; ++char_idx)
{
u8 c = str.text[char_idx];
if (c >= '0' && c <= '9')
{
u8 digit = c - '0';
frac_part += digit * PowU64(10, str.len - (char_idx + 1));
}
else
{
ok = 0;
}
}
if (ok)
{
if (frac_part != 0)
{
result = ((f64)whole_part + ((f64)frac_part / PowU64(10, str.len - (frac_start_idx + 1)))) * sign;
}
else
{
result = (f64)whole_part * sign;
}
}
else
{
result = 0;
}
return result;
}
////////////////////////////////////////////////////////////
//~ String helpers
@ -653,12 +555,12 @@ String TrimWhitespace(String s)
b32 stop = 0;
while (!stop)
{
if (StringBeginsWith(s, Lit("\n")) || StringBeginsWith(s, Lit("\r")) || StringBeginsWith(s, Lit(" ")))
if (StringBeginsWith(s, Lit(" ")) || StringBeginsWith(s, Lit("\t")) || StringBeginsWith(s, Lit("\n")) || StringBeginsWith(s, Lit("\r")))
{
s.text += 1;
s.len -= 1;
}
else if (StringEndsWith(s, Lit("\n")) || StringEndsWith(s, Lit("\r")) || StringEndsWith(s, Lit(" ")))
else if (StringEndsWith(s, Lit(" ")) || StringEndsWith(s, Lit("\t")) || StringEndsWith(s, Lit("\n")) || StringEndsWith(s, Lit("\r")))
{
s.len -= 1;
}
@ -1057,6 +959,67 @@ String32 String32FromString(Arena *arena, String str8)
return result;
}
////////////////////////////////////////////////////////////
//~ Base64
u64 Base64LenFromStringLen(u64 len)
{
return (len * 4 + 2) / 3;
}
u64 StringLenFromBase64Len(u64 len)
{
return len * 3 / 4;
}
String Base64FromString(Arena *arena, String str)
{
String result = Zi;
PERSIST Readonly u8 to_base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
result.len = Base64LenFromStringLen(str.len);
result.text = PushStructsNoZero(arena, u8, result.len);
u64 src_byte_pos = 0;
u64 out_byte_pos = 0;
while (src_byte_pos < str.len)
{
u32 chunk = 0;
chunk |= str.text[src_byte_pos] << 16;
if (src_byte_pos + 1 < str.len) chunk |= str.text[src_byte_pos + 1] << 8;
if (src_byte_pos + 2 < str.len) chunk |= str.text[src_byte_pos + 2] << 0;
result.text[out_byte_pos + 0] = to_base64[(chunk >> 18) & 0x3F];
result.text[out_byte_pos + 1] = to_base64[(chunk >> 12) & 0x3F];
result.text[out_byte_pos + 2] = to_base64[(chunk >> 6) & 0x3F];
result.text[out_byte_pos + 3] = to_base64[(chunk >> 0) & 0x3F];
src_byte_pos += 3;
out_byte_pos += 4;
}
return result;
}
String StringFromBase64(Arena *arena, String str)
{
String result = Zi;
result.len = StringLenFromBase64Len(str.len);
result.text = PushStructsNoZero(arena, u8, result.len);
PERSIST Readonly u8 from_base64[256] = {['A']=0,['B']=1,['C']=2,['D']=3,['E']=4,['F']=5,['G']=6,['H']=7,['I']=8,['J']=9,['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,['Q']=16,['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,['Y']=24,['Z']=25,['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,['g']=32,['h']=33,['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,['o']=40,['p']=41,['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,['w']=48,['x']=49,['y']=50,['z']=51,['0']=52,['1']=53,['2']=54,['3']=55,['4']=56,['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,['-']=62,['_']=63};
u64 src_byte_pos = 0;
u64 out_byte_pos = 0;
while (src_byte_pos < str.len)
{
u32 chunk = 0;
chunk |= (from_base64[str.text[src_byte_pos + 0]]) << 18;
chunk |= (from_base64[str.text[src_byte_pos + 1]]) << 12;
chunk |= (from_base64[str.text[src_byte_pos + 2]]) << 6;
chunk |= (from_base64[str.text[src_byte_pos + 3]]) << 0;
result.text[out_byte_pos + 0] = (chunk >> 16) & 0xFF;
result.text[out_byte_pos + 1] = (chunk >> 8) & 0xFF;
result.text[out_byte_pos + 2] = (chunk >> 0) & 0xFF;
src_byte_pos += 4;
out_byte_pos += 3;
}
return result;
}
////////////////////////////////////////////////////////////
//~ Null-terminated strings

View File

@ -2,7 +2,7 @@
//~ Formatting types
#define DefaultFmtPrecision 3
#define IntChars ("0123456789abcdef")
#define Base16Chars ("0123456789abcdef")
Enum(FmtArgKind)
{
@ -84,12 +84,6 @@ String StringFromPtr(Arena *arena, void *ptr);
String StringFromhandle(Arena *arena, u64 v);
String StringFromUid(Arena *arena, Uid uid);
////////////////////////////////////////////////////////////
//~ Parsing helpers
b32 BoolFromString(String str);
f64 FloatFromString(String str);
////////////////////////////////////////////////////////////
//~ String helpers
@ -173,6 +167,15 @@ String StringFromString32(Arena *arena, String32 str32);
String16 String16FromString(Arena *arena, String str8);
String32 String32FromString(Arena *arena, String str8);
////////////////////////////////////////////////////////////
//~ Base64
u64 Base64LenFromStringLen(u64 len);
u64 StringLenFromBase64Len(u64 len);
String Base64FromString(Arena *arena, String str);
String StringFromBase64(Arena *arena, String str);
////////////////////////////////////////////////////////////
//~ Null-terminated strings

View File

@ -110,7 +110,7 @@ b32 TweakBool_(String name, b32 initial, TweakBoolDesc desc)
var.initial = initial_str;
}
String tweak_str = TweakEx(scratch.arena, var, 0);
result = BoolFromString(tweak_str);
result = CR_BoolFromString(tweak_str);
}
EndScratch(scratch);
return result;
@ -135,7 +135,7 @@ f64 TweakFloat_(String name, f64 initial, TweakFloatDesc desc)
var.precision = desc.precision;
}
String tweak_str = TweakEx(scratch.arena, var, 0);
result = FloatFromString(tweak_str);
result = CR_FloatFromString(tweak_str);
}
EndScratch(scratch);
return result;

View File

@ -131,7 +131,7 @@ M_TokenFileList M_TokensFromSrcDirs(Arena *arena, StringList src_dirs)
{
u8 c = t[p];
b32 is_eol = c == '\r' || c == '\n' || c == 0 || (p + 1) >= l;
b32 is_whitespace = is_eol || c == ' ';
b32 is_whitespace = is_eol || c == ' ' || c == '\t';
if (is_whitespace)
{ // Whitespace
if (cur_ident.len > 0)

View File

@ -1127,17 +1127,41 @@ S_Ent *S_PushTempEnt(Arena *arena, S_EntList *list)
void S_SpawnEntsFromList(Arena *arena, S_World *world, S_EntList ents)
{
// FIXME: Don't reinsert duplicates
// FIXME: Don't insert nil keys
for (S_EntListNode *n = ents.first; n; n = n->next)
{
S_Ent *src = &n->ent;
S_Ent *dst = PushStructNoZero(arena, S_Ent);
*dst = *src;
S_EntBin *bin = &world->ent_bins[dst->key.v % world->ent_bins_count];
DllQueuePush(world->first_ent, world->last_ent, dst);
DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin);
++world->ents_count;
S_Key key = src->key;
if (!S_IsKeyNil(src->key))
{
S_EntBin *bin = &world->ent_bins[key.v % world->ent_bins_count];
S_Ent *dst = bin->first;
for (; dst; dst = dst->next_in_bin)
{
if (S_MatchKey(dst->key, key))
{
break;
}
}
if (!dst)
{
// FIXME: Use free list
dst = PushStructNoZero(arena, S_Ent);
DllQueuePush(world->first_ent, world->last_ent, dst);
DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin);
}
S_Ent *old_next = dst->next;
S_Ent *old_prev = dst->prev;
S_Ent *old_next_in_bin = dst->next_in_bin;
S_Ent *old_prev_in_bin = dst->prev_in_bin;
{
*dst = *src;
}
dst->next = old_next;
dst->prev = old_prev;
dst->next_in_bin = old_next_in_bin;
dst->prev_in_bin = old_prev_in_bin;
++world->ents_count;
}
}
}
@ -1232,38 +1256,30 @@ void S_TickForever(WaveLaneCtx *lane)
{
packed = SwappedStateFromName(frame_arena, Lit("pp_sim.swp"));
}
S_TranscodeResult tr = S_TranscodeWorld(frame_arena, 0, packed, 0);
S_UnpackedWorld unpacked = S_UnpackWorld(frame_arena, packed);
ResetArena(world_arena);
world = PushStruct(world_arena, S_World);
world->seed = tr.unpacked->seed;
world->tick = tr.unpacked->tick;
world->time_ns = tr.unpacked->time_ns;
world->seed = unpacked.seed;
world->tick = unpacked.tick;
world->time_ns = unpacked.time_ns;
world->ent_bins_count = Kibi(16);
world->ent_bins = PushStructs(world_arena, S_EntBin, world->ent_bins_count);
// Copy tiles
world->tiles = PushStructsNoZero(world_arena, u8, S_TilesCount);
CopyStructs(world->tiles, tr.unpacked->tiles, S_TilesCount);
CopyStructs(world->tiles, unpacked.tiles, S_TilesCount);
// Copy ents
world->ents_count = tr.unpacked->ents_count;
for (S_Ent *src = tr.unpacked->first_ent; src; src = src->next)
{
S_Ent *dst = PushStructNoZero(world_arena, S_Ent);
*dst = *src;
S_EntBin *bin = &world->ent_bins[dst->key.v % world->ent_bins_count];
DllQueuePush(world->first_ent, world->last_ent, dst);
DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin);
}
S_SpawnEntsFromList(world_arena, world, unpacked.ents);
}
//- Swap out
if (swapout)
{
S_TranscodeResult tr = S_TranscodeWorld(frame_arena, world, Zstr, 1);
WriteSwappedState(Lit("pp_sim.swp"), tr.packed);
String packed = S_PackWorld(frame_arena, world);
WriteSwappedState(Lit("pp_sim.swp"), packed);
}
}
@ -1520,7 +1536,7 @@ void S_TickForever(WaveLaneCtx *lane)
// // Vec2 shape0_pt = S_SupportPointFromShape(shape0, shape_dir);
// // Vec2 shape1_pt = S_SupportPointFromShape(shape1, neg_shape_dir);
// S_CollisionResult collision_data = S_CollisionResultFromShapes(shape0, shape1, VEC2(0, 0));
// S_CollisionResult collision_data = S_CollisionResultFromShapes(shape0, shape1);
// Vec2 shape0_pt = collision_data.closest_p0;
// Vec2 shape1_pt = collision_data.closest_p1;
@ -1606,8 +1622,8 @@ void S_TickForever(WaveLaneCtx *lane)
S_EntList bullets_to_spawn = Zi;
for (S_Ent *firer = S_FirstEnt(world); firer->valid; firer = S_NextEnt(firer))
{
// if (firer->fire_held)
if (firer->fire_presses)
if (firer->fire_held)
// if (firer->fire_presses)
{
// i64 fire_delta_ns = world->time_ns - firer->last_fire_ns;

View File

@ -1,153 +1,307 @@
////////////////////////////////////////////////////////////
//~ Transcode
S_TranscodeResult S_TranscodeWorld(Arena *arena, S_World *src_world, String src_packed, b32 pack)
// FIXME: Header
String S_PackWorld(Arena *arena, S_World *src_world)
{
S_TranscodeResult result = Zi;
result.ok = 1;
result.version = S_Tv_Latest;
String result = Zi;
result.text = ArenaNext(arena, u8);
TempArena scratch = BeginScratch(arena);
//////////////////////////////
//- Init bitbuff
result.len += StringF(arena, "version: %F\n", FmtUint(S_Tv_Latest)).len;
u32 level_magic = 0xa2bf209c;
BB_Buff bb = Zi;
BB_Writer bw = Zi;
BB_Reader br = Zi;
if (pack)
// Pack entities
// FIXME: Precision
result.len += PushString(arena, Lit("\nentities:\n")).len;
result.len += PushString(arena, Lit("{\n")).len;
for (S_Ent *ent = S_FirstEnt(src_world); ent->valid; ent = S_NextEnt(ent))
{
bb = BB_AcquireDynamicBuff(Gibi(4));
bw = BB_WriterFromBuff(&bb);
BB_WriteUBits(&bw, level_magic, 32);
BB_WriteUBits(&bw, result.version, 32);
}
else
{
bb = BB_BuffFromString(src_packed);
br = BB_ReaderFromBuff(&bb);
result.ok = BB_ReadUBits(&br, 32) == level_magic;
result.version = (S_Tv)BB_ReadUBits(&br, 32);
result.unpacked = PushStruct(arena, S_World);
result.len += StringF(arena, " 0x%F:\n", FmtHex(ent->key.v)).len;
result.len += PushString(arena, Lit(" {\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;
if (ent->is_player)
{
result.len += PushString(arena, Lit(" player: true\n")).len;
}
}
result.len += PushString(arena, Lit(" }\n")).len;
}
result.len += PushString(arena, Lit("}\n")).len;
//////////////////////////////
//- Transcode world metadata
if (pack)
{
BB_WriteUBits(&bw, src_world->seed, 64);
BB_WriteIBits(&bw, src_world->tick, 64);
BB_WriteIBits(&bw, src_world->time_ns, 64);
}
else
{
result.unpacked->seed = BB_ReadUBits(&br, 64);
result.unpacked->tick = BB_ReadIBits(&br, 64);
result.unpacked->time_ns = BB_ReadIBits(&br, 64);
}
//////////////////////////////
//- Transcode tiles
// Pack tiles
// TODO: Compress tile data
if (pack)
result.len += PushString(arena, Lit("\ntiles:\n")).len;
result.len += PushString(arena, Lit("{\n")).len;
{
String tiles_str = Zi;
tiles_str.len = S_TilesCount;
tiles_str.text = src_world->tiles;
BB_WriteUBits(&bw, tiles_str.len, 64);
BB_WriteBytes(&bw, tiles_str);
}
else
{
u64 raw_tiles_count = BB_ReadUBits(&br, 64);
u8 *raw_tiles = BB_ReadBytesRaw(&br, raw_tiles_count);
if (raw_tiles && raw_tiles_count == S_TilesCount)
{
result.unpacked->tiles = PushStructsNoZero(arena, u8, raw_tiles_count);
CopyBytes(result.unpacked->tiles, raw_tiles, raw_tiles_count);
}
else
{
result.unpacked->tiles = PushStructs(arena, u8, S_TilesCount);
result.ok = 0;
}
}
//////////////////////////////
//- Transcode entities
// TODO: Compress entity data
if (result.ok)
{
if (pack)
{
u32 ent_size = sizeof(S_Ent);
u32 ent_align = alignof(S_Ent);
i64 ents_count = src_world->ents_count;
BB_WriteUBits(&bw, ent_size, 32);
BB_WriteUBits(&bw, ent_align, 32);
BB_WriteUBits(&bw, ents_count, 64);
for (S_Ent *ent = S_FirstEnt(src_world); ent->valid; ent = S_NextEnt(ent))
{
BB_WriteBytes(&bw, StringFromStruct(ent));
}
}
else
{
i64 ent_size = BB_ReadUBits(&br, 32);
i64 ent_align = BB_ReadUBits(&br, 32);
i64 ents_count = BB_ReadUBits(&br, 64);
if (ent_size == sizeof(S_Ent) && ent_align == alignof(S_Ent))
{
for (i64 i = 0; i < ents_count; ++i)
{
S_Ent *raw_ent = (S_Ent *)BB_ReadBytesRaw(&br, sizeof(S_Ent));
if (raw_ent)
{
S_Ent *ent = PushStructNoZero(arena, S_Ent);
{
CopyBytes(ent, raw_ent, ent_size);
ent->valid = 1;
}
DllQueuePush(result.unpacked->first_ent, result.unpacked->last_ent, ent);
result.unpacked->ents_count += 1;
}
else
{
result.ok = 0;
break;
}
}
}
else
{
result.ok = 0;
}
}
}
//////////////////////////////
//- Finalize
if (result.ok)
{
if (pack)
{
result.packed = BB_GetWritten(arena, &bw);
BB_ReleaseDynamicBuff(&bb);
}
else
{
if (!result.unpacked->seed)
{
TrueRand(StringFromStruct(&result.unpacked->seed));
}
}
String tiles_str = Base64FromString(scratch.arena, STRING(S_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;
}
S_UnpackedWorld S_UnpackWorld(Arena *arena, String packed)
{
S_UnpackedWorld result = Zi;
TempArena scratch = BeginScratch(arena);
CR_Item *root = CR_ItemFromString(scratch.arena, packed);
// Get version
S_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)
{
// Parse entities
if (MatchString(top_item->name, Lit("entities")))
{
for (CR_Item *ent_item = top_item->first; ent_item; ent_item = ent_item->next)
{
S_Ent *ent = S_PushTempEnt(arena, &result.ents);
ent->key = (S_Key) { .v = CR_IntFromString(ent_item->name) };
for (CR_Item *prop = ent_item->first; prop; prop = prop->next)
{
if (MatchString(prop->name, Lit("pos")))
{
Vec2 pos = CR_Vec2FromString(prop->value);
ent->xf.og = pos;
}
if (MatchString(prop->name, Lit("rot")))
{
Vec2 rot = CR_Vec2FromString(prop->value);
ent->xf = XformWithWorldRotation(ent->xf, AngleFromVec2(rot));
}
if (MatchString(prop->name, Lit("exists")))
{
ent->exists = CR_FloatFromString(prop->value);
}
if (MatchString(prop->name, Lit("health")))
{
ent->health = CR_FloatFromString(prop->value);
}
if (MatchString(prop->name, Lit("look")))
{
Vec2 look = CR_Vec2FromString(prop->value);
ent->look = look;
}
if (MatchString(prop->name, Lit("player")) && CR_BoolFromString(prop->value))
{
ent->is_player = 1;
ent->has_weapon = 1;
}
// if (MatchString(prop->name, Lit("bullet")) && CR_BoolFromString(prop->value))
// {
// ent->is_bullet = 1;
// }
}
}
}
// 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)
{
tiles_base64.len += PushString(scratch.arena, tile_item->value).len;
}
if (StringLenFromBase64Len(tiles_base64.len) == S_TilesCount)
{
result.tiles = StringFromBase64(arena, tiles_base64).text;
}
}
if (!result.tiles)
{
result.tiles = PushStructs(arena, u8, S_TilesCount);
}
EndScratch(scratch);
return result;
};
// ////////////////////////////////////////////////////////////
// //~ Transcode
// S_TranscodeResult S_TranscodeWorld(Arena *arena, S_World *src_world, String src_packed, b32 pack)
// {
// S_TranscodeResult result = Zi;
// result.ok = 1;
// result.version = S_Tv_Latest;
// //////////////////////////////
// //- Init bitbuff
// u32 level_magic = 0xa2bf209c;
// BB_Buff bb = Zi;
// BB_Writer bw = Zi;
// BB_Reader br = Zi;
// if (pack)
// {
// bb = BB_AcquireDynamicBuff(Gibi(4));
// bw = BB_WriterFromBuff(&bb);
// BB_WriteUBits(&bw, level_magic, 32);
// BB_WriteUBits(&bw, result.version, 32);
// }
// else
// {
// bb = BB_BuffFromString(src_packed);
// br = BB_ReaderFromBuff(&bb);
// result.ok = BB_ReadUBits(&br, 32) == level_magic;
// result.version = (S_Tv)BB_ReadUBits(&br, 32);
// result.unpacked = PushStruct(arena, S_World);
// }
// //////////////////////////////
// //- Transcode world metadata
// if (pack)
// {
// BB_WriteUBits(&bw, src_world->seed, 64);
// BB_WriteIBits(&bw, src_world->tick, 64);
// BB_WriteIBits(&bw, src_world->time_ns, 64);
// }
// else
// {
// result.unpacked->seed = BB_ReadUBits(&br, 64);
// result.unpacked->tick = BB_ReadIBits(&br, 64);
// result.unpacked->time_ns = BB_ReadIBits(&br, 64);
// }
// //////////////////////////////
// //- Transcode tiles
// // TODO: Compress tile data
// if (pack)
// {
// String tiles_str = Zi;
// tiles_str.len = S_TilesCount;
// tiles_str.text = src_world->tiles;
// BB_WriteUBits(&bw, tiles_str.len, 64);
// BB_WriteBytes(&bw, tiles_str);
// }
// else
// {
// u64 raw_tiles_count = BB_ReadUBits(&br, 64);
// u8 *raw_tiles = BB_ReadBytesRaw(&br, raw_tiles_count);
// if (raw_tiles && raw_tiles_count == S_TilesCount)
// {
// result.unpacked->tiles = PushStructsNoZero(arena, u8, raw_tiles_count);
// CopyBytes(result.unpacked->tiles, raw_tiles, raw_tiles_count);
// }
// else
// {
// result.unpacked->tiles = PushStructs(arena, u8, S_TilesCount);
// result.ok = 0;
// }
// }
// //////////////////////////////
// //- Transcode entities
// // TODO: Compress entity data
// if (result.ok)
// {
// if (pack)
// {
// u32 ent_size = sizeof(S_Ent);
// u32 ent_align = alignof(S_Ent);
// i64 ents_count = src_world->ents_count;
// BB_WriteUBits(&bw, ent_size, 32);
// BB_WriteUBits(&bw, ent_align, 32);
// BB_WriteUBits(&bw, ents_count, 64);
// for (S_Ent *ent = S_FirstEnt(src_world); ent->valid; ent = S_NextEnt(ent))
// {
// BB_WriteBytes(&bw, StringFromStruct(ent));
// }
// }
// else
// {
// i64 ent_size = BB_ReadUBits(&br, 32);
// i64 ent_align = BB_ReadUBits(&br, 32);
// i64 ents_count = BB_ReadUBits(&br, 64);
// if (ent_size == sizeof(S_Ent) && ent_align == alignof(S_Ent))
// {
// for (i64 i = 0; i < ents_count; ++i)
// {
// S_Ent *raw_ent = (S_Ent *)BB_ReadBytesRaw(&br, sizeof(S_Ent));
// if (raw_ent)
// {
// S_Ent *ent = PushStructNoZero(arena, S_Ent);
// {
// CopyBytes(ent, raw_ent, ent_size);
// ent->valid = 1;
// }
// DllQueuePush(result.unpacked->first_ent, result.unpacked->last_ent, ent);
// result.unpacked->ents_count += 1;
// }
// else
// {
// result.ok = 0;
// break;
// }
// }
// }
// else
// {
// result.ok = 0;
// }
// }
// }
// //////////////////////////////
// //- Finalize
// if (result.ok)
// {
// if (pack)
// {
// result.packed = BB_GetWritten(arena, &bw);
// BB_ReleaseDynamicBuff(&bb);
// }
// else
// {
// if (!result.unpacked->seed)
// {
// TrueRand(StringFromStruct(&result.unpacked->seed));
// }
// }
// }
// return result;
// }

View File

@ -4,22 +4,27 @@
Enum(S_Tv)
{
S_Tv_None = 0,
S_Tv_Initial = 1,
S_Tv_COUNT
};
#define S_Tv_Latest (S_Tv_COUNT - 1)
Struct(S_TranscodeResult)
Struct(S_UnpackedWorld)
{
b32 ok;
S_Tv version;
String packed;
S_World *unpacked;
u64 seed;
i64 tick;
i64 time_ns;
S_EntList ents;
u8 *tiles;
};
////////////////////////////////////////////////////////////
//~ Transcode
S_TranscodeResult S_TranscodeWorld(Arena *arena, S_World *world, String packed, b32 pack);
String S_PackWorld(Arena *arena, S_World *src_world);
S_UnpackedWorld S_UnpackWorld(Arena *arena, String packed);

View File

@ -939,7 +939,7 @@ void V_TickForever(WaveLaneCtx *lane)
frame->draw_cursor = MulXformV2(frame->xf.ui_to_draw, frame->ui_cursor);
frame->world_cursor = MulXformV2(frame->xf.ui_to_world, frame->ui_cursor);
b32 show_editor_ui = TweakBool("Show editor UI", 0);
b32 show_editor_ui = TweakBool("Show editor UI", 1);
frame->world_selection_start = frame->world_cursor;
if (frame->is_editing)
@ -1972,7 +1972,7 @@ void V_TickForever(WaveLaneCtx *lane)
UI_Key cb_key = UI_KeyF("tweak checkbox");
UI_BoxReport cb_rep = UI_ReportsFromKey(cb_key).draw;
b32 tweak_bool = BoolFromString(new_tweak_str);
b32 tweak_bool = CR_BoolFromString(new_tweak_str);
if (cb_rep.m1.downs)
{
tweak_bool = !tweak_bool;
@ -2023,7 +2023,7 @@ void V_TickForever(WaveLaneCtx *lane)
range_max = range_min + 1;
}
f64 tweak_float = FloatFromString(new_tweak_str);
f64 tweak_float = CR_FloatFromString(new_tweak_str);
{
if (is_active)
{
@ -2821,7 +2821,7 @@ void V_TickForever(WaveLaneCtx *lane)
if (0)
{
for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet))
{

View File

@ -84,6 +84,7 @@ ComputeShader2D(V_BackdropCS, 8, 8)
Vec4 result = Vec4(0.025, 0.025, 0.025, 1);
Vec2 world_pos = mul(params.xf.ui_to_world, Vec3(ui_pos, 1));
Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos);
S_TileKind tile = tiles.Load(Vec3I32(tile_pos, 0));
f32 half_thickness = 1;
f32 half_bounds_size = S_WorldPitch * 0.5;
@ -138,7 +139,6 @@ ComputeShader2D(V_BackdropCS, 8, 8)
}
// Tile
{
S_TileKind tile = tiles.Load(Vec3I32(tile_pos, 0));
switch (tile)
{
default: break;
@ -204,7 +204,6 @@ ComputeShader2D(V_BackdropCS, 8, 8)
// cell.rgb *= cell.a;
// stain.rgb *= stain.a;
result.rgb = (stain.rgb * stain.a) + (result.rgb * (1.0 - stain.a));
result.a = (stain.a * 1) + (result.a * (1.0 - stain.a));