#include #include #include "log.h" #include "lephisto.h" #include "xml.h" #include "hook.h" /* The hook. */ typedef struct Hook_ { 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. */ int delete; /* Indicates it should be deleted when possible. */ } Hook; /* The stack. */ static unsigned int hook_id = 0; /* Unique hook id. */ static Hook* hook_stack = NULL; static int hook_mstack = 0; static int hook_nstack = 0; static int hook_runningstack = 0; /* Check if stack is running. */ /* Extern. */ 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); /* Extern. */ int hook_save(xmlTextWriterPtr writer); int hook_load(xmlNodePtr parent); static int hook_run(Hook* hook) { int i; Mission* misn; if(hook->delete) return 0; /* Hook should be deleted not run. */ /* Locate the mission. */ for(i = 0; i < MISSION_MAX; i++) if(player_missions[i].id == hook->parent) break; if(i >= MISSION_MAX) { WARN("Trying to run hook with parent not in player mission stack: deleting"); hook->delete = 1; /* So delete it. */ return -1; } misn = &player_missions[i]; if(misn_run(misn, hook->func) < 0) /* Error has accured. */ WARN("Hook [%s] '%d' -> '%s' failed", hook->stack, hook->id, hook->func); return 0; } /* Add/Remove hooks. */ unsigned int hook_add(unsigned int parent, char* func, char* stack) { Hook* new_hook; /* If the memory must grow. */ if(hook_nstack+1 > hook_mstack) { hook_mstack += 5; hook_stack = realloc(hook_stack, hook_mstack*sizeof(Hook)); } /* Create the new hook. */ new_hook = &hook_stack[hook_nstack]; new_hook->id = ++hook_id; new_hook->parent = parent; new_hook->func = strdup(func); new_hook->stack = strdup(stack); new_hook->delete = 0; hook_nstack++; return new_hook->id; } void hook_rm(unsigned int id) { int l, m, h; l = 0; h = hook_nstack-1; while(l <= h) { m = (l+h)/2; if(hook_stack[m].id > id) h = m-1; else if(hook_stack[m].id < id) l = m+1; else break; } if(hook_runningstack) { hook_stack[m].delete = 1; return; } /* 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+1], sizeof(Hook)*(hook_nstack-(m+1))); hook_nstack--; } void hook_rmParent(unsigned int parent) { int i; for(i = 0; i < hook_nstack; i++) if(parent == hook_stack[i].parent) { hook_rm(hook_stack[i].id); if(!hook_runningstack) i--; } } /* Run all hooks off the stack. */ int hooks_run(char* stack) { int i; hook_runningstack = 1; /* Running hooks. */ for(i = 0; i < hook_nstack; i++) if((strcmp(stack, hook_stack[i].stack)==0) && !hook_stack[i].delete) { hook_run(&hook_stack[i]); } hook_runningstack = 0; /* Not running hooks anymore. */ for(i = 0; i < hook_nstack; i++) if((strcmp(stack, hook_stack[i].stack)==0) && hook_stack[i].delete) { hook_rm(hook_stack[i].id); i--; } return 0; } /* Run a single hook by id. */ void hook_runID(unsigned int id) { int i; for(i = 0; i < hook_nstack; i++) if(hook_stack[i].id == id) { hook_run(&hook_stack[i]); return; } 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. */ hook_nstack = 0; 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; }