This commit is contained in:
2025-11-24 08:40:20 -06:00
parent 8bdcaf7d9d
commit b613c7b6fa
6 changed files with 646 additions and 112 deletions

View File

@@ -372,16 +372,16 @@ cell_dep = declare_dependency(
)
# Create core.zip from scripts folder
zip_target = custom_target('core.zip',
output: 'core.zip',
command: ['sh', '-c', 'cd ' + meson.project_source_root() / 'scripts' + ' && zip -r ' + meson.current_build_dir() / 'core.zip' + ' .'],
qop_target = custom_target('core.qop',
output: 'core.qop',
command: ['sh', '-c', 'qopconv -d ' + meson.project_source_root() / 'scripts . core.qop'],
build_by_default: true,
build_always_stale: true
)
# Create final cell executable by appending core.zip to cell_bin
cell = custom_target('cell',
input: [cell_bin, zip_target],
input: [cell_bin, qop_target],
output: 'cell' + exe_ext,
command: [
'sh', '-c',

491
qopconv.c Normal file
View File

@@ -0,0 +1,491 @@
/*
Copyright (c) 2024, Dominic Szablewski - https://phoboslab.org
SPDX-License-Identifier: MIT
Command line tool to create and unpack qop archives
*/
#define _DEFAULT_SOURCE
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#define QOP_IMPLEMENTATION
#include "qop.h"
#define MAX_PATH_LEN 1024
#define BUFFER_SIZE 4096
#define UNUSED(x) (void)(x)
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define die(...) \
printf("Abort at " TOSTRING(__FILE__) " line " TOSTRING(__LINE__) ": " __VA_ARGS__); \
printf("\n"); \
exit(1)
#define error_if(TEST, ...) \
if (TEST) { \
die(__VA_ARGS__); \
}
// -----------------------------------------------------------------------------
// Platform specific file/dir handling
typedef struct {
char *name;
unsigned char is_dir;
unsigned char is_file;
} pi_dirent;
#if defined(_WIN32)
#include <windows.h>
typedef struct {
WIN32_FIND_DATA data;
pi_dirent current;
HANDLE dir;
unsigned char is_first;
} pi_dir;
pi_dir *pi_dir_open(const char *path) {
char find_str[MAX_PATH_LEN];
snprintf(find_str, MAX_PATH_LEN, "%s/*", path);
pi_dir *d = malloc(sizeof(pi_dir));
d->is_first = 1;
d->dir = FindFirstFile(find_str, &d->data);
if (d->dir == INVALID_HANDLE_VALUE) {
free(d);
return NULL;
}
return d;
}
pi_dirent *pi_dir_next(pi_dir *d) {
if (!d->is_first) {
if (FindNextFile(d->dir, &d->data) == 0) {
return NULL;
}
}
d->is_first = 0;
d->current.name = d->data.cFileName;
d->current.is_dir = d->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
d->current.is_file = !d->current.is_dir;
return &d->current;
}
void pi_dir_close(pi_dir *d) {
FindClose(d->dir);
free(d);
}
int pi_mkdir(char *path, int mode) {
UNUSED(mode);
return CreateDirectory(path, NULL) ? 0 : -1;
}
#else
#include <dirent.h>
typedef struct {
DIR *dir;
struct dirent *data;
pi_dirent current;
} pi_dir;
pi_dir *pi_dir_open(const char *path) {
DIR *dir = opendir(path);
if (!dir) {
return NULL;
}
pi_dir *d = malloc(sizeof(pi_dir));
d->dir = dir;
return d;
}
pi_dirent *pi_dir_next(pi_dir *d) {
d->data = readdir(d->dir);
if (!d->data) {
return NULL;
}
d->current.name = d->data->d_name;
d->current.is_dir = d->data->d_type & DT_DIR;
d->current.is_file = d->data->d_type == DT_REG;
return &d->current;
}
void pi_dir_close(pi_dir *d) {
closedir(d->dir);
free(d);
}
int pi_mkdir(char *path, int mode) {
return mkdir(path, mode);
}
#endif
// -----------------------------------------------------------------------------
// Unpack
int create_path(const char *path, const mode_t mode) {
char tmp[MAX_PATH_LEN];
char *p = NULL;
struct stat sb;
size_t len;
// copy path
len = strnlen(path, MAX_PATH_LEN);
if (len == 0 || len == MAX_PATH_LEN) {
return -1;
}
memcpy(tmp, path, len);
tmp[len] = '\0';
// remove file part
char *last_slash = strrchr(tmp, '/');
if (last_slash == NULL) {
return 0;
}
*last_slash = '\0';
// check if path exists and is a directory
if (stat(tmp, &sb) == 0) {
if (S_ISDIR(sb.st_mode)) {
return 0;
}
}
// recursive mkdir
for (p = tmp + 1; *p; p++) {
if (*p == '/') {
*p = 0;
if (stat(tmp, &sb) != 0) {
if (pi_mkdir(tmp, mode) < 0) {
return -1;
}
}
else if (!S_ISDIR(sb.st_mode)) {
return -1;
}
*p = '/';
}
}
if (stat(tmp, &sb) != 0) {
if (pi_mkdir(tmp, mode) < 0) {
return -1;
}
}
else if (!S_ISDIR(sb.st_mode)) {
return -1;
}
return 0;
}
unsigned int copy_out(FILE *src, unsigned int offset, unsigned int size, const char *dest_path) {
FILE *dest = fopen(dest_path, "wb");
error_if(!dest, "Could not open file %s for writing", dest_path);
char buffer[BUFFER_SIZE];
size_t bytes_read, bytes_written;
unsigned int bytes_total = 0;
unsigned int read_size = size < BUFFER_SIZE ? size : BUFFER_SIZE;
fseek(src, offset, SEEK_SET);
while (read_size > 0 && (bytes_read = fread(buffer, 1, read_size, src)) > 0) {
bytes_written = fwrite(buffer, 1, bytes_read, dest);
error_if(bytes_written != bytes_read, "Write error");
bytes_total += bytes_written;
if (bytes_total >= size) {
break;
}
if (size - bytes_total < read_size) {
read_size = size - bytes_total;
}
}
error_if(ferror(src), "read error for file %s", dest_path);
fclose(dest);
return bytes_total;
}
void unpack(const char *archive_path, int list_only) {
qop_desc qop;
int archive_size = qop_open(archive_path, &qop);
error_if(archive_size == 0, "Could not open archive %s", archive_path);
// Read the archive index
int index_len = qop_read_index(&qop, malloc(qop.hashmap_size));
error_if(index_len == 0, "Could not read index from archive %s", archive_path);
// Extract all files
for (unsigned int i = 0; i < qop.hashmap_len; i++) {
qop_file *file = &qop.hashmap[i];
if (file->size == 0) {
continue;
}
error_if(file->path_len >= MAX_PATH_LEN, "Path for file %016llx exceeds %d", file->hash, MAX_PATH_LEN);
char path[MAX_PATH_LEN];
qop_read_path(&qop, file, path);
// Integrity check
// error_if(!qop_find(&qop, path), "could not find %s", path);
printf("%6d %016llx %10d %s\n", i, file->hash, file->size, path);
if (!list_only) {
error_if(create_path(path, 0755) != 0, "Could not create path %s", path);
copy_out(qop.fh, qop.files_offset + file->offset + file->path_len, file->size, path);
}
}
free(qop.hashmap);
qop_close(&qop);
}
// -----------------------------------------------------------------------------
// Pack
typedef struct {
qop_file *files;
int len;
int capacity;
int size;
char dest_path[MAX_PATH_LEN];
} pack_state;
int canonicalize_path(const char *path, char *buffer, size_t buffer_len) {
#if defined(_WIN32)
return _fullpath(buffer, path, buffer_len) != NULL;
#else
UNUSED(buffer_len);
return realpath(path, buffer) != NULL;
#endif
}
void write_16(unsigned int v, FILE *fh) {
unsigned char b[sizeof(unsigned short)];
b[0] = 0xff & (v );
b[1] = 0xff & (v >> 8);
int written = fwrite(b, sizeof(unsigned short), 1, fh);
error_if(!written, "Write error");
}
void write_32(unsigned int v, FILE *fh) {
unsigned char b[sizeof(unsigned int)];
b[0] = 0xff & (v );
b[1] = 0xff & (v >> 8);
b[2] = 0xff & (v >> 16);
b[3] = 0xff & (v >> 24);
int written = fwrite(b, sizeof(unsigned int), 1, fh);
error_if(!written, "Write error");
}
void write_64(qop_uint64_t v, FILE *fh) {
unsigned char b[sizeof(qop_uint64_t)];
b[0] = 0xff & (v );
b[1] = 0xff & (v >> 8);
b[2] = 0xff & (v >> 16);
b[3] = 0xff & (v >> 24);
b[4] = 0xff & (v >> 32);
b[5] = 0xff & (v >> 40);
b[6] = 0xff & (v >> 48);
b[7] = 0xff & (v >> 56);
int written = fwrite(b, sizeof(qop_uint64_t), 1, fh);
error_if(!written, "Write error");
}
unsigned int copy_into(const char *src_path, FILE *dest) {
FILE *src = fopen(src_path, "rb");
error_if(!src, "Could not open file %s for reading", src_path);
char buffer[BUFFER_SIZE];
size_t bytes_read, bytes_written;
unsigned int bytes_total = 0;
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, src)) > 0) {
bytes_written = fwrite(buffer, 1, bytes_read, dest);
error_if(bytes_written != bytes_read, "Write error");
bytes_total += bytes_written;
}
error_if(ferror(src), "read error for file %s", src_path);
fclose(src);
return bytes_total;
}
void add_file(const char *path, FILE *dest, pack_state *state) {
if (state->dest_path[0]) {
char absolute[MAX_PATH_LEN];
if (canonicalize_path(path, absolute, MAX_PATH_LEN) && strcmp(absolute, state->dest_path) == 0) {
return;
}
}
if (state->len >= state->capacity) {
state->capacity *= 2;
state->files = realloc(state->files, state->capacity * sizeof(qop_file));
}
// Strip leading "./" from path for archive
const char *archive_path = path;
if (path[0] == '.' && path[1] == '/') {
archive_path = path + 2;
}
qop_uint64_t hash = qop_hash(archive_path);
// Write the path into the archive
int path_len = strlen(archive_path) + 1;
int path_written = fwrite(archive_path, sizeof(char), path_len, dest);
error_if(path_written != path_len, "Write error");
// Copy the file into the archive
unsigned int size = copy_into(path, dest);
printf("%6d %016llx %10d %s\n", state->len, hash, size, archive_path);
// Collect file info for the index
state->files[state->len] = (qop_file){
.hash = hash,
.offset = state->size,
.size = size,
.path_len = path_len,
.flags = QOP_FLAG_NONE
};
state->size += size + path_len;
state->len++;
}
void add_dir(const char *path, FILE *dest, pack_state *state) {
pi_dir *dir = pi_dir_open(path);
error_if(!dir, "Could not open directory %s for reading", path);
pi_dirent *entry;
while ((entry = pi_dir_next(dir))) {
if (
entry->is_dir &&
strcmp(entry->name, ".") != 0 &&
strcmp(entry->name, "..") != 0
) {
char subpath[MAX_PATH_LEN];
snprintf(subpath, MAX_PATH_LEN, "%s/%s", path, entry->name);
add_dir(subpath, dest, state);
}
else if (entry->is_file) {
char subpath[MAX_PATH_LEN];
snprintf(subpath, MAX_PATH_LEN, "%s/%s", path, entry->name);
add_file(subpath, dest, state);
}
}
pi_dir_close(dir);
}
void pack(const char *read_dir, char **sources, int sources_len, const char *archive_path) {
FILE *dest = fopen(archive_path, "wb");
error_if(!dest, "Could not open file %s for writing", archive_path);
pack_state state;
state.files = malloc(sizeof(qop_file) * 1024);
state.len = 0;
state.capacity = 1024;
state.size = 0;
state.dest_path[0] = '\0';
if (!canonicalize_path(archive_path, state.dest_path, MAX_PATH_LEN)) {
state.dest_path[0] = '\0';
}
if (read_dir) {
error_if(chdir(read_dir) != 0, "Could not change to directory %s", read_dir);
}
// Add files/directories
for (int i = 0; i < sources_len; i++) {
struct stat s;
error_if(stat(sources[i], &s) != 0, "Could not stat file %s", sources[i]);
if (S_ISDIR(s.st_mode)) {
add_dir(sources[i], dest, &state);
}
else if (S_ISREG(s.st_mode)) {
add_file(sources[i], dest, &state);
}
else {
die("Path %s is neither a directory nor a regular file", sources[i]);
}
}
// Write index and header
unsigned int total_size = state.size + QOP_HEADER_SIZE;
for (int i = 0; i < state.len; i++) {
write_64(state.files[i].hash, dest);
write_32(state.files[i].offset, dest);
write_32(state.files[i].size, dest);
write_16(state.files[i].path_len, dest);
write_16(state.files[i].flags, dest);
total_size += 20;
}
write_32(state.len, dest);
write_32(total_size, dest);
write_32(QOP_MAGIC, dest);
free(state.files);
fclose(dest);
printf("files: %d, size: %d bytes\n", state.len, total_size);
}
void exit_usage(void) {
puts(
"Usage: qopconv [OPTION...] FILE...\n"
"\n"
"Examples:\n"
" qopconv dir1 archive.qop # Create archive.qop from dir1/\n"
" qopconv foo bar archive.qop # Create archive.qop from files foo and bar\n"
" qoponvv -u archive.qop # Unpack archive.qop in current directory\n"
" qopconv -l archive.qop # List files in archive.qop\n"
" qopconv -d dir1 dir2 archive.qop # Use dir1 prefix for reading, create\n"
" archive.qop from files in dir1/dir2/\n"
"\n"
"Options (mutually exclusive):\n"
" -u <archive> ... unpack archive\n"
" -l <archive> ... list contents of archive\n"
" -d <dir> ....... change read dir when creating archives\n"
);
exit(1);
}
int main(int argc, char **argv) {
if (argc < 3) {
exit_usage();
}
// Unpack
if (strcmp(argv[1], "-u") == 0) {
unpack(argv[2], 0);
}
else if (strcmp(argv[1], "-l") == 0) {
unpack(argv[2], 1);
}
else {
int files_start = 1;
char *read_dir = NULL;
if (strcmp(argv[1], "-d") == 0) {
read_dir = argv[2];
files_start = 3;
}
if (argc < 2 + files_start) {
exit_usage();
}
pack(read_dir, argv + files_start, argc - 1 - files_start, argv[argc-1]);
}
return 0;
}

