// 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 // --- 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; }