Files
cell/playdate/file_playdate.c
2025-12-10 15:01:20 -06:00

176 lines
6.0 KiB
C

// file_playdate.c - Cell integration for Playdate File API
// Wraps pd_api_file.h functions for JavaScript access
// Note: fd_playdate.c already provides the main file operations.
// This module provides additional raw file API access.
#include "cell.h"
#include "common.h"
#include <string.h>
// --- Helpers ---
static SDFile* js2sdfile(JSContext *js, JSValueConst val) {
int64_t ptr;
if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
return (SDFile*)(intptr_t)ptr;
}
// --- File Functions ---
JSC_CCALL(file_geterr,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
const char *err = pd_file->geterr();
return err ? JS_NewString(js, err) : JS_NULL;
)
JSC_SCALL(file_stat,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
FileStat st;
if (pd_file->stat(str, &st) != 0) {
ret = JS_NULL;
} else {
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "isdir", JS_NewBool(js, st.isdir));
JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.size));
JS_SetPropertyStr(js, obj, "year", JS_NewInt32(js, st.m_year));
JS_SetPropertyStr(js, obj, "month", JS_NewInt32(js, st.m_month));
JS_SetPropertyStr(js, obj, "day", JS_NewInt32(js, st.m_day));
JS_SetPropertyStr(js, obj, "hour", JS_NewInt32(js, st.m_hour));
JS_SetPropertyStr(js, obj, "minute", JS_NewInt32(js, st.m_minute));
JS_SetPropertyStr(js, obj, "second", JS_NewInt32(js, st.m_second));
ret = obj;
}
)
JSC_SCALL(file_mkdir,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
ret = JS_NewBool(js, pd_file->mkdir(str) == 0);
)
JSC_SCALL(file_unlink,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
int recursive = argc > 1 ? JS_ToBool(js, argv[1]) : 0;
ret = JS_NewBool(js, pd_file->unlink(str, recursive) == 0);
)
JSC_SCALL(file_rename,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
const char *to = JS_ToCString(js, argv[1]);
int result = pd_file->rename(str, to);
JS_FreeCString(js, to);
ret = JS_NewBool(js, result == 0);
)
JSC_SCALL(file_open,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
FileOptions mode = kFileRead;
if (argc > 1) mode = (FileOptions)(int)js2number(js, argv[1]);
SDFile *f = pd_file->open(str, mode);
ret = f ? JS_NewInt64(js, (int64_t)(intptr_t)f) : JS_NULL;
)
JSC_CCALL(file_close,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
SDFile *f = js2sdfile(js, argv[0]);
return JS_NewBool(js, pd_file->close(f) == 0);
)
JSC_CCALL(file_read,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
SDFile *f = js2sdfile(js, argv[0]);
unsigned int len = (unsigned int)js2number(js, argv[1]);
void *buf = malloc(len);
if (!buf) return JS_ThrowInternalError(js, "malloc failed");
int read = pd_file->read(f, buf, len);
if (read < 0) {
free(buf);
return JS_NULL;
}
JSValue blob = js_new_blob_stoned_copy(js, buf, read);
free(buf);
return blob;
)
JSC_CCALL(file_write,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
SDFile *f = js2sdfile(js, argv[0]);
size_t len;
const void *data = js_get_blob_data(js, &len, argv[1]);
if (data == (void*)-1) return JS_EXCEPTION;
int written = pd_file->write(f, data, (unsigned int)len);
return JS_NewInt32(js, written);
)
JSC_CCALL(file_flush,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
SDFile *f = js2sdfile(js, argv[0]);
return JS_NewBool(js, pd_file->flush(f) == 0);
)
JSC_CCALL(file_tell,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
SDFile *f = js2sdfile(js, argv[0]);
return JS_NewInt32(js, pd_file->tell(f));
)
JSC_CCALL(file_seek,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
SDFile *f = js2sdfile(js, argv[0]);
int pos = (int)js2number(js, argv[1]);
int whence = argc > 2 ? (int)js2number(js, argv[2]) : SEEK_SET;
return JS_NewBool(js, pd_file->seek(f, pos, whence) == 0);
)
struct listfiles_ctx {
JSContext *js;
JSValue array;
int index;
};
static void listfiles_cb(const char *path, void *userdata) {
struct listfiles_ctx *ctx = (struct listfiles_ctx*)userdata;
JS_SetPropertyUint32(ctx->js, ctx->array, ctx->index++, JS_NewString(ctx->js, path));
}
JSC_SCALL(file_listfiles,
if (!pd_file) return JS_ThrowInternalError(js, "file not initialized");
int showhidden = argc > 1 ? JS_ToBool(js, argv[1]) : 0;
JSValue arr = JS_NewArray(js);
struct listfiles_ctx ctx = { js, arr, 0 };
if (pd_file->listfiles(str, listfiles_cb, &ctx, showhidden) != 0) {
JS_FreeValue(js, arr);
ret = JS_NULL;
} else {
ret = arr;
}
)
static const JSCFunctionListEntry js_file_funcs[] = {
MIST_FUNC_DEF(file, geterr, 0),
MIST_FUNC_DEF(file, stat, 1),
MIST_FUNC_DEF(file, mkdir, 1),
MIST_FUNC_DEF(file, unlink, 2),
MIST_FUNC_DEF(file, rename, 2),
MIST_FUNC_DEF(file, open, 2),
MIST_FUNC_DEF(file, close, 1),
MIST_FUNC_DEF(file, read, 2),
MIST_FUNC_DEF(file, write, 2),
MIST_FUNC_DEF(file, flush, 1),
MIST_FUNC_DEF(file, tell, 1),
MIST_FUNC_DEF(file, seek, 3),
MIST_FUNC_DEF(file, listfiles, 2),
};
JSValue js_file_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_file_funcs, countof(js_file_funcs));
// Export constants
JS_SetPropertyStr(js, mod, "READ", JS_NewInt32(js, kFileRead));
JS_SetPropertyStr(js, mod, "READ_DATA", JS_NewInt32(js, kFileReadData));
JS_SetPropertyStr(js, mod, "WRITE", JS_NewInt32(js, kFileWrite));
JS_SetPropertyStr(js, mod, "APPEND", JS_NewInt32(js, kFileAppend));
JS_SetPropertyStr(js, mod, "SEEK_SET", JS_NewInt32(js, SEEK_SET));
JS_SetPropertyStr(js, mod, "SEEK_CUR", JS_NewInt32(js, SEEK_CUR));
JS_SetPropertyStr(js, mod, "SEEK_END", JS_NewInt32(js, SEEK_END));
return mod;
}