diff --git a/include/mgba/script/storage.h b/include/mgba/script/storage.h index 4766ae8e8..629158e83 100644 --- a/include/mgba/script/storage.h +++ b/include/mgba/script/storage.h @@ -15,6 +15,7 @@ CXX_GUARD_START struct VFile; void mScriptContextAttachStorage(struct mScriptContext* context); +void mScriptStorageFlushAll(struct mScriptContext* context); bool mScriptStorageSaveBucket(struct mScriptContext* context, const char* bucket); bool mScriptStorageSaveBucketVF(struct mScriptContext* context, const char* bucket, struct VFile* vf); diff --git a/src/script/storage.c b/src/script/storage.c index bf0e73547..d845db708 100644 --- a/src/script/storage.c +++ b/src/script/storage.c @@ -24,15 +24,18 @@ struct mScriptStorageContext { }; void mScriptStorageBucketDeinit(void*); -struct mScriptValue* mScriptStorageBucketGet(struct mScriptStorageBucket* adapter, const char* key); -static void mScriptStorageBucketSet(struct mScriptStorageBucket* adapter, const char* key, struct mScriptValue* value); -static void mScriptStorageBucketSetVoid(struct mScriptStorageBucket* adapter, const char* key, struct mScriptValue* value); -static void mScriptStorageBucketSetSInt(struct mScriptStorageBucket* adapter, const char* key, int64_t value); -static void mScriptStorageBucketSetUInt(struct mScriptStorageBucket* adapter, const char* key, uint64_t value); -static void mScriptStorageBucketSetFloat(struct mScriptStorageBucket* adapter, const char* key, double value); -static void mScriptStorageBucketSetBool(struct mScriptStorageBucket* adapter, const char* key, bool value); +struct mScriptValue* mScriptStorageBucketGet(struct mScriptStorageBucket* bucket, const char* key); +static void mScriptStorageBucketSet(struct mScriptStorageBucket* bucket, const char* key, struct mScriptValue* value); +static void mScriptStorageBucketSetVoid(struct mScriptStorageBucket* bucket, const char* key, struct mScriptValue* value); +static void mScriptStorageBucketSetSInt(struct mScriptStorageBucket* bucket, const char* key, int64_t value); +static void mScriptStorageBucketSetUInt(struct mScriptStorageBucket* bucket, const char* key, uint64_t value); +static void mScriptStorageBucketSetFloat(struct mScriptStorageBucket* bucket, const char* key, double value); +static void mScriptStorageBucketSetBool(struct mScriptStorageBucket* bucket, const char* key, bool value); +static bool mScriptStorageBucketReload(struct mScriptStorageBucket* bucket); +static bool mScriptStorageBucketFlush(struct mScriptStorageBucket* bucket); -void mScriptStorageContextDeinit(struct mScriptStorageContext*); +static void mScriptStorageContextDeinit(struct mScriptStorageContext*); +static void mScriptStorageContextFlushAll(struct mScriptStorageContext*); struct mScriptStorageBucket* mScriptStorageGetBucket(struct mScriptStorageContext*, const char* name); static bool mScriptStorageToJson(struct mScriptValue* value, struct json_object** out); @@ -48,6 +51,8 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setStr, mScriptStorageB mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setList, mScriptStorageBucketSet, 2, CHARP, key, WLIST, value); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setTable, mScriptStorageBucketSet, 2, CHARP, key, WTABLE, value); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageBucket, setVoid, mScriptStorageBucketSetVoid, 2, CHARP, key, NUL, value); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptStorageBucket, BOOL, reload, mScriptStorageBucketReload, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptStorageBucket, BOOL, flush, mScriptStorageBucketFlush, 0); mSCRIPT_DEFINE_STRUCT(mScriptStorageBucket) mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setSInt) @@ -59,15 +64,19 @@ mSCRIPT_DEFINE_STRUCT(mScriptStorageBucket) mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setTable) mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(mScriptStorageBucket, setVoid) mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(mScriptStorageBucket) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptStorageBucket, reload) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptStorageBucket, flush) mSCRIPT_DEFINE_END; mSCRIPT_DECLARE_STRUCT(mScriptStorageContext); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageContext, _deinit, mScriptStorageContextDeinit, 0); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptStorageContext, S(mScriptStorageBucket), getBucket, mScriptStorageGetBucket, 1, CHARP, key); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptStorageContext, flushAll, mScriptStorageContextFlushAll, 0); mSCRIPT_DEFINE_STRUCT(mScriptStorageContext) mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptStorageContext) mSCRIPT_DEFINE_STRUCT_METHOD(mScriptStorageContext, getBucket) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptStorageContext, flushAll) mSCRIPT_DEFINE_END; struct mScriptValue* mScriptStorageBucketGet(struct mScriptStorageBucket* bucket, const char* key) { @@ -218,14 +227,7 @@ bool mScriptStorageToJson(struct mScriptValue* value, struct json_object** out) return ok; } -bool mScriptStorageSaveBucketVF(struct mScriptContext* context, const char* bucketName, struct VFile* vf) { - struct mScriptValue* value = mScriptContextGetGlobal(context, "storage"); - if (!value) { - vf->close(vf); - return false; - } - struct mScriptStorageContext* storage = value->value.opaque; - struct mScriptStorageBucket* bucket = mScriptStorageGetBucket(storage, bucketName); +static bool _mScriptStorageBucketFlushVF(struct mScriptStorageBucket* bucket, struct VFile* vf) { struct json_object* rootObj; bool ok = mScriptStorageToJson(bucket->root, &rootObj); if (!ok) { @@ -249,6 +251,24 @@ bool mScriptStorageSaveBucketVF(struct mScriptContext* context, const char* buck return true; } +bool mScriptStorageBucketFlush(struct mScriptStorageBucket* bucket) { + char path[PATH_MAX]; + mScriptStorageGetBucketPath(bucket->name, path); + struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); + return _mScriptStorageBucketFlushVF(bucket, vf); +} + +bool mScriptStorageSaveBucketVF(struct mScriptContext* context, const char* bucketName, struct VFile* vf) { + struct mScriptValue* value = mScriptContextGetGlobal(context, "storage"); + if (!value) { + vf->close(vf); + return false; + } + struct mScriptStorageContext* storage = value->value.opaque; + struct mScriptStorageBucket* bucket = mScriptStorageGetBucket(storage, bucketName); + return _mScriptStorageBucketFlushVF(bucket, vf); +} + bool mScriptStorageSaveBucket(struct mScriptContext* context, const char* bucketName) { char path[PATH_MAX]; mScriptStorageGetBucketPath(bucketName, path); @@ -314,6 +334,51 @@ struct mScriptValue* mScriptStorageFromJson(struct json_object* json) { return value; } +static struct mScriptValue* _mScriptStorageLoadJson(struct VFile* vf) { + ssize_t size = vf->size(vf); + if (size < 2) { + vf->close(vf); + return NULL; + } + char* json = calloc(1, size + 1); + if (vf->read(vf, json, size) != size) { + vf->close(vf); + return NULL; + } + vf->close(vf); + + struct json_object* obj = json_tokener_parse(json); + free(json); + if (!obj) { + return NULL; + } + + struct mScriptValue* root = mScriptStorageFromJson(obj); + json_object_put(obj); + return root; +} + +bool mScriptStorageBucketReload(struct mScriptStorageBucket* bucket) { + char path[PATH_MAX]; + mScriptStorageGetBucketPath(bucket->name, path); + struct VFile* vf = VFileOpen(path, O_RDONLY); + if (!vf) { + return false; + } + struct mScriptValue* root = _mScriptStorageLoadJson(vf); + if (!root) { + return false; + } + if (bucket->root) { + mScriptValueDeref(bucket->root); + } + bucket->root = root; + + bucket->dirty = false; + + return true; +} + bool mScriptStorageLoadBucketVF(struct mScriptContext* context, const char* bucketName, struct VFile* vf) { struct mScriptValue* value = mScriptContextGetGlobal(context, "storage"); if (!value) { @@ -321,35 +386,16 @@ bool mScriptStorageLoadBucketVF(struct mScriptContext* context, const char* buck return false; } struct mScriptStorageContext* storage = value->value.opaque; - - ssize_t size = vf->size(vf); - if (size < 2) { - vf->close(vf); - return false; - } - char* json = calloc(1, size + 1); - if (vf->read(vf, json, size) != size) { - vf->close(vf); - return false; - } - vf->close(vf); - - struct json_object* obj = json_tokener_parse(json); - free(json); - if (!obj) { - return false; - } - - struct mScriptValue* root = mScriptStorageFromJson(obj); - json_object_put(obj); + struct mScriptValue* root = _mScriptStorageLoadJson(vf); if (!root) { return false; } - struct mScriptStorageBucket* bucket = mScriptStorageGetBucket(storage, bucketName); mScriptValueDeref(bucket->root); bucket->root = root; + bucket->dirty = false; + return true; } @@ -375,10 +421,29 @@ void mScriptContextAttachStorage(struct mScriptContext* context) { mScriptContextSetDocstring(context, "storage", "Singleton instance of struct::mScriptStorageContext"); } +void mScriptStorageFlushAll(struct mScriptContext* context) { + struct mScriptValue* value = mScriptContextGetGlobal(context, "storage"); + if (!value) { + return; + } + struct mScriptStorageContext* storage = value->value.opaque; + mScriptStorageContextFlushAll(storage); +} + void mScriptStorageContextDeinit(struct mScriptStorageContext* storage) { HashTableDeinit(&storage->buckets); } +void mScriptStorageContextFlushAll(struct mScriptStorageContext* storage) { + struct TableIterator iter; + if (HashTableIteratorStart(&storage->buckets, &iter)) { + do { + struct mScriptStorageBucket* bucket = HashTableIteratorGetValue(&storage->buckets, &iter); + mScriptStorageBucketFlush(bucket); + } while (HashTableIteratorNext(&storage->buckets, &iter)); + } +} + struct mScriptStorageBucket* mScriptStorageGetBucket(struct mScriptStorageContext* storage, const char* name) { if (!name) { return NULL; @@ -401,14 +466,19 @@ struct mScriptStorageBucket* mScriptStorageGetBucket(struct mScriptStorageContex } bucket = calloc(1, sizeof(*bucket)); - bucket->root = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); bucket->name = strdup(name); + if (!mScriptStorageBucketReload(bucket)) { + bucket->root = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + } HashTableInsert(&storage->buckets, name, bucket); return bucket; } void mScriptStorageBucketDeinit(void* data) { struct mScriptStorageBucket* bucket = data; + if (bucket->dirty) { + mScriptStorageBucketFlush(bucket); + } mScriptValueDeref(bucket->root); free(bucket->name); free(bucket); diff --git a/src/script/test/storage.c b/src/script/test/storage.c index a64c5b899..9978776b0 100644 --- a/src/script/test/storage.c +++ b/src/script/test/storage.c @@ -16,7 +16,10 @@ mScriptContextInit(&context); \ struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA); \ mScriptContextAttachStdlib(&context); \ - mScriptContextAttachStorage(&context) + mScriptContextAttachStorage(&context); \ + char bucketPath[PATH_MAX]; \ + mScriptStorageGetBucketPath("xtest", bucketPath); \ + remove(bucketPath) M_TEST_SUITE_SETUP(mScriptStorage) { if (mSCRIPT_ENGINE_LUA->init) {