Opened 13 years ago

Last modified 11 years ago

#30 closed change

Write/import new savefile code — at Version 11

Reported by: takkaria Owned by: Elly
Milestone: 3.1.1 beta Keywords:
Cc:

Description (last modified by takkaria)

There are comments at the top of save.c (see #ifdef FUTURE_SAVEFILES), which indicate plans to create a modular and better-versioned savefile format. I used that as a base to create a new format, which has better versioning support, and is probably a whole lot more future-proof than what's currently there. It also unified save/load code as far as possible.

The new savefile is constructed of a header, followed by a number of blocks, followed by a special "end block". For a fuller description, see http://rephial.org/wiki/SavefileCode.

Change History (10)

comment:1 Changed 13 years ago by pmac

I have a different model of how to write a save file, using generated code. The following is done
with the C preprocessor, but you can use perl or m4 to generate C files from a class definition just as well.
The methods are equivalent; it's just that the perl input can be cleaner-looking than c preprocessor macros.

Define two sets of macros that have the same names but different actions as follows:

gen_types.h:

#define F(type, var) type var;
#define C(type, var) type var;
#SERIALIZATION(clazz, fields) \
     struct clazz { fields };

gen_serialization.h:

#define F(type, var)  {"type", "var", TRUE},
#define C(type, var) {"type", "var", FALSE},

#define SERIALIZABLE(type, fields) \
    serialization type [] = { \
        fields \
        {0, 0, 0} \
    };

Then when you define a serializable type, you can generate either the struct or the names of the types (and thus offsets) of the fields in that struct. To save the struct, you just read the field values, then go to the appropriated computed offset.
This can be recursive for saving equipment in the pack or in the store.

So, unless there is a significant structural change from one release to another, save files are compatible without any effort, so long as you initialize default values in a sane manner. (That can be done programmatically, or by adding another argument
to the F() macros.

For example, the following macro header file:

SERIALIZABLE(player_t,
        F(char,name[100])
        F(byte,race)
        C(int,computed)
        F(u8b,inven_count)
        F(object_t,inventory[24])
)

Can generate either of the following, depending on which macros are included:

struct player_t {
    char name[100];
    byte race;
    int computed;
    u8b inven_count;
    object_t inventory[24];
};

OR this, which defines a (mostly) backwards-compatible serialization of the above structure. Just read or write the data structures, as tuples of (name, type, value)

serialization player_t [] = {
    {"char", "name[100]", TRUE},
    {"byte", "race", TRUE},
    {"int", "computed", FALSE},
    {"u8b", "inven_count", TRUE},
    {"object_t", "inventory[24]", TRUE},
    {0, 0, 0}
};

comment:2 Changed 12 years ago by takkaria

  • component changed from Project admin to Cleanup

comment:4 Changed 12 years ago by takkaria

  • Owner changed from takkaria to elly

comment:5 Changed 12 years ago by takkaria

[b9ffb12] (SVN r482), [0968c19] (SVN r491), [0b26f49] (SVN r493), [1598a7f] (SVN r496), [ade0b2a] (SVN r497), [e1b318c] (SVN r500) consist of the work done so far.

comment:6 follow-up: Changed 12 years ago by takkaria

  • Owner elly deleted

Given up on trying to make this performant at the moment; it makes far too many allocations and takes up too much disk space. Running into weird issues while trying to get z-blockfile to use compression routines, so abandoning this for a while.

comment:7 in reply to: ↑ 6 Changed 12 years ago by konijn_

Replying to takkaria:

Given up on trying to make this performant at the moment; it makes far too many allocations and takes up too much disk space. Running into weird issues while trying to get z-blockfile to use compression routines, so abandoning this for a while.

Hey, dont give up yet!!
I really, no seriously, REALLY like the idea of having 1 routine for 'doing' a savegame with the helperfunctions doing either save or load depending on the context. This could save over a thousand lines of code and makes screw ups ( adding a var to save, but forgetting to load ) impossible ( or really really hard ).

If you cannot do the blocks thing, please do the unified loading thing.

For sure, I will implement this in Hellband anyway since I had this epiphany about that in the bathroom before even reading this ;)

Cheers,
T.

comment:8 Changed 12 years ago by takkaria

  • Milestone changed from 3.2.0 to 3.1.0

comment:9 Changed 12 years ago by takkaria

  • Milestone changed from 3.1.0 to 3.1.1

Reassigning to a more realistic 3.1.1 version, though it might end up being later than that.

comment:10 Changed 12 years ago by takkaria

  • Owner set to Elly

Elly is currently working on this, but it may be a while in coming.

comment:11 Changed 11 years ago by takkaria

  • Description modified (diff)
Note: See TracTickets for help on using tickets.