Improved save file format inspired by IFF.

Savefile is built of chunks that include their length. The only
two right now are Summ (the summary, must be first) and OmnZ
(the omnibus compressed data).

Padding that was originally used for alignment purposes has been
removed from the format, unless is it is part of the compressed
data.
This commit is contained in:
Michael Martin
2013-09-25 22:31:42 -07:00
parent 27087d0ea8
commit e4657ea3d1
3 changed files with 98 additions and 27 deletions
+82 -17
View File
@@ -160,6 +160,18 @@ read_a8 (void *fp, BYTE *ar, COUNT count)
return ReadResFile (ar, 1, count, fp) == count;
}
static inline size_t
skip_8 (void *fp, COUNT count)
{
int i;
for (i = 0; i < count; ++i)
{
if (read_8(fp, NULL) != 1)
return 0;
}
return 1;
}
static inline size_t
read_str (void *fp, char *str, COUNT count)
{
@@ -478,7 +490,7 @@ LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh)
}
static BOOLEAN
LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first)
LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first, BOOLEAN legacy)
{
SSPtr->log_x = first;
if (
@@ -496,26 +508,31 @@ LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first)
read_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 ||
read_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 ||
read_16 (fp, NULL) != 1 /* padding */
read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1
)
return FALSE;
else
return TRUE;
if (legacy && (read_16 (fp, NULL) != 1))
return FALSE;
return TRUE;
}
static BOOLEAN
LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
LoadSummary (SUMMARY_DESC *SummPtr, void *fp, BOOLEAN *legacy_ptr)
{
SDWORD magic;
DWORD nameSize = 0;
BOOLEAN legacy;
SummPtr->SaveName[0] = 0;
if (!read_32s (fp, &magic))
return FALSE;
if (magic == SAVE_MAGIC)
{
legacy = FALSE;
*legacy_ptr = FALSE;
if (read_32 (fp, &magic) != 1 || magic != SUMMARY_MAGIC)
return FALSE;
if (read_32 (fp, &magic) != 1 || magic < 161)
return FALSE;
nameSize = magic - 160;
// Read in the real first value for LoadSisState
if (read_32 (fp, &magic) != 1)
return FALSE;
@@ -525,9 +542,10 @@ LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
// Otherwise, we're legacy and the "magic" number was
// really LoadSisState's first value
legacy = TRUE;
*legacy_ptr = TRUE;
}
if (!LoadSisState (&SummPtr->SS, fp, magic))
if (!LoadSisState (&SummPtr->SS, fp, magic, legacy))
return FALSE;
if (
@@ -544,14 +562,32 @@ LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1
)
return FALSE;
if (!legacy && (read_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE) != 1))
return FALSE;
// Don't trust the savefile to properly null-terminate!
SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0;
if (read_16 (fp, NULL) != 1) /* padding */
return FALSE;
if (!legacy)
{
if (nameSize < SAVE_NAME_SIZE)
{
if (read_a8 (fp, SummPtr->SaveName, nameSize) != 1)
return FALSE;
SummPtr->SaveName[nameSize] = 0;
}
else
{
DWORD remaining = nameSize - SAVE_NAME_SIZE + 1;
if (read_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE-1) != 1)
return FALSE;
SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0;
if (skip_8 (fp, remaining) != 1)
return FALSE;
}
}
else
return TRUE;
{
SummPtr->SaveName[0] = 0;
if (read_16 (fp, NULL) != 1) /* padding */
return FALSE;
}
return TRUE;
}
static void
@@ -577,13 +613,15 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
COUNT num_links;
STAR_DESC SD;
ACTIVITY Activity;
BOOLEAN legacy;
DWORD chunk, chunkSize;
sprintf (file, "starcon2.%02u", which_game);
in_fp = res_OpenResFile (saveDir, file, "rb");
if (!in_fp)
return FALSE;
if (!LoadSummary (&loc_sd, in_fp))
if (!LoadSummary (&loc_sd, in_fp, &legacy))
{
log_add (log_Error, "Warning: Savegame is corrupt");
res_CloseResFile (in_fp);
@@ -618,6 +656,33 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
GlobData.SIS_state = SummPtr->SS;
if (!legacy)
{
chunk = 0;
while (chunk != OMNIZIP_MAGIC)
{
if (read_32(in_fp, &chunk) != 1)
{
res_CloseResFile (in_fp);
return FALSE;
}
if (read_32(in_fp, &chunkSize) != 1)
{
res_CloseResFile (in_fp);
return FALSE;
}
if (chunk == OMNIZIP_MAGIC)
break;
log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize);
if (skip_8(in_fp, chunkSize) != 1)
return FALSE;
}
/* Fortunately for us, the OmnZ chunk is internally
* self-sizing, so we can ignore the chunk size as long as we
* aren't skipping it. */
}
if ((fh = copen (in_fp, FILE_STREAM, STREAM_READ)) == 0)
{
res_CloseResFile (in_fp);
+12 -8
View File
@@ -431,9 +431,7 @@ SaveSisState (const SIS_STATE *SSPtr, void *fp)
write_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 ||
write_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 ||
write_16 (fp, 0) != 1 /* padding */
write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1
)
return FALSE;
else
@@ -443,7 +441,11 @@ SaveSisState (const SIS_STATE *SSPtr, void *fp)
static BOOLEAN
SaveSummary (const SUMMARY_DESC *SummPtr, void *fp)
{
if (write_32 (fp, SAVE_MAGIC) != 1)
if (
write_32 (fp, SAVE_MAGIC) != 1 ||
write_32 (fp, SUMMARY_MAGIC) != 1 ||
write_32 (fp, 161 + strlen(SummPtr->SaveName)) != 1
)
return FALSE;
if (!SaveSisState (&SummPtr->SS, fp))
return FALSE;
@@ -460,9 +462,7 @@ SaveSummary (const SUMMARY_DESC *SummPtr, void *fp)
write_8 (fp, SummPtr->NumDevices) != 1 ||
write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 ||
write_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE) != 1 ||
write_16 (fp, 0) != 1 /* padding */
write_a8 (fp, SummPtr->SaveName, strlen(SummPtr->SaveName)+1) != 1
)
return FALSE;
else
@@ -835,13 +835,17 @@ RetrySave:
// Write the memory file to the actual savegame file.
sprintf (file, "starcon2.%02u", which_game);
log_add (log_Debug, "'%s' is %u bytes long", file,
flen + sizeof (*SummPtr));
flen + 181 + strlen(SummPtr->SaveName));
if (flen && (out_fp = res_OpenResFile (saveDir, file, "wb")))
{
PrepareSummary (SummPtr, name);
success = SaveSummary (SummPtr, out_fp);
// Then write the rest of the data.
if (success && write_32 (out_fp, OMNIZIP_MAGIC) != 1)
success = FALSE;
if (success && write_32 (out_fp, flen) != 1)
success = FALSE;
if (success && WriteResFile (h, flen, 1, out_fp) != 1)
success = FALSE;
+4 -2
View File
@@ -31,8 +31,10 @@ extern "C" {
// is only used for displaying savegame summaries. There is also
// room for only 16 devices on screen.
#define MAX_EXCLUSIVE_DEVICES 16
#define SAVE_MAGIC 0x01534d55
#define SAVE_NAME_SIZE 24
#define SAVE_MAGIC 0x01534d55 // "UMS\x01": UQM Save version 1
#define SUMMARY_MAGIC 0x6d6d7553 // "Summ": Summary. Must be first!
#define OMNIZIP_MAGIC 0x5a6e6d4f // "OmnZ": All data compressed.
#define SAVE_NAME_SIZE 64
typedef struct
{