summaryrefslogtreecommitdiff
path: root/doc/devel/savefile
blob: 306c1ab105ed0d15b9a0ecc5dbab0713071f8a3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
                           SAVEFILE FORMAT
                           ---------------

This document represents a work in progress. The save format described
here will evolve before finalization for 0.8.

The old save file format used a custom compressor and was very finicky
about padding and alignment for machines that were not even in use
anymore. It was also extremely fragile and hard for modders to extend.

The new save format seeks to alleviate these problems.

                            GENERAL FORMAT
                            --------------

All multibyte values are little-endian. There are no alignment
restrictions inherent in the format.

The save file begins with a 32-bit identifier and version number (a
"magic number") that identifies it as a particular version of an UQM
save file. If the format changes in a way that older versions of UQM
cannot read it, either the identifier or the version number should
change.

The vanilla UQM system has a save number of 0x01534d55, which, if
interpreted as a byte stream is UMS (Ur-quan Masters Save) and a
binary 1 (version 1). Vanilla UQM reserves the tags "UMSx" for all x
for use to evolve the core save format. Modders are encouraged, but
not required, to also use the fourth bit as a version number.

Following the 32-bit file identifier comes a series of chunks. All
chunks have the same general format: a 32-bit tag, similar to that of
the file as a whole, followed by a 32-bit integer specifying the size
of the chunk, and then that many bytes of data.

Bytes in a chunk tag should all be in the range 0x20-0x7E -- that is,
they should be printable ASCII characters. Chunks are traditionally
referred to by their tag names.

Chunks whose tag has a least significant byte in the range 0x41-0x5a,
inclusive---that is to say, whose names start with a capital
letter---are mandatory. If you extend the set of mandatory chunks, you
must increase the version of the file. Tags that do not start with a
capital letter may be ignored by other versions of UQM that otherwise
understand that data version.

(Why would you want to have ignorable bits of save file? Such chunks
may contain helpful but ancillary information. For instance, at the
time of this writing, there is an outstanding bug that life forms are
un-stunned if you save and load in orbit. One could add a new chunk
that tracks the stunned status of life forms on the planet you're in
orbit around, and just revert to the old behavior if it's not there or
if you're running on a version without that fix. Such an ancillary
data chunk would have a tag like "stun" or "biot".)

Note also that despite being "mandatory", it is not the case that all
chunks will be present in all save files. Different data is saved out
depending on the situation you saved in.

Unless otherwise specified, chunks may be stored in any order in the
save file.

                           CHUNK INVENTORY
                           ---------------

- "Summ": Summary. This chunk must come first. This chunk
  carries the flagship configuration information and some overview
  information that is displayed on the savegame view screen. It is of
  variable length, because the last element of this chunk is the name
  of the save as chosen by the user.

- "GlSt": Global State. This chunk must come second, after
  Summ. Represents most of the data in the global state structure in
  globdata.h. BattleGroupRef is excised from this; its value is
  computed later on.

- "GmSt": Game State. This chunk must come third, after GlSt. This is
  the gigantic bitfield that the GET_GAME_STATE macros modify. It is
  variably-sized; excess bytes in this array will be ignored, and if
  there are insufficient bytes in the save file, the remaining bits
  will be initialized to zero. Modders setting extra event flags may
  be able to import a legacy game into a sensible state by choosing
  their defaults judiciously.

- "Evts": Events. An array of values describing scripted future
  events.

- "Enct": Encounters. Details of battle groups that are pursuing you
  through HyperSpace.

- "RacQ": Available Race Queue. Which species are active in the game,
  where they are, etc. Corresponds to avail_race_q.

- "IGpQ": Interplanetary Group Queue. Battlegroup information for
  ships in your current star system, if you're in a current star
  system. It is currently unclear whether or not this ever needs to
  exist, but in keeping with legacy logic, it will appear whenever you
  are in a star system but not in the middle of an encounter.

- "NpcQ": NPC Queue. Battlegroup information for ships you are in the
  middle of encountering. This should only appear if your loaded
  activity is "IN_ENCOUNTER" and loading the game will trigger the red
  alert.

- "ShpQ": Ship Queue. Battlegroup information for your flagship's
  escort fleet.

- "Star": Star Description. Basic indexing information to indicate
  which star system you are in.

- "Scan": Scanner Masks. This is a semi-structured tree of DWORDs that
  represents which planetary resources have been captured or
  removed. It is a format roughly similar to the old star info
  statefile format (see doc/devel/statefile) but little-endianness is
  enforced, making this chunk endian-safe where the old statefile dump
  was not.

- "BtGp": Battle Group. Defines the relevant information for all ships
  in a given star system. This includes an "encounter ID" - randomly
  generated fleets have an encounter ID of zero, and ones built by the
  plot have an 32-bit identifier. Vanilla UQM reserves the first 32
  encounter IDs for itself, and uses 15 of them (random encounter,
  Ur-Quan Probe, Shofixti Survivor, Zoq-Fot-Pik Emissary, Unzervalt
  Guardian, nine Melnorme Traders, and the final boss). This also
  includes the expiration date for random encounters and which system
  they are relevant to. Much of this information was originally stored
  in randgrp.dat, but it has echoes in defgrp.dat as well. The data
  here is a ragged 2D array of a slight extension of the SHIP_FRAGMENT
  structure. There is one BtGp chunk per defined group. (Since one of
  these is defined at game start, and the random encounter structure
  has values that mean 'no encounter present', there should always be
  at least two of these chunks in any save.)

- "Grps": Active Battle Groups. These are IP_GROUP structures to
  supplement the SHIP_FRAGMENTs specified in BtGp chunks. They give
  more detailed information about the precise location and disposition
  of each ship in the system you are either in or most recently left.

                          THINGS LEFT TO DO
                          -----------------

The last 448 bits in GmSt probably shouldn't exist. However, we should
not remove them from the source base (and thus the save file) until
after other pending commits have been merged. When this happens, we
can break out those bits into an array of DWORDs instead. The loading
code basically ignores those GmSt bits by overwriting them while
loading later chunks, and GmSt is an expandable array in the first
place, so removing those final bits from the Game State array should
be compatible in both directions.