Major revision to save format

This accomplishes what I set out to do, but doesn't actually
solve the problem well enough given what I learned while doing it.

Documentation forthcoming in future commit.
This commit is contained in:
Michael Martin
2013-10-13 05:07:04 -07:00
parent eca23397c3
commit 7138aeca64
3 changed files with 474 additions and 589 deletions
+124 -155
View File
@@ -126,26 +126,9 @@ read_a16 (void *fp, UWORD *ar, COUNT count)
} }
static void static void
LoadEmptyQueue (void *fh) LoadShipQueue (void *fh, QUEUE *pQueue, DWORD size)
{ {
COUNT num_links; COUNT num_links = size / 11;
read_16 (fh, &num_links);
if (num_links)
{
log_add (log_Error, "LoadEmptyQueue(): BUG: the queue is not empty!");
#ifdef DEBUG
explode ();
#endif
}
}
static void
LoadShipQueue (void *fh, QUEUE *pQueue)
{
COUNT num_links;
read_16 (fh, &num_links);
while (num_links--) while (num_links--)
{ {
@@ -172,11 +155,9 @@ LoadShipQueue (void *fh, QUEUE *pQueue)
} }
static void static void
LoadRaceQueue (void *fh, QUEUE *pQueue) LoadRaceQueue (void *fh, QUEUE *pQueue, DWORD size)
{ {
COUNT num_links; COUNT num_links = size / 30;
read_16 (fh, &num_links);
while (num_links--) while (num_links--)
{ {
@@ -214,11 +195,9 @@ LoadRaceQueue (void *fh, QUEUE *pQueue)
} }
static void static void
LoadGroupQueue (void *fh, QUEUE *pQueue) LoadGroupQueue (void *fh, QUEUE *pQueue, DWORD size)
{ {
COUNT num_links; COUNT num_links = size / 13;
read_16 (fh, &num_links);
while (num_links--) while (num_links--)
{ {
@@ -299,9 +278,21 @@ LoadClockState (CLOCK_STATE *ClockPtr, void *fh)
read_16s (fh, &ClockPtr->day_in_ticks); read_16s (fh, &ClockPtr->day_in_ticks);
} }
static void static BOOLEAN
LoadGameState (GAME_STATE *GSPtr, void *fh) LoadGameState (GAME_STATE *GSPtr, void *fh)
{ {
DWORD magic;
read_32 (fh, &magic);
if (magic != GLOBAL_STATE_TAG)
{
return FALSE;
}
read_32 (fh, &magic);
if (magic != 79)
{
/* Chunk is the wrong size. */
return FALSE;
}
read_8 (fh, &GSPtr->glob_flags); read_8 (fh, &GSPtr->glob_flags);
read_8 (fh, &GSPtr->CrewCost); read_8 (fh, &GSPtr->CrewCost);
read_8 (fh, &GSPtr->FuelCost); read_8 (fh, &GSPtr->FuelCost);
@@ -335,7 +326,23 @@ LoadGameState (GAME_STATE *GSPtr, void *fh)
read_32 (fh, &GSPtr->BattleGroupRef); read_32 (fh, &GSPtr->BattleGroupRef);
read_32 (fh, &magic);
if (magic != GAME_STATE_TAG)
{
return FALSE;
}
memset (GSPtr->GameState, 0, sizeof (GSPtr->GameState));
read_32 (fh, &magic);
if (magic > sizeof (GSPtr->GameState))
{
read_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); read_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState));
skip_8 (fh, magic - sizeof (GSPtr->GameState));
}
else
{
read_a8 (fh, GSPtr->GameState, magic);
}
return TRUE;
} }
static BOOLEAN static BOOLEAN
@@ -429,14 +436,33 @@ LoadStarDesc (STAR_DESC *SDPtr, void *fh)
read_8 (fh, &SDPtr->Postfix); read_8 (fh, &SDPtr->Postfix);
} }
static void
LoadStateFile (int file_type, void *fh, DWORD flen)
{
GAME_STATE_FILE *fp = OpenStateFile (file_type, "wb");
char buf[256];
if (fp)
{
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
read_a8 (fh, buf, num_bytes);
WriteStateFile (buf, num_bytes, 1, fp);
flen -= num_bytes;
}
CloseStateFile (fp);
}
}
BOOLEAN BOOLEAN
LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr) LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
{ {
uio_Stream *in_fp; uio_Stream *in_fp;
char file[PATH_MAX]; char file[PATH_MAX];
char buf[256];
SUMMARY_DESC loc_sd; SUMMARY_DESC loc_sd;
GAME_STATE_FILE *fp;
COUNT num_links; COUNT num_links;
STAR_DESC SD; STAR_DESC SD;
ACTIVITY Activity; ACTIVITY Activity;
@@ -466,27 +492,6 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
GlobData.SIS_state = SummPtr->SS; GlobData.SIS_state = SummPtr->SS;
chunk = 0;
while (chunk != OMNIBUS_TAG)
{
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 == OMNIBUS_TAG)
break;
log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize);
if (skip_8(in_fp, chunkSize) != 1)
return FALSE;
}
ReinitQueue (&GLOBAL (GameClock.event_q)); ReinitQueue (&GLOBAL (GameClock.event_q));
ReinitQueue (&GLOBAL (encounter_q)); ReinitQueue (&GLOBAL (encounter_q));
ReinitQueue (&GLOBAL (ip_group_q)); ReinitQueue (&GLOBAL (ip_group_q));
@@ -495,35 +500,52 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
memset (&GLOBAL (GameState[0]), 0, sizeof (GLOBAL (GameState))); memset (&GLOBAL (GameState[0]), 0, sizeof (GLOBAL (GameState)));
Activity = GLOBAL (CurrentActivity); Activity = GLOBAL (CurrentActivity);
LoadGameState (&GlobData.Game_state, in_fp); if (!LoadGameState (&GlobData.Game_state, in_fp))
{
res_CloseResFile (in_fp);
return FALSE;
}
NextActivity = GLOBAL (CurrentActivity); NextActivity = GLOBAL (CurrentActivity);
GLOBAL (CurrentActivity) = Activity; GLOBAL (CurrentActivity) = Activity;
LoadRaceQueue (in_fp, &GLOBAL (avail_race_q)); chunk = 0;
// START_INTERPLANETARY is only set when saving from Homeworld while (TRUE)
// encounter screen. When the game is loaded, the
// GenerateOrbitalFunction for the current star system will
// create the encounter anew and populate the npc queue.
if (!(NextActivity & START_INTERPLANETARY))
{ {
if (NextActivity & START_ENCOUNTER) if (read_32(in_fp, &chunk) != 1)
LoadShipQueue (in_fp, &GLOBAL (npc_built_ship_q)); {
else if (LOBYTE (NextActivity) == IN_INTERPLANETARY) break;
// XXX: Technically, this queue does not need to be
// saved/loaded at all. IP groups will be reloaded
// from group state files. But the original code did,
// and so will we until we can prove we do not need to.
LoadGroupQueue (in_fp, &GLOBAL (ip_group_q));
else
// XXX: The empty queue read is only needed to maintain
// the savegame compatibility
LoadEmptyQueue (in_fp);
} }
LoadShipQueue (in_fp, &GLOBAL (built_ship_q)); if (read_32(in_fp, &chunkSize) != 1)
// Load the game events (compressed)
read_16 (in_fp, &num_links);
{ {
res_CloseResFile (in_fp);
return FALSE;
}
switch (chunk)
{
case RACE_Q_TAG:
LoadRaceQueue (in_fp, &GLOBAL (avail_race_q), chunkSize);
break;
case IP_GRP_Q_TAG:
LoadGroupQueue (in_fp, &GLOBAL (ip_group_q), chunkSize);
break;
case ENCOUNTERS_TAG:
num_links = chunkSize / 65;
while (num_links--)
{
HENCOUNTER hEncounter;
ENCOUNTER *EncounterPtr;
hEncounter = AllocEncounter ();
LockEncounter (hEncounter, &EncounterPtr);
LoadEncounter (EncounterPtr, in_fp);
UnlockEncounter (hEncounter);
PutEncounter (hEncounter);
}
break;
case EVENTS_TAG:
num_links = chunkSize / 5;
#ifdef DEBUG_LOAD #ifdef DEBUG_LOAD
log_add (log_Debug, "EVENTS:"); log_add (log_Debug, "EVENTS:");
#endif /* DEBUG_LOAD */ #endif /* DEBUG_LOAD */
@@ -547,88 +569,35 @@ LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
UnlockEvent (hEvent); UnlockEvent (hEvent);
PutEvent (hEvent); PutEvent (hEvent);
} }
} break;
case STAR_TAG:
// Load the encounters (black globes in HS/QS (compressed))
read_16 (in_fp, &num_links);
{
while (num_links--)
{
HENCOUNTER hEncounter;
ENCOUNTER *EncounterPtr;
hEncounter = AllocEncounter ();
LockEncounter (hEncounter, &EncounterPtr);
LoadEncounter (EncounterPtr, in_fp);
UnlockEncounter (hEncounter);
PutEncounter (hEncounter);
}
}
// Copy the star info file from the compressed stream
fp = OpenStateFile (STARINFO_FILE, "wb");
if (fp)
{
DWORD flen;
read_32 (in_fp, &flen);
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
read_a8 (in_fp, buf, num_bytes);
WriteStateFile (buf, num_bytes, 1, fp);
flen -= num_bytes;
}
CloseStateFile (fp);
}
// Copy the defined groupinfo file from the compressed stream
fp = OpenStateFile (DEFGRPINFO_FILE, "wb");
if (fp)
{
DWORD flen;
read_32 (in_fp, &flen);
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
read_a8 (in_fp, buf, num_bytes);
WriteStateFile (buf, num_bytes, 1, fp);
flen -= num_bytes;
}
CloseStateFile (fp);
}
// Copy the random groupinfo file from the compressed stream
fp = OpenStateFile (RANDGRPINFO_FILE, "wb");
if (fp)
{
DWORD flen;
read_32 (in_fp, &flen);
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
read_a8 (in_fp, buf, num_bytes);
WriteStateFile (buf, num_bytes, 1, fp);
flen -= num_bytes;
}
CloseStateFile (fp);
}
LoadStarDesc (&SD, in_fp); LoadStarDesc (&SD, in_fp);
break;
case NPC_SHIP_Q_TAG:
LoadShipQueue (in_fp, &GLOBAL (npc_built_ship_q), chunkSize);
break;
case SHIP_Q_TAG:
LoadShipQueue (in_fp, &GLOBAL (built_ship_q), chunkSize);
break;
case STAR_SF_TAG:
LoadStateFile (STARINFO_FILE, in_fp, chunkSize);
break;
case DEFGRP_SF_TAG:
LoadStateFile (DEFGRPINFO_FILE, in_fp, chunkSize);
break;
case RANDGRP_SF_TAG:
LoadStateFile (RANDGRPINFO_FILE, in_fp, chunkSize);
break;
default:
log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize);
if (skip_8(in_fp, chunkSize) != 1)
{
res_CloseResFile (in_fp);
return FALSE;
}
break;
}
}
res_CloseResFile (in_fp); res_CloseResFile (in_fp);
EncounterGroup = 0; EncounterGroup = 0;
+300 -395
View File
@@ -42,118 +42,78 @@
#include "libs/log.h" #include "libs/log.h"
#include "libs/memlib.h" #include "libs/memlib.h"
typedef struct savebuf_struct // Status boolean. If for some insane reason you need to
{ // save games in different threads, you'll need to
char *buf; // protect your calls to SaveGame with a mutex.
size_t bufsize;
int index, error; // It's arguably over-paranoid to check for error on
} SAVEBUF; // every single write, but this preserves the older
// behavior.
static BOOLEAN io_ok = TRUE;
// XXX: these should handle endian conversions later // XXX: these should handle endian conversions later
static inline COUNT static inline void
cwrite_8 (SAVEBUF *fh, BYTE v)
{
if (!fh)
return 0;
if (!fh->error && fh->index < fh->bufsize)
{
fh->buf[fh->index++] = v;
return 1;
}
fh->error = 1;
return 0;
}
static inline COUNT
cwrite_16 (SAVEBUF *fh, UWORD v)
{
if (cwrite_8 (fh, (BYTE)(v & 0xFF)) &&
cwrite_8 (fh, (BYTE)((v >> 8) & 0xFF)))
return 1;
return 0;
}
static inline COUNT
cwrite_32 (SAVEBUF *fh, DWORD v)
{
if (cwrite_8 (fh, (BYTE)(v & 0xFF)) &&
cwrite_8 (fh, (BYTE)((v >> 8) & 0xFF)) &&
cwrite_8 (fh, (BYTE)((v >> 16) & 0xFF)) &&
cwrite_8 (fh, (BYTE)((v >> 24) & 0xFF)))
return 1;
return 0;
}
static inline COUNT
cwrite_a8 (SAVEBUF *fh, const BYTE *ar, COUNT count)
{
int i;
for (i = 0; i < count; ++i)
if (!cwrite_8 (fh, ar[i]))
return 0;
return 1;
}
static inline size_t
write_8 (void *fp, BYTE v) write_8 (void *fp, BYTE v)
{ {
return WriteResFile (&v, 1, 1, fp); if (io_ok)
if (WriteResFile (&v, 1, 1, fp) != 1)
io_ok = FALSE;
} }
static inline size_t static inline void
write_16 (void *fp, UWORD v) write_16 (void *fp, UWORD v)
{ {
return WriteResFile (&v, 2, 1, fp); if (io_ok)
if (WriteResFile (&v, 2, 1, fp) != 1)
io_ok = FALSE;
} }
static inline size_t static inline void
write_32 (void *fp, DWORD v) write_32 (void *fp, DWORD v)
{ {
return WriteResFile (&v, 4, 1, fp); if (io_ok)
if (WriteResFile (&v, 4, 1, fp) != 1)
io_ok = FALSE;
} }
static inline size_t static inline void
write_a8 (void *fp, const BYTE *ar, COUNT count) write_a8 (void *fp, const BYTE *ar, COUNT count)
{ {
return WriteResFile (ar, 1, count, fp) == count; if (io_ok)
if (WriteResFile (ar, 1, count, fp) != count)
io_ok = FALSE;
} }
static inline size_t static inline void
write_str (void *fp, const char *str, COUNT count) write_str (void *fp, const char *str, COUNT count)
{ {
// no type conversion needed for strings // no type conversion needed for strings
return write_a8 (fp, (const BYTE *)str, count); write_a8 (fp, (const BYTE *)str, count);
} }
static inline size_t static inline void
write_a16 (void *fp, const UWORD *ar, COUNT count) write_a16 (void *fp, const UWORD *ar, COUNT count)
{ {
for ( ; count > 0; --count, ++ar) for ( ; count > 0; --count, ++ar)
{ {
if (write_16 (fp, *ar) != 1) if (!io_ok)
return 0; break;
write_16 (fp, *ar);
} }
return 1;
} }
static void static void
SaveEmptyQueue (SAVEBUF *fh) SaveShipQueue (uio_Stream *fh, QUEUE *pQueue, DWORD tag)
{
COUNT num_links = 0;
// Write the number of entries in the queue.
cwrite_16 (fh, num_links);
}
static void
SaveShipQueue (SAVEBUF *fh, QUEUE *pQueue)
{ {
COUNT num_links; COUNT num_links;
HSHIPFRAG hStarShip; HSHIPFRAG hStarShip;
// Write the number of entries in the queue.
num_links = CountLinks (pQueue); num_links = CountLinks (pQueue);
cwrite_16 (fh, num_links); if (num_links == 0)
return;
write_32 (fh, tag);
write_32 (fh, num_links * 11); // Size of chunk: each entry is 11 bytes long.
hStarShip = GetHeadLink (pQueue); hStarShip = GetHeadLink (pQueue);
while (num_links--) while (num_links--)
@@ -168,16 +128,16 @@ SaveShipQueue (SAVEBUF *fh, QUEUE *pQueue)
Index = FragPtr->race_id; Index = FragPtr->race_id;
// Write the number identifying this ship type. // Write the number identifying this ship type.
// See races.h; look for the enum containing NUM_AVAILABLE_RACES. // See races.h; look for the enum containing NUM_AVAILABLE_RACES.
cwrite_16 (fh, Index); write_16 (fh, Index);
// Write SHIP_FRAGMENT elements // Write SHIP_FRAGMENT elements
cwrite_8 (fh, FragPtr->captains_name_index); write_8 (fh, FragPtr->captains_name_index);
cwrite_8 (fh, FragPtr->race_id); write_8 (fh, FragPtr->race_id);
cwrite_8 (fh, FragPtr->index); write_8 (fh, FragPtr->index);
cwrite_16 (fh, FragPtr->crew_level); write_16 (fh, FragPtr->crew_level);
cwrite_16 (fh, FragPtr->max_crew); write_16 (fh, FragPtr->max_crew);
cwrite_8 (fh, FragPtr->energy_level); write_8 (fh, FragPtr->energy_level);
cwrite_8 (fh, FragPtr->max_energy); write_8 (fh, FragPtr->max_energy);
UnlockShipFrag (pQueue, hStarShip); UnlockShipFrag (pQueue, hStarShip);
hStarShip = hNextShip; hStarShip = hNextShip;
@@ -185,14 +145,17 @@ SaveShipQueue (SAVEBUF *fh, QUEUE *pQueue)
} }
static void static void
SaveRaceQueue (SAVEBUF *fh, QUEUE *pQueue) SaveRaceQueue (uio_Stream *fh, QUEUE *pQueue)
{ {
COUNT num_links; COUNT num_links;
HFLEETINFO hFleet; HFLEETINFO hFleet;
// Write the number of entries in the queue.
num_links = CountLinks (pQueue); num_links = CountLinks (pQueue);
cwrite_16 (fh, num_links); if (num_links == 0)
return;
write_32 (fh, RACE_Q_TAG);
// Write chunk size: 30 bytes per entry
write_32 (fh, num_links * 30);
hFleet = GetHeadLink (pQueue); hFleet = GetHeadLink (pQueue);
while (num_links--) while (num_links--)
@@ -206,27 +169,27 @@ SaveRaceQueue (SAVEBUF *fh, QUEUE *pQueue)
Index = GetIndexFromStarShip (pQueue, hFleet); Index = GetIndexFromStarShip (pQueue, hFleet);
// The index is the position in the queue. // The index is the position in the queue.
cwrite_16 (fh, Index); write_16 (fh, Index);
// Write FLEET_INFO elements // Write FLEET_INFO elements
cwrite_16 (fh, FleetPtr->allied_state); write_16 (fh, FleetPtr->allied_state);
cwrite_8 (fh, FleetPtr->days_left); write_8 (fh, FleetPtr->days_left);
cwrite_8 (fh, FleetPtr->growth_fract); write_8 (fh, FleetPtr->growth_fract);
cwrite_16 (fh, FleetPtr->crew_level); write_16 (fh, FleetPtr->crew_level);
cwrite_16 (fh, FleetPtr->max_crew); write_16 (fh, FleetPtr->max_crew);
cwrite_8 (fh, FleetPtr->growth); write_8 (fh, FleetPtr->growth);
cwrite_8 (fh, FleetPtr->max_energy); write_8 (fh, FleetPtr->max_energy);
cwrite_16 (fh, FleetPtr->loc.x); write_16 (fh, FleetPtr->loc.x);
cwrite_16 (fh, FleetPtr->loc.y); write_16 (fh, FleetPtr->loc.y);
cwrite_16 (fh, FleetPtr->actual_strength); write_16 (fh, FleetPtr->actual_strength);
cwrite_16 (fh, FleetPtr->known_strength); write_16 (fh, FleetPtr->known_strength);
cwrite_16 (fh, FleetPtr->known_loc.x); write_16 (fh, FleetPtr->known_loc.x);
cwrite_16 (fh, FleetPtr->known_loc.y); write_16 (fh, FleetPtr->known_loc.y);
cwrite_8 (fh, FleetPtr->growth_err_term); write_8 (fh, FleetPtr->growth_err_term);
cwrite_8 (fh, FleetPtr->func_index); write_8 (fh, FleetPtr->func_index);
cwrite_16 (fh, FleetPtr->dest_loc.x); write_16 (fh, FleetPtr->dest_loc.x);
cwrite_16 (fh, FleetPtr->dest_loc.y); write_16 (fh, FleetPtr->dest_loc.y);
UnlockFleetInfo (pQueue, hFleet); UnlockFleetInfo (pQueue, hFleet);
hFleet = hNextFleet; hFleet = hNextFleet;
@@ -234,12 +197,16 @@ SaveRaceQueue (SAVEBUF *fh, QUEUE *pQueue)
} }
static void static void
SaveGroupQueue (SAVEBUF *fh, QUEUE *pQueue) SaveGroupQueue (uio_Stream *fh, QUEUE *pQueue)
{ {
HIPGROUP hGroup, hNextGroup; HIPGROUP hGroup, hNextGroup;
COUNT num_links;
// Write the number of entries in the queue. num_links = CountLinks (pQueue);
cwrite_16 (fh, CountLinks (pQueue)); if (num_links == 0)
return;
write_32 (fh, IP_GRP_Q_TAG);
write_32 (fh, num_links * 13); // 13 bytes per element right now
for (hGroup = GetHeadLink (pQueue); hGroup; hGroup = hNextGroup) for (hGroup = GetHeadLink (pQueue); hGroup; hGroup = hNextGroup)
{ {
@@ -248,177 +215,220 @@ SaveGroupQueue (SAVEBUF *fh, QUEUE *pQueue)
GroupPtr = LockIpGroup (pQueue, hGroup); GroupPtr = LockIpGroup (pQueue, hGroup);
hNextGroup = _GetSuccLink (GroupPtr); hNextGroup = _GetSuccLink (GroupPtr);
cwrite_16 (fh, GroupPtr->group_counter); write_16 (fh, GroupPtr->group_counter);
cwrite_8 (fh, GroupPtr->race_id); write_8 (fh, GroupPtr->race_id);
cwrite_8 (fh, GroupPtr->sys_loc); write_8 (fh, GroupPtr->sys_loc);
cwrite_8 (fh, GroupPtr->task); write_8 (fh, GroupPtr->task);
cwrite_8 (fh, GroupPtr->in_system); /* was crew_level */ write_8 (fh, GroupPtr->in_system); /* was crew_level */
cwrite_8 (fh, GroupPtr->dest_loc); write_8 (fh, GroupPtr->dest_loc);
cwrite_8 (fh, GroupPtr->orbit_pos); write_8 (fh, GroupPtr->orbit_pos);
cwrite_8 (fh, GroupPtr->group_id); /* was max_energy */ write_8 (fh, GroupPtr->group_id); /* was max_energy */
cwrite_16 (fh, GroupPtr->loc.x); write_16 (fh, GroupPtr->loc.x);
cwrite_16 (fh, GroupPtr->loc.y); write_16 (fh, GroupPtr->loc.y);
UnlockIpGroup (pQueue, hGroup); UnlockIpGroup (pQueue, hGroup);
} }
} }
static void static void
SaveEncounter (const ENCOUNTER *EncounterPtr, SAVEBUF *fh) SaveEncounters (uio_Stream *fh)
{ {
COUNT num_links;
HENCOUNTER hEncounter;
num_links = CountLinks (&GLOBAL (encounter_q));
if (num_links == 0)
return;
write_32 (fh, ENCOUNTERS_TAG);
write_32 (fh, 65 * num_links);
hEncounter = GetHeadLink (&GLOBAL (encounter_q));
while (num_links--)
{
HENCOUNTER hNextEncounter;
ENCOUNTER *EncounterPtr;
COUNT i; COUNT i;
cwrite_16 (fh, EncounterPtr->transition_state); LockEncounter (hEncounter, &EncounterPtr);
cwrite_16 (fh, EncounterPtr->origin.x); hNextEncounter = GetSuccEncounter (EncounterPtr);
cwrite_16 (fh, EncounterPtr->origin.y);
cwrite_16 (fh, EncounterPtr->radius); write_16 (fh, EncounterPtr->transition_state);
write_16 (fh, EncounterPtr->origin.x);
write_16 (fh, EncounterPtr->origin.y);
write_16 (fh, EncounterPtr->radius);
// former STAR_DESC fields // former STAR_DESC fields
cwrite_16 (fh, EncounterPtr->loc_pt.x); write_16 (fh, EncounterPtr->loc_pt.x);
cwrite_16 (fh, EncounterPtr->loc_pt.y); write_16 (fh, EncounterPtr->loc_pt.y);
cwrite_8 (fh, EncounterPtr->race_id); write_8 (fh, EncounterPtr->race_id);
cwrite_8 (fh, EncounterPtr->num_ships); write_8 (fh, EncounterPtr->num_ships);
cwrite_8 (fh, EncounterPtr->flags); write_8 (fh, EncounterPtr->flags);
// Save each entry in the BRIEF_SHIP_INFO array // Save each entry in the BRIEF_SHIP_INFO array
for (i = 0; i < MAX_HYPER_SHIPS; i++) for (i = 0; i < MAX_HYPER_SHIPS; i++)
{ {
const BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i]; const BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i];
cwrite_8 (fh, ShipInfo->race_id); write_8 (fh, ShipInfo->race_id);
cwrite_16 (fh, ShipInfo->crew_level); write_16 (fh, ShipInfo->crew_level);
cwrite_16 (fh, ShipInfo->max_crew); write_16 (fh, ShipInfo->max_crew);
cwrite_8 (fh, ShipInfo->max_energy); write_8 (fh, ShipInfo->max_energy);
} }
// Save the stuff after the BRIEF_SHIP_INFO array // Save the stuff after the BRIEF_SHIP_INFO array
cwrite_32 (fh, EncounterPtr->log_x); write_32 (fh, EncounterPtr->log_x);
cwrite_32 (fh, EncounterPtr->log_y); write_32 (fh, EncounterPtr->log_y);
UnlockEncounter (hEncounter);
hEncounter = hNextEncounter;
}
} }
static void static void
SaveEvent (const EVENT *EventPtr, SAVEBUF *fh) SaveEvents (uio_Stream *fh)
{ {
cwrite_8 (fh, EventPtr->day_index); COUNT num_links;
cwrite_8 (fh, EventPtr->month_index); HEVENT hEvent;
cwrite_16 (fh, EventPtr->year_index); num_links = CountLinks (&GLOBAL (GameClock.event_q));
cwrite_8 (fh, EventPtr->func_index); if (num_links == 0)
return;
write_32 (fh, EVENTS_TAG);
write_32 (fh, num_links * 5); /* Event chunks are five bytes each */
hEvent = GetHeadLink (&GLOBAL (GameClock.event_q));
while (num_links--)
{
HEVENT hNextEvent;
EVENT *EventPtr;
LockEvent (hEvent, &EventPtr);
hNextEvent = GetSuccEvent (EventPtr);
write_8 (fh, EventPtr->day_index);
write_8 (fh, EventPtr->month_index);
write_16 (fh, EventPtr->year_index);
write_8 (fh, EventPtr->func_index);
UnlockEvent (hEvent);
hEvent = hNextEvent;
}
} }
/* The clock state is folded in with the game state chunk. */
static void static void
SaveClockState (const CLOCK_STATE *ClockPtr, SAVEBUF *fh) SaveClockState (const CLOCK_STATE *ClockPtr, uio_Stream *fh)
{ {
cwrite_8 (fh, ClockPtr->day_index); write_8 (fh, ClockPtr->day_index);
cwrite_8 (fh, ClockPtr->month_index); write_8 (fh, ClockPtr->month_index);
cwrite_16 (fh, ClockPtr->year_index); write_16 (fh, ClockPtr->year_index);
cwrite_16 (fh, ClockPtr->tick_count); write_16 (fh, ClockPtr->tick_count);
cwrite_16 (fh, ClockPtr->day_in_ticks); write_16 (fh, ClockPtr->day_in_ticks);
} }
/* Save out the game state chunks. There are two of these; the Global
* State chunk is fixed size, but the Game State tag can be extended
* by modders. */
static void static void
SaveGameState (const GAME_STATE *GSPtr, SAVEBUF *fh) SaveGameState (const GAME_STATE *GSPtr, uio_Stream *fh)
{ {
cwrite_8 (fh, GSPtr->glob_flags); write_32 (fh, GLOBAL_STATE_TAG);
cwrite_8 (fh, GSPtr->CrewCost); write_32 (fh, 79);
cwrite_8 (fh, GSPtr->FuelCost); write_8 (fh, GSPtr->glob_flags);
cwrite_a8 (fh, GSPtr->ModuleCost, NUM_MODULES); write_8 (fh, GSPtr->CrewCost);
cwrite_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES); write_8 (fh, GSPtr->FuelCost);
cwrite_16 (fh, GSPtr->CurrentActivity); write_a8 (fh, GSPtr->ModuleCost, NUM_MODULES);
write_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES);
write_16 (fh, GSPtr->CurrentActivity);
SaveClockState (&GSPtr->GameClock, fh); SaveClockState (&GSPtr->GameClock, fh);
cwrite_16 (fh, GSPtr->autopilot.x); write_16 (fh, GSPtr->autopilot.x);
cwrite_16 (fh, GSPtr->autopilot.y); write_16 (fh, GSPtr->autopilot.y);
cwrite_16 (fh, GSPtr->ip_location.x); write_16 (fh, GSPtr->ip_location.x);
cwrite_16 (fh, GSPtr->ip_location.y); write_16 (fh, GSPtr->ip_location.y);
/* STAMP ShipStamp */ /* STAMP ShipStamp */
cwrite_16 (fh, GSPtr->ShipStamp.origin.x); write_16 (fh, GSPtr->ShipStamp.origin.x);
cwrite_16 (fh, GSPtr->ShipStamp.origin.y); write_16 (fh, GSPtr->ShipStamp.origin.y);
cwrite_16 (fh, GSPtr->ShipFacing); write_16 (fh, GSPtr->ShipFacing);
cwrite_8 (fh, GSPtr->ip_planet); write_8 (fh, GSPtr->ip_planet);
cwrite_8 (fh, GSPtr->in_orbit); write_8 (fh, GSPtr->in_orbit);
/* VELOCITY_DESC velocity */ /* VELOCITY_DESC velocity */
cwrite_16 (fh, GSPtr->velocity.TravelAngle); write_16 (fh, GSPtr->velocity.TravelAngle);
cwrite_16 (fh, GSPtr->velocity.vector.width); write_16 (fh, GSPtr->velocity.vector.width);
cwrite_16 (fh, GSPtr->velocity.vector.height); write_16 (fh, GSPtr->velocity.vector.height);
cwrite_16 (fh, GSPtr->velocity.fract.width); write_16 (fh, GSPtr->velocity.fract.width);
cwrite_16 (fh, GSPtr->velocity.fract.height); write_16 (fh, GSPtr->velocity.fract.height);
cwrite_16 (fh, GSPtr->velocity.error.width); write_16 (fh, GSPtr->velocity.error.width);
cwrite_16 (fh, GSPtr->velocity.error.height); write_16 (fh, GSPtr->velocity.error.height);
cwrite_16 (fh, GSPtr->velocity.incr.width); write_16 (fh, GSPtr->velocity.incr.width);
cwrite_16 (fh, GSPtr->velocity.incr.height); write_16 (fh, GSPtr->velocity.incr.height);
cwrite_32 (fh, GSPtr->BattleGroupRef); write_32 (fh, GSPtr->BattleGroupRef);
cwrite_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState)); /* The Game state bits. Vanilla UQM uses 155 bytes here. */
write_32 (fh, GAME_STATE_TAG);
write_32 (fh, sizeof (GSPtr->GameState));
write_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState));
} }
static BOOLEAN /* This is folded into the Summary chunk */
static void
SaveSisState (const SIS_STATE *SSPtr, void *fp) SaveSisState (const SIS_STATE *SSPtr, void *fp)
{ {
if ( write_32 (fp, SSPtr->log_x);
write_32 (fp, SSPtr->log_x) != 1 || write_32 (fp, SSPtr->log_y);
write_32 (fp, SSPtr->log_y) != 1 || write_32 (fp, SSPtr->ResUnits);
write_32 (fp, SSPtr->ResUnits) != 1 || write_32 (fp, SSPtr->FuelOnBoard);
write_32 (fp, SSPtr->FuelOnBoard) != 1 || write_16 (fp, SSPtr->CrewEnlisted);
write_16 (fp, SSPtr->CrewEnlisted) != 1 || write_16 (fp, SSPtr->TotalElementMass);
write_16 (fp, SSPtr->TotalElementMass) != 1 || write_16 (fp, SSPtr->TotalBioMass);
write_16 (fp, SSPtr->TotalBioMass) != 1 || write_a8 (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS);
write_a8 (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS) != 1 || write_a8 (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS);
write_a8 (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS) != 1 || write_a8 (fp, SSPtr->JetSlots, NUM_JET_SLOTS);
write_a8 (fp, SSPtr->JetSlots, NUM_JET_SLOTS) != 1 || write_8 (fp, SSPtr->NumLanders);
write_8 (fp, SSPtr->NumLanders) != 1 || write_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES);
write_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES) != 1 ||
write_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 || write_str (fp, SSPtr->ShipName, SIS_NAME_SIZE);
write_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 || write_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE);
write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE);
)
return FALSE;
else
return TRUE;
} }
/* Write out the Summary Chunk. This is variable length because of the
savegame name */
static BOOLEAN static BOOLEAN
SaveSummary (const SUMMARY_DESC *SummPtr, void *fp) SaveSummary (const SUMMARY_DESC *SummPtr, void *fp)
{ {
if ( write_32 (fp, SUMMARY_TAG);
write_32 (fp, SAVEFILE_TAG) != 1 || write_32 (fp, 160 + strlen(SummPtr->SaveName));
write_32 (fp, SUMMARY_TAG) != 1 || SaveSisState (&SummPtr->SS, fp);
write_32 (fp, 160 + strlen(SummPtr->SaveName)) != 1
)
return FALSE;
if (!SaveSisState (&SummPtr->SS, fp))
return FALSE;
if ( write_8 (fp, SummPtr->Activity);
write_8 (fp, SummPtr->Activity) != 1 || write_8 (fp, SummPtr->Flags);
write_8 (fp, SummPtr->Flags) != 1 || write_8 (fp, SummPtr->day_index);
write_8 (fp, SummPtr->day_index) != 1 || write_8 (fp, SummPtr->month_index);
write_8 (fp, SummPtr->month_index) != 1 || write_16 (fp, SummPtr->year_index);
write_16 (fp, SummPtr->year_index) != 1 || write_8 (fp, SummPtr->MCreditLo);
write_8 (fp, SummPtr->MCreditLo) != 1 || write_8 (fp, SummPtr->MCreditHi);
write_8 (fp, SummPtr->MCreditHi) != 1 || write_8 (fp, SummPtr->NumShips);
write_8 (fp, SummPtr->NumShips) != 1 || write_8 (fp, SummPtr->NumDevices);
write_8 (fp, SummPtr->NumDevices) != 1 || write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS);
write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 || write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES);
write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 || write_a8 (fp, SummPtr->SaveName, strlen(SummPtr->SaveName));
write_a8 (fp, SummPtr->SaveName, strlen(SummPtr->SaveName)) != 1
)
return FALSE;
else
return TRUE;
} }
/* Save the Star Description chunk. This is not to be confused with
* the Star *Info* chunk, which records which planetary features you
* have exploited with your lander */
static void static void
SaveStarDesc (const STAR_DESC *SDPtr, SAVEBUF *fh) SaveStarDesc (const STAR_DESC *SDPtr, uio_Stream *fh)
{ {
cwrite_16 (fh, SDPtr->star_pt.x); write_32 (fh, STAR_TAG);
cwrite_16 (fh, SDPtr->star_pt.y); write_32 (fh, 8);
cwrite_8 (fh, SDPtr->Type); write_16 (fh, SDPtr->star_pt.x);
cwrite_8 (fh, SDPtr->Index); write_16 (fh, SDPtr->star_pt.y);
cwrite_8 (fh, SDPtr->Prefix); write_8 (fh, SDPtr->Type);
cwrite_8 (fh, SDPtr->Postfix); write_8 (fh, SDPtr->Index);
write_8 (fh, SDPtr->Prefix);
write_8 (fh, SDPtr->Postfix);
} }
static void static void
@@ -578,36 +588,40 @@ SaveFlagshipState (void)
} }
} }
static void
SaveStateFile (DWORD statefileId, DWORD tag, uio_Stream *fh)
{
GAME_STATE_FILE *fp;
char buf[256];
fp = OpenStateFile (statefileId, "rb");
if (fp)
{
DWORD flen = LengthStateFile (fp);
write_32 (fh, tag);
write_32 (fh, flen);
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
ReadStateFile (buf, num_bytes, 1, fp);
write_a8 (fh, buf, num_bytes);
flen -= num_bytes;
}
CloseStateFile (fp);
}
}
// This function first writes to a memory file, and then writes the whole // This function first writes to a memory file, and then writes the whole
// lot to the actual save file at once. // lot to the actual save file at once.
BOOLEAN BOOLEAN
SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name) SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name)
{ {
BOOLEAN success; uio_Stream *out_fp;
void *out_fp, *h;
SAVEBUF fh_backing, *fh;
success = TRUE;
fh = &fh_backing;
h = HMalloc (128 * 1024);
if (h == 0)
{
log_add (log_Debug, "Insufficient room for save buffers");
}
else
{
GAME_STATE_FILE *fp;
DWORD flen;
COUNT num_links;
POINT pt; POINT pt;
STAR_DESC SD; STAR_DESC SD;
char buf[256], file[PATH_MAX]; char file[PATH_MAX];
fh->buf = h;
fh->bufsize = 128 * 1024;
fh->index = 0;
fh->error = 0;
success = TRUE;
if (CurStarDescPtr) if (CurStarDescPtr)
SD = *CurStarDescPtr; SD = *CurStarDescPtr;
else else
@@ -621,14 +635,24 @@ SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name)
& (START_ENCOUNTER | START_INTERPLANETARY))) & (START_ENCOUNTER | START_INTERPLANETARY)))
PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP); PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
SaveGameState (&GlobData.Game_state, fh); // Write the memory file to the actual savegame file.
sprintf (file, "uqmsave.%02u", which_game);
if ((out_fp = res_OpenResFile (saveDir, file, "wb")))
{
io_ok = TRUE;
write_32 (out_fp, SAVEFILE_TAG);
PrepareSummary (SummPtr, name);
SaveSummary (SummPtr, out_fp);
SaveGameState (&GlobData.Game_state, out_fp);
// XXX: Restore // XXX: Restore
GLOBAL (ip_location) = pt; GLOBAL (ip_location) = pt;
// Only relevant when loading a game and must be cleaned // Only relevant when loading a game and must be cleaned
GLOBAL (in_orbit) = 0; GLOBAL (in_orbit) = 0;
SaveRaceQueue (fh, &GLOBAL (avail_race_q)); SaveRaceQueue (out_fp, &GLOBAL (avail_race_q));
// START_INTERPLANETARY is only set when saving from Homeworld // START_INTERPLANETARY is only set when saving from Homeworld
// encounter screen. When the game is loaded, the // encounter screen. When the game is loaded, the
// GenerateOrbitalFunction for the current star system // GenerateOrbitalFunction for the current star system
@@ -636,160 +660,41 @@ SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name)
if (!(GLOBAL (CurrentActivity) & START_INTERPLANETARY)) if (!(GLOBAL (CurrentActivity) & START_INTERPLANETARY))
{ {
if (GLOBAL (CurrentActivity) & START_ENCOUNTER) if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
SaveShipQueue (fh, &GLOBAL (npc_built_ship_q)); SaveShipQueue (out_fp, &GLOBAL (npc_built_ship_q), NPC_SHIP_Q_TAG);
else if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY) else if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY)
// XXX: Technically, this queue does not need to be // XXX: Technically, this queue does not need to be
// saved/loaded at all. IP groups will be reloaded // saved/loaded at all. IP groups will be reloaded
// from group state files. But the original code did, // from group state files. But the original code did,
// and so will we until we can prove we do not need to. // and so will we until we can prove we do not need to.
SaveGroupQueue (fh, &GLOBAL (ip_group_q)); SaveGroupQueue (out_fp, &GLOBAL (ip_group_q));
else
// XXX: empty queue write-out is only needed to maintain
// the savegame compatibility
SaveEmptyQueue (fh);
} }
SaveShipQueue (fh, &GLOBAL (built_ship_q)); SaveShipQueue (out_fp, &GLOBAL (built_ship_q), SHIP_Q_TAG);
// Save the number of game events (compressed). // Save the game event chunk
num_links = CountLinks (&GLOBAL (GameClock.event_q)); SaveEvents (out_fp);
cwrite_16 (fh, num_links);
// Save the game events themselves (compressed): // Save the encounter chunk (black globes in HS/QS)
SaveEncounters (out_fp);
// Save out the state file chunks.
SaveStateFile (STARINFO_FILE, STAR_SF_TAG, out_fp);
SaveStateFile (DEFGRPINFO_FILE, DEFGRP_SF_TAG, out_fp);
SaveStateFile (RANDGRPINFO_FILE, RANDGRP_SF_TAG, out_fp);
// Save out the Star Descriptor
SaveStarDesc (&SD, out_fp);
res_CloseResFile (out_fp);
if (!io_ok)
{ {
HEVENT hEvent;
hEvent = GetHeadLink (&GLOBAL (GameClock.event_q));
while (num_links--)
{
HEVENT hNextEvent;
EVENT *EventPtr;
LockEvent (hEvent, &EventPtr);
hNextEvent = GetSuccEvent (EventPtr);
SaveEvent (EventPtr, fh);
UnlockEvent (hEvent);
hEvent = hNextEvent;
}
}
// Save the number of encounters (black globes in HS/QS (compressed))
num_links = CountLinks (&GLOBAL (encounter_q));
// Save the encounters themselves (compressed):
cwrite_16 (fh, num_links);
{
HENCOUNTER hEncounter;
hEncounter = GetHeadLink (&GLOBAL (encounter_q));
while (num_links--)
{
HENCOUNTER hNextEncounter;
ENCOUNTER *EncounterPtr;
LockEncounter (hEncounter, &EncounterPtr);
hNextEncounter = GetSuccEncounter (EncounterPtr);
SaveEncounter (EncounterPtr, fh);
UnlockEncounter (hEncounter);
hEncounter = hNextEncounter;
}
}
// Copy the star info file to the memory file (compressed).
fp = OpenStateFile (STARINFO_FILE, "rb");
if (fp)
{
flen = LengthStateFile (fp);
// Write the uncompressed size.
cwrite_32 (fh, flen);
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
ReadStateFile (buf, num_bytes, 1, fp);
cwrite_a8 (fh, buf, num_bytes);
flen -= num_bytes;
}
CloseStateFile (fp);
}
// Copy the defined groupinfo file into the memory file (compressed)
fp = OpenStateFile (DEFGRPINFO_FILE, "rb");
if (fp)
{
flen = LengthStateFile (fp);
// Write the uncompressed size.
cwrite_32 (fh, flen);
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
ReadStateFile (buf, num_bytes, 1, fp);
cwrite_a8 (fh, buf, num_bytes);
flen -= num_bytes;
}
CloseStateFile (fp);
}
// Copy the random groupinfo file into the memory file (compressed)
fp = OpenStateFile (RANDGRPINFO_FILE, "rb");
if (fp)
{
flen = LengthStateFile (fp);
// Write the uncompressed size.
cwrite_32 (fh, flen);
while (flen)
{
COUNT num_bytes;
num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
ReadStateFile (buf, num_bytes, 1, fp);
cwrite_a8 (fh, buf, num_bytes);
flen -= num_bytes;
}
CloseStateFile (fp);
}
// Write the current star desc into the memory file (compressed).
SaveStarDesc (&SD, fh);
flen = fh->index;
// Write the memory file to the actual savegame file.
sprintf (file, "uqmsave.%02u", which_game);
log_add (log_Debug, "'%s' is %u bytes long", file,
flen + 180 + 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, OMNIBUS_TAG) != 1)
success = FALSE;
if (success && write_32 (out_fp, flen) != 1)
success = FALSE;
if (success && WriteResFile (h, flen, 1, out_fp) != 1)
success = FALSE;
if (res_CloseResFile ((uio_Stream *)out_fp) == 0)
success = FALSE;
}
else
success = FALSE;
if (!success)
DeleteResFile(saveDir, file); DeleteResFile(saveDir, file);
return FALSE;
}
}
else
{
return FALSE;
} }
HFree (h); return TRUE;
return (success);
} }
+12 -1
View File
@@ -35,8 +35,19 @@ extern "C" {
// The savefile tag numbers. // The savefile tag numbers.
#define SAVEFILE_TAG 0x01534d55 // "UMS\x01": UQM Save version 1 #define SAVEFILE_TAG 0x01534d55 // "UMS\x01": UQM Save version 1
#define OMNIBUS_TAG 0x696e6d4f // "Omni": All data, uncompressed
#define SUMMARY_TAG 0x6d6d7553 // "Summ": Summary. Must be first! #define SUMMARY_TAG 0x6d6d7553 // "Summ": Summary. Must be first!
#define GLOBAL_STATE_TAG 0x74536c47 // "GlSt": Global State. Must be 2nd!
#define GAME_STATE_TAG 0x74536d47 // "GmSt": Game State Bits. Must be 3rd!
#define EVENTS_TAG 0x73747645 // "Evts": Events
#define ENCOUNTERS_TAG 0x74636e45 // "Enct": Encounters
#define RACE_Q_TAG 0x51636152 // "RacQ": avail_race_q
#define IP_GRP_Q_TAG 0x51704749 // "IGpQ": ip_group_q
#define NPC_SHIP_Q_TAG 0x5163704e // "NpcQ": npc_built_ship_q
#define SHIP_Q_TAG 0x51706853 // "ShpQ": built_ship_q
#define STAR_TAG 0x72617453 // "Star": STAR_DESC
#define STAR_SF_TAG 0x46534953 // "SISF": Star Info State File
#define DEFGRP_SF_TAG 0x46534744 // "DGSF": Defined Group State File
#define RANDGRP_SF_TAG 0x46534752 // "RGSF": Random Group State File
typedef struct typedef struct
{ {