#include #include #include "lephisto.h" #include "log.h" #include "pack.h" #include "xml.h" #include "faction.h" #define XML_FACTION_ID "Factions" // XML section id. #define XML_FACTION_TAG "faction" #define XML_ALLIANCE_ID "Alliances" #define XML_ALLIANCE_TAG "alliance" #define XML_ENEMIES_ID "Enemies" #define XML_ENEMIES_TAG "enemies" #define FACTION_DATA "../dat/faction.xml" #define ALLIANCE_OFFSET 27182 // Special offset for alliances. typedef struct Faction_ { char* name; int* enemies; int nenemies; int* allies; int nallies; } Faction; static Faction* faction_stack = NULL; static int nfactions = 0; // Save alliance. typedef struct Alliance_ { char* name; int* factions; int nfactions; } Alliance; // Stack of alliances. static Alliance* alliances = NULL; static int nalliances = 0; static Faction* faction_parse(xmlNodePtr parent); static void alliance_parse(xmlNodePtr parent); static void enemies_parse(xmlNodePtr parent); static Alliance* alliance_get(char* name); // Return the faction of name "name". int faction_get(const char* name) { int i; for(i = 0; i < nfactions; i++) if(strcmp(faction_stack[i].name, name)==0) break; if(i != nfactions) return i; DEBUG("Faction '%s' not found in stack.", name); return -1; } // Return the id of an alliance. int faction_getAlliance(char* name) { int i; for(i = 0; i < nalliances; i++) if(strcmp(alliances[i].name, name)==0) break; if(i != nalliances) return ALLIANCE_OFFSET + i; DEBUG("Alliance '%s' not found in stack.", name); return -1; } char* faction_name(int f) { return faction_stack[f].name; } // Return the alliance of name 'name'. static Alliance* alliance_get(char* name) { int i; for(i = 0; i < nalliances; i++) if(strcmp(alliances[i].name, name)==0) break; if(i != nalliances) return alliances+i; return NULL; } // Return 1 if Faction a and b are enemies. int areEnemies(int a, int b) { Faction* fa, *fb; int i = 0; if(a == b) return 0; // Luckily our factions aren't masochistic. // Handle a. if(faction_isFaction(a)) fa = &faction_stack[a]; else { // a isn't valid. DEBUG("areEnemies: %d is an invalid faction/alliance", a); return 0; } // Handle b. if(faction_isFaction(b)) fb = &faction_stack[b]; else { // b isn't valid. DEBUG("areEnemies: %d is an invalid faction/alliance", b); return 0; } if(fa && fb) { // Both are factions. for(i = 0; i < fa->nenemies; i++) if(fa->enemies[i] == b) return 1; for(i = 0; i < fb->nenemies; i++) if(fb->enemies[i] == a) return 1; } return 0; } // Return 1 if Faction a and b are allies. int areAllies(int a, int b) { Faction* fa, *fb; int i = 0; // Handle a. if(faction_isFaction(a)) fa = &faction_stack[a]; else { // b isn't valid. DEBUG("areEnemies: %d is an invalid faction/alliance", a); return 0; } // Handle b. if(faction_isFaction(b)) fb = &faction_stack[b]; else { // b isn't valid. DEBUG("areEnemies: %d is an invalid faction/alliance", b); return 0; } if(a == b) return 0; if(fa && fb) { // Both are factions. for(i = 0; i < fa->nallies; i++) if(fa->allies[i] == b) return 1; for(i = 0; i < fb->nallies; i++) if(fb->allies[i] == a) return 1; } return 0; } // Is faction f part of alliance a? int faction_ofAlliance(int f, int a) { int i; Alliance* aa; if(!faction_isFaction(f)) { DEBUG("faction_ofAlliance: invalid alliance '%d'", f); return 0; } if(!faction_isAlliance(a)) { DEBUG("faction_ofAlliance: invalid alliance '%d'", a); return 0; } aa = &alliances[a-ALLIANCE_OFFSET]; for(i = 0; i < aa->nfactions; i++) if(aa->factions[i] == f) return 1; return 0; } // Return true if a s an alliance. int faction_isAlliance(int a) { if((a < ALLIANCE_OFFSET) || (a >= ALLIANCE_OFFSET + nalliances)) return 0; return 1; } // Return true if f is a faction. int faction_isFaction(int f) { if((f < 0) || (f >= nfactions)) return 0; return 1; } // Parses a single faction, but does not set the allies/enemies. static Faction* faction_parse(xmlNodePtr parent) { Faction* tmp = CALLOC_L(Faction); tmp->name = (char*)xmlGetProp(parent, (xmlChar*)"name"); if(tmp->name == NULL) WARN("Faction from "FACTION_DATA" has invalid or no name"); return tmp; } // We set allies/enemies here, in the faction_stack. static void alliance_parse(xmlNodePtr parent) { Alliance* a; int* i, j, n, m; Faction* ft; xmlNodePtr node, cur; node = parent->xmlChildrenNode; do { if((node->type == XML_NODE_START) && (strcmp((char*)node->name, XML_ALLIANCE_TAG)==0)) { // Allocate a new alliance. alliances = realloc(alliances, sizeof(Alliance)*(++nalliances)); alliances[nalliances-1].name = (char*)xmlGetProp(node,(xmlChar*)"name"); alliances[nalliances-1].factions = NULL; alliances[nalliances-1].nfactions = 0; // Parse the current alliance's allies. cur = node->xmlChildrenNode; do { if(strcmp((char*)cur->name, "ally")==0) { // Add the faction (and pointers to make things simple). a = alliances + nalliances-1; i = &a->nfactions; (*i)++; // Load the faction. a->factions = realloc(a->factions, (*i)*sizeof(int)); a->factions[(*i)-1] = faction_get((char*)cur->children->content); if(a->factions[(*i)-1] == -1) WARN("Faction '%s' in alliance '%s' does not exist in "FACTION_DATA, (char*)cur->children->content, a->name); } } while((cur = cur->next)); // Set the crap needed by faction_stack. for(j = 0; j < (*i); j++) { ft = &faction_stack[a->factions[j]]; ft->nallies += (*i)-1; ft->allies = realloc(ft->allies, (ft->nallies)*sizeof(int)); for(n = 0, m = 0; n < (*i); n++, m++) { // Add as ally for all factions exept self. if(n == j) m--; else if(n != j) ft->allies[ft->nallies-(*i)+1+m] = a->factions[n]; } } } } while((node = node->next)); } static void enemies_parse(xmlNodePtr parent) { xmlNodePtr node, cur; int** f; Faction* ft; Alliance* a; int i, *j, n, m, x, y, z, e; char* type; node = parent->xmlChildrenNode; do { if((node->type == XML_NODE_START) && (strcmp((char*)node->name, XML_ENEMIES_TAG)==0)) { i = 0; f = NULL; j = NULL; cur = node->xmlChildrenNode; do { if(strcmp((char*)cur->name,"enemy")==0) { type = (char*)xmlGetProp(cur, (xmlChar*)"type"); i++; j = realloc(j, sizeof(int)*i); f = realloc(f, sizeof(int*)*i); if(strcmp(type, "alliance")==0) { // Enemy thing is an alliance. a = alliance_get((char*)cur->children->content); if(a == NULL) WARN("Alliance %s not found in stack", (char*)cur->children->content); j[i-1] = a->nfactions; f[i-1] = a->factions; } else if(strcmp(type,"faction")==0) { // Enemy thing is only a faction. j[i-1] = 1; f[i-1] = malloc(sizeof(int)); f[i-1][0] = faction_get((char*)cur->children->content); if(f[i-1][0] == -1) WARN("Faction %s not found in stack", (char*)cur->children->content); } free(type); } } while((cur = cur->next)); // Now actually parse and load up the enemies. for(n = 0; n < i; n++) { // Unsinged int. for(m = 0; m < j[n]; m++) { // Unsigned int. // Faction. // Add all the faction enemies to nenemies and alloc. for(e = 0, x = 0; x < i; x++) if(x != n) e += j[x]; // Store the total enemies. // Now allocate the memory. ft = &faction_stack[f[n][m]]; ft->nenemies += e; ft->enemies = realloc(ft->enemies, sizeof(int)*ft->nenemies); // Add the actualy enemies. for(x = 0, z = 0; x < i; x++) if(x != n) // Make sure it's not from the same group. if(x != n) for(y = 0; y < j[x]; y++, z++) ft->enemies[ft->nenemies-e+z] = f[x][y]; } } // Free al the temp memory. for(x = 0; x < i; x++) if(j[x]==1) free(f[x]); // Free the single malloced factions. free(f); // Free the rest. free(j); } } while((node = node->next)); } // Load all the factions. int factions_load(void) { uint32_t bufsize; char* buf = pack_readfile(DATA, FACTION_DATA, &bufsize); xmlNodePtr node; xmlDocPtr doc = xmlParseMemory(buf, bufsize); Faction* tmp = NULL; node = doc->xmlChildrenNode; // Faction node. if(strcmp((char*)node->name, XML_FACTION_ID)) { ERR("Malformed "FACTION_DATA" file: missing root element '"XML_FACTION_ID"'"); return -1; } node = node->xmlChildrenNode; // First faction node. if(node == NULL) { ERR("Malformed "FACTION_DATA" file: does not contain elements"); return -1; } do { if(node->type == XML_NODE_START) { if(strcmp((char*)node->name, XML_FACTION_TAG)==0) { tmp = faction_parse(node); faction_stack = realloc(faction_stack, sizeof(Faction)*(++nfactions)); memcpy(faction_stack+nfactions-1, tmp, sizeof(Faction)); free(tmp); } else if(strcmp((char*)node->name, XML_ALLIANCE_ID)==0) alliance_parse(node); else if(strcmp((char*)node->name, XML_ENEMIES_ID)==0) enemies_parse(node); } } while((node = node->next)); xmlFreeDoc(doc); free(buf); xmlCleanupParser(); DEBUG("Loaded %d Faction%s", nfactions, (nfactions==1) ?"": "s"); return 0; } void factions_free(void) { int i; // Free alliances. for(i = 0; i < nalliances; i++) { free(alliances[i].name); free(alliances[i].factions); } free(alliances); alliances = NULL; nalliances = 0; // Free factions. for(i = 0; i < nfactions; i++) { free(faction_stack[i].name); if(faction_stack[i].nallies > 0) free(faction_stack[i].allies); if(faction_stack[i].nenemies > 0) free(faction_stack[i].enemies); } free(faction_stack); faction_stack = NULL; nfactions = 0; }