Lephisto/src/hook.c
2013-11-12 00:42:31 +00:00

261 lines
5.9 KiB
C

#include <malloc.h>
#include <string.h>
#include "log.h"
#include "lephisto.h"
#include "xml.h"
#include "hook.h"
#define HOOK_CHUNK 32 /* Size to grow by when out of space. */
/* 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 += HOOK_CHUNK;
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;
}
/* Mark to delete, but do not delete yet, hooks are running. */
if(hook_runningstack) {
hook_stack[m].delete = 1;
return;
}
/* Free the hook. */
hook_free(&hook_stack[m]);
/* Last hook, just clip the stack. */
if(m == (hook_nstack-1)) {
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(hook_stack[i].delete) { /* Delete any that need deleting. */
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", "board", "disable", "jump", /* Pilot hooks. */
"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;
}