From edadf2cbabef1432f064e423499a985cf55ab97c Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 28 Apr 2018 15:58:14 +0200 Subject: [PATCH] exparse: parse (most of) the resource data. --- src/exparse.c | 317 ++++++++++++++++++++++++++++++++++++++++- src/kernel/item.c | 10 +- src/kernel/item.h | 3 +- src/kernel/region.c | 12 +- src/kernel/resources.c | 18 +-- src/spells/unitcurse.c | 18 +-- src/xmlreader.c | 31 +++- 7 files changed, 374 insertions(+), 35 deletions(-) diff --git a/src/exparse.c b/src/exparse.c index 011212831..44593f3cf 100644 --- a/src/exparse.c +++ b/src/exparse.c @@ -3,21 +3,330 @@ #endif #include "exparse.h" +#include "kernel/build.h" +#include "kernel/item.h" +#include "kernel/resources.h" + #include "util/log.h" #include +#include +#include + +#ifdef XML_LARGE_SIZE +# if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 +# define XML_FMT_INT_MOD "I64" +# else +# define XML_FMT_INT_MOD "ll" +# endif +#else +# define XML_FMT_INT_MOD "l" +#endif + +#ifdef XML_UNICODE_WCHAR_T +# define XML_FMT_STR "ls" +#else +# define XML_FMT_STR "s" +#endif + +enum { + EXP_UNKNOWN, + + EXP_RESOURCES, + EXP_BUILDINGS, + EXP_SHIPS, + EXP_MESSAGES, + EXP_STRINGS, +}; + +typedef struct userdata { + int type; + int depth; + int errors; + XML_Char *cdata; + size_t clength; + void *object; +} userdata; + +static int xml_strcmp(const XML_Char *xs, const char *cs) { + return strcmp(xs, cs); +} + +static const char * attr_get(const XML_Char **attr, const char *key) { + int i; + for (i = 0; attr[i]; i += 2) { + if (xml_strcmp(attr[i], key) == 0) { + return (const char *)attr[i + 1]; + } + } + return NULL; +} + +static bool xml_bool(const XML_Char *val) { + if (xml_strcmp(val, "yes") == 0) return true; + if (xml_strcmp(val, "true") == 0) return true; + if (xml_strcmp(val, "1") == 0) return true; + return false; +} + +static int xml_int(const XML_Char *val) { + return atoi((const char *)val); +} + +static bool attr_bool(XML_Char **pair, const char *key) { + if (xml_strcmp(pair[0], key) == 0) { + return xml_bool(pair[1]); + } + return false; +} + +static void handle_bad_input(userdata *ud, const XML_Char *el, const XML_Char *attr) { + if (attr) { + log_error("unknown attribute in <%s>: %s", (const char *)el, (const char *)attr); + } + else { + log_error("unexpected element <%s>", (const char *)el); + } + ++ud->errors; +} + +static bool handle_flag(int *flags, const XML_Char **pair, const char *names[]) { + int i; + for (i = 0; names[i]; ++i) { + if (xml_strcmp(pair[0], names[i]) == 0) { + if (xml_bool(pair[1])) { + *flags |= (1 << i); + } + else { + *flags &= ~(1 << i); + } + return true; + } + } + return false; +} + +static void handle_resource(userdata *ud, const XML_Char *el, const XML_Char **attr) { + const char *flag_names[] = { "item", "limited", "pooled", NULL }; + int i; + const char *name = NULL, *appear = NULL; + int flags = RTF_POOLED; + bool material = false; + (void)el; + for (i = 0; attr[i]; i += 2) { + if (xml_strcmp(attr[i], "name") == 0) { + name = (const char *)attr[i + 1]; + } + else if (xml_strcmp(attr[i], "appearance") == 0) { + /* TODO: appearance should be a property of item, not resource */ + appear = (const char *)attr[i + 1]; + flags |= RTF_ITEM; + } + else if (xml_strcmp(attr[i], "material") == 0) { + /* TODO: appearance should be a property of item, not resource */ + material = xml_bool(attr[i + 1]); + } + else if (!handle_flag(&flags, attr + i, flag_names)) { + handle_bad_input(ud, el, attr[i]); + } + } + if (name) { + resource_type *rtype = rt_get_or_create(name); + rtype->flags = flags; + if (appear) { + /* TODO: appearance should be a property of item, not resource */ + rtype->itype = it_get_or_create(rtype); + it_set_appearance(rtype->itype, appear); + } + if (material) { + rmt_create(rtype); + } + ud->object = rtype; + } +} + +static void handle_item(userdata *ud, const XML_Char *el, const XML_Char **attr) { + const char *flag_names[] = { "herb", "cursed", "notlost", "big", "animal", "vehicle", "use", NULL }; + int i, flags = ITF_NONE; + resource_type *rtype = (resource_type *)ud->object; + item_type * itype = rtype->itype; + assert(rtype); + if (!itype) { + itype = it_get_or_create(rtype); + } + for (i = 0; attr[i]; i += 2) { + if (xml_strcmp(attr[i], "weight") == 0) { + itype->weight = xml_int(attr[i + 1]); + } + else if (xml_strcmp(attr[i], "capacity") == 0) { + itype->capacity = xml_int(attr[i + 1]); + } + else if (xml_strcmp(attr[i], "score") == 0) { + itype->score = xml_int(attr[i + 1]); + } + else if (!handle_flag(&flags, attr + i, flag_names)) { + handle_bad_input(ud, el, attr[i]); + } + } + itype->flags = flags; +} + +static void XMLCALL handle_resources(userdata *ud, const XML_Char *el, const XML_Char **attr) { + resource_type *rtype = (resource_type *)ud->object; + if (xml_strcmp(el, "resource") == 0) { + handle_resource(ud, el, attr); + } + else if (rtype) { + if (xml_strcmp(el, "item") == 0) { + assert(rtype); + handle_item(ud, el, attr); + } + else if (xml_strcmp(el, "function") == 0) { + assert(rtype); + /* TODO */ + } + else if (rtype->itype) { + item_type *itype = rtype->itype; + if (xml_strcmp(el, "construction") == 0) { + construction *con = calloc(sizeof(construction), 1); + int i; + for (i = 0; attr[i]; i += 2) { + if (xml_strcmp(attr[i], "skill") == 0) { + con->skill = findskill((const char *)attr[i + 1]); + } + else if (xml_strcmp(attr[i], "maxsize") == 0) { + con->maxsize = xml_int(attr[i + 1]); + } + else if (xml_strcmp(attr[i], "reqsize") == 0) { + con->reqsize = xml_int(attr[i + 1]); + } + else if (xml_strcmp(attr[i], "minskill") == 0) { + con->minskill = xml_int(attr[i + 1]); + } + else { + handle_bad_input(ud, el, attr[i]); + } + } + itype->construction = con; + } + else if (xml_strcmp(el, "requirement") == 0) { + assert(itype->construction); + /* TODO */ + } + else if (xml_strcmp(el, "luxury") == 0) { + /* TODO */ + } + else if (xml_strcmp(el, "potion") == 0) { + /* TODO */ + } + else if (xml_strcmp(el, "armor") == 0) { + /* TODO */ + rtype->atype = new_armortype(itype, 0.0, frac_zero, 0, 0); + } + else if (xml_strcmp(el, "weapon") == 0) { + rtype->wtype = new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE); + /* TODO */ + } + else if (xml_strcmp(el, "damage") == 0) { + assert(rtype->wtype); + /* TODO */ + } + else { + handle_bad_input(ud, el, NULL); + } + } + else { + handle_bad_input(ud, el, NULL); + } + } + else { + handle_bad_input(ud, el, NULL); + } +} + +static void XMLCALL handle_start(void *data, const XML_Char *el, const XML_Char **attr) { + userdata *ud = (userdata *)data; + if (ud->depth == 0) { + ud->type = EXP_UNKNOWN; + if (xml_strcmp(el, "eressea") != 0) { + handle_bad_input(ud, el, NULL); + } + } + else if (ud->depth == 1) { + if (xml_strcmp(el, "resources") == 0) { + ud->type = EXP_RESOURCES; + } + else if (xml_strcmp(el, "buildings") == 0) { + ud->type = EXP_BUILDINGS; + } + else if (xml_strcmp(el, "ships") == 0) { + ud->type = EXP_SHIPS; + } + else if (xml_strcmp(el, "messages") == 0) { + ud->type = EXP_MESSAGES; + } + else if (xml_strcmp(el, "strings") == 0) { + ud->type = EXP_STRINGS; + } + else { + handle_bad_input(ud, el, NULL); + } + } + else { + switch (ud->type) { + case EXP_RESOURCES: + handle_resources(ud, el, attr); + default: + /* not implemented */ + handle_bad_input(ud, el, NULL); + } + } + ++ud->depth; +} + +static void XMLCALL handle_end(void *data, const XML_Char *el) { + userdata *ud = (userdata *)data; + --ud->depth; + if (ud->cdata) { + free(ud->cdata); + ud->cdata = NULL; + ud->clength = 0; + } + if (ud->depth == 0) { + ud->type = EXP_UNKNOWN; + } +} + +static void XMLCALL handle_data(void *data, const XML_Char *xs, int len) { + userdata *ud = (userdata *)data; + if (len > 0) { + if (ud->type == EXP_MESSAGES && ud->depth == 4) { + size_t bytes = (size_t)len; + ud->cdata = realloc(ud->cdata, ud->clength + bytes); + memcpy(ud->cdata + ud->clength, xs, bytes); + ud->clength = ud->clength + bytes; + } + } +} + + int exparse_readfile(const char * filename) { XML_Parser xp; FILE *F; int err = 1; char buf[4096]; + userdata ud; F = fopen(filename, "r"); if (!F) { return 2; } xp = XML_ParserCreate("UTF-8"); + XML_SetElementHandler(xp, handle_start, handle_end); + XML_SetCharacterDataHandler(xp, handle_data); + XML_SetUserData(xp, &ud); + memset(&ud, 0, sizeof(ud)); for (;;) { size_t len = (int) fread(buf, 1, sizeof(buf), F); int done; @@ -29,7 +338,7 @@ int exparse_readfile(const char * filename) { } done = feof(F); if (XML_Parse(xp, buf, len, done) == XML_STATUS_ERROR) { - log_error("parse error at line %u of %s: %s", + log_error("parse error at line %" XML_FMT_INT_MOD " of %s: %" XML_FMT_STR, XML_GetCurrentLineNumber(xp), filename, XML_ErrorString(XML_GetErrorCode(xp))); @@ -40,7 +349,11 @@ int exparse_readfile(const char * filename) { break; } } + assert(ud.depth == 0); XML_ParserFree(xp); fclose(F); - return err; + if (err != 0) { + return err; + } + return ud.errors; } diff --git a/src/kernel/item.c b/src/kernel/item.c index ab6ad8c8d..a573dc4ba 100644 --- a/src/kernel/item.c +++ b/src/kernel/item.c @@ -916,7 +916,11 @@ static void free_itype(item_type *itype) { free(itype); } -static void free_wtype(weapon_type *wtype) { +void free_atype(armor_type *atype) { + free(atype); +} + +void free_wtype(weapon_type *wtype) { assert(wtype); free(wtype->damage[0]); free(wtype->damage[1]); @@ -931,7 +935,9 @@ void free_rtype(resource_type *rtype) { if (rtype->itype) { free_itype(rtype->itype); } - free(rtype->atype); + if (rtype->atype) { + free_atype(rtype->atype); + } free(rtype->modifiers); free(rtype->raw); free(rtype->_name); diff --git a/src/kernel/item.h b/src/kernel/item.h index e7bfef7cc..9079369e8 100644 --- a/src/kernel/item.h +++ b/src/kernel/item.h @@ -224,9 +224,10 @@ extern "C" { weapon_type *new_weapontype(item_type * itype, int wflags, variant magres, const char *damage[], int offmod, int defmod, int reload, skill_t sk); + void free_wtype(struct weapon_type *wtype); armor_type *new_armortype(item_type * itype, double penalty, variant magres, int prot, unsigned int flags); - + void free_atype(struct armor_type *wtype); /* these constants are used with get_resourcetype. * The order of the enum is not important for stored data. * The resourcenames array must be updated to match. diff --git a/src/kernel/region.c b/src/kernel/region.c index 790a8341c..7ceab2f18 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -971,16 +971,16 @@ static char *makename(void) size_t nk, ne, nv, ns; static char name[16]; const char *kons = "bcdfghklmnprstvwz", - *start = "bcdgtskpvfr", - *end = "nlrdst", + *handle_start = "bcdgtskpvfr", + *handle_end = "nlrdst", *vowels = "aaaaaaaaaaaeeeeeeeeeeeeiiiiiiiiiiioooooooooooouuuuuuuuuuyy"; /* const char * vowels_latin1 = "aaaaaaaaaàâeeeeeeeeeéèêiiiiiiiiiíîoooooooooóòôuuuuuuuuuúyy"; */ nk = strlen(kons); - ne = strlen(end); + ne = strlen(handle_end); nv = strlen(vowels); - ns = strlen(start); + ns = strlen(handle_start); for (s = rng_int() % 3 + 2; s > 0; s--) { int v; @@ -991,7 +991,7 @@ static char *makename(void) } else { k = rng_int() % (int)ns; - name[p] = start[k]; + name[p] = handle_start[k]; p++; } v = rng_int() % (int)nv; @@ -999,7 +999,7 @@ static char *makename(void) p++; if (rng_int() % 3 == 2 || s == 1) { e = rng_int() % (int)ne; - name[p] = end[e]; + name[p] = handle_end[e]; p++; x = 1; } diff --git a/src/kernel/resources.c b/src/kernel/resources.c index bcf26efb3..231b763f8 100644 --- a/src/kernel/resources.c +++ b/src/kernel/resources.c @@ -180,15 +180,15 @@ struct rawmaterial_type *rmt_create(struct resource_type *rtype) { rawmaterial_type *rmtype; - assert(!rtype->raw); - assert(!rtype->itype || rtype->itype->construction); - rmtype = rtype->raw = malloc(sizeof(rawmaterial_type)); - rmtype->rtype = rtype; - rmtype->terraform = terraform_default; - rmtype->update = NULL; - rmtype->use = use_default; - rmtype->visible = visible_default; - return rmtype; + if (!rtype->raw) { + rmtype = rtype->raw = malloc(sizeof(rawmaterial_type)); + rmtype->rtype = rtype; + rmtype->terraform = terraform_default; + rmtype->update = NULL; + rmtype->use = use_default; + rmtype->visible = visible_default; + } + return rtype->raw; } int limit_resource(const struct region *r, const resource_type *rtype) diff --git a/src/spells/unitcurse.c b/src/spells/unitcurse.c index 6fc979e38..a99eb082c 100644 --- a/src/spells/unitcurse.c +++ b/src/spells/unitcurse.c @@ -232,7 +232,7 @@ static message *cinfo_sparkle(const void *obj, objtype_t typ, const curse * c, "sparkle_18", NULL, /* end draig */ }; - int m, begin = 0, end = 0; + int m, begin = 0, handle_end = 0; unit *u; UNUSED_ARG(typ); @@ -243,18 +243,18 @@ static message *cinfo_sparkle(const void *obj, objtype_t typ, const curse * c, return NULL; for (m = 0; m != c->magician->faction->magiegebiet; ++m) { - while (effects[end] != NULL) - ++end; - begin = end + 1; - end = begin; + while (effects[handle_end] != NULL) + ++handle_end; + begin = handle_end + 1; + handle_end = begin; } - while (effects[end] != NULL) - ++end; - if (end == begin) + while (effects[handle_end] != NULL) + ++handle_end; + if (handle_end == begin) return NULL; else { - int index = begin + curse_geteffect_int(c) % (end - begin); + int index = begin + curse_geteffect_int(c) % (handle_end - begin); return msg_message(mkname("curseinfo", effects[index]), "unit id", u, c->no); } diff --git a/src/xmlreader.c b/src/xmlreader.c index 66b04e611..a157569fd 100644 --- a/src/xmlreader.c +++ b/src/xmlreader.c @@ -559,8 +559,7 @@ static weapon_type *xml_readweapon(xmlXPathContextPtr xpath, item_type * itype) assert(sk != NOSKILL); xmlFree(propValue); - wtype = - new_weapontype(itype, flags, magres, NULL, offmod, defmod, reload, sk); + wtype = new_weapontype(itype, flags, magres, NULL, offmod, defmod, reload, sk); /* reading weapon/damage */ xpath->node = node; @@ -712,7 +711,14 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) flags |= ITF_ANIMAL; if (xml_bvalue(node, "vehicle", false)) flags |= ITF_VEHICLE; + itype = rtype->itype ? rtype->itype : it_get_or_create(rtype); + /* exparse: recreate child data. */ + if (itype->construction) { + free_construction(itype->construction); + itype->construction = NULL; + } + itype->weight = xml_ivalue(node, "weight", 0); itype->capacity = xml_ivalue(node, "capacity", 0); mask_races(node, "allow", &itype->mask_allow); @@ -823,7 +829,18 @@ static int parse_resources(xmlDocPtr doc) log_error("invalid resource %d has no name", i); continue; } + rtype = rt_get_or_create((const char *)name); + /* exparse: recreate child data. */ + if (rtype->atype) { + free_atype(rtype->atype); + rtype->atype = NULL; + } + if (rtype->wtype) { + free_wtype(rtype->wtype); + rtype->wtype = NULL; + } + rtype->flags |= flags; xmlFree(name); @@ -858,10 +875,6 @@ static int parse_resources(xmlDocPtr doc) } xmlXPathFreeObject(result); - if (xml_bvalue(node, "material", false)) { - rmt_create(rtype); - } - if (xml_bvalue(node, "limited", false)) { rtype->flags |= RTF_LIMITED; } @@ -869,6 +882,7 @@ static int parse_resources(xmlDocPtr doc) result = xmlXPathEvalExpression(BAD_CAST "modifier", xpath); rtype->modifiers = xml_readmodifiers(result, node); xmlXPathFreeObject(result); + /* reading eressea/resources/resource/resourcelimit/function */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "resourcelimit/function", xpath); @@ -889,6 +903,11 @@ static int parse_resources(xmlDocPtr doc) } } xmlXPathFreeObject(result); + + if (xml_bvalue(node, "material", false)) { + assert(!rtype->itype || rtype->itype->construction); + rmt_create(rtype); + } } } xmlXPathFreeObject(resources);