diff --git a/bin/Makefile b/bin/Makefile
index 1c1b399..c846325 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -1,6 +1,6 @@
# OPTIONS.
-DEBUG := 1
-#DEBUG_PARANOID := 1
+#DEBUG := 1
+DEBUG_PARANOID := 1
OS := LINUX
#OS := WIN32
diff --git a/dat/unidiff.xml b/dat/unidiff.xml
new file mode 100644
index 0000000..fcdd2a5
--- /dev/null
+++ b/dat/unidiff.xml
@@ -0,0 +1,11 @@
+
+
+
+
+ remove
+ remove
+ remove
+ remove
+
+
+
diff --git a/src/misn_lua.c b/src/misn_lua.c
index fd68b61..9a123f9 100644
--- a/src/misn_lua.c
+++ b/src/misn_lua.c
@@ -23,6 +23,7 @@
#include "ltime.h"
#include "xml.h"
#include "music.h"
+#include "unidiff.h"
#include "misn_lua.h"
/* Similar to lua vars, but with less variety. */
@@ -156,6 +157,17 @@ static const luaL_reg hook_methods[] = {
{ 0, 0 }
};
+/* Diffs. */
+static int diff_applyL(lua_State* L);
+static int diff_removeL(lua_State* L);
+static int diff_isappliedL(lua_State* L);
+static const luaL_reg diff_methods[] = {
+ { "apply", diff_applyL },
+ { "remove", diff_removeL },
+ { "isApplied", diff_isappliedL },
+ { 0, 0 }
+};
+
/* Register all the libaries. */
int misn_loadLibs(lua_State* L) {
lua_loadLephisto(L);
@@ -915,3 +927,74 @@ static int hook_pilot(lua_State* L) {
return 0;
}
+/**
+ * @defgroup DIFF Universe Diff Lua Bindings.
+ *
+ * @brief Lua bindings to apply/remove Universe Diffs.
+ *
+ * Functions should be called like:
+ *
+ * @code
+ * diff.function(parameters)
+ * @endcode
+ *
+ * @{
+ */
+
+/**
+ * @fn static int diff_applyL(lua_State* L)
+ *
+ * @brief apply(string name)
+ *
+ * Applies a diff by name.
+ * @param name Name of the diff to apply.
+ */
+static int diff_applyL(lua_State* L) {
+ char* name;
+
+ if(lua_isstring(L, 1)) name = (char*)lua_tostring(L, 1);
+ else LLUA_INVALID_PARAMETER();
+
+ diff_apply(name);
+ return 0;
+}
+
+/**
+ * @fn static int diff_removeL(lua_State* L)
+ *
+ * @brief remove(string name)
+ *
+ * Removes a diff by name.
+ * @param name Name of the diff to remove.
+ */
+static int diff_removeL(lua_State* L) {
+ char* name;
+
+ if(lua_isstring(L, 1)) name = (char*)lua_tostring(L, 1);
+ else LLUA_INVALID_PARAMETER();
+
+ diff_remove(name);
+ return 0;
+}
+
+/**
+ * @fn static int diff_isappliedL(lua_State* L)
+ *
+ * @brief bool isApplied(string name)
+ *
+ * Check to see if a diff is currently applied.
+ * @param name Name of the diff to check.
+ * @return true if it is applied, false if it isn't.
+ */
+static int diff_isappliedL(lua_State* L) {
+ char* name;
+ if(lua_isstring(L, 1)) name = (char*)lua_tostring(L, 1);
+ else LLUA_INVALID_PARAMETER();
+
+ lua_pushboolean(L, diff_isApplied(name));
+ return 1;
+}
+/**
+ * @}
+ */
+
diff --git a/src/save.c b/src/save.c
index 9231099..d68ee83 100644
--- a/src/save.c
+++ b/src/save.c
@@ -32,6 +32,8 @@ extern int hook_save(xmlTextWriterPtr writer); /* Hooks. */
extern int hook_load(xmlNodePtr parent);
extern int space_sysSave(xmlTextWriterPtr writer); /* Space stuff. */
extern int space_sysLoad(xmlNodePtr parent);
+extern int diff_save(xmlTextWriterPtr writer);
+extern int diff_load(xmlNodePtr parent);
extern void menu_main_close(void);
/* Static. */
static int save_data(xmlTextWriterPtr writer);
@@ -48,6 +50,7 @@ static int save_data(xmlTextWriterPtr writer) {
if(var_save(writer) < 0) return -1;
if(pfaction_save(writer) < 0) return -1;
if(hook_save(writer) < 0) return -1;
+ if(diff_save(writer) < 0) return -1;
if(space_sysSave(writer) < 0) return -1;
return 0;
}
@@ -212,6 +215,7 @@ static int load_game(char* file) {
missions_loadActive(node);
pfaction_load(node);
hook_load(node);
+ diff_load(node);
space_sysLoad(node);
/* Need to run takeoff hooks since player just "took off". */
diff --git a/src/space.c b/src/space.c
index f00e99e..e66181c 100644
--- a/src/space.c
+++ b/src/space.c
@@ -727,6 +727,89 @@ static int planet_parse(Planet* planet, const xmlNodePtr parent) {
return 0;
}
+/**
+ * @fn int system_addPlanet(StarSystem* sys, char* planetname)
+ *
+ * @brief Add a planet to a star system.
+ * @param sys Star System to add planet to.
+ * @param planetname Name of the planet to add.
+ * @return 0 on success.
+ */
+int system_addPlanet(StarSystem* sys, char* planetname) {
+ Planet* planet;
+
+ if(sys == NULL)
+ return -1;
+
+ /* Check if need to grow the star system planet stack. */
+ sys->nplanets++;
+ if(sys->planets == NULL) {
+ sys->planets = malloc(sizeof(Planet*) * CHUNK_SIZE_SMALL);
+ }
+ else if(sys->nplanets > CHUNK_SIZE_SMALL) {
+ sys->planets = realloc(sys->planets, sizeof(Planet*) * sys->nplanets);
+ }
+ planet = planet_get(planetname);
+ sys->planets[sys->nplanets-1] = planet;
+
+ /* Add planet <-> star system to name stack. */
+ spacename_nstack++;
+ if(spacename_nstack > spacename_mstack) {
+ spacename_stack += CHUNK_SIZE;
+ planetname_stack = realloc(planetname_stack,
+ sizeof(char*) * spacename_mstack);
+ systemname_stack = realloc(systemname_stack,
+ sizeof(char*) * spacename_mstack);
+ }
+ planetname_stack[spacename_nstack-1] = planet->name;
+ systemname_stack[spacename_nstack-1] = sys->name;
+
+ return 0;
+}
+
+/**
+ * @fn int system_rmPlanet(StarSystem* sys, char* planetname)
+ *
+ * @brief Remove a planet from a star system.
+ * @param sys Star System to remove planet from.
+ * @param planetname Name of the planet to remove.
+ * @return 0 on success.
+ */
+int system_rmPlanet(StarSystem* sys, char* planetname) {
+ int i;
+ Planet* planet;
+
+ if(sys == NULL)
+ return -1;
+
+ /* Try to find planet. */
+ planet = planet_get(planetname);
+ for(i = 0; i < sys->nplanets; i++)
+ if(sys->planets[i] == planet)
+ break;
+
+ /* Planet not found. */
+ if(i >= sys->nplanets)
+ return -1;
+
+ /* Remove planet from system. */
+ sys->nplanets--;
+ memmove(&sys->planets[i], &sys->planets[i+1], sizeof(Planet*) * (sys->nplanets-i));
+
+ /* Remove from the name stack thingy. */
+ for(i = 0; i < spacename_nstack; i++)
+ if(strcmp(planetname, planetname_stack[i])==0) {
+ spacename_nstack--;
+ memmove(&planetname_stack[i], &planetname_stack[i+1],
+ sizeof(char*) * (spacename_nstack-i));
+ memmove(&systemname_stack[i], &systemname_stack[i+1],
+ sizeof(char*) * (spacename_nstack-i));
+ break;
+ }
+
+ return 0;
+}
+
/* Parse node 'parent' which should be the node of a system. */
/* Return the StarSystem fully loaded. */
static StarSystem* system_parse(StarSystem* sys, const xmlNodePtr parent) {
@@ -790,29 +873,8 @@ static StarSystem* system_parse(StarSystem* sys, const xmlNodePtr parent) {
else if(xml_isNode(node, "planets")) {
cur = node->children;
do {
- if(cur && xml_isNode(cur, "planet")) {
- /* Add planet to system. */
- sys->nplanets++;
- if(sys->nplanets > size) {
- size += CHUNK_SIZE_SMALL;
- sys->planets = realloc(sys->planets, sizeof(Planet*) * size);
- }
- planet = planet_get(xml_get(cur));
- sys->planets[sys->nplanets-1] = planet;
-
- /* Add planet <-> star system to name stack. */
- spacename_nstack++;
- if(spacename_nstack > spacename_mstack) {
- spacename_mstack += CHUNK_SIZE;
- planetname_stack = realloc(planetname_stack,
- sizeof(char*) * spacename_mstack);
- systemname_stack = realloc(systemname_stack,
- sizeof(char*) * spacename_mstack);
- }
- planetname_stack[spacename_nstack-1] = planet->name;
-
- systemname_stack[spacename_nstack-1] = sys->name;
- }
+ if(xml_isNode(cur, "planet"))
+ system_addPlanet(sys, xml_get(cur));
} while(xml_nextNode(cur));
}
/* Load all the fleets. */
diff --git a/src/space.h b/src/space.h
index 5958749..598f203 100644
--- a/src/space.h
+++ b/src/space.h
@@ -142,6 +142,8 @@ void space_exit(void);
char* planet_getSystem(char* planetname);
Planet* planet_get(char* planetname);
char planet_getClass(Planet* p);
+int system_addPlanet(StarSystem* sys, char* planetname);
+int system_rmPlanet(StarSystem* sys, char* planetname);
/* Render. */
void space_render(const double dt);
diff --git a/src/unidiff.c b/src/unidiff.c
new file mode 100644
index 0000000..b719858
--- /dev/null
+++ b/src/unidiff.c
@@ -0,0 +1,508 @@
+/**
+ * @file unidiff.c
+ *
+ * @brief Handles the application and removal of 'diffs' to the universe.
+ *
+ * Diffs allow changing planets, fleets, factions etc.. in the universe.
+ * These are meant to be applied after the player triggers them, mostly
+ * through missions.
+ */
+
+#include
+#include
+
+#include "lephisto.h"
+#include "log.h"
+#include "xml.h"
+#include "space.h"
+#include "pack.h"
+#include "unidiff.h"
+
+#define CHUNK_SIZE 32 /**< Size of chunk to allocate. */
+#define DIFF_DATA "../dat/unidiff.xml" /**< Unidff XML file. */
+
+/**
+ * @enum UniHunkTargetType_t
+ *
+ * @brief Represents the possible hunk targets.
+ */
+typedef enum UniHunkTargetType_ {
+ HUNK_TARGET_NONE,
+ HUNK_TARGET_SYSTEM
+} UniHunkTargetType_t;
+
+/**
+ * @struct UniHunkTarget_t
+ *
+ * @brief Represents the hunks target.
+ */
+typedef struct UniHunkTarget_ {
+ UniHunkTargetType_t type;
+ union {
+ char* name;
+ } u;
+} UniHunkTarget_t;
+
+/**
+ * @enum UniHunkType_t
+ *
+ * @brief Represents the different type of hunk actions.
+ */
+typedef enum UniHunkType_ {
+ HUNK_TYPE_NONE,
+ /* Target should be system. */
+ HUNK_TYPE_PLANET_ADD,
+ HUNK_TYPE_PLANET_REMOVE,
+ HUNK_TYPE_FLEET_ADD,
+ HUNK_TYPE_FLEET_REMOVE
+} UniHunkType_t;
+
+/**
+ * @struct UniHunk_t
+ *
+ * @brief Represents a single hunk in the diff.
+ */
+typedef struct UniHunk_ {
+ UniHunkTarget_t target; /**< Hunk's target. */
+
+ UniHunkType_t type; /**< Type of hunk it is. */
+ union {
+ char* name;
+ } u; /* Actual data to patch. */
+} UniHunk_t;
+
+/**
+ * @struct UniDiff_t
+ *
+ * @brief Represents each Universe Diff.
+ */
+typedef struct UniDiff_ {
+ char* name; /**< Name of the diff. */
+
+ UniHunk_t* applied; /**< Applied hunks. */
+ int napplied; /**< Number of applied hunks. */
+ int mapplied; /**< Memory of applied hunks. */
+
+ UniHunk_t* failed; /**< Failed hunks. */
+ int nfailed; /**< Number of failed hunks. */
+ int mfailed; /**< Memory of failed hunks. */
+} UniDiff_t;
+
+/* Diff stack. */
+static UniDiff_t* diff_stack = NULL; /**< Currently applied universe diffs. */
+static int diff_nstack = 0; /**< Number of diffs in the stack. */
+static int diff_mstack = 0; /**< Currently allocated diffs. */
+
+static UniDiff_t* diff_get(char* name);
+static UniDiff_t* diff_newDiff(void);
+static int diff_removeDiff(UniDiff_t* diff);
+static int diff_patch(xmlNodePtr parent);
+static int diff_patchHunk(UniHunk_t* hunk);
+static void diff_hunkFailed(UniDiff_t* diff, UniHunk_t* hunk);
+static void diff_hunkSuccess(UniDiff_t* diff, UniHunk_t* hunk);
+static void diff_cleanup(UniDiff_t* diff);
+static void diff_cleanupHunk(UniHunk_t* hunk);
+/* Externed. */
+int diff_save(xmlTextWriterPtr writer);
+int diff_load(xmlNodePtr parent);
+
+/**
+ * @fn int diff_isApplied(char* name)
+ *
+ * @brief Check if a diff is currently applied.
+ * @param name Diff to check.
+ * @return 0 if it's not applied, 1 if it is.
+ */
+int diff_isApplied(char* name) {
+ if(diff_get(name) != NULL)
+ return 1;
+ return 0;
+}
+
+/**
+ * @fn static UniDiff_t* diff_get(char* name)
+ *
+ * @brief Get a diff by name.
+ * @param name Name of the diff to get.
+ * @return The diff if found or NULL if not found.
+ */
+static UniDiff_t* diff_get(char* name) {
+ int i;
+ for(i = 0; i < diff_nstack; i++)
+ if(strcmp(diff_stack[i].name, name)==0)
+ return &diff_stack[i];
+ return NULL;
+}
+
+/**
+ * @fn int diff_apply(char* name)
+ *
+ * @brief Applies a diff to the universe.
+ * @param name Diff to apply.
+ * @return 0 on success.
+ */
+int diff_apply(char* name) {
+ xmlNodePtr node;
+ xmlDocPtr doc;
+ uint32_t bufsize;
+ char* buf;
+ char* diffname;
+
+ /* Check if already applied. */
+ if(diff_isApplied(name))
+ return 0;
+
+ buf = pack_readfile(DATA, DIFF_DATA, &bufsize);
+
+ node = doc->xmlChildrenNode;
+ if(strcmp((char*)node->name, "unidiffs")) {
+ WARN("Malformed unidiff file: missing root element 'unidiffs'");
+ return 0;
+ }
+
+ node = node->xmlChildrenNode; /* First system node. */
+ if(node == NULL) {
+ ERR("Malformed unidiff file: does not contain elements");
+ return 0;
+ }
+
+ do {
+ if(xml_isNode(node, "unidiff")) {
+ /* Check to see if it's the diff we're looking for. */
+ xmlr_attr(node, "name", diffname);
+ if(strcmp(diffname, name)==0) {
+ diff_patch(node);
+ free(diffname);
+ return 0;
+ }
+ free(diffname);
+ }
+ } while(xml_nextNode(node));
+
+ WARN("UniDiff '%s' not found in "DIFF_DATA".", name);
+ return -1;
+}
+
+/**
+ * @fn static int diff_patch(xmlNodePtr parent)
+ *
+ * @brief Actually applies a diff in XML node form.
+ * @param parent Node containing the diff information.
+ * @return 0 on success.
+ */
+static int diff_patch(xmlNodePtr parent) {
+ UniDiff_t* diff;
+ UniHunk_t base, hunk;
+ xmlNodePtr node, cur;
+ char* buf;
+
+ /* Prepare it. */
+ diff = diff_newDiff();
+ memset(diff, 0, sizeof(UniDiff_t));
+ xmlr_attr(parent, "name", diff->name);
+
+ node = parent->xmlChildrenNode;
+ do {
+ if(xml_isNode(node, "system")) {
+ /* Set the target. */
+ memset(&base, 0, sizeof(UniHunk_t));
+ base.target.type = HUNK_TARGET_SYSTEM;
+ xmlr_attr(node, "name", base.target.u.name);
+ if(base.target.u.name == NULL)
+ WARN("UniDiff '%s' has a system node without a 'name' tag", diff->name);
+
+ /* Now parse the possible changes. */
+ cur = node->xmlChildrenNode;
+ do {
+ if(xml_isNode(cur, "planet")) {
+ memcpy(&hunk, &base, sizeof(UniHunk_t));
+
+ /* Get the planet to modify. */
+ xmlr_attr(cur, "name", hunk.u.name);
+
+ /* Get the type. */
+ buf = xml_get(cur);
+ if(strcmp(buf, "add")==0) {
+ hunk.type = HUNK_TYPE_PLANET_ADD;
+ }
+ else if(strcmp(buf, "remove")==0) {
+ hunk.type = HUNK_TYPE_PLANET_REMOVE;
+ }
+
+ if(diff_patchHunk(&hunk) < 0)
+ diff_hunkFailed(diff, &hunk);
+ else
+ diff_hunkSuccess(diff, &hunk);
+ }
+ else if(xml_isNode(cur, "fleet")) {
+ memcpy(&hunk, &base, sizeof(UniHunk_t));
+
+ /* Get the planet to modify. */
+ xmlr_attr(cur, "name", hunk.u.name);
+
+ /* Get the type. */
+ buf = xml_get(cur);
+ if(strcmp(buf, "add")==0) {
+ hunk.type = HUNK_TYPE_FLEET_ADD;
+ }
+ else if(strcmp(buf, "remove")==0) {
+ hunk.type = HUNK_TYPE_FLEET_REMOVE;
+ }
+
+ if(diff_patchHunk(&hunk) < 0)
+ diff_hunkFailed(diff, &hunk);
+ else
+ diff_hunkSuccess(diff, &hunk);
+ }
+ } while(xml_nextNode(cur));
+ }
+ } while(xml_nextNode(node));
+
+ return 0;
+}
+
+/**
+ * @fn static int diff_patchHunk(UniHunk_t* hunk)
+ *
+ * @brief Applies a hunk and adds it to the diff.
+ * @param diff Diff to which the hunk belongs.
+ * @param hunk Hunk to apply.
+ */
+static int diff_patchHunk(UniHunk_t* hunk) {
+ switch(hunk->type) {
+ /* Adding a planet. */
+ case HUNK_TYPE_PLANET_ADD:
+ return system_addPlanet(system_get(hunk->target.u.name), hunk->u.name);
+ /* Removing a planet. */
+ case HUNK_TYPE_PLANET_REMOVE:
+ return system_rmPlanet(system_get(hunk->target.u.name), hunk->u.name);
+
+ case HUNK_TYPE_FLEET_ADD:
+ break;
+ case HUNK_TYPE_FLEET_REMOVE:
+ break;
+ default:
+ WARN("Unkown hunk type '%d'.", hunk->type);
+ break;
+ }
+ return -1;
+}
+
+/**
+ * @fn static void diff_hunkFailed(UniDiff_t* diff, UniHunk_t* hunk)
+ *
+ * @brief Add a hunk to the failed list.
+ * @param diff Diff to add hunk to.
+ * @param hunk Hunk that failed to apply.
+ */
+static void diff_hunkFailed(UniDiff_t* diff, UniHunk_t* hunk) {
+ if(diff == NULL)
+ return;
+
+ diff->nfailed++;
+ if(diff->nfailed > diff->mfailed) {
+ diff->mfailed += CHUNK_SIZE;
+ diff->failed = realloc(diff->failed, sizeof(UniHunk_t) * diff->mfailed);
+ }
+ memcpy(&diff->failed[diff->nfailed-1], &hunk, sizeof(UniHunk_t));
+}
+
+/**
+ * @fn static void diff_hunkSuccess(UniDiff_t* diff, UniHunk_t* hunk)
+ *
+ * @brief Add a hunk to the applied list.
+ * @param diff Diff to add hunk to.
+ * @param hunk Hunk that applied correctly.
+ */
+static void diff_hunkSuccess(UniDiff_t* diff, UniHunk_t* hunk) {
+ if(diff == NULL)
+ return;
+
+ diff->napplied++;
+ if(diff->napplied > diff->mapplied) {
+ diff->mapplied += CHUNK_SIZE;
+ diff->applied = realloc(diff->applied, sizeof(UniHunk_t) * diff->mapplied);
+ }
+ memcpy(&diff->applied[diff->napplied-1], &hunk, sizeof(UniHunk_t));
+}
+
+/**
+ * @fn void diff_remove(char* name)
+ *
+ * @brief Remove a diff from the universe.
+ * @param name Diff to remove.
+ */
+void diff_remove(char* name) {
+ UniDiff_t* diff;
+
+ /* check if already applied. */
+ diff = diff_get(name);
+ if(diff == NULL)
+ return;
+
+ diff_removeDiff(diff);
+}
+
+/**
+ * @fn void diff_clear(void)
+ */
+void diff_clear(void) {
+ while(diff_nstack > 0) {
+ diff_removeDiff(&diff_stack[diff_nstack-1]);
+ }
+}
+
+/**
+ * @fn static UniDiff_t* diff_newDiff(void)
+ *
+ * @brief Creates a new UniDiff_t for usage.
+ * @return A newly created UniDiff_t.
+ */
+static UniDiff_t* diff_newDiff(void) {
+ /* Check if needs initialization. */
+ if(diff_stack == NULL) {
+ diff_mstack = CHUNK_SIZE;
+ diff_stack = malloc(diff_mstack * sizeof(UniDiff_t));
+ diff_nstack = 0;
+ return &diff_stack[0];
+ }
+
+ diff_nstack++;
+ /* Check if need to grow. */
+ if(diff_nstack > diff_mstack) {
+ diff_mstack += CHUNK_SIZE;
+ diff_stack = realloc(diff_stack, diff_mstack * sizeof(UniDiff_t));
+ }
+
+ return &diff_stack[diff_nstack-1];
+}
+
+/**
+ * @fn static int diff_removeDiff(UniDiff_t* diff)
+ *
+ * @brief Removes a diff.
+ * @param diff Diff to remove.
+ * @return 0 on success.
+ */
+static int diff_removeDiff(UniDiff_t* diff) {
+ int i;
+ UniHunk_t hunk;
+
+ for(i = 0; i < diff->napplied; i++) {
+ memcpy(&hunk, &diff->applied[i], sizeof(UniHunk_t));
+ /* Invert the type for reverting. */
+ switch(hunk.type) {
+ case HUNK_TYPE_PLANET_ADD:
+ hunk.type = HUNK_TYPE_PLANET_REMOVE;
+ break;
+
+ case HUNK_TYPE_PLANET_REMOVE:
+ hunk.type = HUNK_TYPE_PLANET_ADD;
+ break;
+
+ case HUNK_TYPE_FLEET_ADD:
+ hunk.type = HUNK_TYPE_FLEET_REMOVE;
+ break;
+
+ case HUNK_TYPE_FLEET_REMOVE:
+ hunk.type = HUNK_TYPE_FLEET_ADD;
+ break;
+
+ default:
+ WARN("Unkown Hunk type '%d'.", hunk.type);
+ continue;
+ }
+
+ diff_patchHunk(&hunk);
+ }
+
+ diff_cleanup(diff);
+ diff_nstack--;
+ i = diff - diff_stack;
+ memmove(&diff_stack[i], &diff_stack[i+1], sizeof(UniDiff_t) * (diff_nstack-i));
+
+ return 0;
+}
+
+/**
+ * @fn static void diff_cleanup(UniDiff_t* diff)
+ *
+ * @brief Clean up a diff.
+ * @param diff Diff to clean up.
+ */
+static void diff_cleanup(UniDiff_t* diff) {
+ int i;
+
+ free(diff->name);
+ for(i = 0; i < diff->napplied; i++)
+ diff_cleanupHunk(&diff->applied[i]);
+ if(diff->applied != NULL)
+ free(diff->applied);
+ for(i = 0; i < diff->nfailed; i++)
+ diff_cleanupHunk(&diff->failed[i]);
+ if(diff->failed != NULL)
+ free(diff->failed);
+ memset(diff, 0, sizeof(UniDiff_t));
+}
+
+/**
+ * @fn static void diff_cleanupHunk(UniHunk_t* hunk)
+ *
+ * @brief Clean up a hunk.
+ * @param hunk Hunk to clean up.
+ */
+static void diff_cleanupHunk(UniHunk_t* hunk) {
+ if(hunk->target.u.name != NULL)
+ free(hunk->target.u.name);
+ if(hunk->u.name != NULL)
+ free(hunk->u.name);
+ memset(hunk, 0, sizeof(UniHunk_t));
+}
+
+/**
+ * @fn int diff_save(xmlTextWriterPtr writer)
+ *
+ * @brief Save the active diffs.
+ * @param writer XML Writer to use.
+ * @return 0 on success.
+ */
+int diff_save(xmlTextWriterPtr writer) {
+ int i;
+ UniDiff_t* diff;
+
+ xmlw_startElem(writer, "diffs");
+ for(i = 0; i < diff_nstack; i++) {
+ diff = &diff_stack[i];
+
+ xmlw_elem(writer, "diff", diff->name);
+ }
+ xmlw_endElem(writer); /* "diffs" */
+
+ return 0;
+}
+
+/**
+ * @fn int diff_load(xmlNodePtr parent)
+ *
+ * @brief Loads the diffs.
+ * @param parent Parent node containing diffs.
+ * @return 0 on success.
+ */
+int diff_load(xmlNodePtr parent) {
+ xmlNodePtr node, cur;
+
+ diff_clear();
+
+ node = parent->xmlChildrenNode;
+ do {
+ if(xml_isNode(node, "diffs")) {
+ cur = node->xmlChildrenNode;
+ do {
+ if(xml_isNode(cur, "diff"))
+ diff_apply(xml_get(cur));
+ } while(xml_nextNode(cur));
+ }
+ } while(xml_nextNode(node));
+ return 0;
+}
+
diff --git a/src/unidiff.h b/src/unidiff.h
new file mode 100644
index 0000000..5c0ac80
--- /dev/null
+++ b/src/unidiff.h
@@ -0,0 +1,7 @@
+#pragma once
+
+int diff_apply(char* name);
+void diff_remove(char* name);
+void diff_clear(void);
+int diff_isApplied(char* name);
+