diff --git a/src/kernel/connection.c b/src/kernel/connection.c index dbf29bff9..257a78af3 100644 --- a/src/kernel/connection.c +++ b/src/kernel/connection.c @@ -659,7 +659,7 @@ int read_borders(struct storage *store) type->read(b, store); if (global.data_version < NOBORDERATTRIBS_VERSION) { attrib *a = NULL; - int result = a_read(store, &a, b); + int result = read_attribs(store, &a, b); if (border_convert_cb) { border_convert_cb(b, a); } diff --git a/src/kernel/group.c b/src/kernel/group.c index 35f7e93a7..7d57511d1 100755 --- a/src/kernel/group.c +++ b/src/kernel/group.c @@ -209,7 +209,7 @@ void write_groups(struct storage *store, const faction * f) } } WRITE_INT(store, 0); - a_write(store, g->attribs, g); + write_attribs(store, g->attribs, g); WRITE_SECTION(store); } WRITE_INT(store, 0); @@ -241,6 +241,6 @@ void read_groups(struct storage *store, faction * f) if (!a->faction) ur_add(fid, &a->faction, resolve_faction); } - a_read(store, &g->attribs, g); + read_attribs(store, &g->attribs, g); } } diff --git a/src/kernel/save.c b/src/kernel/save.c index 7e5112443..c5239d2a5 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -577,6 +577,23 @@ const struct locale *lang) WRITE_STR(data->store, obuf); } +int read_attribs(storage *store, attrib **alist, void *owner) { +#if RELEASE_VERSION < ATHASH_VERSION + return a_read_orig(store, alist, owner); +#else + return a_read(store, alist, owner); +#endif +} + +void write_attribs(storage *store, attrib *alist, const void *owner) +{ +#if RELEASE_VERSION < ATHASH_VERSION + a_write_orig(store, alist, owner); +#else + a_write(store, alist, owner); +#endif +} + unit *read_unit(struct gamedata *data) { unit *u; @@ -748,8 +765,7 @@ unit *read_unit(struct gamedata *data) log_error("Einheit %s hat %u Personen, und %u Trefferpunkte\n", itoa36(u->no), u->number, u->hp); u->hp = u->number; } - - a_read(data->store, &u->attribs, u); + read_attribs(data->store, &u->attribs, u); return u; } @@ -823,7 +839,7 @@ void write_unit(struct gamedata *data, const unit * u) } WRITE_INT(data->store, u->hp); WRITE_SECTION(data->store); - a_write(data->store, u->attribs, u); + write_attribs(data->store, u->attribs, u); WRITE_SECTION(data->store); } @@ -982,7 +998,7 @@ static region *readregion(struct gamedata *data, int x, int y) read_owner(data, &r->land->ownership); } } - a_read(data->store, &r->attribs, r); + read_attribs(data->store, &r->attribs, r); return r; } @@ -1046,7 +1062,7 @@ void writeregion(struct gamedata *data, const region * r) WRITE_SECTION(data->store); #endif } - a_write(data->store, r->attribs, r); + write_attribs(data->store, r->attribs, r); WRITE_SECTION(data->store); } @@ -1253,7 +1269,7 @@ faction *readfaction(struct gamedata * data) } } - a_read(data->store, &f->attribs, f); + read_attribs(data->store, &f->attribs, f); read_items(data->store, &f->items); for (;;) { READ_TOK(data->store, name, sizeof(name)); @@ -1338,7 +1354,7 @@ void writefaction(struct gamedata *data, const faction * f) WRITE_INT(data->store, f->magiegebiet); WRITE_INT(data->store, f->flags & FFL_SAVEMASK); - a_write(data->store, f->attribs, f); + write_attribs(data->store, f->attribs, f); WRITE_SECTION(data->store); write_items(data->store, f->items); WRITE_SECTION(data->store); @@ -1451,7 +1467,7 @@ int readgame(const char *filename, bool backup) else { READ_STR(&store, NULL, 0); } - a_read(&store, &global.attribs, NULL); + read_attribs(&store, &global.attribs, NULL); READ_INT(&store, &turn); global.data_turn = turn; log_debug(" - reading turn %d\n", turn); @@ -1512,7 +1528,7 @@ int readgame(const char *filename, bool backup) fno = read_faction_reference(&store); } } - a_read(&store, &pl->attribs, pl); + read_attribs(&store, &pl->attribs, pl); if (pl->id != 1094969858) { // Regatta addlist(&planes, pl); } @@ -1580,7 +1596,7 @@ int readgame(const char *filename, bool backup) READ_STR(&store, name, sizeof(name)); b->type = bt_find(name); b->region = r; - a_read(&store, &b->attribs, b); + read_attribs(&store, &b->attribs, b); if (b->type == bt_lighthouse) { r->flags |= RF_LIGHTHOUSE; } @@ -1627,7 +1643,7 @@ int readgame(const char *filename, bool backup) if (sh->type->flags & SFL_NOCOAST) { sh->coast = NODIRECTION; } - a_read(&store, &sh->attribs, sh); + read_attribs(&store, &sh->attribs, sh); } *shp = 0; @@ -1777,7 +1793,7 @@ int writegame(const char *filename) WRITE_INT(&store, game_id()); WRITE_SECTION(&store); - a_write(&store, global.attribs, NULL); + write_attribs(&store, global.attribs, NULL); WRITE_SECTION(&store); WRITE_INT(&store, turn); @@ -1807,7 +1823,7 @@ int writegame(const char *filename) w = w->next; } write_faction_reference(NULL, &store); /* mark the end of the list */ - a_write(&store, pl->attribs, pl); + write_attribs(&store, pl->attribs, pl); WRITE_SECTION(&store); } @@ -1852,7 +1868,7 @@ int writegame(const char *filename) WRITE_INT(&store, b->size); WRITE_TOK(&store, b->type->_name); WRITE_SECTION(&store); - a_write(&store, b->attribs, b); + write_attribs(&store, b->attribs, b); WRITE_SECTION(&store); } @@ -1870,7 +1886,7 @@ int writegame(const char *filename) assert((sh->type->flags & SFL_NOCOAST) == 0 || sh->coast == NODIRECTION); WRITE_INT(&store, sh->coast); WRITE_SECTION(&store); - a_write(&store, sh->attribs, sh); + write_attribs(&store, sh->attribs, sh); WRITE_SECTION(&store); } diff --git a/src/kernel/save.h b/src/kernel/save.h index ca364fc9c..10e872955 100644 --- a/src/kernel/save.h +++ b/src/kernel/save.h @@ -58,6 +58,9 @@ extern "C" { void read_spellbook(struct spellbook **bookp, struct storage *store, int(*get_level)(const struct spell * sp, void *), void * cbdata); void write_spellbook(const struct spellbook *book, struct storage *store); + void write_attribs(struct storage *store, struct attrib *alist, const void *owner); + int read_attribs(struct storage *store, struct attrib **alist, void *owner); + void write_unit(struct gamedata *data, const struct unit *u); struct unit *read_unit(struct gamedata *data); diff --git a/src/kernel/save.test.c b/src/kernel/save.test.c index 1b4028694..c2558c876 100644 --- a/src/kernel/save.test.c +++ b/src/kernel/save.test.c @@ -72,14 +72,14 @@ static void test_readwrite_attrib(CuTest *tc) { CuAssertPtrNotNull(tc, data); add_key(&a, 41); add_key(&a, 42); - a_write(data->store, a, NULL); + write_attribs(data->store, a, NULL); gamedata_close(data); a_removeall(&a, NULL); CuAssertPtrEquals(tc, 0, a); data = gamedata_open(path, "rb"); CuAssertPtrNotNull(tc, data); - a_read(data->store, &a, NULL); + read_attribs(data->store, &a, NULL); gamedata_close(data); CuAssertPtrNotNull(tc, find_key(a, 41)); CuAssertPtrNotNull(tc, find_key(a, 42)); diff --git a/src/kernel/version.h b/src/kernel/version.h index c18957530..1dae63f7f 100644 --- a/src/kernel/version.h +++ b/src/kernel/version.h @@ -34,7 +34,8 @@ #define OWNER_3_VERSION 349 /* regions store last owner, not last alliance */ #define ATTRIBOWNER_VERSION 350 /* all attrib_type functions know who owns the attribute */ #define CRYPT_VERSION 351 /* passwords are encrypted */ -#define RELEASE_VERSION CRYPT_VERSION /* current datafile */ +#define ATHASH_VERSION 352 /* attribute-type hash, not name */ +#define RELEASE_VERSION ATHASH_VERSION /* current datafile */ #define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */ #define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */ diff --git a/src/util/attrib.c b/src/util/attrib.c index 837f9aff4..af93e5ad6 100644 --- a/src/util/attrib.c +++ b/src/util/attrib.c @@ -276,15 +276,79 @@ int a_age(attrib ** p, void *owner) static critbit_tree cb_deprecated = { 0 }; + +typedef struct deprecated_s { + unsigned int hash; + int(*reader)(attrib *, void *, struct storage *); +} deprecated_t; + void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct storage *)) { - char buffer[64]; - size_t len = strlen(name); - len = cb_new_kv(name, len, &reader, sizeof(reader), buffer); - cb_insert(&cb_deprecated, buffer, len); + deprecated_t value; + + value.hash = __at_hashkey(name); + value.reader = reader; + cb_insert(&cb_deprecated, &value, sizeof(value)); } -int a_read(struct storage *store, attrib ** attribs, void *owner) +static int a_read_i(struct storage *store, attrib ** attribs, void *owner, unsigned int key) { + int retval = AT_READ_FAIL; + int(*reader)(attrib *, void *, struct storage *) = 0; + attrib_type *at = at_find(key); + attrib * na = 0; + + if (at) { + reader = at->read; + na = a_new(at); + } + else { + void *match; + if (cb_find_prefix(&cb_deprecated, &key, sizeof(key), &match, 1, 0)>0) { + deprecated_t *value = (deprecated_t *)match; + reader = value->reader; + } + else { + log_error("unknown attribute hash: %u\n", key); + assert(at || !"attribute not registered"); + } + } + if (reader) { + retval = reader(na, owner, store); + if (na) { + switch (retval) { + case AT_READ_OK: + a_add(attribs, na); + break; + case AT_READ_FAIL: + a_free(na); + break; + default: + assert(!"invalid return value"); + break; + } + } + } + else { + assert(!"error: no registered callback can read attribute"); + } + return retval; +} + +int a_read(struct storage *store, attrib ** attribs, void *owner) { + int key, retval = AT_READ_OK; + char zText[128]; + + zText[0] = 0; + key = -1; + READ_INT(store, &key); + while (key > 0 && retval == AT_READ_OK) { + retval = a_read_i(store, attribs, owner, key); + READ_INT(store, &key); + } + return retval; +} + +int a_read_orig(struct storage *store, attrib ** attribs, void *owner) { int key, retval = AT_READ_OK; char zText[128]; @@ -292,52 +356,14 @@ int a_read(struct storage *store, attrib ** attribs, void *owner) zText[0] = 0; key = -1; READ_TOK(store, zText, sizeof(zText)); - if (strcmp(zText, "end") == 0) + if (strcmp(zText, "end") == 0) { return retval; - else + } + else { key = __at_hashkey(zText); - - while (key != -1) { - int(*reader)(attrib *, void *, struct storage *) = 0; - attrib_type *at = at_find(key); - attrib * na = 0; - - if (at) { - reader = at->read; - na = a_new(at); - } - else { - void * kv = 0; - cb_find_prefix(&cb_deprecated, zText, strlen(zText) + 1, &kv, 1, 0); - if (kv) { - cb_get_kv(kv, &reader, sizeof(reader)); - } - else { - fprintf(stderr, "attribute hash: %d (%s)\n", key, zText); - assert(at || !"attribute not registered"); - } - } - if (reader) { - int i = reader(na, owner, store); - if (na) { - switch (i) { - case AT_READ_OK: - a_add(attribs, na); - break; - case AT_READ_FAIL: - retval = AT_READ_FAIL; - a_free(na); - break; - default: - assert(!"invalid return value"); - break; - } - } - } - else { - assert(!"error: no registered callback can read attribute"); - } - + } + while (key > 0) { + retval = a_read_i(store, attribs, owner, key); READ_TOK(store, zText, sizeof(zText)); if (!strcmp(zText, "end")) break; @@ -346,7 +372,24 @@ int a_read(struct storage *store, attrib ** attribs, void *owner) return retval; } -void a_write(struct storage *store, const attrib * attribs, const void *owner) +void a_write(struct storage *store, const attrib * attribs, const void *owner) { + const attrib *na = attribs; + + while (na) { + if (na->type->write) { + assert(na->type->hashkey || !"attribute not registered"); + WRITE_INT(store, na->type->hashkey); + na->type->write(na, owner, store); + na = na->next; + } + else { + na = na->nexttype; + } + } + WRITE_INT(store, 0); +} + +void a_write_orig(struct storage *store, const attrib * attribs, const void *owner) { const attrib *na = attribs; diff --git a/src/util/attrib.h b/src/util/attrib.h index 14adeffb8..705d127d3 100644 --- a/src/util/attrib.h +++ b/src/util/attrib.h @@ -72,11 +72,13 @@ extern "C" { extern int a_remove(attrib ** pa, attrib * at); extern void a_removeall(attrib ** a, const attrib_type * at); extern attrib *a_new(const attrib_type * at); + int a_age(attrib ** attribs, void *owner); - extern int a_age(attrib ** attribs, void *owner); - extern int a_read(struct storage *store, attrib ** attribs, void *owner); - extern void a_write(struct storage *store, const attrib * attribs, - const void *owner); + int a_read_orig(struct storage *store, attrib ** attribs, void *owner); + void a_write_orig(struct storage *store, const attrib * attribs, const void *owner); + + int a_read(struct storage *store, attrib ** attribs, void *owner); + void a_write(struct storage *store, const attrib * attribs, const void *owner); void free_attribs(void);