View File

@@ -71,6 +71,7 @@ var use_embed = hidden.use_embed
var use_dyn = hidden.use_dyn
var enet = hidden.enet
var nota = hidden.nota
var fd = use_embed('fd')
// Wota decode timing tracking
var wota_decode_times = []
@@ -109,16 +110,33 @@ actor_mod.on_exception(disrupt)
var js = use_embed('js')
var io = use_embed('io')
if (!io.exists('.cell')) {
console_mod.print("No cell directory found. Make one.\n");
//log.console(json.encode(fd))
//log.console(fd.fstat)
//log.console(json.encode(fd.fstat('.cell')))
//log.console(fd.fstat('.cell').isDirectory)
if (!fd.stat('.cell').isDirectory) {
log.console("No cell directory found. Make one.\n");
os.exit(1);
}
function slurp(path) {
var st = fd.stat(path)
if (!st.isFile) return null
var fd_handle = fd.open(path, 'r')
var content_blob = fd.read(fd_handle, st.size)
fd.close(fd_handle)
return text(content_blob)
}
var module_alias = {}
var use_cache = {}
var BASEPATH = 'base' + MOD_EXT
var script = io.slurp(BASEPATH)
var script = slurp(BASEPATH)
log.console(script)
var fnname = "base"
script = `(function ${fnname}() { ${script}; })`
js.eval(BASEPATH, script)()
@@ -268,7 +286,9 @@ config.system.__proto__ = default_config
ENETSERVICE = config.system.net_service
REPLYTIMEOUT = config.system.reply_timeout
log.console(`config loaded in ${time.number()-st_now} seconds`)
globalThis.text = use('text')
// Load actor-specific configuration
@@ -312,6 +332,8 @@ function deepFreeze(object) {
return Object.freeze(object);
}
log.console(`stone initialized in ${time.number()-st_now} seconds`)
globalThis.stone = deepFreeze
stone.p = function(object)
{
@@ -681,6 +703,8 @@ load_actor_config(cell.args.program)
actor_mod.register_actor(cell.id, turn, cell.args.main, config.system.ar_timer)
log.console(`actor registered in ${time.number()-st_now} seconds`)
if (config.system.actor_memory)
js.mem_limit(config.system.actor_memory)
@@ -829,6 +853,9 @@ var prog_script = `(function ${cell.args.program.name()}_start($_, arg) { var ar
// queue up its first turn instead of run immediately
var startfn = js.eval(cell.args.program, prog_script);
log.console(`program compiled in ${time.number()-st_now} seconds`)
$_.clock(_ => {
var val = startfn($_, cell.args.arg);
@@ -836,6 +863,6 @@ $_.clock(_ => {
throw new Error('Program must not return anything');
})
log.console(`startup took ${time.number()-st_now}`)
log.console(`program executed in ${time.number()-st_now} seconds`)
})()

View File

@@ -52,11 +52,15 @@
#elif defined(__linux__) || defined(__GLIBC__)
#define _GNU_SOURCE
#include <malloc.h>
#include <unistd.h>
#define MALLOC_OVERHEAD 8
#else
#define MALLOC_OVERHEAD 0
#endif
#define QOP_IMPLEMENTATION
#include "qop.h"
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
@@ -76,6 +80,8 @@ static SDL_Mutex *actors_mutex = NULL;
static struct { char *key; cell_rt *value; } *actors = NULL;
static unsigned char *zip_buffer_global = NULL;
static char *prosperon = NULL;
static qop_desc qop_core;
static qop_file *qop_hashmap = NULL;
cell_rt *root_cell = NULL;
static SDL_AtomicInt shutting_down;
@@ -110,6 +116,13 @@ static void exit_handler(void)
if (actors_mutex)
SDL_DestroyMutex(actors_mutex);
/* Clean up QOP resources */
if (qop_hashmap) {
free(qop_hashmap);
qop_hashmap = NULL;
}
qop_close(&qop_core);
SDL_Quit();
exit(0);
}
@@ -348,72 +361,59 @@ static void free_zip(void)
zip_buffer_global = NULL;
}
int get_executable_path(char *buffer, unsigned int buffer_size) {
#if defined(__linux__)
ssize_t len = readlink("/proc/self/exe", buffer, buffer_size - 1);
if (len == -1) {
return 0;
}
buffer[len] = '\0';
return len;
#elif defined(__APPLE__)
if (_NSGetExecutablePath(buffer, &buffer_size) == 0) {
return buffer_size;
}
#elif defined(_WIN32)
return GetModuleFileName(NULL, buffer, buffer_size);
#endif
return 0;
}
int prosperon_mount_core(void)
{
size_t size;
char exe_path[PATH_MAX];
// Get the full path of the executable
const char *base_dir = PHYSFS_getBaseDir();
if (base_dir) {
snprintf(exe_path, sizeof(exe_path), "%s%s", base_dir, PHYSFS_getDirSeparator());
// Extract just the executable name from argv[0]
const char *exe_name = strrchr(prosperon, '/');
if (!exe_name) exe_name = strrchr(prosperon, '\\');
if (exe_name) exe_name++; else exe_name = prosperon;
strncat(exe_path, exe_name, sizeof(exe_path) - strlen(exe_path) - 1);
} else {
strncpy(exe_path, prosperon, sizeof(exe_path) - 1);
exe_path[sizeof(exe_path) - 1] = '\0';
}
FILE *f = fopen(exe_path, "rb");
if (!f) return perror("fopen"), 0;
if (fseek(f, 0, SEEK_END) != 0) return perror("fseek"), fclose(f), 0;
size = ftell(f);
if (size < 0) return perror("ftell"), fclose(f), 0;
zip_buffer_global = malloc(size);
if (!zip_buffer_global) return perror("malloc"), fclose(f), 0;
rewind(f);
if (fread(zip_buffer_global, 1, size, f) != size) {
perror("fread");
free(zip_buffer_global);
fclose(f);
return 0;
}
fclose(f);
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) {
free(zip_buffer_global);
int exe_path_len = get_executable_path(exe_path, sizeof(exe_path));
if (exe_path_len == 0) {
printf("ERROR: Could not get executable path\n");
return 0;
}
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);
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) return fprintf(stderr, "Invalid zip offset: %ld\n", zip_offset), free(zip_buffer_global), 0;
int ret = PHYSFS_mountMemory(zip_buffer_global + zip_offset, appended_zip_size, free_zip, "core.zip", NULL, 0);
if (!ret) {
printf("COULD NOT MOUNT! Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
// Open the QOP archive appended to this executable
int archive_size = qop_open(exe_path, &qop_core);
if (archive_size == 0) {
printf("ERROR: Could not open QOP archive\n");
return 0;
}
return ret;
// Read the archive index
qop_hashmap = malloc(qop_core.hashmap_size);
if (!qop_hashmap) {
printf("ERROR: Could not allocate memory for QOP hashmap\n");
qop_close(&qop_core);
return 0;
}
int index_len = qop_read_index(&qop_core, qop_hashmap);
if (index_len == 0) {
printf("ERROR: Could not read QOP index\n");
free(qop_hashmap);
qop_hashmap = NULL;
qop_close(&qop_core);
return 0;
}
return 1;
}
void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
@@ -814,20 +814,29 @@ void script_startup(cell_rt *prt)
prt->context = js;
ffi_load(js);
PHYSFS_File *eng = PHYSFS_openRead(ENGINE);
if (!eng) {
printf("ERROR: Could not open file %s! %s\n", ENGINE, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
// Find and load engine.cm from QOP archive
qop_file *engine_file = qop_find(&qop_core, ENGINE);
if (!engine_file) {
printf("ERROR: Could not find file %s in QOP archive!\n", ENGINE);
return;
}
PHYSFS_Stat stat;
PHYSFS_stat(ENGINE, &stat);
char *data = malloc(stat.filesize+1);
PHYSFS_readBytes(eng, data, stat.filesize);
PHYSFS_close(eng);
data[stat.filesize] = 0;
char *data = malloc(engine_file->size + 1);
if (!data) {
printf("ERROR: Could not allocate memory for %s!\n", ENGINE);
return;
}
int bytes_read = qop_read(&qop_core, engine_file, (unsigned char *)data);
if (bytes_read != (int)engine_file->size) {
printf("ERROR: Could not read file %s from QOP archive!\n", ENGINE);
free(data);
return;
}
data[engine_file->size] = 0;
prt->state = ACTOR_RUNNING;
JSValue v = JS_Eval(js, data, (size_t)stat.filesize, ENGINE, 0);
JSValue v = JS_Eval(js, data, (size_t)engine_file->size, ENGINE, 0);
uncaught_exception(js, v);
prt->state = ACTOR_IDLE;
set_actor_state(prt);
@@ -947,43 +956,13 @@ int main(int argc, char **argv)
prosperon = argv[0];
PHYSFS_init(argv[0]);
/* Mount core.zip attached to executable - this is now mandatory! */
/* Load QOP package attached to executable - this is now mandatory! */
int mounted = prosperon_mount_core();
if (!mounted) {
printf("ERROR: Could not mount core. Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
printf("ERROR: Could not load core QOP package.\n");
return 1;
}
/* Check for .cell directory in the current directory */
char *cell_parent_dir = NULL;
struct stat st;
char *current_dir = SDL_GetCurrentDirectory();
char test_path[PATH_MAX];
snprintf(test_path, sizeof(test_path), "%s/.cell", current_dir);
if (stat(test_path, &st) == 0 && S_ISDIR(st.st_mode)) {
cell_parent_dir = strdup(current_dir);
}
SDL_free(current_dir);
if (cell_parent_dir) {
/* 1) Strip any trailing slash from cell_parent_dir (except if it's just "/") */
size_t proj_len = strlen(cell_parent_dir);
if (proj_len > 1 && cell_parent_dir[proj_len - 1] == '/')
cell_parent_dir[proj_len - 1] = '\0';
char cellpath[PATH_MAX];
snprintf(cellpath, sizeof(cellpath), "%s/.cell/modules", cell_parent_dir);
PHYSFS_mount(cellpath, NULL, 0);
PHYSFS_mount(cell_parent_dir, NULL, 0);
PHYSFS_setWriteDir(cell_parent_dir);
free(cell_parent_dir);
} else {
printf("Cell requires a .cell folder to run.\n");
exit(1);
}
/* Create the initial actor from the command line */
int actor_argc = argc - script_start;
char **actor_argv = argv + script_start;

View File

@@ -180,7 +180,8 @@ JSC_CCALL(fd_fstat,
struct stat st;
if (fstat(fd, &st) != 0)
return JS_ThrowReferenceError(js, "fstat failed: %s", strerror(errno));
printf("fstat on %s\n", argv[0]);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.st_size));
JS_SetPropertyStr(js, obj, "mode", JS_NewInt32(js, st.st_mode));
@@ -213,6 +214,42 @@ JSC_CCALL(fd_fstat,
return obj;
)
JSC_SCALL(fd_stat,
struct stat st;
if (stat(str, &st) != 0)
ret = JS_ThrowReferenceError(js, "stat failed: %s", strerror(errno));
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.st_size));
JS_SetPropertyStr(js, obj, "mode", JS_NewInt32(js, st.st_mode));
JS_SetPropertyStr(js, obj, "uid", JS_NewInt32(js, st.st_uid));
JS_SetPropertyStr(js, obj, "gid", JS_NewInt32(js, st.st_gid));
JS_SetPropertyStr(js, obj, "atime", JS_NewInt64(js, st.st_atime));
JS_SetPropertyStr(js, obj, "mtime", JS_NewInt64(js, st.st_mtime));
JS_SetPropertyStr(js, obj, "ctime", JS_NewInt64(js, st.st_ctime));
JS_SetPropertyStr(js, obj, "nlink", JS_NewInt32(js, st.st_nlink));
JS_SetPropertyStr(js, obj, "ino", JS_NewInt64(js, st.st_ino));
JS_SetPropertyStr(js, obj, "dev", JS_NewInt32(js, st.st_dev));
JS_SetPropertyStr(js, obj, "rdev", JS_NewInt32(js, st.st_rdev));
#ifndef _WIN32
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize));
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_blocks));
#else
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, 4096));
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_size / 512));
#endif
// Add boolean properties for file type
JS_SetPropertyStr(js, obj, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
JS_SetPropertyStr(js, obj, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
JS_SetPropertyStr(js, obj, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
JS_SetPropertyStr(js, obj, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
JS_SetPropertyStr(js, obj, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
JS_SetPropertyStr(js, obj, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
JS_SetPropertyStr(js, obj, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
return obj;
)
static const JSCFunctionListEntry js_fd_funcs[] = {
MIST_FUNC_DEF(fd, open, 2),
@@ -224,6 +261,7 @@ static const JSCFunctionListEntry js_fd_funcs[] = {
MIST_FUNC_DEF(fd, mkdir, 1),
MIST_FUNC_DEF(fd, fsync, 1),
MIST_FUNC_DEF(fd, close, 1),
MIST_FUNC_DEF(fd, stat, 1),
MIST_FUNC_DEF(fd, fstat, 1),
};

View File

@@ -1,5 +1,4 @@
#include "qjs_qop.h"
#define QOP_IMPLEMENTATION
#include "qop.h"
#include "qjs_blob.h"
#include "jsffi.h"