Opened 10 years ago

Closed 9 years ago

#30 closed change (fixed (in master))

Write/import new savefile code

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

Description (last modified by takkaria)

At the top of save.c, there was once a comment about moving to a block-based format. I spent some time speccing a format at http://rephial.org/wiki/SavefileCode, which I intend to implement. Its chief advantage over current practice is that instead of the entire savefile having the same version number as the game, each block of data can have its own, which should mean that changes to savefiles aren't quite as scary. Also, a new format with blocks will make it much easier to catch load/save errors, since only a block will be in memory at a time.

Steps:

  • copy load.c to load-old.c
  • change magic numbers at start of file
  • add in buffer implementation
  • make sf_put() and sf_get() work from buffers
  • remove encryption
  • add headers before each write / turn load into a switch
  • add checksum at end of block
  • refactor

Change History (13)

comment:1 Changed 10 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 10 years ago by takkaria

  • component changed from Project admin to Cleanup

comment:4 Changed 10 years ago by takkaria

  • Owner changed from takkaria to elly

comment:5 Changed 10 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 10 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 10 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 10 years ago by takkaria

  • Milestone changed from 3.2.0 to 3.1.0

comment:9 Changed 10 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 10 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 9 years ago by takkaria

  • Description modified (diff)

comment:12 Changed 9 years ago by takkaria

  • Owner changed from Elly to takkaria

comment:13 Changed 9 years ago by takkaria

  • Description modified (diff)

comment:14 Changed 9 years ago by takkaria

  • Resolution set to fixed
  • Status changed from new to closed

[e534667] (SVN r1206), [057b8f3] (SVN r1207), [b8b257f] (SVN r1209), [ef1807b] (SVN r1214)-1217, [7ccc209] (SVN r1235)-[3348a47] (SVN r1238), [765542a] (SVN r1241)-[5da2f9a] (SVN r1244) together implement a new block-based format, as noted in description. thus, fixed.

Note: See TracTickets for help on using tickets.