377 lines
10 KiB
C
377 lines
10 KiB
C
#include "jsffi.h"
|
|
#include "stb_ds.h"
|
|
#include "string.h"
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include "HandmadeMath.h"
|
|
#include <stdint.h>
|
|
#include "cell.h"
|
|
|
|
#include "qjs_wota.h"
|
|
|
|
#if defined(_WIN32)
|
|
#include <windows.h>
|
|
#else
|
|
#include <dlfcn.h>
|
|
#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_<basename>_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_<name>_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);
|
|
}
|