#include "jsffi.h" #include "stb_ds.h" #include "string.h" #include #include #include #include #include #include #include #include "HandmadeMath.h" #include #include "cell.h" #include "qjs_wota.h" #if defined(_WIN32) #include #else #include #endif // Random number generation constants for MT19937-64 #define NN STATE_VECTOR_LENGTH #define MM STATE_VECTOR_M #define MATRIX_A 0xB5026F5AA96619E9ULL #define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */ #define LM 0x7FFFFFFFULL /* Least significant 31 bits */ // Random number generation functions void m_seedRand(MTRand* rand, uint64_t seed) { rand->mt[0] = seed; for(rand->index = 1; rand->index < NN; rand->index++) { rand->mt[rand->index] = (6364136223846793005ULL * (rand->mt[rand->index-1] ^ (rand->mt[rand->index-1] >> 62)) + rand->index); } } int64_t genRandLong(MTRand* rand) { int i; uint64_t x; static uint64_t mag01[2] = {0ULL, MATRIX_A}; if (rand->index >= NN) { /* generate NN words at one time */ /* if init_genrand64() has not been called, */ /* a default initial seed is used */ if (rand->index == NN+1) m_seedRand(rand, 5489ULL); for (i = 0; i < NN-MM; i++) { x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM); rand->mt[i] = rand->mt[i+MM] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; } for (; i < NN-1; i++) { x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM); rand->mt[i] = rand->mt[i+(MM-NN)] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; } x = (rand->mt[NN-1] & UM) | (rand->mt[0] & LM); rand->mt[NN-1] = rand->mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; rand->index = 0; } x = rand->mt[rand->index++]; x ^= (x >> 29) & 0x5555555555555555ULL; x ^= (x << 17) & 0x71D67FFFEDA60000ULL; x ^= (x << 37) & 0xFFF7EEE000000000ULL; x ^= (x >> 43); return (int64_t)(x & 0x000FFFFFFFFFFFFFULL); /* return 52-bit value safe for JS */ } double genRand(MTRand* rand) { /* generates a random number on [0,1)-real-interval */ return (genRandLong(rand) >> 11) * (1.0/9007199254740992.0); } double rand_range(JSContext *js, double min, double max) { MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand; return genRand(mrand) * (max-min)+min; } #define JS_GETNUM(JS,VAL,I,TO,TYPE) { \ JSValue val = JS_GetPropertyUint32(JS,VAL,I); \ TO = js2##TYPE(JS, val); \ JS_FreeValue(JS, val); } \ int js2bool(JSContext *js, JSValue v) { return JS_ToBool(js,v); } JSValue bool2js(JSContext *js, int b) { return JS_NewBool(js,b); } JSValue number2js(JSContext *js, double g) { return JS_NewFloat64(js,g); } double js2number(JSContext *js, JSValue v) { double g; JS_ToFloat64(js, &g, v); if (isnan(g)) g = 0; return g; } JSValue js_getproperty(JSContext *js, JSValue v, const char *prop) { JSValue ret = JS_GetPropertyStr(js, v, prop); JS_FreeValue(js,ret); return ret; } JSValue make_quad_indices_buffer(JSContext *js, int quads) { cell_rt *rt = JS_GetContextOpaque(js); int count = quads*6; if (!JS_IsNull(rt->idx_buffer) && rt->idx_count >= count) return JS_DupValue(js,rt->idx_buffer); int verts = quads*4; uint16_t *indices = malloc(sizeof(*indices)*count); for (int i = 0, v = 0; v < verts; i +=6, v += 4) { indices[i] = v; indices[i+1] = v+2; indices[i+2] = v+1; indices[i+3] = v+2; indices[i+4] = v+3; indices[i+5] = v+1; } if (!JS_IsNull(rt->idx_buffer)) JS_FreeValue(js,rt->idx_buffer); // rt->idx_buffer = make_gpu_buffer(js,indices, sizeof(*indices)*count, JS_TYPED_ARRAY_UINT16, 1,0,1); rt->idx_count = count; return JS_DupValue(js,rt->idx_buffer); } char *js2strdup(JSContext *js, JSValue v) { const char *str = JS_ToCString(js, v); char *ret = strdup(str); JS_FreeCString(js, str); return ret; } #include "qjs_macros.h" JSValue angle2js(JSContext *js,double g) { return number2js(js,g*HMM_RadToTurn); } double js2angle(JSContext *js,JSValue v) { double n = js2number(js,v); return n * HMM_TurnToRad; } static uint32_t rng_state = 123456789; static uint32_t xorshift32(){ uint32_t x = rng_state; x ^= x << 13; x ^= x >> 17; x ^= x << 5; return rng_state = x; } JSC_CCALL(os_guid, uint8_t data[16]; for(int i = 0; i < 4; i++){ uint32_t v = xorshift32(); memcpy(&data[i*4], &v, 4); } static const char hex[] = "0123456789abcdef"; char buf[32]; for(int i = 0; i < 16; i++){ uint8_t b = data[i]; buf[i*2 ] = hex[b >> 4]; buf[i*2 + 1] = hex[b & 0x0f]; } return JS_NewStringLen(js, buf, 32); ) JSC_SCALL(console_print, printf("%s", str); ) static const JSCFunctionListEntry js_console_funcs[] = { MIST_FUNC_DEF(console,print,1), }; JSC_SCALL(os_system, ret = number2js(js,system(str)); ) JSC_CCALL(os_rand, MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand; return number2js(js, genRand(mrand)); ) JSC_CCALL(os_randi, MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand; return JS_NewInt64(js, genRandLong(mrand)); ) JSC_CCALL(os_srand, MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand; m_seedRand(mrand, js2number(js,argv[0])); ) static const JSCFunctionListEntry js_util_funcs[] = { MIST_FUNC_DEF(os, guid, 0), }; static void *get_main_module_handle() { #if defined(_WIN32) return GetModuleHandle(NULL); #else return dlopen(NULL, RTLD_LAZY); #endif } static void *get_symbol(void *handle, const char *name) { #if defined(_WIN32) return (void*)GetProcAddress((HMODULE)handle, name); #else return dlsym(handle, name); #endif } JSC_CCALL(os_use_dyn, const char *path = JS_ToCString(js, argv[0]); if (!path) return JS_ThrowTypeError(js, "path must be a string"); // 1. Load the library #if defined(_WIN32) HMODULE handle = LoadLibraryA(path); #else void *handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); #endif if (!handle) { JS_FreeCString(js, path); #if defined(_WIN32) return JS_ThrowReferenceError(js, "Could not load library"); #else return JS_ThrowReferenceError(js, "Could not load library: %s", dlerror()); #endif } // 2. Construct symbol name: js__use // Extract basename from path const char *base = strrchr(path, '/'); #if defined(_WIN32) const char *base2 = strrchr(path, '\\'); if (base2 > base) base = base2; #endif if (base) base++; // Skip the slash else base = path; // No slash, use whole path char name_buf[256]; const char *dot = strrchr(base, '.'); size_t len = dot ? (size_t)(dot - base) : strlen(base); if (len > 100) len = 100; // Safety cap char clean_base[128]; memcpy(clean_base, base, len); clean_base[len] = '\0'; snprintf(name_buf, sizeof(name_buf), "js_%s_use", clean_base); // 3. Get the symbol typedef JSValue (*init_fn)(JSContext*); init_fn fn = (init_fn)get_symbol(handle, name_buf); JS_FreeCString(js, path); if (!fn) { // Try without stripping extension? No, standard is usually without. // Maybe try "js_main_use"? No. // Let's stick to the plan. #if defined(_WIN32) FreeLibrary(handle); #else dlclose(handle); #endif return JS_ThrowReferenceError(js, "Could not find entry point %s in library", name_buf); } // 4. Call the function return fn(js); ) JSC_SCALL(os_load_internal, void *handle = get_main_module_handle(); if (!handle) { return JS_ThrowReferenceError(js, "Could not get main module handle"); } char func_name[256]; snprintf(func_name, sizeof(func_name), "js_%s_use", str); JSValue (*js_use)(JSContext*) = get_symbol(handle, func_name); if (!js_use) { // Try without "js_" prefix or other variations if needed, but standard is js__use return JS_NULL; } ret = js_use(js); ) JSValue js_util_use(JSContext *js) { JSValue mod = JS_NewObject(js); JS_SetPropertyFunctionList(js,mod,js_util_funcs,countof(js_util_funcs)); return mod; } JSValue js_console_use(JSContext *js) { JSValue mod = JS_NewObject(js); JS_SetPropertyFunctionList(js,mod,js_console_funcs,countof(js_console_funcs)); return mod; } JSC_CCALL(os_value_id, JS_SetPropertyStr(js, argv[0], "id", number2js(js, (uintptr_t)JS_VALUE_GET_PTR(argv[0]))); return argv[0]; ) void ffi_load(JSContext *js) { cell_rt *rt = JS_GetContextOpaque(js); JS_FreeValue(js, js_blob_use(js)); uint64_t rr; // randombytes is defined in qjs_crypto.c which is linked, but header was removed. // We declare it here to avoid implicit declaration error. extern int randombytes(void *buf, size_t n); randombytes(&rr,4); m_seedRand(&rt->mrand, rr); JSValue globalThis = JS_GetGlobalObject(js); JSValue prosp = JS_NewObject(js); JS_SetPropertyStr(js,globalThis,"prosperon", prosp); JSValue hidden_fn = JS_NewObject(js); // Add functions that should only be accessible to engine.js JS_SetPropertyStr(js, hidden_fn, "load_internal", JS_NewCFunction(js, js_os_load_internal, "load_internal", 1)); JS_SetPropertyStr(js, hidden_fn, "rand", JS_NewCFunction(js, js_os_rand, "rand", 0)); JS_SetPropertyStr(js, hidden_fn, "randi", JS_NewCFunction(js, js_os_randi, "randi", 0)); JS_SetPropertyStr(js, hidden_fn, "srand", JS_NewCFunction(js, js_os_srand, "srand", 1)); JS_SetPropertyStr(js, hidden_fn, "use_dyn", JS_NewCFunction(js, js_os_use_dyn, "use_dyn", 1)); #if defined(_WIN32) JS_SetPropertyStr(js, hidden_fn, "dylib_ext", JS_NewString(js, ".dll")); #elif defined(__APPLE__) JS_SetPropertyStr(js, hidden_fn, "dylib_ext", JS_NewString(js, ".dylib")); #else JS_SetPropertyStr(js, hidden_fn, "dylib_ext", JS_NewString(js, ".so")); #endif const char actorsym_script[] = "var sym = Symbol(`actordata`); sym;"; JSValue actorsym = JS_Eval(js, actorsym_script, sizeof(actorsym_script)-1, "internal", 0); JS_SetPropertyStr(js, hidden_fn, "actorsym", actorsym); rt->actor_sym = JS_ValueToAtom(js, actorsym); if (rt->init_wota) { JS_SetPropertyStr(js, hidden_fn, "init", wota2value(js, rt->init_wota)); // init wota can now be freed free(rt->init_wota); rt->init_wota = NULL; } JS_SetPropertyStr(js, prosp, "hidden", hidden_fn); JS_FreeValue(js,globalThis); }