From 304bc81ea17eee2412b7688cb264c81c31665f29 Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Thu, 30 May 2013 18:33:15 +0100
Subject: [PATCH] [Add] mission/hook save disabled right now - Pluto is still
 being a bitch!

---
 lib/pluto/pluto.c |   3 -
 src/hook.c        | 109 ++++++++++++++++++++++++++++++++---
 src/hook.h        |   8 +--
 src/misn_lua.c    |  10 ++--
 src/mission.c     | 142 +++++++++++++++++++++++++++++++++++++---------
 src/save.c        |  15 +++--
 6 files changed, 235 insertions(+), 52 deletions(-)

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);