Files
armorpaint/base/sources/plugins/plugin_api.c
T
2025-09-23 21:44:39 +02:00

626 lines
14 KiB
C

#include "plugin_api.h"
#include "iron_ui.h"
#include "iron_ui_nodes.h"
#include "iron_array.h"
#include "iron_map.h"
#include "iron_armpack.h"
#include "iron_obj.h"
#include "iron_vec4.h"
#include "iron_mat4.h"
void plugin_embed();
// ██╗ ██╗██████╗
// ██║ ██║██╔══██╗
// ██║ ██║██████╔╝
// ██║ ██║██╔══██╗
// ███████╗██║██████╔╝
// ╚══════╝╚═╝╚═════╝
const char *lib = "\
function buffer_to_string(b) {\
let str = \"\";\
let u8a = new Uint8Array(b);\
for (let i = 0; i < u8a.length; ++i) {\
str += String.fromCharCode(u8a[i]);\
}\
return str;\
}\
\
function string_to_buffer(str) {\
let u8a = new Uint8Array(str.length);\
for (let i = 0; i < str.length; ++i) {\
u8a[i] = str.charCodeAt(i);\
}\
return u8a.buffer;\
}\
\
let pos = 0;\
let k_type = \"\";\
\
function armpack_decode(b) {\
pos = 0;\
return read(new DataView(b));\
}\
\
function read_u8(v) {\
let i = v.getUint8(pos);\
pos++;\
return i;\
}\
\
function read_i16(v) {\
let i = v.getInt16(pos, true);\
pos += 2;\
return i;\
}\
\
function read_i32(v) {\
let i = v.getInt32(pos, true);\
pos += 4;\
return i;\
}\
\
function read_u32(v) {\
let i = v.getUint32(pos, true);\
pos += 4;\
return i;\
}\
\
function read_f32(v) {\
let f = v.getFloat32(pos, true);\
pos += 4;\
return f;\
}\
\
function read_string(v, len) {\
let s = \"\";\
for (let i = 0; i < len; ++i) {\
s += String.fromCharCode(read_u8(v));\
}\
return s;\
}\
\
function read(v) {\
let b = read_u8(v);\
switch (b) {\
case 0xc0: return null;\
case 0xc2: return false;\
case 0xc3: return true;\
case 0xca: { k_type = \"__f32\"; return read_f32(v); }\
case 0xd2: return read_i32(v);\
case 0xdb: return read_string(v, read_u32(v));\
case 0xdd: return read_array(v, read_i32(v));\
case 0xdf: return read_map(v, read_i32(v));\
}\
return 0;\
}\
\
function read_array(v, length) {\
let b = read_u8(v);\
\
if (b == 0xca) { /* Typed f32 */ \
let a = new Array(length);\
for (let x = 0; x < length; ++x) a[x] = read_f32(v);\
k_type = \"__f32\";\
return a;\
}\
else if (b == 0xd2) { /* Typed i32 */ \
let a = new Array(length);\
for (let x = 0; x < length; ++x) a[x] = read_i32(v);\
k_type = \"__i32\";\
return a;\
}\
else if (b == 0xd1) { /* Typed i16 */ \
let a = new Array(length);\
for (let x = 0; x < length; ++x) a[x] = read_i16(v);\
k_type = \"__i16\";\
return a;\
}\
else if (b == 0xc4) { /* Typed u8 */ \
let a = new Array(length);\
for (let x = 0; x < length; ++x) a[x] = read_u8(v);\
k_type = \"__u8\";\
return a;\
}\
else { /* Dynamic type-value */ \
pos--;\
let a = new Array(length);\
for (let x = 0; x < length; ++x) a[x] = read(v);\
return a;\
}\
}\
\
function read_map(v, length) {\
let out = {};\
for (let n = 0; n < length; ++n) {\
let k = read(v);\
k_type = \"\";\
let val = read(v);\
k += k_type;\
k_type = \"\";\
out[k] = val;\
}\
return out;\
}\
\
function armpack_encode(d) {\
pos = 0;\
write_dummy(d);\
let b = new ArrayBuffer(pos);\
let v = new DataView(b);\
pos = 0;\
write(v, d);\
return b;\
}\
\
function write_u8(v, i) {\
v.setUint8(pos, i);\
pos += 1;\
}\
\
function write_i16(v, i) {\
v.setInt16(pos, i, true);\
pos += 2;\
}\
\
function write_i32(v, i) {\
v.setInt32(pos, i, true);\
pos += 4;\
}\
\
function write_f32(v, f) {\
v.setFloat32(pos, f, true);\
pos += 4;\
}\
\
function write_string(v, str) {\
for (let i = 0; i < str.length; ++i) {\
write_u8(v, str.charCodeAt(i));\
}\
}\
\
function write(v, d) {\
if (d == null) {\
write_u8(v, 0xc0);\
}\
else if (typeof d == \"boolean\") {\
write_u8(v, d ? 0xc3 : 0xc2);\
}\
else if (typeof d == \"number\") {\
if (Number.isInteger(d) && k_type != \"__f32\") {\
write_u8(v, 0xd2);\
write_i32(v, d);\
}\
else {\
write_u8(v, 0xca);\
write_f32(v, d);\
}\
}\
else if (typeof d == \"string\") {\
write_u8(v, 0xdb);\
write_i32(v, d.length);\
write_string(v, d);\
}\
else if (Array.isArray(d)) {\
write_u8(v, 0xdd);\
write_i32(v, d.length);\
if (k_type == \"__u8\") {\
write_u8(v, 0xc4);\
for (let i = 0; i < d.length; ++i) {\
write_u8(v, d[i]);\
}\
}\
else if (k_type == \"__i16\") {\
write_u8(v, 0xd1);\
for (let i = 0; i < d.length; ++i) {\
write_i16(v, d[i]);\
}\
}\
else if (k_type == \"__f32\") {\
write_u8(v, 0xca);\
for (let i = 0; i < d.length; ++i) {\
write_f32(v, d[i]);\
}\
}\
else if (k_type == \"__i32\") {\
write_u8(v, 0xd2);\
for (let i = 0; i < d.length; ++i) {\
write_i32(v, d[i]);\
}\
}\
else {\
for (let i = 0; i < d.length; ++i) {\
write(v, d[i]);\
}\
}\
}\
else {\
write_object(v, d);\
}\
}\
\
function write_object(v, d) {\
let f = Object.keys(d);\
write_u8(v, 0xdf);\
write_i32(v, f.length);\
for (let i = 0; i < f.length; ++i) {\
let k = f[i];\
\
k_type = \"\";\
if (k.endsWith(\"__f32\")) {\
k_type = \"__f32\";\
}\
else if (k.endsWith(\"__i32\")) {\
k_type = \"__i32\";\
}\
else if (k.endsWith(\"__i16\")) {\
k_type = \"__i16\";\
}\
else if (k.endsWith(\"__u8\")) {\
k_type = \"__u8\";\
}\
\
write_u8(v, 0xdb);\
write_i32(v, k.length - k_type.length);\
\
write_string(v, k.substring(0, k.length - k_type.length));\
write(v, d[k]);\
k_type = \"\";\
}\
}\
\
function write_dummy(d) {\
if (d == null) {\
pos += 1;\
}\
else if (typeof d == \"boolean\") {\
pos += 1;\
}\
else if (typeof d == \"number\") {\
pos += 1;\
pos += 4;\
}\
else if (typeof d == \"string\") {\
pos += 1;\
pos += 4;\
pos += d.length;\
}\
else if (Array.isArray(d)) {\
pos += 1;\
pos += 4;\
if (k_type == \"__u8\") {\
pos += 1;\
for (let i = 0; i < d.length; ++i) {\
pos += 1;\
}\
}\
else if (k_type == \"__i16\") {\
pos += 1;\
for (let i = 0; i < d.length; ++i) {\
pos += 2;\
}\
}\
else if (k_type == \"__f32\") {\
pos += 1;\
for (let i = 0; i < d.length; ++i) {\
pos += 4;\
}\
}\
else if (k_type == \"__i32\") {\
pos += 1;\
for (let i = 0; i < d.length; ++i) {\
pos += 4;\
}\
}\
else {\
for (let i = 0; i < d.length; ++i) {\
write_dummy(d[i]);\
}\
}\
}\
else {\
write_object_dummy(d);\
}\
}\
\
function write_object_dummy(d) {\
let f = Object.keys(d);\
pos += 1;\
pos += 4;\
for (let i = 0; i < f.length; ++i) {\
let k = f[i];\
pos += 1;\
pos += 4;\
\
k_type = \"\";\
if (k.endsWith(\"__f32\")) {\
k_type = \"__f32\";\
}\
else if (k.endsWith(\"__i32\")) {\
k_type = \"__i32\";\
}\
else if (k.endsWith(\"__i16\")) {\
k_type = \"__i16\";\
}\
else if (k.endsWith(\"__u8\")) {\
k_type = \"__u8\";\
}\
\
pos += k.length - k_type.length;\
write_dummy(d[k]);\
k_type = \"\";\
}\
}\
\
globalThis.armpack_encode = armpack_encode;\
globalThis.armpack_decode = armpack_decode;\
globalThis.string_to_buffer = string_to_buffer;\
globalThis.buffer_to_string = buffer_to_string;\
";
// ██████╗ ██╗███╗ ██╗██████╗ ██╗███╗ ██╗ ██████╗ ███████╗
// ██╔══██╗██║████╗ ██║██╔══██╗██║████╗ ██║██╔════╝ ██╔════╝
// ██████╔╝██║██╔██╗ ██║██║ ██║██║██╔██╗ ██║██║ ███╗███████╗
// ██╔══██╗██║██║╚██╗██║██║ ██║██║██║╚██╗██║██║ ██║╚════██║
// ██████╔╝██║██║ ╚████║██████╔╝██║██║ ╚████║╚██████╔╝███████║
// ╚═════╝ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝
any_array_t *plugin_gc;
void gc_root(void *ptr);
void gc_run();
// These could be auto-generated by alang
VOID_FN(gc_run)
void *data_get_blob(char *s);
FN(data_get_blob) {
char *s = (char *)JS_ToCString(ctx, argv[0]);
buffer_t *b = data_get_blob(s);
JSValue val = JS_NewArrayBuffer(ctx, b->buffer, b->length, NULL, NULL, 0);
return val;
}
void *data_delete_blob(char *s);
FN(data_delete_blob) {
char *s = (char *)JS_ToCString(ctx, argv[0]);
data_delete_blob(s);
return JS_UNDEFINED;
}
void *iron_file_save_bytes(char *s, buffer_t *b, int l);
FN(iron_file_save_bytes) {
char *to = (char *)JS_ToCString(ctx, argv[0]);
size_t len;
void *ab = JS_GetArrayBuffer(ctx, &len, argv[1]);
buffer_t b = { .buffer = ab, .length = len, .capacity = len };
iron_file_save_bytes(to, &b, len);
return JS_UNDEFINED;
}
void *gpu_create_texture_from_bytes(void *p, int w, int h, int format);
FN(gpu_create_texture_from_bytes) {
size_t len;
void *ab = JS_GetArrayBuffer(ctx, &len, argv[0]);
buffer_t b = { .buffer = ab, .length = len, .capacity = len };
int64_t w;
JS_ToInt64(ctx, &w, argv[1]);
int64_t h;
JS_ToInt64(ctx, &h, argv[2]);
int64_t format = 0;
if (argc > 3) {
JS_ToInt64(ctx, &format, argv[3]);
}
uint64_t result = (uint64_t)gpu_create_texture_from_bytes(&b, w, h, format);
return JS_NewBigUint64(ctx, result);
}
FN(ui_handle_create) {
uint64_t result = (uint64_t)ui_handle_create();
any_array_push(plugin_gc, (void *)result);
return JS_NewBigUint64(ctx, result);
}
FN(ui_handle_set_value) {
uint64_t p;
JS_ToBigUint64(ctx, &p, argv[0]);
ui_handle_t *h = (ui_handle_t *)p;
double d;
JS_ToFloat64(ctx, &d, argv[1]);
h->f = d;
return JS_UNDEFINED;
}
FN(ui_handle_get_value) {
uint64_t p;
JS_ToBigUint64(ctx, &p, argv[0]);
ui_handle_t *h = (ui_handle_t *)p;
return JS_NewFloat64(ctx, h->f);
}
FN(ui_panel) {
uint64_t p;
JS_ToBigUint64(ctx, &p, argv[0]);
char *s = (char *)JS_ToCString(ctx, argv[1]);
bool result = ui_panel((void *)p, s, false, false);
return JS_NewBool(ctx, result);
}
FN(ui_button) {
char *s = (char *)JS_ToCString(ctx, argv[0]);
bool result = ui_button(s, UI_ALIGN_CENTER, "");
return JS_NewBool(ctx, result);
}
FN(ui_text) {
char *s = (char *)JS_ToCString(ctx, argv[0]);
ui_text(s, UI_ALIGN_LEFT, 0);
return JS_UNDEFINED;
}
FN(ui_text_input) {
uint64_t p;
JS_ToBigUint64(ctx, &p, argv[0]);
char *s = (char *)JS_ToCString(ctx, argv[1]);
ui_text_input((void *)p, s, UI_ALIGN_LEFT, true, false);
return JS_UNDEFINED;
}
FN(ui_slider) {
uint64_t p;
JS_ToBigUint64(ctx, &p, argv[0]);
char *s = (char *)JS_ToCString(ctx, argv[1]);
double from = 0.0;
double to = 1.0;
bool filled = true;
double prec = 100.0;
if (argc > 2) {
JS_ToFloat64(ctx, &from, argv[2]);
}
if (argc > 3) {
JS_ToFloat64(ctx, &to, argv[3]);
}
if (argc > 4) {
filled = JS_ToBool(ctx, argv[4]);
}
if (argc > 5) {
JS_ToFloat64(ctx, &prec, argv[5]);
}
ui_slider((void *)p, s, from, to, filled, prec, true, UI_ALIGN_LEFT, true);
return JS_UNDEFINED;
}
FN(ui_check) {
uint64_t p;
JS_ToBigUint64(ctx, &p, argv[0]);
char *s = (char *)JS_ToCString(ctx, argv[1]);
ui_check((void *)p, s, "");
return JS_UNDEFINED;
}
FN(ui_radio) {
uint64_t p;
JS_ToBigUint64(ctx, &p, argv[0]);
int32_t pos;
JS_ToInt32(ctx, &pos, argv[1]);
char *s = (char *)JS_ToCString(ctx, argv[2]);
ui_radio((void *)p, pos, s, "");
return JS_UNDEFINED;
}
FN(ui_row) {
JSValue val_len = JS_GetPropertyStr(ctx, argv[0], "length");
int len;
JS_ToInt32(ctx, &len, val_len);
f32_array_t *ratios = f32_array_create(len);
for (int i = 0; i < len; ++i) {
JSValue val = JS_GetPropertyUint32(ctx, argv[0], i);
double f;
JS_ToFloat64(ctx, &f, val);
ratios->buffer[i] = f;
}
ui_row(ratios);
return JS_UNDEFINED;
}
FN(ui_combo) {
uint64_t p;
JS_ToBigUint64(ctx, &p, argv[0]);
JSValue val_len = JS_GetPropertyStr(ctx, argv[1], "length");
int len;
JS_ToInt32(ctx, &len, val_len);
char_ptr_array_t *texts = any_array_create(len);
for (int i = 0; i < len; ++i) {
JSValue val = JS_GetPropertyUint32(ctx, argv[1], i);
char *s = (char *)JS_ToCString(ctx, val);
texts->buffer[i] = s;
}
char *label = (char *)JS_ToCString(ctx, argv[2]);
ui_combo((void *)p, texts, label, true, UI_ALIGN_LEFT, true);
return JS_UNDEFINED;
}
FN(plugin_api_make_raw_mesh) {
raw_mesh_t *mesh = calloc(sizeof(raw_mesh_t), 1);
mesh->name = (char *)JS_ToCString(ctx, argv[0]);
size_t len;
void *ab = JS_GetArrayBuffer(ctx, &len, argv[1]);
mesh->posa = malloc(sizeof(i16_array_t));
mesh->posa->buffer = malloc(len);
memcpy(mesh->posa->buffer, ab, len);
mesh->posa->length = len / 2;
ab = JS_GetArrayBuffer(ctx, &len, argv[2]);
mesh->nora = malloc(sizeof(i16_array_t));
mesh->nora->buffer = malloc(len);
memcpy(mesh->nora->buffer, ab, len);
mesh->nora->length = len / 2;
ab = JS_GetArrayBuffer(ctx, &len, argv[3]);
mesh->inda = malloc(sizeof(u32_array_t));
mesh->inda->buffer = malloc(len);
memcpy(mesh->inda->buffer, ab, len);
mesh->inda->length = len / 4;
double d;
JS_ToFloat64(ctx, &d, argv[4]);
mesh->scale_pos = d;
mesh->scale_tex = 1.0;
return JS_NewBigUint64(ctx, (uint64_t)mesh);
}
void transform_rotate(void *raw, vec4_t axis, float f);
FN(transform_rotate) {
uint64_t p;
JS_ToBigUint64(ctx, &p, argv[0]);
vec4_t axis;
axis.x = 0.0;
axis.y = 0.0;
axis.z = 1.0;
double d;
JS_ToFloat64(ctx, &d, argv[1]);
transform_rotate((void *)p, axis, d);
return JS_UNDEFINED;
}
void plugin_api_init() {
JSValue global_obj = JS_GetGlobalObject(js_ctx);
js_eval(lib);
plugin_gc = any_array_create(0);
gc_root(plugin_gc);
BIND(gc_run, 0);
BIND(data_get_blob, 1);
BIND(data_delete_blob, 1);
BIND(iron_file_save_bytes, 3);
BIND(gpu_create_texture_from_bytes, 4);
BIND(ui_handle_create, 0);
BIND(ui_handle_set_value, 2);
BIND(ui_handle_get_value, 1);
BIND(ui_panel, 2);
BIND(ui_button, 1);
BIND(ui_text, 1);
BIND(ui_text_input, 2);
BIND(ui_slider, 5);
BIND(ui_check, 2);
BIND(ui_radio, 3);
BIND(ui_row, 1);
BIND(ui_combo, 3);
BIND(plugin_api_make_raw_mesh, 5);
BIND(transform_rotate, 3);
plugin_embed();
JS_FreeValue(js_ctx, global_obj);
}