diff --git a/lib/pluto/pluto.c b/lib/pluto/pluto.c index bcf69ba..0a82913 100755 --- a/lib/pluto/pluto.c +++ b/lib/pluto/pluto.c @@ -291,11 +291,8 @@ static void persistfunction(PersistInfo *pi) /* It's a C function. For now, we aren't going to allow * persistence of C closures, even if the "C proto" is * already in the permanents table. */ - /* lua_pushstring(pi->L, "Attempt to persist a C function"); lua_error(pi->L); - */ - return; /* we don't save C closures, they'll have to get repushed */ } else { /* It's a Lua closure. */ { diff --git a/src/hook.c b/src/hook.c index 603960b..30c6214 100644 --- a/src/hook.c +++ b/src/hook.c @@ -3,11 +3,12 @@ #include "log.h" #include "lephisto.h" +#include "xml.h" #include "hook.h" // The hook. typedef struct Hook_ { - int id; // Unique id. + unsigned int id; // Unique id. unsigned int parent; // Mission it's connected to. char* func; // Function returned. char* stack; // Stack it's a part of. @@ -26,6 +27,9 @@ static int hook_runningstack = 0; // Check if stack is running. extern int misn_run(Mission* misn, char* func); // Intern. static int hook_run(Hook* hook); +static void hook_free(Hook* h); +static int hook_needSave(Hook* h); +static int hook_parse(xmlNodePtr base); static int hook_run(Hook* hook) { int i; @@ -52,7 +56,7 @@ static int hook_run(Hook* hook) { } // Add/Remove hooks. -int hook_add(unsigned int parent, char* func, char* stack) { +unsigned int hook_add(unsigned int parent, char* func, char* stack) { Hook* new_hook; // If the memory must grow. @@ -65,8 +69,8 @@ int hook_add(unsigned int parent, char* func, char* stack) { new_hook = &hook_stack[hook_nstack]; new_hook->id = ++hook_id; new_hook->parent = parent; - new_hook->func = func; - new_hook->stack = stack; + new_hook->func = strdup(func); + new_hook->stack = strdup(stack); new_hook->delete = 0; hook_nstack++; @@ -74,7 +78,7 @@ int hook_add(unsigned int parent, char* func, char* stack) { return new_hook->id; } -void hook_rm(int id) { +void hook_rm(unsigned int id) { int l, m, h; l = 0; h = hook_nstack-1; @@ -93,12 +97,13 @@ void hook_rm(int id) { // Last hook, just clip the stack. if(m == (hook_nstack-1)) { + hook_free(&hook_stack[m]); hook_nstack--; return; } // Move it! - memmove(&hook_stack[m], &hook_stack[m], sizeof(Hook)*(hook_nstack-(m+1))); + memmove(&hook_stack[m], &hook_stack[m+1], sizeof(Hook)*(hook_nstack-(m+1))); hook_nstack--; } @@ -132,7 +137,7 @@ int hooks_run(char* stack) { } // Run a single hook by id. -void hook_runID(int id) { +void hook_runID(unsigned int id) { int i; for(i = 0; i < hook_nstack; i++) @@ -143,8 +148,18 @@ void hook_runID(int id) { DEBUG("Attempting to run hook of id '%d' which is not in the stack", id); } +// Free a hook. +static void hook_free(Hook* h) { + if(h->func != NULL) free(h->func); + if(h->stack != NULL) free(h->stack); +} + // Clean upi after ourselves. void hook_cleanup(void) { + int i; + + for(i = 0; i < hook_nstack; i++) + hook_free(&hook_stack[i]); free(hook_stack); hook_stack = NULL; // Sane defaults just in case. @@ -152,3 +167,83 @@ void hook_cleanup(void) { hook_mstack = 0; } +// Save the hooks. +static int hook_needSave(Hook* h) { + int i; + char* nosave[] = { "death", "end" }; + + for(i = 0; strcmp(nosave[i], "end") != 0; i++) + if(strcmp(nosave[i], h->stack)==0) return 0; + + return 1; +} + +int hook_save(xmlTextWriterPtr writer) { + int i; + Hook* h; + + xmlw_startElem(writer, "hooks"); + for(i = 0; i < hook_nstack; i++) { + h = &hook_stack[i]; + + if(!hook_needSave(h)) continue; // No need to save it. + + xmlw_startElem(writer, "hook"); + + //xmlw_attr(writer, "id", "%u", h->id); // I don't think it's needed. + xmlw_elem(writer, "parent", "%u", h->parent); + xmlw_elem(writer, "func", "%s", h->func); + xmlw_elem(writer, "stack", "%s", h->stack); + + xmlw_endElem(writer); // Hook. + } + xmlw_endElem(writer); // Hooks. + + return 0; +} + +// Load hooks for a player. +int hook_load(xmlNodePtr parent) { + xmlNodePtr node; + + hook_cleanup(); + + node = parent->xmlChildrenNode; + do { + if(xml_isNode(node, "hooks")) + hook_parse(node); + } while(xml_nextNode(node)); + + return 0; +} + +static int hook_parse(xmlNodePtr base) { + xmlNodePtr node, cur; + char* func, *stack; + unsigned int parent; + + node = base->xmlChildrenNode; + do { + if(xml_isNode(node, "hook")) { + parent = 0; + func = NULL; + stack = NULL; + + cur = node->xmlChildrenNode; + do { + xmlr_long(cur, "parent", parent); + xmlr_str(cur, "func", func); + xmlr_str(cur, "stack", stack); + } while(xml_nextNode(cur)); + + if((parent == 0) || (func == NULL) || (stack == NULL)) { + WARN("Invalid hook."); + return -1; + } + hook_add(parent, func, stack); + } + } while(xml_nextNode(node)); + + return 0; +} + diff --git a/src/hook.h b/src/hook.h index ebd89d1..dbc88c0 100644 --- a/src/hook.h +++ b/src/hook.h @@ -2,15 +2,15 @@ #include "mission.h" // Add/Run hooks. -int hook_add(unsigned int parent, char* func, char* stack); -void hook_rm(int id); +unsigned int hook_add(unsigned int parent, char* func, char* stack); +void hook_rm(unsigned int id); void hook_rmParent(unsigned int parent); // ======================================================== // Run Hooks: // // Currently used: -// -- "land" - When landed. +// -- "land" - When landed. // -- "takeoff" - When taking off. // -- "jump" - When changing systems. // -- "time" - When time is increment drastically @@ -18,7 +18,7 @@ void hook_rmParent(unsigned int parent); // ======================================================== int hooks_run(char* stack); -void hook_runID(int id); // Runs hook of specific id. +void hook_runID(unsigned int id); // Runs hook of specific id. // Destroy hook. void hook_cleanup(void); diff --git a/src/misn_lua.c b/src/misn_lua.c index 4add485..b12154e 100644 --- a/src/misn_lua.c +++ b/src/misn_lua.c @@ -55,7 +55,7 @@ static int misn_delete = 0; // If 1 delete current mission. static int var_add(misn_var* var); static void var_free(misn_var* var); -static int hook_generic(lua_State* L, char* stack); +static unsigned int hook_generic(lua_State* L, char* stack); // -- Libraries. -- @@ -872,7 +872,7 @@ static int tk_input(lua_State* L) { } // -- HOOK -- -static int hook_generic(lua_State* L, char* stack) { +static unsigned int hook_generic(lua_State* L, char* stack) { int i; char* func; @@ -893,8 +893,7 @@ static int hook_generic(lua_State* L, char* stack) { cur_mission->data->name); return 0; } - i = hook_add(cur_mission->id, func, stack); - return i; + return hook_add(cur_mission->id, func, stack); } static int hook_land(lua_State* L) { @@ -919,8 +918,7 @@ static int hook_enter(lua_State* L) { static int hook_pilotDeath(lua_State* L) { MIN_ARGS(2); - int h; - unsigned int p; + unsigned int h, p; if(lua_isnumber(L, -2)) p = (unsigned int) lua_tonumber(L, -2); else MISN_INVALID_PARAMETER(); diff --git a/src/mission.c b/src/mission.c index be0575e..4b59886 100644 --- a/src/mission.c +++ b/src/mission.c @@ -48,6 +48,8 @@ static int mission_matchFaction(MissionData* misn, int faction); static int mission_location(char* loc); static MissionData* mission_parse(const xmlNodePtr parent); static int missions_parseActive(xmlNodePtr parent); +static int mission_persistTable(lua_State* L); +static int mission_unpersistTable(lua_State* L); // Generate a new id for the mission. static unsigned int mission_genID(void) { @@ -104,7 +106,7 @@ static int mission_init(Mission* mission, MissionData* misn) { return -1; } - luaopen_base(mission->L); // Can be useful. + //luaopen_base(mission->L); // Can be useful. luaopen_string(mission->L); // string.format can be very useful. misn_loadLibs(mission->L); // Load our custom libraries. @@ -224,18 +226,33 @@ void mission_unlinkCargo(Mission* misn, unsigned int cargo_id) { // Clean up a mission. void mission_cleanup(Mission* misn) { int i; - if(misn->id) hook_rmParent(misn->id); // Remove existing hooks. - if(misn->title) free(misn->title); - if(misn->desc) free(misn->desc); - if(misn->reward) free(misn->reward); + if(misn->id != 0) { + hook_rmParent(misn->id); // Remove existing hooks. + misn->id = 0; + } + if(misn->title != NULL) { + free(misn->title); + misn->title = NULL; + } + if(misn->desc != NULL) { + free(misn->desc); + misn->desc = NULL; + } + if(misn->reward) { + free(misn->reward); + misn->reward = NULL; + } if(misn->cargo) { for(i = 0; i < misn->ncargo; i++) mission_unlinkCargo(misn, misn->cargo[i]); free(misn->cargo); + misn->cargo = NULL; misn->ncargo = 0; } - if(misn->L) lua_close(misn->L); - memset(misn, 0, sizeof(Mission)); + if(misn->L) { + lua_close(misn->L); + misn->L = NULL; + } } // Free a mission. @@ -432,6 +449,7 @@ void missions_cleanup(void) { mission_cleanup(&player_missions[i]); } +// Memory buffer structure to handle lua writers/readers. typedef struct MBuf_ { char* data; ssize_t len, alloc; // Size of each data chunk, chunks to alloc when growing. @@ -482,6 +500,53 @@ static int mission_writeLua(lua_State* L, const void* p, size_t sz, void* ud) { return 0; } +static int mission_persistTable(lua_State* L) { + lua_newtable(L); + // Table. + lua_pushnil(L); + // table, nil. + while(lua_next(L, LUA_GLOBALSINDEX) != 0) { + // table, key, value. + switch(lua_type(L, -1)) { + case LUA_TNUMBER: + case LUA_TBOOLEAN: + case LUA_TSTRING: + lua_pushvalue(L, -2); // Copy key. + // table, key, value, key. + lua_insert(L, -3); // key << 2. + // table, key, key, value. + lua_settable(L, -4); // table[key] = value. + // table, key. + break; + default: + lua_pop(L, 1); + // table, key. + continue; + } + } + // table. + return 0; +} + +static int mission_unpersistTable(lua_State* L) { + // Table. + lua_pushnil(L); + // Table, Nil. + while(lua_next(L, -2) != 0) { + // Table, key, value. + lua_pushvalue(L, -2); // Copy key. + // Table, key, value, key. + lua_insert(L, -3); // key << 2. + // table, key, key, value. + lua_settable(L, LUA_GLOBALSINDEX); + // table, key. + } + // Table. + lua_pop(L, 1); + + return 0; +} + int missions_saveActive(xmlTextWriterPtr writer) { MBuf* buf; char* data; @@ -494,8 +559,9 @@ int missions_saveActive(xmlTextWriterPtr writer) { if(player_missions[i].id != 0) { xmlw_startElem(writer, "mission"); - xmlw_elem(writer, "data", player_missions[i].data->name); - xmlw_elem(writer, "id", "%u", player_missions[i].id); + // Data and id are attributes because they must be loaded first. + xmlw_attr(writer, "data", player_missions[i].data->name); + xmlw_attr(writer, "id", "%u", player_missions[i].id); xmlw_elem(writer, "title", player_missions[i].title); xmlw_elem(writer, "desc", player_missions[i].desc); @@ -506,14 +572,25 @@ int missions_saveActive(xmlTextWriterPtr writer) { xmlw_elem(writer, "cargo", "%u", player_missions[i].cargo[j]); xmlw_endElem(writer); // Cargo. + // Write lua magic. xmlw_startElem(writer, "lua"); + + // We need to use a special data struct. buf = mbuf_create(1, 128); - lua_pushvalue(player_missions[i].L, LUA_GLOBALSINDEX); + + // Prepare the data. + lua_pushnil(player_missions[i].L); + mission_persistTable(player_missions[i].L); // We don't save it all. pluto_persist(player_missions[i].L, mission_writeLua, buf); + + // Now process it to save it. data = base64_encode(&sz, buf->data, buf->ndata); - mbuf_free(buf); xmlw_raw(writer, data, sz); + + // Cleanup. + mbuf_free(buf); free(data); + xmlw_endElem(writer); // Lua. xmlw_endElem(writer); // Mission. @@ -529,21 +606,23 @@ const char* mission_readLua(lua_State* L, void* data, size_t* size) { MBuf* dat; char* pos; char* buf; + size_t len; dat = (MBuf*) data; + len = dat->alloc * dat->len; pos = dat->data; buf = &pos[dat->ndata]; - if(dat->mdata >= dat->ndata) { + if(dat->ndata >= dat->mdata) { (*size) = 0; return NULL; } - if(dat->mdata < (dat->ndata + dat->alloc * dat->len)) { // Last chunk. + if(dat->mdata < (dat->ndata + len)) { // Last chunk. (*size) = dat->mdata - dat->ndata; dat->ndata = dat->mdata; } else { - (*size) = dat->alloc * dat->len; - dat->ndata += dat->alloc * dat->len; + (*size) = len; + dat->ndata += len; } return buf; } @@ -566,7 +645,7 @@ int missions_loadActive(xmlNodePtr parent) { static int missions_parseActive(xmlNodePtr parent) { Mission* misn; int m; - char* buf; + char* buf, *str; MBuf dat; xmlNodePtr node, cur, nest; @@ -576,16 +655,19 @@ static int missions_parseActive(xmlNodePtr parent) { do { if(xml_isNode(node, "mission")) { misn = &player_missions[m]; - mission_init(misn, NULL); // Won't set data nor id. + + // Process the attributes to create the mission. + xmlr_attr(node, "data", buf); + mission_init(misn, mission_get(mission_getID(buf))); + free(buf); + + // This will orphan an identifier. + xmlr_attr(node, "id", buf); + misn->id = atol(buf); + free(buf); cur = node->xmlChildrenNode; do { - if(xml_isNode(cur, "data")) { - buf = xml_get(cur); - misn->data = mission_get(mission_getID(buf)); - } - xmlr_long(cur, "id", misn->id); - xmlr_strd(cur, "title", misn->title); xmlr_strd(cur, "desc", misn->desc); xmlr_strd(cur, "reward", misn->reward); @@ -599,12 +681,20 @@ static int missions_parseActive(xmlNodePtr parent) { } if(xml_isNode(cur, "lua")) { - buf = xml_get(cur); + // Prepare the data. + str = xml_get(cur); dat.ndata = 0; - dat.len = 1; + dat.len = 1024; dat.alloc = 128; - dat.data = base64_decode(&dat.mdata, buf, strlen(buf)); + dat.data = base64_decode(&dat.mdata, str, strlen(str)); + + // Start the unpersist routine. + lua_pushnil(misn->L); pluto_unpersist(misn->L, mission_readLua, &dat); + mission_unpersistTable(misn->L); + + // Cleanup. + free(dat.data); } } while(xml_nextNode(cur)); diff --git a/src/save.c b/src/save.c index 9ff1bf7..fd1ed5e 100644 --- a/src/save.c +++ b/src/save.c @@ -14,14 +14,16 @@ #define BUTTON_HEIGHT 30 // Externs. -extern int player_save(xmlTextWriterPtr writer); // A lot of stuff. +extern int player_save(xmlTextWriterPtr writer); // A lot of stuff. extern int player_load(xmlNodePtr parent); extern int missions_saveActive(xmlTextWriterPtr writer); // Active missions. extern int missions_loadActive(xmlNodePtr parent); -extern int var_save(xmlTextWriterPtr writer); // misn var. +extern int var_save(xmlTextWriterPtr writer); // misn var. extern int var_load(xmlNodePtr parent); -extern int pfaction_save(xmlTextWriterPtr writer); // Faction data. +extern int pfaction_save(xmlTextWriterPtr writer); // Faction data. extern int pfaction_load(xmlNodePtr parent); +extern int hook_save(xmlTextWriterPtr writer); // Hooks. +extern int hook_load(xmlNodePtr parent); extern void menu_main_close(void); // Static. static int save_data(xmlTextWriterPtr writer); @@ -33,9 +35,9 @@ static int load_game(char* file); static int save_data(xmlTextWriterPtr writer) { // The data itself. if(player_save(writer) < 0) return -1; - if(missions_saveActive(writer) < 0) return -1; + //if(missions_saveActive(writer) < 0) return -1; if(var_save(writer) < 0) return -1; - if(pfaction_save(writer) < 0) return -1; + //if(pfaction_save(writer) < 0) return -1; return 0; } @@ -171,8 +173,9 @@ static int load_game(char* file) { player_load(node); var_load(node); - missions_loadActive(node); + //missions_loadActive(node); pfaction_load(node); + //hook_load(node); xmlFreeDoc(doc);