fn_test and ui_text_input work

This commit is contained in:
Krzosa Karol
2025-01-23 19:02:20 +01:00
parent fdbc8490c4
commit 0b6ea60fa7
9 changed files with 265 additions and 35 deletions

View File

@@ -81,7 +81,7 @@ fn void w32_push_event(app_frame_t *frame, app_event_t event) {
} }
fn void w32_push_wm_char_event(WPARAM wparam) { fn void w32_push_wm_char_event(WPARAM wparam) {
if (wparam <= 32 || wparam == 127) return; if (wparam < 32 || wparam == 127) return;
s8_t string = s8_lit("?"); s8_t string = s8_lit("?");
utf32_result_t encode = utf16_to_utf32((u16 *)&wparam, 1); utf32_result_t encode = utf16_to_utf32((u16 *)&wparam, 1);

View File

@@ -26,6 +26,7 @@ typedef double f64;
#endif #endif
#define fn #define fn
#define fn_test
#define gb #define gb
#define locl static #define locl static

View File

@@ -775,3 +775,27 @@ fn_inline r2i64_t r2i64_add_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i6
fn_inline r2i64_t r2i64_sub_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_sub(a.min, b), v2i64_sub(a.max, b) }; } fn_inline r2i64_t r2i64_sub_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_sub(a.min, b), v2i64_sub(a.max, b) }; }
fn_inline r2i64_t r2i64_mul_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_mul(a.min, b), v2i64_mul(a.max, b) }; } fn_inline r2i64_t r2i64_mul_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_mul(a.min, b), v2i64_mul(a.max, b) }; }
fn_inline r2i64_t r2i64_div_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_div(a.min, b), v2i64_div(a.max, b) }; } fn_inline r2i64_t r2i64_div_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_div(a.min, b), v2i64_div(a.max, b) }; }
fn_inline f32 r1f32_size(r1f32_t a) { return a.max - a.min; }
fn_inline r1f32_t r1f32(f32 min, f32 max) { return (r1f32_t){min,max}; }
fn_inline r1f32_t r1f32_auto(f32 a, f32 b) { return (r1f32_t){MIN(a,b),MAX(a,b)}; }
fn_inline r1f32_t r1f32s(f32 a) { return (r1f32_t){a,a}; }
fn_inline r1f32_t r1f32_clamp(r1f32_t a, f32 min, f32 max) { return (r1f32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
gb_read_only r1f32_t r1f32_null;
fn_inline f64 r1f64_size(r1f64_t a) { return a.max - a.min; }
fn_inline r1f64_t r1f64(f64 min, f64 max) { return (r1f64_t){min,max}; }
fn_inline r1f64_t r1f64_auto(f64 a, f64 b) { return (r1f64_t){MIN(a,b),MAX(a,b)}; }
fn_inline r1f64_t r1f64s(f64 a) { return (r1f64_t){a,a}; }
fn_inline r1f64_t r1f64_clamp(r1f64_t a, f64 min, f64 max) { return (r1f64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
gb_read_only r1f64_t r1f64_null;
fn_inline i32 r1i32_size(r1i32_t a) { return a.max - a.min; }
fn_inline r1i32_t r1i32(i32 min, i32 max) { return (r1i32_t){min,max}; }
fn_inline r1i32_t r1i32_auto(i32 a, i32 b) { return (r1i32_t){MIN(a,b),MAX(a,b)}; }
fn_inline r1i32_t r1i32s(i32 a) { return (r1i32_t){a,a}; }
fn_inline r1i32_t r1i32_clamp(r1i32_t a, i32 min, i32 max) { return (r1i32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
gb_read_only r1i32_t r1i32_null;
fn_inline i64 r1i64_size(r1i64_t a) { return a.max - a.min; }
fn_inline r1i64_t r1i64(i64 min, i64 max) { return (r1i64_t){min,max}; }
fn_inline r1i64_t r1i64_auto(i64 a, i64 b) { return (r1i64_t){MIN(a,b),MAX(a,b)}; }
fn_inline r1i64_t r1i64s(i64 a) { return (r1i64_t){a,a}; }
fn_inline r1i64_t r1i64_clamp(r1i64_t a, i64 min, i64 max) { return (r1i64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
gb_read_only r1i64_t r1i64_null;

View File

@@ -349,3 +349,18 @@ fn_inline r2f64_t r2f64_sub_v2f64(r2f64_t a, v2f64_t b) { return (r2f64_t){ v2f6
s = s.replace("f64", bt) s = s.replace("f64", bt)
s = s.replace("sub", word_op) s = s.replace("sub", word_op)
print(s) print(s)
##########
# r1 type stuff
for bt in basic_types:
s = """
fn_inline i32 r1i32_size(r1i32_t a) { return a.max - a.min; }
fn_inline r1i32_t r1i32(i32 min, i32 max) { return (r1i32_t){min,max}; }
fn_inline r1i32_t r1i32_auto(i32 a, i32 b) { return (r1i32_t){MIN(a,b),MAX(a,b)}; }
fn_inline r1i32_t r1i32s(i32 a) { return (r1i32_t){a,a}; }
fn_inline r1i32_t r1i32_clamp(r1i32_t a, i32 min, i32 max) { return (r1i32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
gb_read_only r1i32_t r1i32_null;
""".strip()
s = s.replace("i32", bt)
print(s)

View File

@@ -206,38 +206,147 @@ fn ui_signal_t ui_signal_from_box(ui_box_t *box) {
} }
fn void ui_text_input_draw(ui_box_t *box) { fn void ui_text_input_draw(ui_box_t *box) {
r2f32_t rect = box->final_rect;
v4f32_t background_color = box->background_color; v4f32_t background_color = box->background_color;
v4f32_t text_color = box->text_color;
v4f32_t border_color = box->border_color; v4f32_t border_color = box->border_color;
r2f32_t rect = box->final_rect; // @todo: this or clipped? if (ui_is_active_box(box)) {
f32 active_t = f32_ease_out_n(box->active_t, 50.f);
active_t = f32_clamp01(active_t);
background_color = v4f32_lerp(background_color, box->bg_active_color, 1.0);
text_color = v4f32_lerp(text_color, box->text_active_color, active_t);
} else if (ui_is_hot_box(box)) {
f32 hot_t = f32_ease_out_n(box->hot_t, 3.f);
hot_t = f32_clamp01(hot_t);
background_color = v4f32_lerp(background_color, box->bg_hot_color, hot_t);
text_color = v4f32_lerp(background_color, box->text_hot_color, hot_t);
} else if (ui_is_focused_box(box)) {
background_color = v4f32_hsla_to_rgba((v4f32_t){0.1f, 0.6f, 0.95f, 1.0f});
text_color = v4f32_hsla_to_rgba((v4f32_t){0.1f, 0.6f, 0.7f, 1.0f});
}
rn_draw_rect(rect, background_color); rn_draw_rect(rect, background_color);
rn_draw_rect_border(rect, border_color, box->border_thickness); rn_draw_rect_border(rect, border_color, box->border_thickness);
s8_t string = s8(box->ti_buffer, box->ti_buffer_len); ui_text_input_t *ti = &box->text_input;
v2f32_t string_size = rn_measure_string(rn_state.main_font, string); s8_t string = s8(ti->str, ti->len);
v2f32_t rect_size = r2f32_get_size(rect); v2f32_t pos = ui_aligned_text_pos(box->text_align, rect, string);
v2f32_t rect_string_diff = v2f32_sub(rect_size, string_size); rn_draw_string(rn_state.main_font, pos, text_color, string);
v2f32_t center_pos = v2f32_divs(rect_string_diff, 2);
v2f32_t pos_in_rect = v2f32(0, center_pos.y); ti->caret = ui_caret_clamp(ti->caret, 0, (i32)ti->len);
if (box->text_align == ui_text_align_center) { {
pos_in_rect = center_pos; s8_t string_min = s8(ti->str, ti->caret.range.min);
s8_t string_max = s8(ti->str, ti->caret.range.max);
v2f32_t size_min = rn_measure_string(rn_state.main_font, string_min);
v2f32_t size_max = rn_measure_string(rn_state.main_font, string_max);
r2f32_t selection_rect = r2f32(rect.min.x + size_min.x, rect.min.y, rect.min.x + size_max.x, rect.min.y + ui_em(1));
rn_draw_rect(selection_rect, v4f32(1,1,1,0.2f));
v2f32_t size_front = ti->caret.ifront == 0 ? size_min : size_max;
r2f32_t caret_rect = r2f32(rect.min.x + size_front.x, rect.min.y, rect.min.x + size_front.x + 1, rect.min.y + ui_em(1));
rn_draw_rect(caret_rect, text_color);
} }
v2f32_t pos = v2f32_add(pos_in_rect, rect.min); }
rn_draw_string(rn_state.main_font, pos, box->text_color, string);
fn i32 ui_text_replace(ui_text_input_t *ti, r1i32_t range, s8_t string) {
range.min = CLAMP(range.min, 0, (i32)ti->len);
range.max = CLAMP(range.max, range.min, (i32)ti->len);
assert(range.min >= 0 && range.max <= ti->len);
i32 size_to_remove = r1i32_size(range);
i32 size_to_add = (i32)string.len;
i32 change_size = size_to_add - size_to_remove;
if (ti->len + change_size > ti->cap) {
i32 rem = ((i32)ti->len + change_size) - ti->cap;
size_to_add -= rem;
change_size -= rem;
assert(change_size >= 0);
}
u8 *begin_remove = ti->str + range.min;
u8 *end_remove = begin_remove + size_to_remove;
i32 remain_len = (i32)ti->len - (range.min + size_to_remove);
u8 *begin_add = begin_remove;
u8 *end_add = begin_add + size_to_add;
memory_move(end_add, end_remove, remain_len);
memory_copy(begin_add, string.str, size_to_add);
ti->len = ti->len + change_size;
return change_size;
}
fn_test void ui_test_text_replace(void) {
ui_text_input_t ti = {0};
char buff[4] = {0};
ti.str = buff;
ti.cap = lengthof(buff);
ui_text_replace(&ti, r1i32(0,0), s8_lit("astfpo"));
assert(s8_are_equal(ti.string, s8_lit("astf")));
ui_text_replace(&ti, r1i32(0,4), s8_lit("qwer"));
assert(s8_are_equal(ti.string, s8_lit("qwer")));
ui_text_replace(&ti, r1i32(1,2), s8_lit("a"));
assert(s8_are_equal(ti.string, s8_lit("qaer")));
} }
fn ui_signal_t ui_text_input(ui_code_loc_t loc, char *buffer, i32 buffer_size) { fn ui_signal_t ui_text_input(ui_code_loc_t loc, char *buffer, i32 buffer_size) {
ui_box_t *box = ui_build_box_from_string(loc, (ui_box_flags_t){ .draw_border = true, .draw_rect = true, .draw_text = true }, s8_lit("text_input")); ui_box_t *box = ui_build_box_from_string(loc, (ui_box_flags_t){ .draw_border = true, .draw_rect = true, .draw_text = true }, s8_lit("text_input"));
box->custom_draw = ui_text_input_draw; box->custom_draw = ui_text_input_draw;
box->ti_buffer = buffer;
box->ti_buffer_cap = buffer_size; ui_text_input_t *ti = &box->text_input;
ti->str = buffer;
ti->cap = buffer_size;
ui_signal_t signal = ui_signal_from_box(box); ui_signal_t signal = ui_signal_from_box(box);
if (ui_is_focused_box(box)) { if (ui_is_focused_box(box)) {
app_event_t *ev = ui->event; app_event_t *ev = ui->event;
i32 sel_size = r1i32_size(ti->caret.range);
if (ev->kind == app_event_kind_text) { if (ev->kind == app_event_kind_text) {
for (i64 i = 0; i < ev->text.len; i += 1) { ui_text_replace(ti, ti->caret.range, ev->text);
box->ti_buffer[box->ti_buffer_len++] = ev->text.str[i]; if (sel_size) {
ti->caret = ui_carets(ti->caret.range.min);
} else {
ti->caret = ui_carets(ti->caret.range.min + 1);
} }
} else if (ev->kind == app_event_kind_key_down) {
if (ev->key == app_key_backspace) {
if (sel_size) {
ui_text_replace(ti, ti->caret.range, s8_lit(""));
} else {
ui_text_replace(ti, r1i32(ti->caret.e[0] - 1, ti->caret.e[0]), s8_null);
ti->caret = ui_carets(ti->caret.range.min - 1);
}
} else if (ev->key == app_key_delete) {
if (sel_size) {
ui_text_replace(ti, ti->caret.range, s8_lit(""));
} else {
ui_text_replace(ti, r1i32(ti->caret.e[0], ti->caret.e[0] + 1), s8_null);
}
} else if (ev->key == app_key_left) {
if (ev->shift) {
ti->caret = ui_caret_set_front(ti->caret, ui_caret_front(ti->caret) - 1);
} else {
ti->caret = ui_carets(ui_caret_front(ti->caret) - 1);
}
} else if (ev->key == app_key_right) {
if (ev->shift) {
ti->caret = ui_caret_set_front(ti->caret, ui_caret_front(ti->caret) + 1);
} else {
ti->caret = ui_carets(ui_caret_front(ti->caret) + 1);
}
}
} else if (signal.dragging) {
v2f32_t size = rn_measure_string(rn_state.main_font, s8_lit("_"));
v2f32_t pos = v2f32_sub(ev->mouse_pos, box->final_rect.min);
i32 p = (i32)f32_round(pos.x / size.x);
if (ev->kind == app_event_kind_mouse_down && ev->mouse_button == app_mouse_button_left) {
ti->caret = ui_carets(p);
}
ti->caret = ui_caret_set_front(ti->caret, p);
} }
ti->caret = ui_caret_clamp(ti->caret, 0, (i32)ti->len);
} }
return signal; return signal;
} }
@@ -336,12 +445,25 @@ fn void ui_end_build(void) {
} }
} }
fn v2f32_t ui_aligned_text_pos(ui_text_align_t text_align, r2f32_t rect, s8_t string) {
v2f32_t string_size = rn_measure_string(rn_state.main_font, string);
v2f32_t rect_size = r2f32_get_size(rect);
v2f32_t rect_string_diff = v2f32_sub(rect_size, string_size);
v2f32_t center_pos = v2f32_divs(rect_string_diff, 2);
v2f32_t pos_in_rect = v2f32(0, center_pos.y);
if (text_align == ui_text_align_center) {
pos_in_rect = center_pos;
}
v2f32_t pos = v2f32_add(pos_in_rect, rect.min);
return pos;
}
fn void ui_default_draw_box(ui_box_t *box) { fn void ui_default_draw_box(ui_box_t *box) {
r2f32_t rect = box->final_rect;
v4f32_t background_color = box->background_color; v4f32_t background_color = box->background_color;
v4f32_t text_color = box->text_color; v4f32_t text_color = box->text_color;
v4f32_t border_color = box->border_color; v4f32_t border_color = box->border_color;
r2f32_t rect = box->final_rect; // @todo: this or clipped?
if (ui_is_active_box(box)) { if (ui_is_active_box(box)) {
f32 active_t = f32_ease_out_n(box->active_t, 50.f); f32 active_t = f32_ease_out_n(box->active_t, 50.f);
active_t = f32_clamp01(active_t); active_t = f32_clamp01(active_t);
@@ -365,15 +487,7 @@ fn void ui_default_draw_box(ui_box_t *box) {
rn_draw_rect_border(rect, border_color, box->border_thickness); rn_draw_rect_border(rect, border_color, box->border_thickness);
} }
if (box->flags.draw_text) { if (box->flags.draw_text) {
v2f32_t string_size = rn_measure_string(rn_state.main_font, box->string); v2f32_t pos = ui_aligned_text_pos(box->text_align, rect, box->string);
v2f32_t rect_size = r2f32_get_size(rect);
v2f32_t rect_string_diff = v2f32_sub(rect_size, string_size);
v2f32_t center_pos = v2f32_divs(rect_string_diff, 2);
v2f32_t pos_in_rect = v2f32(0, center_pos.y);
if (box->text_align == ui_text_align_center) {
pos_in_rect = center_pos;
}
v2f32_t pos = v2f32_add(pos_in_rect, rect.min);
rn_draw_string(rn_state.main_font, pos, text_color, box->string); rn_draw_string(rn_state.main_font, pos, text_color, box->string);
} }
} }

View File

@@ -42,6 +42,63 @@ typedef enum {
} ui_lop_t; } ui_lop_t;
#include "ui.gen.h" #include "ui.gen.h"
typedef struct ui_caret_t ui_caret_t;
struct ui_caret_t {
union {
i32 e[2];
struct { r1i32_t range; };
};
i32 ifront;
};
fn ui_caret_t ui_caret_clamp(ui_caret_t c, i32 min, i32 max) {
return (ui_caret_t){CLAMP(c.e[0],min,max), CLAMP(c.e[1],min,max), c.ifront};
}
fn i32 ui_caret_front(ui_caret_t c) { return c.e[c.ifront]; }
fn i32 ui_caret_back(ui_caret_t c) { return c.e[c.ifront ? 0 : 1]; }
ui_caret_t ui_caret(i32 front, i32 back) {
ui_caret_t result = {0};
if (front >= back) {
result.range.min = back;
result.range.max = front;
result.ifront = 1;
} else {
result.range.min = front;
result.range.max = back;
result.ifront = 0;
}
return result;
}
ui_caret_t ui_caret_set_back(ui_caret_t caret, i32 back) {
i32 front = ui_caret_front(caret);
ui_caret_t result = ui_caret(front, back);
return result;
}
ui_caret_t ui_caret_set_front(ui_caret_t caret, i32 front) {
i32 back = ui_caret_back(caret);
ui_caret_t result = ui_caret(front, back);
return result;
}
fn ui_caret_t ui_carets(i32 x) {
return ui_caret(x, x);
}
typedef struct ui_text_input_t ui_text_input_t;
struct ui_text_input_t {
union {
struct { char *str; i64 len; };
s8_t string;
};
i32 cap;
ui_caret_t caret;
};
typedef struct ui_box_t ui_box_t; typedef struct ui_box_t ui_box_t;
struct ui_box_t { struct ui_box_t {
ui_box_t *next; ui_box_t *next;
@@ -74,10 +131,7 @@ struct ui_box_t {
r2f32_t final_rect; r2f32_t final_rect;
b32 expanded; b32 expanded;
i32 ti_cursor; ui_text_input_t text_input;
char *ti_buffer;
i32 ti_buffer_len;
i32 ti_buffer_cap;
}; };
typedef struct ui_signal_t ui_signal_t; typedef struct ui_signal_t ui_signal_t;
@@ -138,4 +192,5 @@ fn void ui_set_rect(ui_box_t *box, r2f32_t rect) { box->rect = box->full_rect =
#define ui_box_flags(...) (ui_box_flag_t){__VA_ARGS__} #define ui_box_flags(...) (ui_box_flag_t){__VA_ARGS__}
fn ui_id_t ui_id(s8_t string); fn ui_id_t ui_id(s8_t string);
fn ui_id_t ui_idf(char *str, ...); fn ui_id_t ui_idf(char *str, ...);
fn v2f32_t ui_aligned_text_pos(ui_text_align_t text_align, r2f32_t rect, s8_t string);

View File

@@ -10,6 +10,7 @@
#include "wasm_app.gen.c" #include "wasm_app.gen.c"
fn void app_init(f32 dpr) { fn void app_init(f32 dpr) {
run_all_tests();
ma_arena_t *perm = &tcx._perm; ma_arena_t *perm = &tcx._perm;
mt_tweak_f32(font_size, 50, 4, 200); mt_tweak_f32(font_size, 50, 4, 200);
mt_tweak_f32(_font_size, 50, 50, 50); mt_tweak_f32(_font_size, 50, 50, 50);

View File

@@ -2,7 +2,10 @@
gb f32 font_size = 50; gb f32 font_size = 50;
gb f32 _font_size = 50; gb f32 _font_size = 50;
gb_read_only mt_tweak_t tweak_table[] = { gb_read_only mt_tweak_t tweak_table[] = {
{type(f32), s8_const_lit("font_size"), &font_size, 4, 200}, {type(f32), s8_const_lit("font_size"), &font_size, 4, 200},
{type(f32), s8_const_lit("_font_size"), &_font_size, 50, 50}, {type(f32), s8_const_lit("_font_size"), &_font_size, 50, 50},
}; };
void run_all_tests(void) {
ui_test_text_replace();
}// run_all_tests()

View File

@@ -15,6 +15,8 @@ void mt_wasm_app(ma_arena_t *arena) {
cg_tweak_t *first_tweak = NULL; cg_tweak_t *first_tweak = NULL;
cg_tweak_t *last_tweak = NULL; cg_tweak_t *last_tweak = NULL;
sb8_t *tests = sb8_serial_begin(arena);
sb8_t *sb_embeds = sb8_serial_begin(arena); sb8_t *sb_embeds = sb8_serial_begin(arena);
sb8_printf(sb_embeds, "// automatically generated using: " __FILE__ "\n"); sb8_printf(sb_embeds, "// automatically generated using: " __FILE__ "\n");
for (mt_file_t *it = files.first; it; it = it->next) { for (mt_file_t *it = files.first; it; it = it->next) {
@@ -22,6 +24,13 @@ void mt_wasm_app(ma_arena_t *arena) {
for (;par->at->kind != lex_kind_eof;) { for (;par->at->kind != lex_kind_eof;) {
b32 matched = false; b32 matched = false;
if (par->at->inside_macro == false && parser_matchi(par, s8_lit("fn_test"))) {
parser_expecti(par, s8_lit("void"));
lex_t *ident = parser_match(par, lex_kind_ident);
sb8_append(tests, ident->string);
matched = true;
}
if (par->at->inside_macro == false && parser_matchi(par, s8_lit("mt_embed_file"))) { if (par->at->inside_macro == false && parser_matchi(par, s8_lit("mt_embed_file"))) {
parser_expect(par, lex_kind_open_paren); parser_expect(par, lex_kind_open_paren);
lex_t *var_name = parser_expect(par, lex_kind_ident); lex_t *var_name = parser_expect(par, lex_kind_ident);
@@ -88,17 +97,25 @@ void mt_wasm_app(ma_arena_t *arena) {
} }
} }
if (first_tweak != NULL) { if (first_tweak != NULL) {
for (cg_tweak_t *it = first_tweak; it; it = it->next) { for (cg_tweak_t *it = first_tweak; it; it = it->next) {
sb8_printf(sb_embeds, "gb %S %S = %S;\n", it->type->name, it->name, it->value); sb8_printf(sb_embeds, "gb %S %S = %S;\n", it->type->name, it->name, it->value);
} }
sb8_printf(sb_embeds, "gb_read_only mt_tweak_t tweak_table[] = {\n"); sb8_printf(sb_embeds, "gb_read_only mt_tweak_t tweak_table[] = {\n");
for (cg_tweak_t *it = first_tweak; it; it = it->next) { for (cg_tweak_t *it = first_tweak; it; it = it->next) {
sb8_printf(sb_embeds, "{type(%S), s8_const_lit(\"%S\"), &%S, %S, %S},\n", it->type->name, it->name, it->name, it->min, it->max); sb8_printf(sb_embeds, " {type(%S), s8_const_lit(\"%S\"), &%S, %S, %S},\n", it->type->name, it->name, it->name, it->min, it->max);
} }
sb8_printf(sb_embeds, "\n};\n"); sb8_printf(sb_embeds, "\n};\n");
} }
sb8_printf(sb_embeds, "void run_all_tests(void) {\n");
for (sb8_node_t *it = tests->first; it; it = it->next) {
sb8_printf(sb_embeds, " %S();\n", it->string);
}
sb8_printf(sb_embeds, "}// run_all_tests()\n");
s8_t embeds = sb8_serial_end(arena, sb_embeds); s8_t embeds = sb8_serial_end(arena, sb_embeds);
os_write_file(mt_cpath(arena), embeds); os_write_file(mt_cpath(arena), embeds);