From 14feeb0c16dcc4716d8b7b561d21732c37ea2280 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sat, 1 Feb 2025 21:16:38 -0600 Subject: [PATCH] core.zip is now bundled into prosperon executables --- meson.build | 37 +++++++++++++--- scripts/cmd.js | 2 - scripts/engine.js | 39 ++++++++++++----- scripts/modules/sprite.js | 15 ++++++- scripts/render.js | 3 +- scripts/sdl_gpu.js | 1 - source/jsffi.c | 20 ++++----- source/prosperon.c | 92 ++++++++++++++++++++++++++++++++++++++- source/script.c | 61 ++++++++------------------ 9 files changed, 192 insertions(+), 78 deletions(-) diff --git a/meson.build b/meson.build index ba8e1fde..59722158 100644 --- a/meson.build +++ b/meson.build @@ -144,18 +144,45 @@ foreach folder: zip_folders zip_paths += meson.project_source_root() / folder endforeach +# Now use the hash file as a dependency so that any change in the files causes a rebuild. core = custom_target('core.zip', - output:'core.zip', - command: ['zip', '-r', '-j', '@OUTPUT@'] + zip_paths, + output : 'core.zip', + command : ['sh', '-c', + 'cd ' + meson.project_source_root() + + ' && echo "Rebuilding core.zip" && ' + + 'zip -r ' + meson.current_build_dir() + '/core.zip scripts fonts icons shaders' + ], + build_always : true, + build_by_default: true ) -prosperon = executable('prosperon', sources, +prosperon_raw = executable('prosperon_raw', sources, dependencies: deps, include_directories: includers, link_args: link, build_rpath: '$ORIGIN', - install:true, - install_dir:'bin' + install:false +) + +if host_machine.system() == 'windows' + exe_ext = '.exe' +else + exe_ext = '' +endif + +prosperon = custom_target('prosperon', + output: 'prosperon' + exe_ext, + input: [prosperon_raw, core], + command: [ + 'sh', '-c', + 'cat "$1" "$2" > "$3"', + 'concat', + '@INPUT0@', + '@INPUT1@', + '@OUTPUT@' + ], + build_always: true, + build_by_default: true ) prosperon_dep = declare_dependency( diff --git a/scripts/cmd.js b/scripts/cmd.js index 14f15207..0a97bdbc 100644 --- a/scripts/cmd.js +++ b/scripts/cmd.js @@ -69,8 +69,6 @@ Cmdline.register_order( Cmdline.register_order( "play", function (argv) { - if (argv[0]) io.chdir(argv[0]); - var app if (io.exists("main.js")) app = actor.spawn("main.js", {}, function(underling, msg) { diff --git a/scripts/engine.js b/scripts/engine.js index 3cf7d0c3..5468992c 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -54,13 +54,10 @@ Object.defineProperty(Function.prototype, "hashify", { }); var io = use_embed('io') -io.mount("core/scripts") -io.mount("core/scripts/modules") -io.mount("core") -var canonical = io.realdir('resources.js') + 'resources.js' -var content = io.slurp('resources.js') -var resources = js.eval('resources.js', `(function setup_resources(){${content}})`).call({}) +var canonical = io.realdir('scripts/resources.js') + 'resources.js' +var content = io.slurp('scripts/resources.js') +var resources = js.eval('scripts/resources.js', `(function setup_resources(){${content}})`).call({}) console.print(resources.canonical('resources.js')) use_cache[resources.canonical('resources.js')] = resources @@ -76,10 +73,31 @@ function print_api(obj) { } } +prosperon.PATH = [ + "/", + "scripts/", + "scripts/modules/" +] + +function find_in_path(filename, exts = []) { + for (var dir of prosperon.PATH) { + var candidate = dir + filename; + if (io.exists(candidate)) return candidate; + + for (var ext of exts) { + candidate = dir + filename + ext; + if (io.exists(candidate)) return candidate; + } + } + return undefined; +} + // path is the path of a module or script to resolve var script_fn = function script_fn(path) { var parsed = {} - var file = resources.find_script(path); + + var file = resources.find_script(find_in_path(path, ['.js', '.jsc'])); + if (!file) { // attempt to bare load parsed.module_ret = bare_load(path); @@ -177,8 +195,6 @@ console.log = function(msg) pprint(msg, 2) } -console.log(io.searchpath()) - console.error = function(e) { if (!e) e = new Error(); @@ -211,10 +227,10 @@ console.doc = { clear: "Clear console.", }; -var script = io.slurp("core/scripts/base.js") +var script = io.slurp("scripts/base.js") var fnname = "base" script = `(function ${fnname}() { ${script}; })` -js.eval('core/scripts/base.js', script)() +js.eval('scripts/base.js', script)() function add_timer(obj, fn, seconds) { @@ -535,4 +551,3 @@ actor[UNDERLINGS] = new Set() globalThis.mixin("color"); use('cmd')(prosperon.argv) - diff --git a/scripts/modules/sprite.js b/scripts/modules/sprite.js index 03c403cf..68ec319e 100644 --- a/scripts/modules/sprite.js +++ b/scripts/modules/sprite.js @@ -63,6 +63,14 @@ sprite_pipeline.blend = { op_alpha: "add" }; +sprite_pipeline.target = { + color_targets: [{ + format:"rgba8", + blend:sprite_pipeline.blend + }], + depth: "d32 float s8" +}; + var sprite = { image: undefined, @@ -251,14 +259,16 @@ sprite.inputs.kp1 = function () { var tree = graphics.make_rtree() sprite.tree = tree; +var IN = Symbol() + sprite.t_hook = function() { var msp = this.sprite._sprite; - if (this.__in) + if (this[IN]) tree.delete(msp); msp.rect = this.torect() msp.set_affine(this) tree.add(msp) - this.__in = true + this[IN] = true } Object.mixin(sprite,use("transform")) @@ -297,3 +307,4 @@ this._sprite = msp; msp.color = Color.white; this.transform.sprite = this + diff --git a/scripts/render.js b/scripts/render.js index 11c26ed5..5c084835 100644 --- a/scripts/render.js +++ b/scripts/render.js @@ -433,7 +433,7 @@ var pass; var pass = cmds.render_pass(camera.target); var camera = prosperon.camera; - var pipeline; + var pipeline ; var mesh; var img; var modelslot; @@ -779,7 +779,6 @@ std_sampler = render._main.make_sampler({ address_mode_w: "repeat" }); -io.mount("core"); render._main.present = gpupresent; imgui.init(render._main, prosperon.window); tracy.gpu_init() diff --git a/scripts/sdl_gpu.js b/scripts/sdl_gpu.js index f24dfddd..83d10eb4 100644 --- a/scripts/sdl_gpu.js +++ b/scripts/sdl_gpu.js @@ -287,7 +287,6 @@ render.device = { render.device.doc = `Device resolutions given as [x,y,inches diagonal].`; var std_sampler; render.init = function () { - io.mount("core"); // var ducky = render._main.load_gltf_model("Duck.glb"); // console.log(json.encode(ducky)) var sprite_vert = make_shader("sprite.vert", true,0,0,0,1); diff --git a/source/jsffi.c b/source/jsffi.c index aa3cd263..4de813d9 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -1762,19 +1762,19 @@ shader_globals camera_globals(JSContext *js, JSValue camera) shader_globals data = {0}; HMM_Vec2 size; transform *transform; - double fov; - double aspect; + double fov = 0; + double aspect = 0; int ortho; - double near; - double far; + double near_z = 0; + double far_z = 0; JS_GETPROP(js, size, camera, size, vec2) JS_GETPROP(js, transform, camera, transform, transform) JS_GETPROP(js, fov, camera, fov, number) JS_GETPROP(js, aspect, camera, aspect, number) JS_GETPROP(js, ortho, camera,ortho,bool) - JS_GETPROP(js,near,camera,near,number) - JS_GETPROP(js,far,camera,far,number) + JS_GETPROP(js,near_z,camera,near_z,number) + JS_GETPROP(js,far_z,camera,far_z,number) HMM_Mat4 proj; HMM_Mat4 view; @@ -1788,7 +1788,7 @@ shader_globals camera_globals(JSContext *js, JSValue camera) view = HMM_Translate((HMM_Vec3){ -transform->pos.x, -transform->pos.y, 0.0f }); } else { - proj = HMM_Perspective_RH_NO(fov, aspect,near,far); + proj = HMM_Perspective_RH_NO(fov, aspect,near_z,far_z); HMM_Mat4 camera_transform = HMM_Translate(transform->pos); camera_transform = HMM_MulM4(camera_transform, HMM_QToM4(transform->rotation)); @@ -1800,8 +1800,8 @@ shader_globals camera_globals(JSContext *js, JSValue camera) data.world_to_projection = HMM_MulM4(proj, view); data.projection_to_world = HMM_InvGeneralM4(data.world_to_projection); data.camera_pos_world = transform->pos; - data.viewport_min_z = near; - data.viewport_max_z = far; + data.viewport_min_z = near_z; + data.viewport_max_z = far_z; data.render_size = size; data.world_to_view = view; data.view_to_projection = proj; @@ -5835,7 +5835,7 @@ JSC_SCALL(io_slurpwrite, ) JSC_SSCALL(io_mount, - if (!PHYSFS_mount(str,str2,0)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + if (!PHYSFS_mount(str,str2,0)) ret = JS_ThrowReferenceError(js,"Unable to mount %s at %s: %s", str, str2, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); ) JSC_SCALL(io_unmount, diff --git a/source/prosperon.c b/source/prosperon.c index 9ec1f536..7423460f 100644 --- a/source/prosperon.c +++ b/source/prosperon.c @@ -1,12 +1,100 @@ #include "script.h" #include "physfs.h" +#include +#include +#include +#include + +static unsigned char *zip_buffer_global; + +void free_zip(void) +{ + free(zip_buffer_global); +} int main(int argc, char **argv) { + printf("%s\n", argv[0]); + FILE *f = fopen(argv[0], "rb"); + if (!f) { perror("fopen"); return 1; } + if (fseek(f, 0, SEEK_END) != 0) { perror("fseek"); fclose(f); return 1; } + long size = ftell(f); + if (size < 0) { perror("ftell"); fclose(f); return 1; } + zip_buffer_global = malloc(size); + if (!zip_buffer_global) { perror("malloc"); fclose(f); return 1; } + rewind(f); + if (fread(zip_buffer_global, 1, size, f) != (size_t)size) { + perror("fread"); + free(zip_buffer_global); + fclose(f); + return 1; + } + fclose(f); + + // Search backwards for the EOCD signature "PK\x05\x06". + // The EOCD record is at most 0xFFFF (65535) bytes plus 22 bytes long. + long max_comment_len = 0xFFFF; + long eocd_search_start = (size > (max_comment_len + 22)) ? (size - (max_comment_len + 22)) : 0; + long eocd_pos = -1; + for (long i = size - 22; i >= eocd_search_start; i--) { + if (zip_buffer_global[i] == 'P' && + zip_buffer_global[i + 1] == 'K' && + zip_buffer_global[i + 2] == 0x05 && + zip_buffer_global[i + 3] == 0x06) { + eocd_pos = i; + break; + } + } + if (eocd_pos < 0) { + fprintf(stderr, "EOCD not found\n"); + free(zip_buffer_global); + return 1; + } + + // Parse the EOCD record. + // EOCD record layout (without the comment): + // Offset 0: 4 bytes signature ("PK\x05\x06") + // Offset 4: 2 bytes disk number + // Offset 6: 2 bytes disk with central directory + // Offset 8: 2 bytes number of central dir records on this disk + // Offset 10:2 bytes total number of central dir records + // Offset 12:4 bytes size of central directory (cd_size) + // Offset 16:4 bytes offset of start of central directory (cd_offset_rel, relative to zip start) + // Offset 20:2 bytes comment length + uint16_t comment_length = zip_buffer_global[eocd_pos + 20] | + (zip_buffer_global[eocd_pos + 21] << 8); + int eocd_size = 22 + comment_length; + uint32_t cd_size = zip_buffer_global[eocd_pos + 12] | + (zip_buffer_global[eocd_pos + 13] << 8) | + (zip_buffer_global[eocd_pos + 14] << 16) | + (zip_buffer_global[eocd_pos + 15] << 24); + uint32_t cd_offset_rel = zip_buffer_global[eocd_pos + 16] | + (zip_buffer_global[eocd_pos + 17] << 8) | + (zip_buffer_global[eocd_pos + 18] << 16) | + (zip_buffer_global[eocd_pos + 19] << 24); + + // The size of the appended zip archive is given by: + // appended_zip_size = (offset of central directory + size of central directory + EOCD record size) + // Since the EOCD record is the last part of the zip archive, + // we can compute the start of the zip archive (zip_offset) as: + uint32_t appended_zip_size = cd_offset_rel + cd_size + eocd_size; + long zip_offset = size - appended_zip_size; + if (zip_offset < 0 || zip_offset >= size) { + fprintf(stderr, "Invalid zip offset computed: %ld\n", zip_offset); + free(zip_buffer_global); + return 1; + } + printf("Zip data found at offset %ld, appended zip size %u bytes\n", + zip_offset, appended_zip_size); + PHYSFS_init(argv[0]); char *base = PHYSFS_getBaseDir(); PHYSFS_setWriteDir(base); - PHYSFS_mount(base,NULL,0); - PHYSFS_mount(base,"/",0); + PHYSFS_mount(base, NULL, 0); + PHYSFS_mount(base, "/", 0); + int ret = PHYSFS_mountMemory(zip_buffer_global + zip_offset, appended_zip_size, free_zip, + "core.zip", "/", 0); + + if (!ret) printf("COULD NOT MOUNT! Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); script_startup(argc, argv); // runs engine.js return 0; } diff --git a/source/script.c b/source/script.c index fef2c1ec..0eb32a8c 100644 --- a/source/script.c +++ b/source/script.c @@ -10,6 +10,8 @@ #include #include +#include "physfs.h" + static JSContext *js = NULL; static JSRuntime *rt = NULL; @@ -23,44 +25,6 @@ JSValue on_exception = JS_UNDEFINED; #define JS_EVAL_FLAGS JS_EVAL_FLAG_STRICT | JS_EVAL_FLAG_STRIP #endif -static JSValue report_gc; - -char* read_file(const char* filename) { - // Open the file in read mode - FILE *file = fopen(filename, "r"); - if (!file) { - perror("Failed to open file"); - return NULL; - } - - // Seek to the end of the file to get its size - fseek(file, 0, SEEK_END); - long file_size = ftell(file); - fseek(file, 0, SEEK_SET); - - // Allocate memory for the file content, including the null terminator - char *content = (char*)malloc(file_size + 1); - if (!content) { - perror("Failed to allocate memory"); - fclose(file); - return NULL; - } - - // Read the entire file into the content buffer - fread(content, 1, file_size, file); - - // Null-terminate the string - content[file_size] = '\0'; - - fclose(file); - return content; -} - -int js_interrupt(JSRuntime *rt, void *data) -{ -// printf("INTERRUPT\n"); -} - void script_startup(int argc, char **argv) { rt = JS_NewRuntime(); js = JS_NewContextRaw(rt); @@ -78,9 +42,19 @@ void script_startup(int argc, char **argv) { JS_AddIntrinsicOperators(js); ffi_load(js, argc, argv); - - char *eng = read_file("core/scripts/engine.js"); - JSValue v = script_eval(js, "core/scripts/engine.js", eng); + + PHYSFS_File *eng = PHYSFS_openRead("scripts/engine.js"); + if (!eng) { + printf("Could not open file! %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return; + } + + PHYSFS_Stat stat; + PHYSFS_stat("scripts/engine.js", &stat); + void *data = malloc(stat.filesize); + PHYSFS_readBytes(eng,data,stat.filesize); + PHYSFS_close(eng); + JSValue v = script_eval(js, "scripts/engine.js", data); uncaught_exception(js,v); free(eng); } @@ -109,11 +83,14 @@ void uncaught_exception(JSContext *js, JSValue v) JS_FreeValue(js,ex); } else { JSValue ex = JS_GetException(js); + const char *strex = JS_ToCString(js,ex); JSValue stack = JS_GetPropertyStr(js,ex,"stack"); const char *st = JS_ToCString(js,stack); - printf("Unhandled exception:\n%s\n", st); + printf("Unhandled exception: %s\n%s\n", strex, st); JS_FreeValue(js,stack); JS_FreeValue(js,ex); + JS_FreeCString(js,st); + JS_FreeCString(js,strex); } JS_FreeValue(js,v);