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:
+82
-17
@@ -160,6 +160,18 @@ read_a8 (void *fp, BYTE *ar, COUNT count)
|
|||||||
return ReadResFile (ar, 1, count, fp) == 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
|
static inline size_t
|
||||||
read_str (void *fp, char *str, COUNT count)
|
read_str (void *fp, char *str, COUNT count)
|
||||||
{
|
{
|
||||||
@@ -478,7 +490,7 @@ LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static BOOLEAN
|
static BOOLEAN
|
||||||
LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first)
|
LoadSisState (SIS_STATE *SSPtr, void *fp, SDWORD first, BOOLEAN legacy)
|
||||||
{
|
{
|
||||||
SSPtr->log_x = first;
|
SSPtr->log_x = first;
|
||||||
if (
|
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->ShipName, SIS_NAME_SIZE) != 1 ||
|
||||||
read_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
|
read_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
|
||||||
read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 ||
|
read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1
|
||||||
|
|
||||||
read_16 (fp, NULL) != 1 /* padding */
|
|
||||||
)
|
)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
else
|
if (legacy && (read_16 (fp, NULL) != 1))
|
||||||
return TRUE;
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOLEAN
|
static BOOLEAN
|
||||||
LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
|
LoadSummary (SUMMARY_DESC *SummPtr, void *fp, BOOLEAN *legacy_ptr)
|
||||||
{
|
{
|
||||||
SDWORD magic;
|
SDWORD magic;
|
||||||
|
DWORD nameSize = 0;
|
||||||
BOOLEAN legacy;
|
BOOLEAN legacy;
|
||||||
SummPtr->SaveName[0] = 0;
|
|
||||||
if (!read_32s (fp, &magic))
|
if (!read_32s (fp, &magic))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
if (magic == SAVE_MAGIC)
|
if (magic == SAVE_MAGIC)
|
||||||
{
|
{
|
||||||
legacy = FALSE;
|
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
|
// Read in the real first value for LoadSisState
|
||||||
if (read_32 (fp, &magic) != 1)
|
if (read_32 (fp, &magic) != 1)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -525,9 +542,10 @@ LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
|
|||||||
// Otherwise, we're legacy and the "magic" number was
|
// Otherwise, we're legacy and the "magic" number was
|
||||||
// really LoadSisState's first value
|
// really LoadSisState's first value
|
||||||
legacy = TRUE;
|
legacy = TRUE;
|
||||||
|
*legacy_ptr = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LoadSisState (&SummPtr->SS, fp, magic))
|
if (!LoadSisState (&SummPtr->SS, fp, magic, legacy))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -544,14 +562,32 @@ LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
|
|||||||
read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1
|
read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1
|
||||||
)
|
)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
if (!legacy && (read_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE) != 1))
|
|
||||||
return FALSE;
|
if (!legacy)
|
||||||
// Don't trust the savefile to properly null-terminate!
|
{
|
||||||
SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0;
|
if (nameSize < SAVE_NAME_SIZE)
|
||||||
if (read_16 (fp, NULL) != 1) /* padding */
|
{
|
||||||
return FALSE;
|
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
|
else
|
||||||
return TRUE;
|
{
|
||||||
|
SummPtr->SaveName[0] = 0;
|
||||||
|
if (read_16 (fp, NULL) != 1) /* padding */
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -577,13 +613,15 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
|
|||||||
COUNT num_links;
|
COUNT num_links;
|
||||||
STAR_DESC SD;
|
STAR_DESC SD;
|
||||||
ACTIVITY Activity;
|
ACTIVITY Activity;
|
||||||
|
BOOLEAN legacy;
|
||||||
|
DWORD chunk, chunkSize;
|
||||||
|
|
||||||
sprintf (file, "starcon2.%02u", which_game);
|
sprintf (file, "starcon2.%02u", which_game);
|
||||||
in_fp = res_OpenResFile (saveDir, file, "rb");
|
in_fp = res_OpenResFile (saveDir, file, "rb");
|
||||||
if (!in_fp)
|
if (!in_fp)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!LoadSummary (&loc_sd, in_fp))
|
if (!LoadSummary (&loc_sd, in_fp, &legacy))
|
||||||
{
|
{
|
||||||
log_add (log_Error, "Warning: Savegame is corrupt");
|
log_add (log_Error, "Warning: Savegame is corrupt");
|
||||||
res_CloseResFile (in_fp);
|
res_CloseResFile (in_fp);
|
||||||
@@ -618,6 +656,33 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
|
|||||||
|
|
||||||
GlobData.SIS_state = SummPtr->SS;
|
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)
|
if ((fh = copen (in_fp, FILE_STREAM, STREAM_READ)) == 0)
|
||||||
{
|
{
|
||||||
res_CloseResFile (in_fp);
|
res_CloseResFile (in_fp);
|
||||||
|
|||||||
+12
-8
@@ -431,9 +431,7 @@ SaveSisState (const SIS_STATE *SSPtr, void *fp)
|
|||||||
|
|
||||||
write_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 ||
|
write_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 ||
|
||||||
write_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
|
write_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
|
||||||
write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 ||
|
write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1
|
||||||
|
|
||||||
write_16 (fp, 0) != 1 /* padding */
|
|
||||||
)
|
)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
else
|
else
|
||||||
@@ -443,7 +441,11 @@ SaveSisState (const SIS_STATE *SSPtr, void *fp)
|
|||||||
static BOOLEAN
|
static BOOLEAN
|
||||||
SaveSummary (const SUMMARY_DESC *SummPtr, void *fp)
|
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;
|
return FALSE;
|
||||||
if (!SaveSisState (&SummPtr->SS, fp))
|
if (!SaveSisState (&SummPtr->SS, fp))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -460,9 +462,7 @@ SaveSummary (const SUMMARY_DESC *SummPtr, void *fp)
|
|||||||
write_8 (fp, SummPtr->NumDevices) != 1 ||
|
write_8 (fp, SummPtr->NumDevices) != 1 ||
|
||||||
write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
|
write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
|
||||||
write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 ||
|
write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 ||
|
||||||
write_a8 (fp, SummPtr->SaveName, SAVE_NAME_SIZE) != 1 ||
|
write_a8 (fp, SummPtr->SaveName, strlen(SummPtr->SaveName)+1) != 1
|
||||||
|
|
||||||
write_16 (fp, 0) != 1 /* padding */
|
|
||||||
)
|
)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
else
|
else
|
||||||
@@ -835,13 +835,17 @@ RetrySave:
|
|||||||
// Write the memory file to the actual savegame file.
|
// Write the memory file to the actual savegame file.
|
||||||
sprintf (file, "starcon2.%02u", which_game);
|
sprintf (file, "starcon2.%02u", which_game);
|
||||||
log_add (log_Debug, "'%s' is %u bytes long", file,
|
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")))
|
if (flen && (out_fp = res_OpenResFile (saveDir, file, "wb")))
|
||||||
{
|
{
|
||||||
PrepareSummary (SummPtr, name);
|
PrepareSummary (SummPtr, name);
|
||||||
|
|
||||||
success = SaveSummary (SummPtr, out_fp);
|
success = SaveSummary (SummPtr, out_fp);
|
||||||
// Then write the rest of the data.
|
// 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)
|
if (success && WriteResFile (h, flen, 1, out_fp) != 1)
|
||||||
success = FALSE;
|
success = FALSE;
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -31,8 +31,10 @@ extern "C" {
|
|||||||
// is only used for displaying savegame summaries. There is also
|
// is only used for displaying savegame summaries. There is also
|
||||||
// room for only 16 devices on screen.
|
// room for only 16 devices on screen.
|
||||||
#define MAX_EXCLUSIVE_DEVICES 16
|
#define MAX_EXCLUSIVE_DEVICES 16
|
||||||
#define SAVE_MAGIC 0x01534d55
|
#define SAVE_MAGIC 0x01534d55 // "UMS\x01": UQM Save version 1
|
||||||
#define SAVE_NAME_SIZE 24
|
#define SUMMARY_MAGIC 0x6d6d7553 // "Summ": Summary. Must be first!
|
||||||
|
#define OMNIZIP_MAGIC 0x5a6e6d4f // "OmnZ": All data compressed.
|
||||||
|
#define SAVE_NAME_SIZE 64
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user