From fdd5e930f57cb9b19c439713874ea04198f538f1 Mon Sep 17 00:00:00 2001 From: Allanis Date: Tue, 10 Dec 2013 22:51:00 +0000 Subject: [PATCH] [Add] First release of unidiff - Save changes to the universe in a diff format. --- bin/Makefile | 4 +- dat/unidiff.xml | 11 ++ src/misn_lua.c | 83 ++++++++ src/save.c | 4 + src/space.c | 108 +++++++--- src/space.h | 2 + src/unidiff.c | 508 ++++++++++++++++++++++++++++++++++++++++++++++++ src/unidiff.h | 7 + 8 files changed, 702 insertions(+), 25 deletions(-) create mode 100644 dat/unidiff.xml create mode 100644 src/unidiff.c create mode 100644 src/unidiff.h 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); +