summaryrefslogtreecommitdiff
path: root/src/uqm/load_legacy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/load_legacy.c')
-rw-r--r--src/uqm/load_legacy.c821
1 files changed, 821 insertions, 0 deletions
diff --git a/src/uqm/load_legacy.c b/src/uqm/load_legacy.c
new file mode 100644
index 0000000..6470a52
--- /dev/null
+++ b/src/uqm/load_legacy.c
@@ -0,0 +1,821 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <assert.h>
+
+#include "build.h"
+#include "libs/declib.h"
+#include "encount.h"
+#include "starmap.h"
+#include "libs/file.h"
+#include "globdata.h"
+#include "options.h"
+#include "save.h"
+#include "setup.h"
+#include "state.h"
+#include "grpinfo.h"
+
+#include "libs/tasklib.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+
+//#define DEBUG_LOAD
+
+// XXX: these should handle endian conversions later
+static inline COUNT
+cread_8 (DECODE_REF fh, BYTE *v)
+{
+ BYTE t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return cread (v, 1, 1, fh);
+}
+
+static inline COUNT
+cread_16 (DECODE_REF fh, UWORD *v)
+{
+ UWORD t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return cread (v, 2, 1, fh);
+}
+
+static inline COUNT
+cread_16s (DECODE_REF fh, SWORD *v)
+{
+ UWORD t;
+ COUNT ret;
+ // value was converted to unsigned when saved
+ ret = cread_16 (fh, &t);
+ // unsigned to signed conversion
+ if (v)
+ *v = t;
+ return ret;
+}
+
+static inline COUNT
+cread_32 (DECODE_REF fh, DWORD *v)
+{
+ DWORD t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return cread (v, 4, 1, fh);
+}
+
+static inline COUNT
+cread_32s (DECODE_REF fh, SDWORD *v)
+{
+ DWORD t;
+ COUNT ret;
+ // value was converted to unsigned when saved
+ ret = cread_32 (fh, &t);
+ // unsigned to signed conversion
+ if (v)
+ *v = t;
+ return ret;
+}
+
+static inline COUNT
+cread_ptr (DECODE_REF fh)
+{
+ DWORD t;
+ return cread_32 (fh, &t); /* ptrs are useless in saves */
+}
+
+static inline COUNT
+cread_a8 (DECODE_REF fh, BYTE *ar, COUNT count)
+{
+ assert (ar != NULL);
+ return cread (ar, 1, count, fh) == count;
+}
+
+static inline size_t
+read_8 (void *fp, BYTE *v)
+{
+ BYTE t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return ReadResFile (v, 1, 1, fp);
+}
+
+static inline size_t
+read_16 (void *fp, UWORD *v)
+{
+ UWORD t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return ReadResFile (v, 2, 1, fp);
+}
+
+static inline size_t
+read_32 (void *fp, DWORD *v)
+{
+ DWORD t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return ReadResFile (v, 4, 1, fp);
+}
+
+static inline size_t
+read_32s (void *fp, SDWORD *v)
+{
+ DWORD t;
+ COUNT ret;
+ // value was converted to unsigned when saved
+ ret = read_32 (fp, &t);
+ // unsigned to signed conversion
+ if (v)
+ *v = t;
+ return ret;
+}
+
+static inline size_t
+read_ptr (void *fp)
+{
+ DWORD t;
+ return read_32 (fp, &t); /* ptrs are useless in saves */
+}
+
+static inline size_t
+read_a8 (void *fp, BYTE *ar, COUNT count)
+{
+ assert (ar != NULL);
+ return ReadResFile (ar, 1, count, fp) == count;
+}
+
+static inline size_t
+read_str (void *fp, char *str, COUNT count)
+{
+ // no type conversion needed for strings
+ return read_a8 (fp, (BYTE *)str, count);
+}
+
+static inline size_t
+read_a16 (void *fp, UWORD *ar, COUNT count)
+{
+ assert (ar != NULL);
+
+ for ( ; count > 0; --count, ++ar)
+ {
+ if (read_16 (fp, ar) != 1)
+ return 0;
+ }
+ return 1;
+}
+
+typedef struct struct_GAMESTATE_TRANSPOSE {
+ int start, end, target;
+} GAMESTATE_TRANSPOSE;
+
+#define LEGACY_GAMESTATE_SIZE 155
+
+/* The *_GRPOFFS* states are no longer intermingled with the rest of
+ * the state. We need to shuffle all the rest of the state data
+ * down. */
+static GAMESTATE_TRANSPOSE transpose[] = {
+ { 0, 51, 0 },
+ { 404, 450, 52 },
+ { 483, 878, 99 },
+ { 911, 930, 495 },
+ { 963, 1237, 515 },
+ { -1, -1, -1 } };
+
+static DWORD old_defgrp_offsets[] = { 0, 52, 84, 116, 148, 180, 212, 244,
+ 276, 308, 340, 372, 451, 879, 931 };
+
+static DWORD new_defgrp_offsets[] = {
+ 0,
+ SHOFIXTI_GRPOFFS0,
+ ZOQFOT_GRPOFFS0,
+ MELNORME0_GRPOFFS0,
+ MELNORME1_GRPOFFS0,
+ MELNORME2_GRPOFFS0,
+ MELNORME3_GRPOFFS0,
+ MELNORME4_GRPOFFS0,
+ MELNORME5_GRPOFFS0,
+ MELNORME6_GRPOFFS0,
+ MELNORME7_GRPOFFS0,
+ MELNORME8_GRPOFFS0,
+ URQUAN_PROBE_GRPOFFS0,
+ COLONY_GRPOFFS0,
+ SAMATRA_GRPOFFS0
+};
+
+static void
+InterpretLegacyGameState (BYTE *result, BYTE *legacy)
+{
+ int i;
+ DWORD grpoffs[NUM_DEFGRPS];
+ GAMESTATE_TRANSPOSE *t = &transpose[0];
+ grpoffs[0] = 0;
+ for (i = 1; i < NUM_DEFGRPS; ++i)
+ {
+ grpoffs[i] = getGameState32 (legacy, old_defgrp_offsets[i]);
+ }
+ while (t->start >= 0)
+ {
+ copyGameState (result, t->target, legacy, t->start, t->end);
+ ++t;
+ }
+ for (i = 1; i < NUM_DEFGRPS; ++i)
+ {
+ setGameState32 (result, new_defgrp_offsets[i], grpoffs[i]);
+ }
+}
+
+static void
+LoadEmptyQueue (DECODE_REF fh)
+{
+ COUNT num_links;
+
+ cread_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 (DECODE_REF fh, QUEUE *pQueue)
+{
+ COUNT num_links;
+
+ cread_16 (fh, &num_links);
+
+ while (num_links--)
+ {
+ HSHIPFRAG hStarShip;
+ SHIP_FRAGMENT *FragPtr;
+ COUNT Index;
+ BYTE tmpb;
+
+ cread_16 (fh, &Index);
+
+ hStarShip = CloneShipFragment (Index, pQueue, 0);
+ FragPtr = LockShipFrag (pQueue, hStarShip);
+
+ // Read SHIP_FRAGMENT elements
+ cread_16 (fh, NULL); /* unused: was which_side */
+ cread_8 (fh, &FragPtr->captains_name_index);
+ cread_8 (fh, NULL); /* padding */
+ cread_16 (fh, NULL); /* unused: was ship_flags */
+ cread_8 (fh, &FragPtr->race_id);
+ cread_8 (fh, &FragPtr->index);
+ // XXX: reading crew as BYTE to maintain savegame compatibility
+ cread_8 (fh, &tmpb);
+ FragPtr->crew_level = tmpb;
+ cread_8 (fh, &tmpb);
+ FragPtr->max_crew = tmpb;
+ cread_8 (fh, &FragPtr->energy_level);
+ cread_8 (fh, &FragPtr->max_energy);
+ cread_16 (fh, NULL); /* unused; was loc.x */
+ cread_16 (fh, NULL); /* unused; was loc.y */
+
+ UnlockShipFrag (pQueue, hStarShip);
+ }
+}
+
+static void
+LoadRaceQueue (DECODE_REF fh, QUEUE *pQueue)
+{
+ COUNT num_links;
+
+ cread_16 (fh, &num_links);
+
+ while (num_links--)
+ {
+ HFLEETINFO hStarShip;
+ FLEET_INFO *FleetPtr;
+ COUNT Index;
+ BYTE tmpb;
+
+ cread_16 (fh, &Index);
+
+ hStarShip = GetStarShipFromIndex (pQueue, Index);
+ FleetPtr = LockFleetInfo (pQueue, hStarShip);
+
+ // Read FLEET_INFO elements
+ cread_16 (fh, &FleetPtr->allied_state);
+ cread_8 (fh, &FleetPtr->days_left);
+ cread_8 (fh, &FleetPtr->growth_fract);
+ cread_8 (fh, &tmpb);
+ FleetPtr->crew_level = tmpb;
+ cread_8 (fh, &tmpb);
+ FleetPtr->max_crew = tmpb;
+ cread_8 (fh, &FleetPtr->growth);
+ cread_8 (fh, &FleetPtr->max_energy);
+ cread_16s(fh, &FleetPtr->loc.x);
+ cread_16s(fh, &FleetPtr->loc.y);
+
+ cread_16 (fh, &FleetPtr->actual_strength);
+ cread_16 (fh, &FleetPtr->known_strength);
+ cread_16s(fh, &FleetPtr->known_loc.x);
+ cread_16s(fh, &FleetPtr->known_loc.y);
+ cread_8 (fh, &FleetPtr->growth_err_term);
+ cread_8 (fh, &FleetPtr->func_index);
+ cread_16s(fh, &FleetPtr->dest_loc.x);
+ cread_16s(fh, &FleetPtr->dest_loc.y);
+ cread_16 (fh, NULL); /* alignment padding */
+
+ UnlockFleetInfo (pQueue, hStarShip);
+ }
+}
+
+static void
+LoadGroupQueue (DECODE_REF fh, QUEUE *pQueue)
+{
+ COUNT num_links;
+
+ cread_16 (fh, &num_links);
+
+ while (num_links--)
+ {
+ HIPGROUP hGroup;
+ IP_GROUP *GroupPtr;
+ BYTE tmpb;
+
+ cread_16 (fh, NULL); /* unused; was race_id */
+
+ hGroup = BuildGroup (pQueue, 0);
+ GroupPtr = LockIpGroup (pQueue, hGroup);
+
+ cread_16 (fh, NULL); /* unused; was which_side */
+ cread_8 (fh, NULL); /* unused; was captains_name_index */
+ cread_8 (fh, NULL); /* padding; for savegame compat */
+ cread_16 (fh, &GroupPtr->group_counter);
+ cread_8 (fh, &GroupPtr->race_id);
+ cread_8 (fh, &tmpb); /* was var2 */
+ GroupPtr->sys_loc = LONIBBLE (tmpb);
+ GroupPtr->task = HINIBBLE (tmpb);
+ cread_8 (fh, &GroupPtr->in_system); /* was crew_level */
+ cread_8 (fh, NULL); /* unused; was max_crew */
+ cread_8 (fh, &tmpb); /* was energy_level */
+ GroupPtr->dest_loc = LONIBBLE (tmpb);
+ GroupPtr->orbit_pos = HINIBBLE (tmpb);
+ cread_8 (fh, &GroupPtr->group_id); /* was max_energy */
+ cread_16s(fh, &GroupPtr->loc.x);
+ cread_16s(fh, &GroupPtr->loc.y);
+
+ UnlockIpGroup (pQueue, hGroup);
+ }
+}
+
+static void
+LoadEncounter (ENCOUNTER *EncounterPtr, DECODE_REF fh)
+{
+ COUNT i;
+ BYTE tmpb;
+
+ cread_ptr (fh); /* useless ptr; HENCOUNTER pred */
+ EncounterPtr->pred = 0;
+ cread_ptr (fh); /* useless ptr; HENCOUNTER succ */
+ EncounterPtr->succ = 0;
+ cread_ptr (fh); /* useless ptr; HELEMENT hElement */
+ EncounterPtr->hElement = 0;
+ cread_16s (fh, &EncounterPtr->transition_state);
+ cread_16s (fh, &EncounterPtr->origin.x);
+ cread_16s (fh, &EncounterPtr->origin.y);
+ cread_16 (fh, &EncounterPtr->radius);
+ // former STAR_DESC fields
+ cread_16s (fh, &EncounterPtr->loc_pt.x);
+ cread_16s (fh, &EncounterPtr->loc_pt.y);
+ cread_8 (fh, &EncounterPtr->race_id);
+ cread_8 (fh, &tmpb);
+ EncounterPtr->num_ships = tmpb & ENCOUNTER_SHIPS_MASK;
+ EncounterPtr->flags = tmpb & ENCOUNTER_FLAGS_MASK;
+ cread_16 (fh, NULL); /* alignment padding */
+
+ // Load each entry in the BRIEF_SHIP_INFO array
+ for (i = 0; i < MAX_HYPER_SHIPS; i++)
+ {
+ BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i];
+
+ cread_16 (fh, NULL); /* useless; was SHIP_INFO.ship_flags */
+ cread_8 (fh, &ShipInfo->race_id);
+ cread_8 (fh, NULL); /* useless; was SHIP_INFO.var2 */
+ // XXX: reading crew as BYTE to maintain savegame compatibility
+ cread_8 (fh, &tmpb);
+ ShipInfo->crew_level = tmpb;
+ cread_8 (fh, &tmpb);
+ ShipInfo->max_crew = tmpb;
+ cread_8 (fh, NULL); /* useless; was SHIP_INFO.energy_level */
+ cread_8 (fh, &ShipInfo->max_energy);
+ cread_16 (fh, NULL); /* useless; was SHIP_INFO.loc.x */
+ cread_16 (fh, NULL); /* useless; was SHIP_INFO.loc.y */
+ cread_32 (fh, NULL); /* useless val; STRING race_strings */
+ cread_ptr (fh); /* useless ptr; FRAME icons */
+ cread_ptr (fh); /* useless ptr; FRAME melee_icon */
+ }
+
+ // Load the stuff after the BRIEF_SHIP_INFO array
+ cread_32s (fh, &EncounterPtr->log_x);
+ cread_32s (fh, &EncounterPtr->log_y);
+}
+
+static void
+LoadEvent (EVENT *EventPtr, DECODE_REF fh)
+{
+ cread_ptr (fh); /* useless ptr; HEVENT pred */
+ EventPtr->pred = 0;
+ cread_ptr (fh); /* useless ptr; HEVENT succ */
+ EventPtr->succ = 0;
+ cread_8 (fh, &EventPtr->day_index);
+ cread_8 (fh, &EventPtr->month_index);
+ cread_16 (fh, &EventPtr->year_index);
+ cread_8 (fh, &EventPtr->func_index);
+ cread_8 (fh, NULL); /* padding */
+ cread_16 (fh, NULL); /* padding */
+}
+
+static void
+DummyLoadQueue (QUEUE *QueuePtr, DECODE_REF fh)
+{
+ /* QUEUE should never actually be loaded since it contains
+ * purely internal representation and the lists
+ * involved are actually loaded separately */
+ (void)QueuePtr; /* silence compiler */
+
+ /* QUEUE format with QUEUE_TABLE defined -- UQM default */
+ cread_ptr (fh); /* HLINK head */
+ cread_ptr (fh); /* HLINK tail */
+ cread_ptr (fh); /* BYTE* pq_tab */
+ cread_ptr (fh); /* HLINK free_list */
+ cread_16 (fh, NULL); /* MEM_HANDLE hq_tab */
+ cread_16 (fh, NULL); /* COUNT object_size */
+ cread_8 (fh, NULL); /* BYTE num_objects */
+
+ cread_8 (fh, NULL); /* padding */
+ cread_16 (fh, NULL); /* padding */
+}
+
+static void
+LoadClockState (CLOCK_STATE *ClockPtr, DECODE_REF fh)
+{
+ cread_8 (fh, &ClockPtr->day_index);
+ cread_8 (fh, &ClockPtr->month_index);
+ cread_16 (fh, &ClockPtr->year_index);
+ cread_16s (fh, &ClockPtr->tick_count);
+ cread_16s (fh, &ClockPtr->day_in_ticks);
+ cread_ptr (fh); /* not loading ptr; Semaphore clock_sem */
+ cread_ptr (fh); /* not loading ptr; Task clock_task */
+ cread_32 (fh, NULL); /* not loading; DWORD TimeCounter */
+
+ DummyLoadQueue (&ClockPtr->event_q, fh);
+}
+
+static void
+LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh)
+{
+ BYTE dummy8, oldstate[LEGACY_GAMESTATE_SIZE];
+
+ cread_8 (fh, &dummy8); /* obsolete */
+ cread_8 (fh, &GSPtr->glob_flags);
+ cread_8 (fh, &GSPtr->CrewCost);
+ cread_8 (fh, &GSPtr->FuelCost);
+ cread_a8 (fh, GSPtr->ModuleCost, NUM_MODULES);
+ cread_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES);
+ cread_ptr (fh); /* not loading ptr; PRIMITIVE *DisplayArray */
+ cread_16 (fh, &GSPtr->CurrentActivity);
+
+ cread_16 (fh, NULL); /* CLOCK_STATE alignment padding */
+ LoadClockState (&GSPtr->GameClock, fh);
+
+ cread_16s (fh, &GSPtr->autopilot.x);
+ cread_16s (fh, &GSPtr->autopilot.y);
+ cread_16s (fh, &GSPtr->ip_location.x);
+ cread_16s (fh, &GSPtr->ip_location.y);
+ /* STAMP ShipStamp */
+ cread_16s (fh, &GSPtr->ShipStamp.origin.x);
+ cread_16s (fh, &GSPtr->ShipStamp.origin.y);
+ cread_16 (fh, &GSPtr->ShipFacing);
+ cread_8 (fh, &GSPtr->ip_planet);
+ cread_8 (fh, &GSPtr->in_orbit);
+
+ /* VELOCITY_DESC velocity */
+ cread_16 (fh, &GSPtr->velocity.TravelAngle);
+ cread_16s (fh, &GSPtr->velocity.vector.width);
+ cread_16s (fh, &GSPtr->velocity.vector.height);
+ cread_16s (fh, &GSPtr->velocity.fract.width);
+ cread_16s (fh, &GSPtr->velocity.fract.height);
+ cread_16s (fh, &GSPtr->velocity.error.width);
+ cread_16s (fh, &GSPtr->velocity.error.height);
+ cread_16s (fh, &GSPtr->velocity.incr.width);
+ cread_16s (fh, &GSPtr->velocity.incr.height);
+ cread_16 (fh, NULL); /* VELOCITY_DESC padding */
+
+ cread_32 (fh, &GSPtr->BattleGroupRef);
+
+ DummyLoadQueue (&GSPtr->avail_race_q, fh);
+ DummyLoadQueue (&GSPtr->npc_built_ship_q, fh);
+ // Not loading ip_group_q, was not there originally
+ DummyLoadQueue (&GSPtr->encounter_q, fh);
+ DummyLoadQueue (&GSPtr->built_ship_q, fh);
+
+ cread_a8 (fh, oldstate, LEGACY_GAMESTATE_SIZE);
+ InterpretLegacyGameState (GSPtr->GameState, oldstate);
+
+ cread_8 (fh, NULL); /* GAME_STATE alignment padding */
+}
+
+static BOOLEAN
+LoadSisState (SIS_STATE *SSPtr, void *fp)
+{
+ if (
+ read_32s (fp, &SSPtr->log_x) != 1 ||
+ read_32s (fp, &SSPtr->log_y) != 1 ||
+ read_32 (fp, &SSPtr->ResUnits) != 1 ||
+ read_32 (fp, &SSPtr->FuelOnBoard) != 1 ||
+ read_16 (fp, &SSPtr->CrewEnlisted) != 1 ||
+ read_16 (fp, &SSPtr->TotalElementMass) != 1 ||
+ read_16 (fp, &SSPtr->TotalBioMass) != 1 ||
+ read_a8 (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS) != 1 ||
+ read_a8 (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS) != 1 ||
+ read_a8 (fp, SSPtr->JetSlots, NUM_JET_SLOTS) != 1 ||
+ read_8 (fp, &SSPtr->NumLanders) != 1 ||
+ read_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES) != 1 ||
+
+ 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 */
+ )
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static BOOLEAN
+LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
+{
+ if (!LoadSisState (&SummPtr->SS, fp))
+ return FALSE;
+
+ if (
+ read_8 (fp, &SummPtr->Activity) != 1 ||
+ read_8 (fp, &SummPtr->Flags) != 1 ||
+ read_8 (fp, &SummPtr->day_index) != 1 ||
+ read_8 (fp, &SummPtr->month_index) != 1 ||
+ read_16 (fp, &SummPtr->year_index) != 1 ||
+ read_8 (fp, &SummPtr->MCreditLo) != 1 ||
+ read_8 (fp, &SummPtr->MCreditHi) != 1 ||
+ read_8 (fp, &SummPtr->NumShips) != 1 ||
+ read_8 (fp, &SummPtr->NumDevices) != 1 ||
+ read_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
+ read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 ||
+
+ read_16 (fp, NULL) != 1 /* padding */
+ )
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static void
+LoadStarDesc (STAR_DESC *SDPtr, DECODE_REF fh)
+{
+ cread_16s(fh, &SDPtr->star_pt.x);
+ cread_16s(fh, &SDPtr->star_pt.y);
+ cread_8 (fh, &SDPtr->Type);
+ cread_8 (fh, &SDPtr->Index);
+ cread_8 (fh, &SDPtr->Prefix);
+ cread_8 (fh, &SDPtr->Postfix);
+}
+
+BOOLEAN
+LoadLegacyGame (COUNT which_game, SUMMARY_DESC *SummPtr)
+{
+ uio_Stream *in_fp;
+ char file[PATH_MAX];
+ char buf[256];
+ SUMMARY_DESC loc_sd;
+ GAME_STATE_FILE *fp;
+ DECODE_REF fh;
+ COUNT num_links;
+ STAR_DESC SD;
+ ACTIVITY Activity;
+
+ sprintf (file, "starcon2.%02u", which_game);
+ in_fp = res_OpenResFile (saveDir, file, "rb");
+ if (!in_fp)
+ return FALSE;
+
+ loc_sd.SaveName[0] = '\0';
+ if (!LoadSummary (&loc_sd, in_fp))
+ {
+ log_add (log_Error, "Warning: Savegame is corrupt");
+ res_CloseResFile (in_fp);
+ return FALSE;
+ }
+
+ if (!SummPtr)
+ {
+ SummPtr = &loc_sd;
+ }
+ else
+ { // only need summary for displaying to user
+ memcpy (SummPtr, &loc_sd, sizeof (*SummPtr));
+ res_CloseResFile (in_fp);
+ return TRUE;
+ }
+
+ // Crude check for big-endian/little-endian incompatibilities.
+ // year_index is suitable as it's a multi-byte value within
+ // a specific recognisable range.
+ if (SummPtr->year_index < START_YEAR ||
+ SummPtr->year_index >= START_YEAR +
+ YEARS_TO_KOHRAH_VICTORY + 1 /* Utwig intervention */ +
+ 1 /* time to destroy all races, plenty */ +
+ 25 /* for cheaters */)
+ {
+ log_add (log_Error, "Warning: Savegame corrupt or from "
+ "an incompatible platform.");
+ res_CloseResFile (in_fp);
+ return FALSE;
+ }
+
+ GlobData.SIS_state = SummPtr->SS;
+
+ if ((fh = copen (in_fp, FILE_STREAM, STREAM_READ)) == 0)
+ {
+ res_CloseResFile (in_fp);
+ return FALSE;
+ }
+
+ ReinitQueue (&GLOBAL (GameClock.event_q));
+ ReinitQueue (&GLOBAL (encounter_q));
+ ReinitQueue (&GLOBAL (ip_group_q));
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ ReinitQueue (&GLOBAL (built_ship_q));
+
+ memset (&GLOBAL (GameState[0]), 0, sizeof (GLOBAL (GameState)));
+ Activity = GLOBAL (CurrentActivity);
+ LoadGameState (&GlobData.Game_state, fh);
+ NextActivity = GLOBAL (CurrentActivity);
+ GLOBAL (CurrentActivity) = Activity;
+
+ LoadRaceQueue (fh, &GLOBAL (avail_race_q));
+ // START_INTERPLANETARY is only set when saving from Homeworld
+ // 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)
+ LoadShipQueue (fh, &GLOBAL (npc_built_ship_q));
+ else if (LOBYTE (NextActivity) == IN_INTERPLANETARY)
+ // 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 (fh, &GLOBAL (ip_group_q));
+ else
+ // XXX: The empty queue read is only needed to maintain
+ // the savegame compatibility
+ LoadEmptyQueue (fh);
+ }
+ LoadShipQueue (fh, &GLOBAL (built_ship_q));
+
+ // Load the game events (compressed)
+ cread_16 (fh, &num_links);
+ {
+#ifdef DEBUG_LOAD
+ log_add (log_Debug, "EVENTS:");
+#endif /* DEBUG_LOAD */
+ while (num_links--)
+ {
+ HEVENT hEvent;
+ EVENT *EventPtr;
+
+ hEvent = AllocEvent ();
+ LockEvent (hEvent, &EventPtr);
+
+ LoadEvent (EventPtr, fh);
+
+#ifdef DEBUG_LOAD
+ log_add (log_Debug, "\t%u/%u/%u -- %u",
+ EventPtr->month_index,
+ EventPtr->day_index,
+ EventPtr->year_index,
+ EventPtr->func_index);
+#endif /* DEBUG_LOAD */
+ UnlockEvent (hEvent);
+ PutEvent (hEvent);
+ }
+ }
+
+ // Load the encounters (black globes in HS/QS (compressed))
+ cread_16 (fh, &num_links);
+ {
+ while (num_links--)
+ {
+ HENCOUNTER hEncounter;
+ ENCOUNTER *EncounterPtr;
+
+ hEncounter = AllocEncounter ();
+ LockEncounter (hEncounter, &EncounterPtr);
+
+ LoadEncounter (EncounterPtr, fh);
+
+ UnlockEncounter (hEncounter);
+ PutEncounter (hEncounter);
+ }
+ }
+
+ // Copy the star info file from the compressed stream
+ fp = OpenStateFile (STARINFO_FILE, "wb");
+ if (fp)
+ {
+ DWORD flen;
+
+ cread_32 (fh, &flen);
+ while (flen)
+ {
+ COUNT num_bytes;
+
+ num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
+ cread (buf, num_bytes, 1, fh);
+ 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;
+
+ cread_32 (fh, &flen);
+ while (flen)
+ {
+ COUNT num_bytes;
+
+ num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
+ cread (buf, num_bytes, 1, fh);
+ 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;
+
+ cread_32 (fh, &flen);
+ while (flen)
+ {
+ COUNT num_bytes;
+
+ num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
+ cread (buf, num_bytes, 1, fh);
+ WriteStateFile (buf, num_bytes, 1, fp);
+
+ flen -= num_bytes;
+ }
+ CloseStateFile (fp);
+ }
+
+ LoadStarDesc (&SD, fh);
+
+ cclose (fh);
+ res_CloseResFile (in_fp);
+
+ EncounterGroup = 0;
+ EncounterRace = -1;
+
+ ReinitQueue (&race_q[0]);
+ ReinitQueue (&race_q[1]);
+ CurStarDescPtr = FindStar (NULL, &SD.star_pt, 0, 0);
+ if (!(NextActivity & START_ENCOUNTER)
+ && LOBYTE (NextActivity) == IN_INTERPLANETARY)
+ NextActivity |= START_INTERPLANETARY;
+
+ return TRUE;
+}
+
+