Logo Search packages:      
Sourcecode: warzone2100 version File versions

game.c

/*
      This file is part of Warzone 2100.
      Copyright (C) 1999-2004  Eidos Interactive
      Copyright (C) 2005-2007  Warzone Resurrection Project

      Warzone 2100 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.

      Warzone 2100 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 Warzone 2100; if not, write to the Free Software
      Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lib/framework/frame.h"

/* Standard library headers */
#include <physfs.h>
#include <string.h>

/* Warzone src and library headers */
#include "lib/framework/strres.h"
#include "lib/framework/frameint.h"
#include "lib/framework/tagfile.h"
#include "lib/framework/file.h"
#include "lib/framework/physfs_ext.h"

#include "lib/gamelib/gtime.h"
#include "lib/ivis_common/ivisdef.h"
#include "lib/ivis_common/rendmode.h"
#include "lib/ivis_common/piestate.h"
#include "lib/ivis_common/piepalette.h"
#include "lib/netplay/netplay.h"
#include "lib/script/script.h"
#include "lib/sound/audio.h"
#include "lib/sound/audio_id.h"

#include "game.h"

#include "map.h"
#include "droid.h"
#include "action.h"
#include "research.h"
#include "power.h"
#include "projectile.h"
#include "loadsave.h"
#include "text.h"
#include "message.h"
#include "hci.h"
#include "display.h"
#include "display3d.h"
#include "map.h"
#include "effects.h"
#include "init.h"
#include "mission.h"
#include "scores.h"
#include "anim_id.h"
#include "design.h"
#include "lighting.h"
#include "component.h"
#include "radar.h"
#include "cmddroid.h"
#include "formation.h"
#include "formationdef.h"
#include "warzoneconfig.h"
#include "multiplay.h"
#include "frontend.h"
#include "levels.h"
#include "mission.h"
#include "geometry.h"
#include "gateway.h"
#include "scripttabs.h"
#include "scriptextern.h"
#include "multistat.h"
#include "wrappers.h"
#include "scriptfuncs.h"


#define MAX_SAVE_NAME_SIZE_V19      40
#define MAX_SAVE_NAME_SIZE    60

#define NULL_ID UDWORD_MAX
#define MAX_BODY              SWORD_MAX
#define SAVEKEY_ONMISSION     0x100

/*!
 * FIXME
 * The code is reusing some pointers as normal integer values apparently. This
 * should be fixed!
 */
#define FIXME_CAST_ASSIGN(TYPE, lval, rval) { TYPE* __tmp = (TYPE*) &lval; *__tmp = (TYPE)rval; }

/// @note This represents a size internal to savegame files, so: DO NOT CHANGE THIS
#define MAX_GAME_STR_SIZE 20

UDWORD RemapPlayerNumber(UDWORD OldNumber);

typedef struct _game_save_header
{
      char        aFileType[4];
      uint32_t    version;
} GAME_SAVEHEADER;

static bool serializeSaveGameHeader(PHYSFS_file* fileHandle, const GAME_SAVEHEADER* serializeHeader)
{
      if (PHYSFS_write(fileHandle, serializeHeader->aFileType, 4, 1) != 1)
            return false;

      // Write version numbers below version 35 as little-endian, and those above as big-endian
      if (serializeHeader->version < VERSION_35)
            return PHYSFS_writeULE32(fileHandle, serializeHeader->version);
      else
            return PHYSFS_writeUBE32(fileHandle, serializeHeader->version);
}

static bool deserializeSaveGameHeader(PHYSFS_file* fileHandle, GAME_SAVEHEADER* serializeHeader)
{
      // Read in the header from the file
      if (PHYSFS_read(fileHandle, serializeHeader->aFileType, 4, 1) != 1
       || PHYSFS_read(fileHandle, &serializeHeader->version, sizeof(uint32_t), 1) != 1)
            return false;

      // All save game file versions below version 35 (i.e. _not_ version 35 itself)
      // have their version numbers stored as little endian. Versions from 35 and
      // onward use big-endian. This basically means that, because of endian
      // swapping, numbers from 35 and onward will be ridiculously high if a
      // little-endian byte-order is assumed.

      // Convert from little endian to native byte-order and check if we get a
      // ridiculously high number
      endian_udword(&serializeHeader->version);

      if (serializeHeader->version <= VERSION_34)
      {
            // Apparently we don't get a ridiculously high number if we assume
            // little-endian, so lets assume our version number is 34 at max and return
            debug(LOG_SAVE, "Version = %u (little-endian)", serializeHeader->version);

            return true;
      }
      else
      {
            // Apparently we get a larger number than expected if using little-endian.
            // So assume we have a version of 35 and onward

            // Reverse the little-endian decoding
            endian_udword(&serializeHeader->version);
      }

      // Considering that little-endian didn't work we now use big-endian instead
      serializeHeader->version = PHYSFS_swapUBE32(serializeHeader->version);
      debug(LOG_SAVE, "Version %u = (big-endian)", serializeHeader->version);

      return true;
}

typedef struct _droid_save_header
{
      char        aFileType[4];
      UDWORD            version;
      UDWORD            quantity;
} DROID_SAVEHEADER;

typedef struct _struct_save_header
{
      char        aFileType[4];
      UDWORD            version;
      UDWORD            quantity;
} STRUCT_SAVEHEADER;

typedef struct _template_save_header
{
      char        aFileType[4];
      UDWORD            version;
      UDWORD            quantity;
} TEMPLATE_SAVEHEADER;

typedef struct _feature_save_header
{
      char        aFileType[4];
      UDWORD            version;
      UDWORD            quantity;
} FEATURE_SAVEHEADER;

/* Structure definitions for loading and saving map data */
typedef struct {
      char aFileType[4];
      UDWORD version;
      UDWORD quantity;
} TILETYPE_SAVEHEADER;

/* Structure definitions for loading and saving map data */
typedef struct _compList_save_header
{
      char aFileType[4];
      UDWORD version;
      UDWORD quantity;
} COMPLIST_SAVEHEADER;

/* Structure definitions for loading and saving map data */
typedef struct _structList_save_header
{
      char aFileType[4];
      UDWORD version;
      UDWORD quantity;
} STRUCTLIST_SAVEHEADER;

typedef struct _research_save_header
{
      char aFileType[4];
      UDWORD version;
      UDWORD quantity;
} RESEARCH_SAVEHEADER;

typedef struct _message_save_header
{
      char aFileType[4];
      UDWORD version;
      UDWORD quantity;
} MESSAGE_SAVEHEADER;

typedef struct _proximity_save_header
{
      char aFileType[4];
      UDWORD version;
      UDWORD quantity;
} PROXIMITY_SAVEHEADER;

typedef struct _flag_save_header
{
      char aFileType[4];
      UDWORD version;
      UDWORD quantity;
} FLAG_SAVEHEADER;

typedef struct _production_save_header
{
      char aFileType[4];
      UDWORD version;
} PRODUCTION_SAVEHEADER;

typedef struct _structLimits_save_header
{
      char        aFileType[4];
      UDWORD            version;
      UDWORD            quantity;
} STRUCTLIMITS_SAVEHEADER;

typedef struct _command_save_header
{
      char aFileType[4];
      UDWORD version;
      UDWORD quantity;
} COMMAND_SAVEHEADER;

/* Sanity check definitions for the save struct file sizes */
#define GAME_HEADER_SIZE                  8
#define DROID_HEADER_SIZE                 12
#define DROIDINIT_HEADER_SIZE       12
#define STRUCT_HEADER_SIZE                12
#define TEMPLATE_HEADER_SIZE        12
#define FEATURE_HEADER_SIZE               12
#define TILETYPE_HEADER_SIZE        12
#define COMPLIST_HEADER_SIZE        12
#define STRUCTLIST_HEADER_SIZE            12
#define RESEARCH_HEADER_SIZE        12
#define MESSAGE_HEADER_SIZE               12
#define PROXIMITY_HEADER_SIZE       12
#define FLAG_HEADER_SIZE                  12
#define PRODUCTION_HEADER_SIZE            8
#define STRUCTLIMITS_HEADER_SIZE    12
#define COMMAND_HEADER_SIZE               12


// general save definitions
#define MAX_LEVEL_SIZE 20

#define OBJECT_SAVE_V19 \
      char                    name[MAX_SAVE_NAME_SIZE_V19]; \
      UDWORD                        id; \
      UDWORD                        x,y,z; \
      UDWORD                        direction; \
      UDWORD                        player; \
      BOOL                    inFire; \
      UDWORD                        burnStart; \
      UDWORD                        burnDamage

#define OBJECT_SAVE_V20 \
      char                    name[MAX_SAVE_NAME_SIZE]; \
      UDWORD                        id; \
      UDWORD                        x,y,z; \
      UDWORD                        direction; \
      UDWORD                        player; \
      BOOL                    inFire; \
      UDWORD                        burnStart; \
      UDWORD                        burnDamage


typedef struct _save_component_v19
{
      char                    name[MAX_SAVE_NAME_SIZE_V19];
} SAVE_COMPONENT_V19;

typedef struct _save_component
{
      char                    name[MAX_SAVE_NAME_SIZE];
} SAVE_COMPONENT;

typedef struct _save_weapon_v19
{
      char                    name[MAX_SAVE_NAME_SIZE_V19];
      UDWORD                        hitPoints;  //- remove at some point
      UDWORD                        ammo;
      UDWORD                        lastFired;
} SAVE_WEAPON_V19;

typedef struct _save_weapon
{
      char                    name[MAX_SAVE_NAME_SIZE];
      UDWORD                        hitPoints;  //- remove at some point
      UDWORD                        ammo;
      UDWORD                        lastFired;
} SAVE_WEAPON;

typedef struct _savePower
{
      uint32_t    currentPower;
      uint32_t    extractedPower;
} SAVE_POWER;

static bool serializeSavePowerData(PHYSFS_file* fileHandle, const SAVE_POWER* serializePower)
{
      return (PHYSFS_writeUBE32(fileHandle, serializePower->currentPower)
           && PHYSFS_writeUBE32(fileHandle, serializePower->extractedPower));
}

static bool deserializeSavePowerData(PHYSFS_file* fileHandle, SAVE_POWER* serializePower)
{
      return (PHYSFS_readUBE32(fileHandle, &serializePower->currentPower)
           && PHYSFS_readUBE32(fileHandle, &serializePower->extractedPower));
}

static bool serializeVector3i(PHYSFS_file* fileHandle, const Vector3i* serializeVector)
{
      return (PHYSFS_writeSBE32(fileHandle, serializeVector->x)
           && PHYSFS_writeSBE32(fileHandle, serializeVector->y)
           && PHYSFS_writeSBE32(fileHandle, serializeVector->z));
}

static bool deserializeVector3i(PHYSFS_file* fileHandle, Vector3i* serializeVector)
{
      int32_t x, y, z;

      if (!PHYSFS_readSBE32(fileHandle, &x)
       || !PHYSFS_readSBE32(fileHandle, &y)
       || !PHYSFS_readSBE32(fileHandle, &z))
            return false;

      serializeVector-> x = x;
      serializeVector-> y = y;
      serializeVector-> z = z;

      return true;
}

static bool serializeVector2i(PHYSFS_file* fileHandle, const Vector2i* serializeVector)
{
      return (PHYSFS_writeSBE32(fileHandle, serializeVector->x)
           && PHYSFS_writeSBE32(fileHandle, serializeVector->y));
}

static bool deserializeVector2i(PHYSFS_file* fileHandle, Vector2i* serializeVector)
{
      int32_t x, y;

      if (!PHYSFS_readSBE32(fileHandle, &x)
       || !PHYSFS_readSBE32(fileHandle, &y))
            return false;

      serializeVector-> x = x;
      serializeVector-> y = y;

      return true;
}

static bool serializeiViewData(PHYSFS_file* fileHandle, const iView* serializeView)
{
      return (serializeVector3i(fileHandle, &serializeView->p)
           && serializeVector3i(fileHandle, &serializeView->r));
}

static bool deserializeiViewData(PHYSFS_file* fileHandle, iView* serializeView)
{
      return (deserializeVector3i(fileHandle, &serializeView->p)
           && deserializeVector3i(fileHandle, &serializeView->r));
}

static bool serializeRunData(PHYSFS_file* fileHandle, const RUN_DATA* serializeRun)
{
      return (serializeVector2i(fileHandle, &serializeRun->sPos)
           && PHYSFS_writeUBE8(fileHandle, serializeRun->forceLevel)
           && PHYSFS_writeUBE8(fileHandle, serializeRun->healthLevel)
           && PHYSFS_writeUBE8(fileHandle, serializeRun->leadership));
}

static bool deserializeRunData(PHYSFS_file* fileHandle, RUN_DATA* serializeRun)
{
      return (deserializeVector2i(fileHandle, &serializeRun->sPos)
           && PHYSFS_readUBE8(fileHandle, &serializeRun->forceLevel)
           && PHYSFS_readUBE8(fileHandle, &serializeRun->healthLevel)
           && PHYSFS_readUBE8(fileHandle, &serializeRun->leadership));
}

static bool serializeLandingZoneData(PHYSFS_file* fileHandle, const LANDING_ZONE* serializeLandZone)
{
      return (PHYSFS_writeUBE8(fileHandle, serializeLandZone->x1)
           && PHYSFS_writeUBE8(fileHandle, serializeLandZone->y1)
           && PHYSFS_writeUBE8(fileHandle, serializeLandZone->x2)
           && PHYSFS_writeUBE8(fileHandle, serializeLandZone->y2));
}

static bool deserializeLandingZoneData(PHYSFS_file* fileHandle, LANDING_ZONE* serializeLandZone)
{
      return (PHYSFS_readUBE8(fileHandle, &serializeLandZone->x1)
           && PHYSFS_readUBE8(fileHandle, &serializeLandZone->y1)
           && PHYSFS_readUBE8(fileHandle, &serializeLandZone->x2)
           && PHYSFS_readUBE8(fileHandle, &serializeLandZone->y2));
}

static bool serializeMultiplayerGame(PHYSFS_file* fileHandle, const MULTIPLAYERGAME* serializeMulti)
{
      unsigned int i;

      if (!PHYSFS_writeUBE8(fileHandle, serializeMulti->type)
       || PHYSFS_write(fileHandle, serializeMulti->map, 1, 128) != 128
       || PHYSFS_write(fileHandle, serializeMulti->version, 1, 8) != 8
       || !PHYSFS_writeUBE8(fileHandle, serializeMulti->maxPlayers)
       || PHYSFS_write(fileHandle, serializeMulti->name, 1, 128) != 128
       || !PHYSFS_writeSBE32(fileHandle, serializeMulti->fog)
       || !PHYSFS_writeUBE32(fileHandle, serializeMulti->power)
       || !PHYSFS_writeUBE8(fileHandle, serializeMulti->base)
       || !PHYSFS_writeUBE8(fileHandle, serializeMulti->alliance)
       || !PHYSFS_writeUBE8(fileHandle, 0)
       || !PHYSFS_writeUBE16(fileHandle, 0)     // dummy, was bytesPerSec
       || !PHYSFS_writeUBE8(fileHandle, 0)      // dummy, was packetsPerSec
       || !PHYSFS_writeUBE8(fileHandle, 0))     // dummy, was encryptKey
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_writeUBE8(fileHandle, serializeMulti->skDiff[i]))
                  return false;
      }

      return true;
}

static bool deserializeMultiplayerGame(PHYSFS_file* fileHandle, MULTIPLAYERGAME* serializeMulti)
{
      unsigned int i;
      int32_t boolFog;
      uint8_t dummy8;
      uint16_t dummy16;

      if (!PHYSFS_readUBE8(fileHandle, &serializeMulti->type)
       || PHYSFS_read(fileHandle, serializeMulti->map, 1, 128) != 128
       || PHYSFS_read(fileHandle, serializeMulti->version, 1, 8) != 8
       || !PHYSFS_readUBE8(fileHandle, &serializeMulti->maxPlayers)
       || PHYSFS_read(fileHandle, serializeMulti->name, 1, 128) != 128
       || !PHYSFS_readSBE32(fileHandle, &boolFog)
       || !PHYSFS_readUBE32(fileHandle, &serializeMulti->power)
       || !PHYSFS_readUBE8(fileHandle, &serializeMulti->base)
       || !PHYSFS_readUBE8(fileHandle, &serializeMulti->alliance)
       || !PHYSFS_readUBE8(fileHandle, &dummy8)
       || !PHYSFS_readUBE16(fileHandle, &dummy16)     // dummy, was bytesPerSec
       || !PHYSFS_readUBE8(fileHandle, &dummy8) // dummy, was packetsPerSec
       || !PHYSFS_readUBE8(fileHandle, &dummy8))      // dummy, was encryptKey
            return false;

      serializeMulti->fog = boolFog;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_readUBE8(fileHandle, &serializeMulti->skDiff[i]))
                  return false;
      }

      return true;
}

static bool serializeSessionDesc(PHYSFS_file* fileHandle, const SESSIONDESC* serializeDesc)
{
      return (PHYSFS_writeSBE32(fileHandle, serializeDesc->dwSize)
           && PHYSFS_writeSBE32(fileHandle, serializeDesc->dwFlags)
           && PHYSFS_write(fileHandle, serializeDesc->host, 1, 16) == 16
           && PHYSFS_writeSBE32(fileHandle, serializeDesc->dwMaxPlayers)
           && PHYSFS_writeSBE32(fileHandle, serializeDesc->dwCurrentPlayers)
           && PHYSFS_writeSBE32(fileHandle, serializeDesc->dwUser1)
           && PHYSFS_writeSBE32(fileHandle, serializeDesc->dwUser2)
           && PHYSFS_writeSBE32(fileHandle, serializeDesc->dwUser3)
           && PHYSFS_writeSBE32(fileHandle, serializeDesc->dwUser4));
}

static bool deserializeSessionDesc(PHYSFS_file* fileHandle, SESSIONDESC* serializeDesc)
{
      return (PHYSFS_readSBE32(fileHandle, &serializeDesc->dwSize)
           && PHYSFS_readSBE32(fileHandle, &serializeDesc->dwFlags)
           && PHYSFS_read(fileHandle, serializeDesc->host, 1, 16) == 16
           && PHYSFS_readSBE32(fileHandle, &serializeDesc->dwMaxPlayers)
           && PHYSFS_readSBE32(fileHandle, &serializeDesc->dwCurrentPlayers)
           && PHYSFS_readSBE32(fileHandle, &serializeDesc->dwUser1)
           && PHYSFS_readSBE32(fileHandle, &serializeDesc->dwUser2)
           && PHYSFS_readSBE32(fileHandle, &serializeDesc->dwUser3)
           && PHYSFS_readSBE32(fileHandle, &serializeDesc->dwUser4));
}

static bool serializeGameStruct(PHYSFS_file* fileHandle, const GAMESTRUCT* serializeGame)
{
      return (PHYSFS_write(fileHandle, serializeGame->name, StringSize, 1) == 1
           && serializeSessionDesc(fileHandle, &serializeGame->desc));
}

static bool deserializeGameStruct(PHYSFS_file* fileHandle, GAMESTRUCT* serializeGame)
{
      return (PHYSFS_read(fileHandle, serializeGame->name, StringSize, 1) == 1
           && deserializeSessionDesc(fileHandle, &serializeGame->desc));
}

static bool serializePlayer(PHYSFS_file* fileHandle, const PLAYER* serializePlayer)
{
      return (PHYSFS_writeUBE32(fileHandle, serializePlayer->dpid)
           && PHYSFS_write(fileHandle, serializePlayer->name, StringSize, 1) == 1
           && PHYSFS_writeUBE32(fileHandle, serializePlayer->bHost)
           && PHYSFS_writeUBE32(fileHandle, 0));
}

static bool deserializePlayer(PHYSFS_file* fileHandle, PLAYER* serializePlayer)
{
      uint32_t dummy;

      return (PHYSFS_readUBE32(fileHandle, &serializePlayer->dpid)
           && PHYSFS_read(fileHandle, serializePlayer->name, StringSize, 1) == 1
           && PHYSFS_readUBE32(fileHandle, &serializePlayer->bHost)
           && PHYSFS_readUBE32(fileHandle, &dummy));
}

static bool serializeNetPlay(PHYSFS_file* fileHandle, const NETPLAY* serializeNetPlay)
{
      unsigned int i;

      for (i = 0; i < MaxGames; ++i)
      {
            if (!serializeGameStruct(fileHandle, &serializeNetPlay->games[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!serializePlayer(fileHandle, &serializeNetPlay->players[i]))
                  return false;
      }

      return (PHYSFS_writeUBE32(fileHandle, serializeNetPlay->bComms)
           && PHYSFS_writeUBE32(fileHandle, serializeNetPlay->bHost)
           && PHYSFS_writeUBE32(fileHandle, 0)
           && PHYSFS_writeUBE32(fileHandle, 0)
           && PHYSFS_writeUBE32(fileHandle, 0)
           && PHYSFS_writeUBE32(fileHandle, 0)
           && PHYSFS_writeUBE32(fileHandle, 0));
}

static bool deserializeNetPlay(PHYSFS_file* fileHandle, NETPLAY* serializeNetPlay)
{
      unsigned int i;
      uint32_t dummy;

      for (i = 0; i < MaxGames; ++i)
      {
            if (!deserializeGameStruct(fileHandle, &serializeNetPlay->games[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!deserializePlayer(fileHandle, &serializeNetPlay->players[i]))
                  return false;
      }

      return (PHYSFS_readUBE32(fileHandle, &serializeNetPlay->bComms)
           && PHYSFS_readUBE32(fileHandle, &serializeNetPlay->bHost)
           && PHYSFS_readUBE32(fileHandle, &dummy)
           && PHYSFS_readUBE32(fileHandle, &dummy)
           && PHYSFS_readUBE32(fileHandle, &dummy)
           && PHYSFS_readUBE32(fileHandle, &dummy)
           && PHYSFS_readUBE32(fileHandle, &dummy));
}

#define GAME_SAVE_V7    \
      uint32_t    gameTime;   \
      uint32_t    GameType;         /* Type of game , one of the GTYPE_... enums. */ \
      int32_t     ScrollMinX;       /* Scroll Limits */ \
      int32_t     ScrollMinY; \
      uint32_t    ScrollMaxX; \
      uint32_t    ScrollMaxY; \
      char  levelName[MAX_LEVEL_SIZE]     //name of the level to load up when mid game

typedef struct save_game_v7
{
      GAME_SAVE_V7;
} SAVE_GAME_V7;

static bool serializeSaveGameV7Data(PHYSFS_file* fileHandle, const SAVE_GAME_V7* serializeGame)
{
      return (PHYSFS_writeUBE32(fileHandle, serializeGame->gameTime)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->GameType)
           && PHYSFS_writeSBE32(fileHandle, serializeGame->ScrollMinX)
           && PHYSFS_writeSBE32(fileHandle, serializeGame->ScrollMinY)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->ScrollMaxX)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->ScrollMaxY)
           && PHYSFS_write(fileHandle, serializeGame->levelName, MAX_LEVEL_SIZE, 1) == 1);
}

static bool deserializeSaveGameV7Data(PHYSFS_file* fileHandle, SAVE_GAME_V7* serializeGame)
{
      return (PHYSFS_readUBE32(fileHandle, &serializeGame->gameTime)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->GameType)
           && PHYSFS_readSBE32(fileHandle, &serializeGame->ScrollMinX)
           && PHYSFS_readSBE32(fileHandle, &serializeGame->ScrollMinY)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->ScrollMaxX)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->ScrollMaxY)
           && PHYSFS_read(fileHandle, serializeGame->levelName, MAX_LEVEL_SIZE, 1) == 1);
}

#define GAME_SAVE_V10   \
      GAME_SAVE_V7;           \
      SAVE_POWER  power[MAX_PLAYERS]

typedef struct save_game_v10
{
      GAME_SAVE_V10;
} SAVE_GAME_V10;

static bool serializeSaveGameV10Data(PHYSFS_file* fileHandle, const SAVE_GAME_V10* serializeGame)
{
      unsigned int i;

      if (!serializeSaveGameV7Data(fileHandle, (const SAVE_GAME_V7*) serializeGame))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!serializeSavePowerData(fileHandle, &serializeGame->power[i]))
                  return false;
      }

      return true;
}

static bool deserializeSaveGameV10Data(PHYSFS_file* fileHandle, SAVE_GAME_V10* serializeGame)
{
      unsigned int i;

      if (!deserializeSaveGameV7Data(fileHandle, (SAVE_GAME_V7*) serializeGame))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!deserializeSavePowerData(fileHandle, &serializeGame->power[i]))
                  return false;
      }

      return true;
}

#define GAME_SAVE_V11   \
      GAME_SAVE_V10;          \
      iView currentPlayerPos

typedef struct save_game_v11
{
      GAME_SAVE_V11;
} SAVE_GAME_V11;

static bool serializeSaveGameV11Data(PHYSFS_file* fileHandle, const SAVE_GAME_V11* serializeGame)
{
      return (serializeSaveGameV10Data(fileHandle, (const SAVE_GAME_V10*) serializeGame)
           && serializeiViewData(fileHandle, &serializeGame->currentPlayerPos));
}

static bool deserializeSaveGameV11Data(PHYSFS_file* fileHandle, SAVE_GAME_V11* serializeGame)
{
      return (deserializeSaveGameV10Data(fileHandle, (SAVE_GAME_V10*) serializeGame)
           && deserializeiViewData(fileHandle, &serializeGame->currentPlayerPos));
}

#define GAME_SAVE_V12   \
      GAME_SAVE_V11;          \
      uint32_t    missionTime;\
      uint32_t    saveKey

typedef struct save_game_v12
{
      GAME_SAVE_V12;
} SAVE_GAME_V12;

static bool serializeSaveGameV12Data(PHYSFS_file* fileHandle, const SAVE_GAME_V12* serializeGame)
{
      return (serializeSaveGameV11Data(fileHandle, (const SAVE_GAME_V11*) serializeGame)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->missionTime)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->saveKey));
}

static bool deserializeSaveGameV12Data(PHYSFS_file* fileHandle, SAVE_GAME_V12* serializeGame)
{
      return (deserializeSaveGameV11Data(fileHandle, (SAVE_GAME_V11*) serializeGame)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->missionTime)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->saveKey));
}

#define GAME_SAVE_V14               \
      GAME_SAVE_V12;                      \
      int32_t     missionOffTime;         \
      int32_t     missionETA;             \
      uint16_t    missionHomeLZ_X;  \
      uint16_t    missionHomeLZ_Y;  \
      int32_t     missionPlayerX;         \
      int32_t     missionPlayerY;         \
      uint16_t    iTranspEntryTileX[MAX_PLAYERS];     \
      uint16_t    iTranspEntryTileY[MAX_PLAYERS];     \
      uint16_t    iTranspExitTileX[MAX_PLAYERS];      \
      uint16_t    iTranspExitTileY[MAX_PLAYERS];      \
      uint32_t    aDefaultSensor[MAX_PLAYERS];  \
      uint32_t    aDefaultECM[MAX_PLAYERS];           \
      uint32_t    aDefaultRepair[MAX_PLAYERS]

typedef struct save_game_v14
{
      GAME_SAVE_V14;
} SAVE_GAME_V14;

static bool serializeSaveGameV14Data(PHYSFS_file* fileHandle, const SAVE_GAME_V14* serializeGame)
{
      unsigned int i;

      if (!serializeSaveGameV12Data(fileHandle, (const SAVE_GAME_V12*) serializeGame)
       || !PHYSFS_writeSBE32(fileHandle, serializeGame->missionOffTime)
       || !PHYSFS_writeSBE32(fileHandle, serializeGame->missionETA)
       || !PHYSFS_writeUBE16(fileHandle, serializeGame->missionHomeLZ_X)
       || !PHYSFS_writeUBE16(fileHandle, serializeGame->missionHomeLZ_Y)
       || !PHYSFS_writeSBE32(fileHandle, serializeGame->missionPlayerX)
       || !PHYSFS_writeSBE32(fileHandle, serializeGame->missionPlayerY))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_writeUBE16(fileHandle, serializeGame->iTranspEntryTileX[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_writeUBE16(fileHandle, serializeGame->iTranspEntryTileY[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_writeUBE16(fileHandle, serializeGame->iTranspExitTileX[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_writeUBE16(fileHandle, serializeGame->iTranspExitTileY[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_writeUBE32(fileHandle, serializeGame->aDefaultSensor[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_writeUBE32(fileHandle, serializeGame->aDefaultECM[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_writeUBE32(fileHandle, serializeGame->aDefaultRepair[i]))
                  return false;
      }

      return true;
}

static bool deserializeSaveGameV14Data(PHYSFS_file* fileHandle, SAVE_GAME_V14* serializeGame)
{
      unsigned int i;

      if (!deserializeSaveGameV12Data(fileHandle, (SAVE_GAME_V12*) serializeGame)
       || !PHYSFS_readSBE32(fileHandle, &serializeGame->missionOffTime)
       || !PHYSFS_readSBE32(fileHandle, &serializeGame->missionETA)
       || !PHYSFS_readUBE16(fileHandle, &serializeGame->missionHomeLZ_X)
       || !PHYSFS_readUBE16(fileHandle, &serializeGame->missionHomeLZ_Y)
       || !PHYSFS_readSBE32(fileHandle, &serializeGame->missionPlayerX)
       || !PHYSFS_readSBE32(fileHandle, &serializeGame->missionPlayerY))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_readUBE16(fileHandle, &serializeGame->iTranspEntryTileX[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_readUBE16(fileHandle, &serializeGame->iTranspEntryTileY[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_readUBE16(fileHandle, &serializeGame->iTranspExitTileX[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_readUBE16(fileHandle, &serializeGame->iTranspExitTileY[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_readUBE32(fileHandle, &serializeGame->aDefaultSensor[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_readUBE32(fileHandle, &serializeGame->aDefaultECM[i]))
                  return false;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_readUBE32(fileHandle, &serializeGame->aDefaultRepair[i]))
                  return false;
      }

      return true;
}

#define GAME_SAVE_V15               \
      GAME_SAVE_V14;                      \
      BOOL        offWorldKeepLists;\
      uint8_t     aDroidExperience[MAX_PLAYERS][MAX_RECYCLED_DROIDS];\
      uint32_t    RubbleTile;\
      uint32_t    WaterTile;\
      uint32_t    fogColour;\
      uint32_t    fogState

typedef struct save_game_v15
{
      GAME_SAVE_V15;
} SAVE_GAME_V15;

static bool serializeSaveGameV15Data(PHYSFS_file* fileHandle, const SAVE_GAME_V15* serializeGame)
{
      unsigned int i, j;

      if (!serializeSaveGameV14Data(fileHandle, (const SAVE_GAME_V14*) serializeGame)
       || !PHYSFS_writeSBE32(fileHandle, serializeGame->offWorldKeepLists))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            for (j = 0; j < MAX_RECYCLED_DROIDS; ++j)
            {
                  if (!PHYSFS_writeUBE8(fileHandle, serializeGame->aDroidExperience[i][j]))
                        return false;
            }
      }

      return (PHYSFS_writeUBE32(fileHandle, serializeGame->RubbleTile)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->WaterTile)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->fogColour)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->fogState));
}

static bool deserializeSaveGameV15Data(PHYSFS_file* fileHandle, SAVE_GAME_V15* serializeGame)
{
      unsigned int i, j;
      int32_t boolOffWorldKeepLists;

      if (!deserializeSaveGameV14Data(fileHandle, (SAVE_GAME_V14*) serializeGame)
       || !PHYSFS_readSBE32(fileHandle, &boolOffWorldKeepLists))
            return false;

      serializeGame->offWorldKeepLists = boolOffWorldKeepLists;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            for (j = 0; j < MAX_RECYCLED_DROIDS; ++j)
            {
                  if (!PHYSFS_readUBE8(fileHandle, &serializeGame->aDroidExperience[i][j]))
                        return false;
            }
      }

      return (PHYSFS_readUBE32(fileHandle, &serializeGame->RubbleTile)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->WaterTile)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->fogColour)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->fogState));
}

#define GAME_SAVE_V16               \
      GAME_SAVE_V15;                      \
      LANDING_ZONE   sLandingZone[MAX_NOGO_AREAS]

typedef struct save_game_v16
{
      GAME_SAVE_V16;
} SAVE_GAME_V16;

static bool serializeSaveGameV16Data(PHYSFS_file* fileHandle, const SAVE_GAME_V16* serializeGame)
{
      unsigned int i;

      if (!serializeSaveGameV15Data(fileHandle, (const SAVE_GAME_V15*) serializeGame))
            return false;

      for (i = 0; i < MAX_NOGO_AREAS; ++i)
      {
            if (!serializeLandingZoneData(fileHandle, &serializeGame->sLandingZone[i]))
                  return false;
      }

      return true;
}

static bool deserializeSaveGameV16Data(PHYSFS_file* fileHandle, SAVE_GAME_V16* serializeGame)
{
      unsigned int i;

      if (!deserializeSaveGameV15Data(fileHandle, (SAVE_GAME_V15*) serializeGame))
            return false;

      for (i = 0; i < MAX_NOGO_AREAS; ++i)
      {
            if (!deserializeLandingZoneData(fileHandle, &serializeGame->sLandingZone[i]))
                  return false;
      }

      return true;
}

#define GAME_SAVE_V17               \
      GAME_SAVE_V16;                      \
      uint32_t    objId

typedef struct save_game_v17
{
      GAME_SAVE_V17;
} SAVE_GAME_V17;

static bool serializeSaveGameV17Data(PHYSFS_file* fileHandle, const SAVE_GAME_V17* serializeGame)
{
      return (serializeSaveGameV16Data(fileHandle, (const SAVE_GAME_V16*) serializeGame)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->objId));
}

static bool deserializeSaveGameV17Data(PHYSFS_file* fileHandle, SAVE_GAME_V17* serializeGame)
{
      return (deserializeSaveGameV16Data(fileHandle, (SAVE_GAME_V16*) serializeGame)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->objId));
}

#define GAME_SAVE_V18               \
      GAME_SAVE_V17;                      \
      char        buildDate[MAX_STR_LENGTH];          \
      uint32_t    oldestVersion;    \
      uint32_t    validityKey

typedef struct save_game_v18
{
      GAME_SAVE_V18;
} SAVE_GAME_V18;

static bool serializeSaveGameV18Data(PHYSFS_file* fileHandle, const SAVE_GAME_V18* serializeGame)
{
      return (serializeSaveGameV17Data(fileHandle, (const SAVE_GAME_V17*) serializeGame)
           && PHYSFS_write(fileHandle, serializeGame->buildDate, MAX_STR_LENGTH, 1) == 1
           && PHYSFS_writeUBE32(fileHandle, serializeGame->oldestVersion)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->validityKey));
}

static bool deserializeSaveGameV18Data(PHYSFS_file* fileHandle, SAVE_GAME_V18* serializeGame)
{
      return (deserializeSaveGameV17Data(fileHandle, (SAVE_GAME_V17*) serializeGame)
           && PHYSFS_read(fileHandle, serializeGame->buildDate, MAX_STR_LENGTH, 1) == 1
           && PHYSFS_readUBE32(fileHandle, &serializeGame->oldestVersion)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->validityKey));
}

#define GAME_SAVE_V19               \
      GAME_SAVE_V18;                      \
      uint8_t     alliances[MAX_PLAYERS][MAX_PLAYERS];\
      uint8_t     playerColour[MAX_PLAYERS];\
      uint8_t     radarZoom

typedef struct save_game_v19
{
      GAME_SAVE_V19;
} SAVE_GAME_V19;

static bool serializeSaveGameV19Data(PHYSFS_file* fileHandle, const SAVE_GAME_V19* serializeGame)
{
      unsigned int i, j;

      if (!serializeSaveGameV18Data(fileHandle, (const SAVE_GAME_V18*) serializeGame))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            for (j = 0; j < MAX_PLAYERS; ++j)
            {
                  if (!PHYSFS_writeUBE8(fileHandle, serializeGame->alliances[i][j]))
                        return false;
            }
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_writeUBE8(fileHandle, serializeGame->playerColour[i]))
                  return false;
      }

      return PHYSFS_writeUBE8(fileHandle, serializeGame->radarZoom);
}

static bool deserializeSaveGameV19Data(PHYSFS_file* fileHandle, SAVE_GAME_V19* serializeGame)
{
      unsigned int i, j;

      if (!deserializeSaveGameV18Data(fileHandle, (SAVE_GAME_V18*) serializeGame))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            for (j = 0; j < MAX_PLAYERS; ++j)
            {
                  if (!PHYSFS_readUBE8(fileHandle, &serializeGame->alliances[i][j]))
                        return false;
            }
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_readUBE8(fileHandle, &serializeGame->playerColour[i]))
                  return false;
      }

      return PHYSFS_readUBE8(fileHandle, &serializeGame->radarZoom);
}

#define GAME_SAVE_V20               \
      GAME_SAVE_V19;                      \
      uint8_t     bDroidsToSafetyFlag;    \
      Vector2i    asVTOLReturnPos[MAX_PLAYERS]

typedef struct save_game_v20
{
      GAME_SAVE_V20;
} SAVE_GAME_V20;

static bool serializeSaveGameV20Data(PHYSFS_file* fileHandle, const SAVE_GAME_V20* serializeGame)
{
      unsigned int i;

      if (!serializeSaveGameV19Data(fileHandle, (const SAVE_GAME_V19*) serializeGame)
       || !PHYSFS_writeUBE8(fileHandle, serializeGame->bDroidsToSafetyFlag))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!serializeVector2i(fileHandle, &serializeGame->asVTOLReturnPos[i]))
                  return false;
      }

      return true;
}

static bool deserializeSaveGameV20Data(PHYSFS_file* fileHandle, SAVE_GAME_V20* serializeGame)
{
      unsigned int i;

      if (!deserializeSaveGameV19Data(fileHandle, (SAVE_GAME_V19*) serializeGame)
       || !PHYSFS_readUBE8(fileHandle, &serializeGame->bDroidsToSafetyFlag))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!deserializeVector2i(fileHandle, &serializeGame->asVTOLReturnPos[i]))
                  return false;
      }

      return true;
}

#define GAME_SAVE_V22               \
      GAME_SAVE_V20;                      \
      RUN_DATA    asRunData[MAX_PLAYERS]

typedef struct save_game_v22
{
      GAME_SAVE_V22;
} SAVE_GAME_V22;

static bool serializeSaveGameV22Data(PHYSFS_file* fileHandle, const SAVE_GAME_V22* serializeGame)
{
      unsigned int i;

      if (!serializeSaveGameV20Data(fileHandle, (const SAVE_GAME_V20*) serializeGame))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!serializeRunData(fileHandle, &serializeGame->asRunData[i]))
                  return false;
      }

      return true;
}

static bool deserializeSaveGameV22Data(PHYSFS_file* fileHandle, SAVE_GAME_V22* serializeGame)
{
      unsigned int i;

      if (!deserializeSaveGameV20Data(fileHandle, (SAVE_GAME_V20*) serializeGame))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!deserializeRunData(fileHandle, &serializeGame->asRunData[i]))
                  return false;
      }

      return true;
}

#define GAME_SAVE_V24               \
      GAME_SAVE_V22;                      \
      uint32_t    reinforceTime;          \
      uint8_t     bPlayCountDown;   \
      uint8_t     bPlayerHasWon;    \
      uint8_t     bPlayerHasLost;   \
      uint8_t     dummy3

typedef struct save_game_v24
{
      GAME_SAVE_V24;
} SAVE_GAME_V24;

static bool serializeSaveGameV24Data(PHYSFS_file* fileHandle, const SAVE_GAME_V24* serializeGame)
{
      return (serializeSaveGameV22Data(fileHandle, (const SAVE_GAME_V22*) serializeGame)
           && PHYSFS_writeUBE32(fileHandle, serializeGame->reinforceTime)
           && PHYSFS_writeUBE8(fileHandle, serializeGame->bPlayCountDown)
           && PHYSFS_writeUBE8(fileHandle, serializeGame->bPlayerHasWon)
           && PHYSFS_writeUBE8(fileHandle, serializeGame->bPlayerHasLost)
           && PHYSFS_writeUBE8(fileHandle, serializeGame->dummy3));
}

static bool deserializeSaveGameV24Data(PHYSFS_file* fileHandle, SAVE_GAME_V24* serializeGame)
{
      return (deserializeSaveGameV22Data(fileHandle, (SAVE_GAME_V22*) serializeGame)
           && PHYSFS_readUBE32(fileHandle, &serializeGame->reinforceTime)
           && PHYSFS_readUBE8(fileHandle, &serializeGame->bPlayCountDown)
           && PHYSFS_readUBE8(fileHandle, &serializeGame->bPlayerHasWon)
           && PHYSFS_readUBE8(fileHandle, &serializeGame->bPlayerHasLost)
           && PHYSFS_readUBE8(fileHandle, &serializeGame->dummy3));
}

/*
#define GAME_SAVE_V27         \
      GAME_SAVE_V24

typedef struct save_game_v27
{
      GAME_SAVE_V27;
} SAVE_GAME_V27;
*/
#define GAME_SAVE_V27               \
      GAME_SAVE_V24;                      \
      uint16_t    awDroidExperience[MAX_PLAYERS][MAX_RECYCLED_DROIDS]

typedef struct save_game_v27
{
      GAME_SAVE_V27;
} SAVE_GAME_V27;

static bool serializeSaveGameV27Data(PHYSFS_file* fileHandle, const SAVE_GAME_V27* serializeGame)
{
      unsigned int i, j;

      if (!serializeSaveGameV24Data(fileHandle, (const SAVE_GAME_V24*) serializeGame))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            for (j = 0; j < MAX_RECYCLED_DROIDS; ++j)
            {
                  if (!PHYSFS_writeUBE16(fileHandle, serializeGame->awDroidExperience[i][j]))
                        return false;
            }
      }

      return true;
}

static bool deserializeSaveGameV27Data(PHYSFS_file* fileHandle, SAVE_GAME_V27* serializeGame)
{
      unsigned int i, j;

      if (!deserializeSaveGameV24Data(fileHandle, (SAVE_GAME_V24*) serializeGame))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            for (j = 0; j < MAX_RECYCLED_DROIDS; ++j)
            {
                  if (!PHYSFS_readUBE16(fileHandle, &serializeGame->awDroidExperience[i][j]))
                        return false;
            }
      }

      return true;
}

#define GAME_SAVE_V29               \
      GAME_SAVE_V27;                      \
      uint16_t    missionScrollMinX;  \
      uint16_t    missionScrollMinY;  \
      uint16_t    missionScrollMaxX;  \
      uint16_t    missionScrollMaxY

typedef struct save_game_v29
{
      GAME_SAVE_V29;
} SAVE_GAME_V29;

static bool serializeSaveGameV29Data(PHYSFS_file* fileHandle, const SAVE_GAME_V29* serializeGame)
{
      return (serializeSaveGameV27Data(fileHandle, (const SAVE_GAME_V27*) serializeGame)
           && PHYSFS_writeUBE16(fileHandle, serializeGame->missionScrollMinX)
           && PHYSFS_writeUBE16(fileHandle, serializeGame->missionScrollMinY)
           && PHYSFS_writeUBE16(fileHandle, serializeGame->missionScrollMaxX)
           && PHYSFS_writeUBE16(fileHandle, serializeGame->missionScrollMaxY));
}

static bool deserializeSaveGameV29Data(PHYSFS_file* fileHandle, SAVE_GAME_V29* serializeGame)
{
      return (deserializeSaveGameV27Data(fileHandle, (SAVE_GAME_V27*) serializeGame)
           && PHYSFS_readUBE16(fileHandle, &serializeGame->missionScrollMinX)
           && PHYSFS_readUBE16(fileHandle, &serializeGame->missionScrollMinY)
           && PHYSFS_readUBE16(fileHandle, &serializeGame->missionScrollMaxX)
           && PHYSFS_readUBE16(fileHandle, &serializeGame->missionScrollMaxY));
}

#define GAME_SAVE_V30               \
      GAME_SAVE_V29;                      \
      int32_t     scrGameLevel;       \
      uint8_t     bExtraVictoryFlag;  \
      uint8_t     bExtraFailFlag;     \
      uint8_t     bTrackTransporter

typedef struct save_game_v30
{
      GAME_SAVE_V30;
} SAVE_GAME_V30;

static bool serializeSaveGameV30Data(PHYSFS_file* fileHandle, const SAVE_GAME_V30* serializeGame)
{
      return (serializeSaveGameV29Data(fileHandle, (const SAVE_GAME_V29*) serializeGame)
           && PHYSFS_writeSBE32(fileHandle, serializeGame->scrGameLevel)
           && PHYSFS_writeUBE8(fileHandle, serializeGame->bExtraVictoryFlag)
           && PHYSFS_writeUBE8(fileHandle, serializeGame->bExtraFailFlag)
           && PHYSFS_writeUBE8(fileHandle, serializeGame->bTrackTransporter));
}

static bool deserializeSaveGameV30Data(PHYSFS_file* fileHandle, SAVE_GAME_V30* serializeGame)
{
      return (deserializeSaveGameV29Data(fileHandle, (SAVE_GAME_V29*) serializeGame)
           && PHYSFS_readSBE32(fileHandle, &serializeGame->scrGameLevel)
           && PHYSFS_readUBE8(fileHandle, &serializeGame->bExtraVictoryFlag)
           && PHYSFS_readUBE8(fileHandle, &serializeGame->bExtraFailFlag)
           && PHYSFS_readUBE8(fileHandle, &serializeGame->bTrackTransporter));
}

//extra code for the patch - saves out whether cheated with the mission timer
#define GAME_SAVE_V31           \
      GAME_SAVE_V30;                      \
      int32_t     missionCheatTime

typedef struct save_game_v31
{
      GAME_SAVE_V31;
} SAVE_GAME_V31;


static bool serializeSaveGameV31Data(PHYSFS_file* fileHandle, const SAVE_GAME_V31* serializeGame)
{
      return (serializeSaveGameV30Data(fileHandle, (const SAVE_GAME_V30*) serializeGame)
           && PHYSFS_writeSBE32(fileHandle, serializeGame->missionCheatTime));
}

static bool deserializeSaveGameV31Data(PHYSFS_file* fileHandle, SAVE_GAME_V31* serializeGame)
{
      return (deserializeSaveGameV30Data(fileHandle, (SAVE_GAME_V30*) serializeGame)
           && PHYSFS_readSBE32(fileHandle, &serializeGame->missionCheatTime));
}

// alexl. skirmish saves
#define GAME_SAVE_V33           \
      GAME_SAVE_V31;                      \
      MULTIPLAYERGAME sGame;        \
      NETPLAY         sNetPlay;     \
      uint32_t        savePlayer;   \
      char            sPName[32];   \
      BOOL            multiPlayer;\
      uint32_t        sPlayer2dpid[MAX_PLAYERS]

typedef struct save_game_v33
{
      GAME_SAVE_V33;
} SAVE_GAME_V33;

static bool serializeSaveGameV33Data(PHYSFS_file* fileHandle, const SAVE_GAME_V33* serializeGame)
{
      unsigned int i;

      if (!serializeSaveGameV31Data(fileHandle, (const SAVE_GAME_V31*) serializeGame)
       || !serializeMultiplayerGame(fileHandle, &serializeGame->sGame)
       || !serializeNetPlay(fileHandle, &serializeGame->sNetPlay)
       || !PHYSFS_writeUBE32(fileHandle, serializeGame->savePlayer)
       || !PHYSFS_write(fileHandle, serializeGame->sPName, 1, 32) == 32
       || !PHYSFS_writeSBE32(fileHandle, serializeGame->multiPlayer))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_writeUBE32(fileHandle, serializeGame->sPlayer2dpid[i]))
                  return false;
      }

      return true;
}

static bool deserializeSaveGameV33Data(PHYSFS_file* fileHandle, SAVE_GAME_V33* serializeGame)
{
      unsigned int i;
      int32_t boolMultiPlayer;

      if (!deserializeSaveGameV31Data(fileHandle, (SAVE_GAME_V31*) serializeGame)
       || !deserializeMultiplayerGame(fileHandle, &serializeGame->sGame)
       || !deserializeNetPlay(fileHandle, &serializeGame->sNetPlay)
       || !PHYSFS_readUBE32(fileHandle, &serializeGame->savePlayer)
       || PHYSFS_read(fileHandle, serializeGame->sPName, 1, 32) != 32
       || !PHYSFS_readSBE32(fileHandle, &boolMultiPlayer))
            return false;

      serializeGame->multiPlayer = boolMultiPlayer;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (!PHYSFS_readUBE32(fileHandle, &serializeGame->sPlayer2dpid[i]))
                  return false;
      }

      return true;
}

#define GAME_SAVE_V34           \
      GAME_SAVE_V33;                      \
      char        sPlayerName[MAX_PLAYERS][StringSize]

//Now holds AI names for multiplayer
typedef struct save_game_v34
{
      GAME_SAVE_V34;
} SAVE_GAME_V34;


static bool serializeSaveGameV34Data(PHYSFS_file* fileHandle, const SAVE_GAME_V34* serializeGame)
{
      unsigned int i;
      if (!serializeSaveGameV33Data(fileHandle, (const SAVE_GAME_V33*) serializeGame))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (PHYSFS_write(fileHandle, serializeGame->sPlayerName[i], StringSize, 1) != 1)
                  return false;
      }

      return true;
}

static bool deserializeSaveGameV34Data(PHYSFS_file* fileHandle, SAVE_GAME_V34* serializeGame)
{
      unsigned int i;
      if (!deserializeSaveGameV33Data(fileHandle, (SAVE_GAME_V33*) serializeGame))
            return false;

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            if (PHYSFS_read(fileHandle, serializeGame->sPlayerName[i], StringSize, 1) != 1)
                  return false;
      }

      return true;
}

// First version to utilize (de)serialization API and first to be big-endian (instead of little-endian)
#define GAME_SAVE_V35           \
      GAME_SAVE_V34

typedef struct save_game_v35
{
      GAME_SAVE_V35;
} SAVE_GAME_V35;

static bool serializeSaveGameV35Data(PHYSFS_file* fileHandle, const SAVE_GAME_V35* serializeGame)
{
      return serializeSaveGameV34Data(fileHandle, (const SAVE_GAME_V34*) serializeGame);
}

static bool deserializeSaveGameV35Data(PHYSFS_file* fileHandle, SAVE_GAME_V35* serializeGame)
{
      return deserializeSaveGameV34Data(fileHandle, (SAVE_GAME_V34*) serializeGame);
}

// Current save game version
typedef struct save_game
{
      GAME_SAVE_V35;
} SAVE_GAME;

static bool serializeSaveGameData(PHYSFS_file* fileHandle, const SAVE_GAME* serializeGame)
{
      return serializeSaveGameV35Data(fileHandle, (const SAVE_GAME_V35*) serializeGame);
}

static bool deserializeSaveGameData(PHYSFS_file* fileHandle, SAVE_GAME* serializeGame)
{
      return deserializeSaveGameV35Data(fileHandle, (SAVE_GAME_V35*) serializeGame);
}

#define TEMP_DROID_MAXPROGS   3
#define     SAVE_COMP_PROGRAM 8
#define SAVE_COMP_WEAPON      9

typedef struct _save_move_control
{
      UBYTE Status;                                   // Inactive, Navigating or moving point to point status
      UBYTE Position;                           // Position in asPath
      UBYTE numPoints;                          // number of points in asPath
      PATH_POINT  asPath[TRAVELSIZE];           // Pointer to list of block X,Y coordinates.
      SDWORD      DestinationX;                       // DestinationX,Y should match objects current X,Y
      SDWORD      DestinationY;                       //          location for this movement to be complete.
      SDWORD      srcX,srcY,targetX,targetY;
      float fx,fy;                                    // droid location as a fract
      float speed;                                    // Speed of motion
      SWORD boundX,boundY;                      // Vector for the end of path boundary
      SWORD moveDir;                                  // direction of motion (not the direction the droid is facing)
      SWORD bumpDir;                            // direction at last bump
      UDWORD      bumpTime;                           // time of first bump with something
      UWORD lastBump;                           // time of last bump with a droid - relative to bumpTime
      UWORD pauseTime;                          // when MOVEPAUSE started - relative to bumpTime
      UWORD bumpX,bumpY;                        // position of last bump
      UDWORD      shuffleStart;                       // when a shuffle started
      BOOL  isInFormation;                          // Indicates wether this droid is a member of a formation
      SWORD iVertSpeed;
      UDWORD      iAttackRuns[DROID_MAXWEAPS];
      float   fz;
} SAVE_MOVE_CONTROL;


#define DROID_SAVE_V9         \
      OBJECT_SAVE_V19;              \
      SAVE_COMPONENT_V19      asBits[DROID_MAXCOMP]; \
      UDWORD            body;       \
      UBYTE       droidType;  \
      UDWORD            saveType;   \
      UDWORD            numWeaps;   \
      SAVE_WEAPON_V19   asWeaps[TEMP_DROID_MAXPROGS]; \
      UDWORD            numKills

typedef struct _save_droid_v9
{
      DROID_SAVE_V9;
} SAVE_DROID_V9;

/*save DROID SAVE 11 */
#define DROID_SAVE_V11        \
      OBJECT_SAVE_V19;              \
      SAVE_COMPONENT_V19      asBits[DROID_MAXCOMP]; \
      UDWORD            body;       \
      UBYTE       droidType;  \
      UBYTE       saveType;   \
      UDWORD            numWeaps;   \
      SAVE_WEAPON_V19   asWeaps[TEMP_DROID_MAXPROGS]; \
      UDWORD            numKills;   \
      UWORD turretRotation;   \
      UWORD turretPitch

typedef struct _save_droid_v11
{
      DROID_SAVE_V11;
} SAVE_DROID_V11;

#define DROID_SAVE_V12        \
      DROID_SAVE_V9;                \
      UWORD turretRotation;   \
      UWORD turretPitch;      \
      SDWORD      order;                  \
      UWORD orderX,orderY;    \
      UWORD orderX2,orderY2;\
      UDWORD      timeLastHit;      \
      UDWORD      targetID;         \
      UDWORD      secondaryOrder;   \
      SDWORD      action;                 \
      UDWORD      actionX,actionY;\
      UDWORD      actionTargetID;   \
      UDWORD      actionStarted;    \
      UDWORD      actionPoints;     \
      UWORD actionHeight

typedef struct _save_droid_v12
{
      DROID_SAVE_V12;
} SAVE_DROID_V12;

#define DROID_SAVE_V14        \
      DROID_SAVE_V12;               \
      char  tarStatName[MAX_GAME_STR_SIZE];\
    UDWORD  baseStructID;     \
      UBYTE group;                  \
      UBYTE selected;         \
      UBYTE cluster_unused;         \
      UBYTE visible[MAX_PLAYERS];\
      UDWORD      died;             \
      UDWORD      lastEmission

typedef struct _save_droid_v14
{
      DROID_SAVE_V14;
} SAVE_DROID_V14;

//DROID_SAVE_18 replaces DROID_SAVE_14
#define DROID_SAVE_V18        \
      DROID_SAVE_V12;               \
      char  tarStatName[MAX_SAVE_NAME_SIZE_V19];\
    UDWORD  baseStructID;     \
      UBYTE group;                  \
      UBYTE selected;         \
      UBYTE cluster_unused;         \
      UBYTE visible[MAX_PLAYERS];\
      UDWORD      died;             \
      UDWORD      lastEmission

typedef struct _save_droid_v18
{
      DROID_SAVE_V18;
} SAVE_DROID_V18;


//DROID_SAVE_20 replaces all previous saves uses 60 character names
#define DROID_SAVE_V20        \
      OBJECT_SAVE_V20;              \
      SAVE_COMPONENT    asBits[DROID_MAXCOMP]; \
      UDWORD            body;       \
      UBYTE       droidType;  \
      UDWORD            saveType;   \
      UDWORD            numWeaps;   \
      SAVE_WEAPON asWeaps[TEMP_DROID_MAXPROGS]; \
      UDWORD            numKills;   \
      UWORD turretRotation;   \
      UWORD turretPitch;      \
      SDWORD      order;                  \
      UWORD orderX,orderY;    \
      UWORD orderX2,orderY2;\
      UDWORD      timeLastHit;      \
      UDWORD      targetID;         \
      UDWORD      secondaryOrder;   \
      SDWORD      action;                 \
      UDWORD      actionX,actionY;\
      UDWORD      actionTargetID;   \
      UDWORD      actionStarted;    \
      UDWORD      actionPoints;     \
      UWORD actionHeight;     \
      char  tarStatName[MAX_SAVE_NAME_SIZE];\
    UDWORD  baseStructID;     \
      UBYTE group;                  \
      UBYTE selected;         \
      UBYTE cluster_unused;         \
      UBYTE visible[MAX_PLAYERS];\
      UDWORD      died;             \
      UDWORD      lastEmission

typedef struct _save_droid_v20
{
      DROID_SAVE_V20;
} SAVE_DROID_V20;

#define DROID_SAVE_V21        \
      DROID_SAVE_V20;               \
      UDWORD      commandId

typedef struct _save_droid_v21
{
      DROID_SAVE_V21;
} SAVE_DROID_V21;

#define DROID_SAVE_V24        \
      DROID_SAVE_V21;               \
      SDWORD      resistance;       \
      SAVE_MOVE_CONTROL sMove;      \
      SWORD       formationDir;     \
      SDWORD            formationX; \
      SDWORD            formationY

typedef struct _save_droid_v24
{
      DROID_SAVE_V24;
} SAVE_DROID_V24;

//Watermelon: I need DROID_SAVE_V99...
#define DROID_SAVE_V99        \
      OBJECT_SAVE_V20;              \
      SAVE_COMPONENT    asBits[DROID_MAXCOMP]; \
      UDWORD            body;       \
      UBYTE       droidType;  \
      UDWORD            saveType;   \
      UDWORD            numWeaps;   \
      SAVE_WEAPON asWeaps[TEMP_DROID_MAXPROGS]; \
      UDWORD            numKills;   \
      UWORD turretRotation[DROID_MAXWEAPS];     \
      UWORD turretPitch[DROID_MAXWEAPS];  \
      SDWORD      order;                  \
      UWORD orderX,orderY;    \
      UWORD orderX2,orderY2;\
      UDWORD      timeLastHit;      \
      UDWORD      targetID;         \
      UDWORD      secondaryOrder;   \
      SDWORD      action;                 \
      UDWORD      actionX,actionY;\
      UDWORD      actionTargetID;   \
      UDWORD      actionStarted;    \
      UDWORD      actionPoints;     \
      UWORD actionHeight;     \
      char  tarStatName[MAX_SAVE_NAME_SIZE];\
      UDWORD      baseStructID;     \
      UBYTE group;                  \
      UBYTE selected;         \
      UBYTE cluster_unused;         \
      UBYTE visible[MAX_PLAYERS];\
      UDWORD      died;             \
      UDWORD      lastEmission;         \
      UDWORD      commandId;            \
      SDWORD      resistance;           \
      SAVE_MOVE_CONTROL sMove; \
      SWORD formationDir;         \
      SDWORD      formationX;           \
      SDWORD      formationY

typedef struct _save_droid_v99
{
      DROID_SAVE_V99;
} SAVE_DROID_V99;

//Watermelon:V99 'test'
typedef struct _save_droid
{
      DROID_SAVE_V99;
} SAVE_DROID;


typedef struct _droidinit_save_header
{
      char        aFileType[4];
      UDWORD            version;
      UDWORD            quantity;
} DROIDINIT_SAVEHEADER;

typedef struct _save_droidinit
{
      OBJECT_SAVE_V19;
} SAVE_DROIDINIT;

/*
 *    STRUCTURE Definitions
 */

#define STRUCTURE_SAVE_V2 \
      OBJECT_SAVE_V19; \
      UBYTE                   status; \
      SDWORD                        currentBuildPts; \
      UDWORD                        body; \
      UDWORD                        armour; \
      UDWORD                        resistance; \
      UDWORD                        dummy1; \
      UDWORD                        subjectInc;  /*research inc or factory prod id*/\
      UDWORD                        timeStarted; \
      UDWORD                        output; \
      UDWORD                        capacity; \
      UDWORD                        quantity

typedef struct _save_structure_v2
{
      STRUCTURE_SAVE_V2;
} SAVE_STRUCTURE_V2;

#define STRUCTURE_SAVE_V12 \
      STRUCTURE_SAVE_V2; \
      UDWORD                        factoryInc;             \
      UBYTE                   loopsPerformed;         \
      UDWORD                        powerAccrued;           \
      UDWORD                        dummy2;                 \
      UDWORD                        droidTimeStarted; \
      UDWORD                        timeToBuild;            \
      UDWORD                        timeStartHold

typedef struct _save_structure_v12
{
      STRUCTURE_SAVE_V12;
} SAVE_STRUCTURE_V12;

#define STRUCTURE_SAVE_V14 \
      STRUCTURE_SAVE_V12; \
      UBYTE visible[MAX_PLAYERS]

typedef struct _save_structure_v14
{
      STRUCTURE_SAVE_V14;
} SAVE_STRUCTURE_V14;

#define STRUCTURE_SAVE_V15 \
      STRUCTURE_SAVE_V14; \
      char  researchName[MAX_SAVE_NAME_SIZE_V19]

typedef struct _save_structure_v15
{
      STRUCTURE_SAVE_V15;
} SAVE_STRUCTURE_V15;

#define STRUCTURE_SAVE_V17 \
      STRUCTURE_SAVE_V15;\
      SWORD                   currentPowerAccrued

typedef struct _save_structure_v17
{
      STRUCTURE_SAVE_V17;
} SAVE_STRUCTURE_V17;

#define STRUCTURE_SAVE_V20 \
      OBJECT_SAVE_V20; \
      UBYTE                   status; \
      SDWORD                        currentBuildPts; \
      UDWORD                        body; \
      UDWORD                        armour; \
      UDWORD                        resistance; \
      UDWORD                        dummy1; \
      UDWORD                        subjectInc;  /*research inc or factory prod id*/\
      UDWORD                        timeStarted; \
      UDWORD                        output; \
      UDWORD                        capacity; \
      UDWORD                        quantity; \
      UDWORD                        factoryInc;             \
      UBYTE                   loopsPerformed;         \
      UDWORD                        powerAccrued;           \
      UDWORD                        dummy2;                 \
      UDWORD                        droidTimeStarted; \
      UDWORD                        timeToBuild;            \
      UDWORD                        timeStartHold; \
      UBYTE                   visible[MAX_PLAYERS]; \
      char                    researchName[MAX_SAVE_NAME_SIZE]; \
      SWORD                   currentPowerAccrued

typedef struct _save_structure_v20
{
      STRUCTURE_SAVE_V20;
} SAVE_STRUCTURE_V20;

#define STRUCTURE_SAVE_V21 \
      STRUCTURE_SAVE_V20; \
      UDWORD                        commandId

typedef struct _save_structure_v21
{
      STRUCTURE_SAVE_V21;
} SAVE_STRUCTURE_V21;

typedef struct _save_structure
{
      STRUCTURE_SAVE_V21;
} SAVE_STRUCTURE;


//PROGRAMS NEED TO BE REMOVED FROM DROIDS - 7/8/98
// multiPlayerID for templates needs to be saved - 29/10/98
#define TEMPLATE_SAVE_V2 \
      char                    name[MAX_SAVE_NAME_SIZE_V19]; \
      UDWORD                        ref; \
      UDWORD                        player; \
      UBYTE                   droidType; \
      char                    asParts[DROID_MAXCOMP][MAX_SAVE_NAME_SIZE_V19]; \
      UDWORD                        numWeaps; \
      char                    asWeaps[TEMP_DROID_MAXPROGS][MAX_SAVE_NAME_SIZE_V19]; \
      UDWORD                        numProgs; \
      char                    asProgs[TEMP_DROID_MAXPROGS][MAX_SAVE_NAME_SIZE_V19]

// multiPlayerID for templates needs to be saved - 29/10/98
#define TEMPLATE_SAVE_V14 \
      char                    name[MAX_SAVE_NAME_SIZE_V19]; \
      UDWORD                        ref; \
      UDWORD                        player; \
      UBYTE                   droidType; \
      char                    asParts[DROID_MAXCOMP][MAX_SAVE_NAME_SIZE_V19]; \
      UDWORD                        numWeaps; \
      char                    asWeaps[TEMP_DROID_MAXPROGS][MAX_SAVE_NAME_SIZE_V19]; \
      UDWORD                        multiPlayerID

#define TEMPLATE_SAVE_V20 \
      char                    name[MAX_SAVE_NAME_SIZE]; \
      UDWORD                        ref; \
      UDWORD                        player; \
      UBYTE                   droidType; \
      char                    asParts[DROID_MAXCOMP][MAX_SAVE_NAME_SIZE]; \
      UDWORD                        numWeaps; \
      char                    asWeaps[TEMP_DROID_MAXPROGS][MAX_SAVE_NAME_SIZE]; \
      UDWORD                        multiPlayerID



typedef struct _save_template_v2
{
      TEMPLATE_SAVE_V2;
} SAVE_TEMPLATE_V2;

typedef struct _save_template_v14
{
      TEMPLATE_SAVE_V14;
} SAVE_TEMPLATE_V14;

typedef struct _save_template_v20
{
      TEMPLATE_SAVE_V20;
} SAVE_TEMPLATE_V20;

typedef struct _save_template
{
      TEMPLATE_SAVE_V20;
} SAVE_TEMPLATE;


#define FEATURE_SAVE_V2 \
      OBJECT_SAVE_V19

typedef struct _save_feature_v2
{
      FEATURE_SAVE_V2;
} SAVE_FEATURE_V2;

#define FEATURE_SAVE_V14 \
      FEATURE_SAVE_V2; \
      UBYTE visible[MAX_PLAYERS]

typedef struct _save_feature_v14
{
      FEATURE_SAVE_V14;
} SAVE_FEATURE_V14;

#define FEATURE_SAVE_V20 \
      OBJECT_SAVE_V20; \
      UBYTE visible[MAX_PLAYERS]

typedef struct _save_feature_v20
{
      FEATURE_SAVE_V20;
} SAVE_FEATURE_V20;

typedef struct _save_feature
{
      FEATURE_SAVE_V20;
} SAVE_FEATURE;


#define COMPLIST_SAVE_V6 \
      char                    name[MAX_SAVE_NAME_SIZE_V19]; \
      UBYTE                   type; \
      UBYTE                   state; \
      UBYTE                   player

#define COMPLIST_SAVE_V20 \
      char                    name[MAX_SAVE_NAME_SIZE]; \
      UBYTE                   type; \
      UBYTE                   state; \
      UBYTE                   player


typedef struct _save_compList_v6
{
      COMPLIST_SAVE_V6;
} SAVE_COMPLIST_V6;

typedef struct _save_compList_v20
{
      COMPLIST_SAVE_V20;
} SAVE_COMPLIST_V20;

typedef struct _save_compList
{
      COMPLIST_SAVE_V20;
} SAVE_COMPLIST;





#define STRUCTLIST_SAVE_V6 \
      char                    name[MAX_SAVE_NAME_SIZE_V19]; \
      UBYTE                   state; \
      UBYTE                   player

#define STRUCTLIST_SAVE_V20 \
      char                    name[MAX_SAVE_NAME_SIZE]; \
      UBYTE                   state; \
      UBYTE                   player

typedef struct _save_structList_v6
{
      STRUCTLIST_SAVE_V6;
} SAVE_STRUCTLIST_V6;

typedef struct _save_structList_v20
{
      STRUCTLIST_SAVE_V20;
} SAVE_STRUCTLIST_V20;

typedef struct _save_structList
{
      STRUCTLIST_SAVE_V20;
} SAVE_STRUCTLIST;


#define RESEARCH_SAVE_V8 \
      char                    name[MAX_SAVE_NAME_SIZE_V19]; \
      UBYTE                   possible[MAX_PLAYERS]; \
      UBYTE                   researched[MAX_PLAYERS]; \
      UDWORD                        currentPoints[MAX_PLAYERS]

#define RESEARCH_SAVE_V20 \
      char                    name[MAX_SAVE_NAME_SIZE]; \
      UBYTE                   possible[MAX_PLAYERS]; \
      UBYTE                   researched[MAX_PLAYERS]; \
      UDWORD                        currentPoints[MAX_PLAYERS]


typedef struct _save_research_v8
{
      RESEARCH_SAVE_V8;
} SAVE_RESEARCH_V8;

typedef struct _save_research_v20
{
      RESEARCH_SAVE_V20;
} SAVE_RESEARCH_V20;

typedef struct _save_research
{
      RESEARCH_SAVE_V20;
} SAVE_RESEARCH;

typedef struct _save_message
{
      MESSAGE_TYPE      type;             //The type of message
      BOOL              bObj;
      char              name[MAX_GAME_STR_SIZE];
      UDWORD                  objId;                              //Id for Proximity messages!
      BOOL              read;                         //flag to indicate whether message has been read
      UDWORD                  player;                             //which player this message belongs to

} SAVE_MESSAGE;

typedef struct _save_message_v36
{
      MESSAGE_TYPE      type;             //The type of message
      BOOL              bObj;
      char              name[MAX_GAME_STR_SIZE];
      UDWORD                  objId;                              //Id for Proximity messages!
      BOOL              read;                         //flag to indicate whether message has been read
      UDWORD                  player;                             //which player this message belongs to
      MSG_DATA_TYPE     dataType;                     //actual type of pViewData
      UDWORD                  locX,locY;
      SDWORD                  sender;                             //sender of the message
} SAVE_MESSAGE_36;

typedef struct _save_flag_v18
{
      POSITION_TYPE     type;                   /*the type of position obj - FlagPos or ProxDisp*/
      UDWORD                  frameNumber;            /*when the Position was last drawn*/
      UDWORD                  screenX;                /*screen coords and radius of Position imd */
      UDWORD                  screenY;
      UDWORD                  screenR;
      UDWORD                  player;                       /*which player the Position belongs to*/
      BOOL              selected;               /*flag to indicate whether the Position */
      Vector3i          coords;                                         //the world coords of the Position
      UBYTE       factoryInc;                               //indicates whether the first, second etc factory
      UBYTE       factoryType;                              //indicates whether standard, cyborg or vtol factory
      UBYTE       dummyNOTUSED;                                   //sub value. needed to order production points.
      UBYTE       dummyNOTUSED2;
} SAVE_FLAG_V18;

typedef struct _save_flag
{
      POSITION_TYPE     type;                   /*the type of position obj - FlagPos or ProxDisp*/
      UDWORD                  frameNumber;            /*when the Position was last drawn*/
      UDWORD                  screenX;                /*screen coords and radius of Position imd */
      UDWORD                  screenY;
      UDWORD                  screenR;
      UDWORD                  player;                       /*which player the Position belongs to*/
      BOOL              selected;               /*flag to indicate whether the Position */
      Vector3i          coords;                                         //the world coords of the Position
      UBYTE       factoryInc;                               //indicates whether the first, second etc factory
      UBYTE       factoryType;                              //indicates whether standard, cyborg or vtol factory
      UBYTE       dummyNOTUSED;                                   //sub value. needed to order production points.
      UBYTE       dummyNOTUSED2;
      UDWORD            repairId;
} SAVE_FLAG;

//PRODUCTION_RUN        asProductionRun[NUM_FACTORY_TYPES][MAX_FACTORY][MAX_PROD_RUN];
typedef struct _save_production
{
      UBYTE                               quantity;               //number to build
      UBYTE                               built;                        //number built on current run
      UDWORD                                    multiPlayerID;          //template to build
} SAVE_PRODUCTION;

#define STRUCTLIMITS_SAVE_V2 \
      char                    name[MAX_SAVE_NAME_SIZE_V19]; \
      UBYTE                   limit; \
      UBYTE                   player

typedef struct _save_structLimits_v2
{
      STRUCTLIMITS_SAVE_V2;
} SAVE_STRUCTLIMITS_V2;

#define STRUCTLIMITS_SAVE_V20 \
      char                    name[MAX_SAVE_NAME_SIZE]; \
      UBYTE                   limit; \
      UBYTE                   player

typedef struct _save_structLimits_v20
{
      STRUCTLIMITS_SAVE_V20;
} SAVE_STRUCTLIMITS_V20;

typedef struct _save_structLimits
{
      STRUCTLIMITS_SAVE_V20;
} SAVE_STRUCTLIMITS;


#define COMMAND_SAVE_V20 \
      UDWORD                        droidID

typedef struct _save_command_v20
{
      COMMAND_SAVE_V20;
} SAVE_COMMAND_V20;

typedef struct _save_command
{
      COMMAND_SAVE_V20;
} SAVE_COMMAND;


/* The different types of droid */
typedef enum _droid_save_type
{
      DROID_NORMAL,     // Weapon droid
      DROID_ON_TRANSPORT,
} DROID_SAVE_TYPE;

/***************************************************************************/
/*
 *    Local Variables
 */
/***************************************************************************/
extern      UDWORD                        objID;                              // unique ID creation thing..

static UDWORD                 saveGameVersion = 0;
static BOOL                   saveGameOnMission = false;
static SAVE_GAME        saveGameData;
static UDWORD                 oldestSaveGameVersion = CURRENT_VERSION_NUM;
static UDWORD                 validityKey = 0;

static UDWORD     savedGameTime;
static UDWORD     savedObjId;

//static UDWORD               HashedName;
//static STRUCTURE *psStructList;
//static FEATURE *psFeatureList;
//static FLAG_POSITION **ppsCurrentFlagPosLists;
static SDWORD     startX, startY;
static UDWORD   width, height;
static UDWORD     gameType;
static BOOL IsScenario;
/***************************************************************************/
/*
 *    Local ProtoTypes
 */
/***************************************************************************/
static bool gameLoadV7(PHYSFS_file* fileHandle);
static bool gameLoadV(PHYSFS_file* fileHandle, unsigned int version);
static bool writeGameFile(const char* fileName, SDWORD saveType);
static bool writeMapFile(const char* fileName);

static BOOL loadSaveDroidInitV2(char *pFileData, UDWORD filesize,UDWORD quantity);

static BOOL loadSaveDroidInit(char *pFileData, UDWORD filesize);
static DROID_TEMPLATE *FindDroidTemplate(char *name,UDWORD player);

static BOOL loadSaveDroid(char *pFileData, UDWORD filesize, DROID **ppsCurrentDroidLists);
static BOOL loadSaveDroidV11(char *pFileData, UDWORD filesize, UDWORD numDroids, UDWORD version, DROID **ppsCurrentDroidLists);
static BOOL loadSaveDroidV19(char *pFileData, UDWORD filesize, UDWORD numDroids, UDWORD version, DROID **ppsCurrentDroidLists);
static BOOL loadSaveDroidV(char *pFileData, UDWORD filesize, UDWORD numDroids, UDWORD version, DROID **ppsCurrentDroidLists);
static BOOL loadDroidSetPointers(void);
static BOOL writeDroidFile(char *pFileName, DROID **ppsCurrentDroidLists);

static BOOL loadSaveStructure(char *pFileData, UDWORD filesize);
static BOOL loadSaveStructureV7(char *pFileData, UDWORD filesize, UDWORD numStructures);
static BOOL loadSaveStructureV19(char *pFileData, UDWORD filesize, UDWORD numStructures, UDWORD version);
static BOOL loadSaveStructureV(char *pFileData, UDWORD filesize, UDWORD numStructures, UDWORD version);
static BOOL loadStructSetPointers(void);
static BOOL writeStructFile(char *pFileName);

static BOOL loadSaveTemplate(char *pFileData, UDWORD filesize);
static BOOL loadSaveTemplateV7(char *pFileData, UDWORD filesize, UDWORD numTemplates);
static BOOL loadSaveTemplateV14(char *pFileData, UDWORD filesize, UDWORD numTemplates);
static BOOL loadSaveTemplateV(char *pFileData, UDWORD filesize, UDWORD numTemplates);
static BOOL writeTemplateFile(char *pFileName);

static BOOL loadSaveFeature(char *pFileData, UDWORD filesize);
static BOOL loadSaveFeatureV14(char *pFileData, UDWORD filesize, UDWORD numFeatures, UDWORD version);
static BOOL loadSaveFeatureV(char *pFileData, UDWORD filesize, UDWORD numFeatures, UDWORD version);
static BOOL writeFeatureFile(char *pFileName);

static BOOL writeTerrainTypeMapFile(char *pFileName);

static BOOL loadSaveCompList(char *pFileData, UDWORD filesize);
static BOOL loadSaveCompListV9(char *pFileData, UDWORD filesize, UDWORD numRecords, UDWORD version);
static BOOL loadSaveCompListV(char *pFileData, UDWORD filesize, UDWORD numRecords, UDWORD version);
static BOOL writeCompListFile(char *pFileName);

static BOOL loadSaveStructTypeList(char *pFileData, UDWORD filesize);
static BOOL loadSaveStructTypeListV7(char *pFileData, UDWORD filesize, UDWORD numRecords);
static BOOL loadSaveStructTypeListV(char *pFileData, UDWORD filesize, UDWORD numRecords);
static BOOL writeStructTypeListFile(char *pFileName);

static BOOL loadSaveResearch(char *pFileData, UDWORD filesize);
static BOOL loadSaveResearchV8(char *pFileData, UDWORD filesize, UDWORD numRecords);
static BOOL loadSaveResearchV(char *pFileData, UDWORD filesize, UDWORD numRecords);
static BOOL writeResearchFile(char *pFileName);

static BOOL loadSaveMessage(char *pFileData, UDWORD filesize, SWORD levelType);
static BOOL loadSaveMessageV(char *pFileData, UDWORD filesize, UDWORD numMessages, UDWORD version, SWORD levelType);
static BOOL loadSaveMessage36(char *pFileData, UDWORD filesize, UDWORD numMessages, UDWORD version, SWORD levelType);
static BOOL writeMessageFile(char *pFileName);

static BOOL loadSaveFlag(char *pFileData, UDWORD filesize);
static BOOL loadSaveFlagV(char *pFileData, UDWORD filesize, UDWORD numFlags, UDWORD version);
static BOOL writeFlagFile(char *pFileName);

static BOOL loadSaveProduction(char *pFileData, UDWORD filesize);
static BOOL loadSaveProductionV(char *pFileData, UDWORD filesize, UDWORD version);
static BOOL writeProductionFile(char *pFileName);

static BOOL loadSaveStructLimits(char *pFileData, UDWORD filesize);
static BOOL loadSaveStructLimitsV19(char *pFileData, UDWORD filesize, UDWORD numLimits);
static BOOL loadSaveStructLimitsV(char *pFileData, UDWORD filesize, UDWORD numLimits);
static BOOL writeStructLimitsFile(char *pFileName);

static BOOL readFiresupportDesignators(char *pFileName);
static BOOL writeFiresupportDesignators(char *pFileName);

static BOOL writeScriptState(char *pFileName);

static BOOL getNameFromComp(UDWORD compType, char *pDest, UDWORD compIndex);

//adjust the name depending on type of save game and whether resourceNames are used
static BOOL getSaveObjectName(char *pName);

/* set the global scroll values to use for the save game */
static void setMapScroll(void);

static bool gameLoad(const char* fileName);

static char *getSaveStructNameV19(SAVE_STRUCTURE_V17 *psSaveStructure)
{
      return(psSaveStructure->name);
}

static char *getSaveStructNameV(SAVE_STRUCTURE *psSaveStructure)
{
      return(psSaveStructure->name);
}

/*This just loads up the .gam file to determine which level data to set up - split up
so can be called in levLoadData when starting a game from a load save game*/

// -----------------------------------------------------------------------------------------
bool loadGameInit(const char* fileName)
{
      if (!gameLoad(fileName))
      {
            debug(LOG_ERROR, "Corrupted savegame file %s, Unable to load!", fileName);

            // NOTE: why do we start the game clock on a *failed* load?
            // Start the game clock 
            gameTimeStart();

            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
// Load a file from a save game into the psx.
// This is divided up into 2 parts ...
//
// if it is a level loaded up from CD then UserSaveGame will by false
// UserSaveGame ... Extra stuff to load after scripts
BOOL loadMissionExtras(const char *pGameToLoad, SWORD levelType)
{
      char              aFileName[256];
      UDWORD                  fileExten, fileSize;
      char              *pFileData = NULL;

      strcpy(aFileName, pGameToLoad);
      fileExten = strlen(pGameToLoad) - 3;
      aFileName[fileExten - 1] = '\0';
      strcat(aFileName, "/");

      if (saveGameVersion >= VERSION_11)
      {
            //if user save game then load up the messages AFTER any droids or structures are loaded
            if ((gameType == GTYPE_SAVE_START) ||
                  (gameType == GTYPE_SAVE_MIDMISSION))
            {
                  //load in the message list file
                  aFileName[fileExten] = '\0';
                  strcat(aFileName, "messtate.bjo");
                  // Load in the chosen file data
                  pFileData = fileLoadBuffer;
                  if (loadFileToBufferNoError(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
                  {
                        //load the message status data
                        if (pFileData)
                        {
                              if (!loadSaveMessage(pFileData, fileSize, levelType))
                              {
                                    debug( LOG_NEVER, "loadMissionExtras: Fail 2\n" );
                                    return false;
                              }
                        }
                  }

            }
      }

      return true;
}


// -----------------------------------------------------------------------------------------
// UserSaveGame ... this is true when you are loading a players save game
BOOL loadGame(const char *pGameToLoad, BOOL keepObjects, BOOL freeMem, BOOL UserSaveGame)
{
      char              aFileName[256];
      UDWORD                  fileExten, fileSize, pl;
      char              *pFileData = NULL;
      UDWORD                  player, inc, i, j;
      DROID           *psCurr;
      UWORD           missionScrollMinX = 0, missionScrollMinY = 0,
                      missionScrollMaxX = 0, missionScrollMaxY = 0;

      /* Stop the game clock */
      gameTimeStop();

      if ((gameType == GTYPE_SAVE_START) ||
            (gameType == GTYPE_SAVE_MIDMISSION))
      {
            gameTimeReset(savedGameTime);//added 14 may 98 JPS to solve kev's problem with no firing droids
            //need to reset the event timer too - AB 14/01/99
            eventTimeReset(savedGameTime/SCR_TICKRATE);
      }

      /* Clear all the objects off the map and free up the map memory */
      proj_FreeAllProjectiles();    //always clear this
      if (freeMem)
      {
            //clear out the audio
            audio_StopAll();

            freeAllDroids();
            freeAllStructs();
            freeAllFeatures();

      //    droidTemplateShutDown();
            if (psMapTiles)
            {
//                free(psMapTiles);
            }
            //clear all the messages?
            releaseAllProxDisp();
      }


      if (!keepObjects)
      {
            //initialise the lists
            for (player = 0; player < MAX_PLAYERS; player++)
            {
                  apsDroidLists[player] = NULL;
                  apsStructLists[player] = NULL;
                  apsFeatureLists[player] = NULL;
                  apsFlagPosLists[player] = NULL;
                  //clear all the messages?
                  apsProxDisp[player] = NULL;
            }
            initFactoryNumFlag();
      }

      if (UserSaveGame)//always !keepObjects
      {
            //initialise the lists
            for (player = 0; player < MAX_PLAYERS; player++)
            {
                  apsLimboDroids[player] = NULL;
                  mission.apsDroidLists[player] = NULL;
                  mission.apsStructLists[player] = NULL;
                  mission.apsFeatureLists[player] = NULL;
                  mission.apsFlagPosLists[player] = NULL;
            }

            //JPS 25 feb
            //initialise upgrades
            //initialise the structure upgrade arrays
            memset(asStructureUpgrade, 0, MAX_PLAYERS * sizeof(STRUCTURE_UPGRADE));
            memset(asWallDefenceUpgrade, 0, MAX_PLAYERS * sizeof(WALLDEFENCE_UPGRADE));
            memset(asResearchUpgrade, 0, MAX_PLAYERS * sizeof(RESEARCH_UPGRADE));
            memset(asPowerUpgrade, 0, MAX_PLAYERS * sizeof(POWER_UPGRADE));
            memset(asRepairFacUpgrade, 0, MAX_PLAYERS * sizeof(REPAIR_FACILITY_UPGRADE));
            memset(asProductionUpgrade, 0, MAX_PLAYERS * NUM_FACTORY_TYPES * sizeof(PRODUCTION_UPGRADE));
            memset(asReArmUpgrade, 0, MAX_PLAYERS * sizeof(REARM_UPGRADE));

            //initialise the upgrade structures
            memset(asWeaponUpgrade, 0, MAX_PLAYERS * NUM_WEAPON_SUBCLASS * sizeof(WEAPON_UPGRADE));
            memset(asSensorUpgrade, 0, MAX_PLAYERS * sizeof(SENSOR_UPGRADE));
            memset(asECMUpgrade, 0, MAX_PLAYERS * sizeof(ECM_UPGRADE));
            memset(asRepairUpgrade, 0, MAX_PLAYERS * sizeof(REPAIR_UPGRADE));
            memset(asBodyUpgrade, 0, MAX_PLAYERS * sizeof(BODY_UPGRADE) * BODY_TYPE);
            //JPS 25 feb
      }


      if (saveGameVersion >= VERSION_11)
      {
            //camera position
            disp3d_setView(&(saveGameData.currentPlayerPos));
      }
      else
      {
            disp3d_resetView();
      }

      //Stuff added after level load to avoid being reset or initialised during load
      if (UserSaveGame)//always !keepObjects
      {
            if (saveGameVersion >= VERSION_12)
            {
                  mission.startTime = saveGameData.missionTime;
            }

            //set the scroll varaibles
            startX = saveGameData.ScrollMinX;
            startY = saveGameData.ScrollMinY;
            width = saveGameData.ScrollMaxX - saveGameData.ScrollMinX;
            height = saveGameData.ScrollMaxY - saveGameData.ScrollMinY;
            gameType = saveGameData.GameType;

            if (saveGameVersion >= VERSION_14)
            {
                  //mission data
                  mission.time            =     saveGameData.missionOffTime;
                  mission.ETA             =     saveGameData.missionETA;
                  mission.homeLZ_X  =     saveGameData.missionHomeLZ_X;
                  mission.homeLZ_Y  =     saveGameData.missionHomeLZ_Y;
                  mission.playerX         =     saveGameData.missionPlayerX;
                  mission.playerY         =     saveGameData.missionPlayerY;
                  //mission data
                  for (player = 0; player < MAX_PLAYERS; player++)
                  {
                        aDefaultSensor[player]                    = saveGameData.aDefaultSensor[player];
                        aDefaultECM[player]                             = saveGameData.aDefaultECM[player];
                        aDefaultRepair[player]                    = saveGameData.aDefaultRepair[player];
                        //check for self repair having been set
                        if (aDefaultRepair[player] != 0
                         && asRepairStats[aDefaultRepair[player]].location == LOC_DEFAULT)
                        {
                              enableSelfRepair((UBYTE)player);
                        }
                        mission.iTranspEntryTileX[player]   = saveGameData.iTranspEntryTileX[player];
                        mission.iTranspEntryTileY[player]   = saveGameData.iTranspEntryTileY[player];
                        mission.iTranspExitTileX[player]    = saveGameData.iTranspExitTileX[player];
                        mission.iTranspExitTileY[player]    = saveGameData.iTranspExitTileY[player];
                  }
            }

            if (saveGameVersion >= VERSION_15)//V21
            {
                  PIELIGHT colour;

                  offWorldKeepLists = saveGameData.offWorldKeepLists;
                  setRubbleTile(saveGameData.RubbleTile);
                  setUnderwaterTile(saveGameData.WaterTile);
                  if (saveGameData.fogState == 0)//no fog
                  {
                        pie_EnableFog(false);
                        fogStatus = 0;
                  }
                  else if (saveGameData.fogState == 1)//fog using old code assume background and depth
                  {
                        if (war_GetFog())
                        {
                              pie_EnableFog(true);
                        }
                        else
                        {
                              pie_EnableFog(false);
                        }
                        fogStatus = FOG_BACKGROUND + FOG_DISTANCE;
                  }
                  else//version 18+ fog
                  {
                        if (war_GetFog())
                        {
                              pie_EnableFog(true);
                        }
                        else
                        {
                              pie_EnableFog(false);
                        }
                        fogStatus = saveGameData.fogState;
                        fogStatus &= FOG_FLAGS;
                  }
                  colour.rgba = saveGameData.fogColour;
                  pie_SetFogColour(colour);
            }
            if (saveGameVersion >= VERSION_19)//V21
            {
                  for(i=0; i<MAX_PLAYERS; i++)
                  {
                        for(j=0; j<MAX_PLAYERS; j++)
                        {
                              alliances[i][j] = saveGameData.alliances[i][j];
                        }
                  }
                  for(i=0; i<MAX_PLAYERS; i++)
                  {
                        setPlayerColour(i,saveGameData.playerColour[i]);
                  }
                  SetRadarZoom(saveGameData.radarZoom);
            }

            if (saveGameVersion >= VERSION_20)//V21
            {
                  setDroidsToSafetyFlag(saveGameData.bDroidsToSafetyFlag);
                  for (inc = 0; inc < MAX_PLAYERS; inc++)
                  {
                        memcpy(&asVTOLReturnPos[inc], &(saveGameData.asVTOLReturnPos[inc]), sizeof(Vector2i));
                  }
            }

            if (saveGameVersion >= VERSION_22)//V22
            {
                  for (inc = 0; inc < MAX_PLAYERS; inc++)
                  {
                        memcpy(&asRunData[inc], &(saveGameData.asRunData[inc]), sizeof(RUN_DATA));
                  }
            }

            if (saveGameVersion >= VERSION_24)//V24
            {
                  missionSetReinforcementTime(saveGameData.reinforceTime);
                  // horrible hack to catch savegames that were saving garbage into these fields
                  if (saveGameData.bPlayCountDown <= 1)
                  {
                        setPlayCountDown(saveGameData.bPlayCountDown);
                  }
                  if (saveGameData.bPlayerHasWon <= 1)
                  {
                        setPlayerHasWon(saveGameData.bPlayerHasWon);
                  }
                  if (saveGameData.bPlayerHasLost <= 1)
                  {
                        setPlayerHasLost(saveGameData.bPlayerHasLost);
                  }
/*                setPlayCountDown(saveGameData.bPlayCountDown);
                  setPlayerHasWon(saveGameData.bPlayerHasWon);
                  setPlayerHasLost(saveGameData.bPlayerHasLost);*/
            }

            if (saveGameVersion >= VERSION_27)//V27
            {
                  for (player = 0; player < MAX_PLAYERS; player++)
                  {
                        for (inc = 0; inc < MAX_RECYCLED_DROIDS; inc++)
                        {
                              aDroidExperience[player][inc] = saveGameData.awDroidExperience[player][inc];
                        }
                  }
            }
            else
            {
                  for (player = 0; player < MAX_PLAYERS; player++)
                  {
                        for (inc = 0; inc < MAX_RECYCLED_DROIDS; inc++)
                        {
                              aDroidExperience[player][inc] = saveGameData.aDroidExperience[player][inc];
                        }
                  }
            }
            if (saveGameVersion >= VERSION_30)
            {
                  scrGameLevel = saveGameData.scrGameLevel;
                  bExtraVictoryFlag = saveGameData.bExtraVictoryFlag;
                  bExtraFailFlag = saveGameData.bExtraFailFlag;
                  bTrackTransporter = saveGameData.bTrackTransporter;
            }

            //extra code added for the first patch (v1.1) to save out if mission time is not being counted
            if (saveGameVersion >= VERSION_31)
            {
                  //mission data
                  mission.cheatTime = saveGameData.missionCheatTime;
            }

            // skirmish saves.
            if (saveGameVersion >= VERSION_33)
            {
                  PLAYERSTATS       playerStats;

                  game              = saveGameData.sGame;
                  NetPlay                 = saveGameData.sNetPlay;
                  selectedPlayer    = saveGameData.savePlayer;
                  productionPlayer= selectedPlayer;
                  bMultiPlayer      = saveGameData.multiPlayer;
                  cmdDroidMultiExpBoost(true);
                  for(inc=0;inc<MAX_PLAYERS;inc++)
                  {
                        player2dpid[inc]=saveGameData.sPlayer2dpid[inc];
                  }
                  if(bMultiPlayer)
                  {
                        loadMultiStats(saveGameData.sPName,&playerStats);                       // stats stuff
                        setMultiStats(NetPlay.dpidPlayer,playerStats,false);
                        setMultiStats(NetPlay.dpidPlayer,playerStats,true);
                  }
            }


      }

      /* Get human and AI players names */
      if (saveGameVersion >= VERSION_34)
      {
            for(i=0;i<MAX_PLAYERS;i++)
            {
                  (void)setPlayerName(i, saveGameData.sPlayerName[i]);
            }
      }

      //clear the player Power structs
      if ((gameType != GTYPE_SAVE_START) && (gameType != GTYPE_SAVE_MIDMISSION) &&
            (!keepObjects))
      {
            clearPlayerPower();
      }

      //initialise the scroll values
      //startX = startY = width = height = 0;

      //before loading the data - turn power off so don't get any power low warnings
      powerCalculated = false;

      /* Load in the chosen file data */
      strcpy(aFileName, pGameToLoad);
      fileExten = strlen(aFileName) - 3;              // hack - !
      aFileName[fileExten - 1] = '\0';
      strcat(aFileName, "/");

      //the terrain type WILL only change with Campaign changes (well at the moment!)
      //if (freeMem) - this now works for Cam Start and Cam Change
      if (gameType != GTYPE_SCENARIO_EXPAND
       || UserSaveGame)
      {
            LOADBARCALLBACK();      //          loadingScreenCallback();
            //load in the terrain type map
            aFileName[fileExten] = '\0';
            strcat(aFileName, "ttypes.ttp");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail23\n" );
                  goto error;
            }


            //load the terrain type data
            if (pFileData)
            {
                  if (!loadTerrainTypeMap(pFileData, fileSize))
                  {
                        debug( LOG_NEVER, "loadgame: Fail25\n" );
                        goto error;
                  }
            }
      }

      //load up the Droid Templates BEFORE any structures are loaded
      LOADBARCALLBACK();      //    loadingScreenCallback();
      if (IsScenario==false)
      {
            //NOT ANY MORE - use multiPlayerID (unique template id) to prevent duplicate's being loaded
            //Only want to clear the lists in the final version of the game

//#ifdef FINAL
            //first clear the templates
//          droidTemplateShutDown();
            // only free player 0 templates - keep all the others from the stats
//#define ALLOW_ACCESS_TEMPLATES
#ifndef ALLOW_ACCESS_TEMPLATES
            {
                  DROID_TEMPLATE    *pTemplate, *pNext;
                  for(pTemplate = apsDroidTemplates[0]; pTemplate != NULL;
                        pTemplate = pNext)
                  {
                        pNext = pTemplate->psNext;
                        free(pTemplate);
                  }
                  apsDroidTemplates[0] = NULL;
            }
#endif

            // In Multiplayer, clear templates out first.....
            if(   bMultiPlayer)
            {
                  for(inc=0;inc<MAX_PLAYERS;inc++)
                  {
                        while(apsDroidTemplates[inc])                   // clear the old template out.
                        {
                              DROID_TEMPLATE    *psTempl;
                              psTempl = apsDroidTemplates[inc]->psNext;
                              free(apsDroidTemplates[inc]);
                              apsDroidTemplates[inc] = psTempl;
                        }
                  }
            }

//load in the templates
            LOADBARCALLBACK();      //    loadingScreenCallback();
            aFileName[fileExten] = '\0';
            strcat(aFileName, "templ.bjo");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail20\n" );
                  goto error;
            }
            //load the data into apsTemplates
            if (!loadSaveTemplate(pFileData, fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail22\n" );
                  goto error;
            }
            //JPS 25 feb (reverse templates moved from here)
      }

      if (saveGameOnMission && UserSaveGame)
      {
            LOADBARCALLBACK();      //          loadingScreenCallback();

            //the scroll limits for the mission map have already been written
            if (saveGameVersion >= VERSION_29)
            {
                  missionScrollMinX = (UWORD)mission.scrollMinX;
                  missionScrollMinY = (UWORD)mission.scrollMinY;
                  missionScrollMaxX = (UWORD)mission.scrollMaxX;
                  missionScrollMaxY = (UWORD)mission.scrollMaxY;
            }

            //load the map and the droids then swap pointers
//          psMapTiles = NULL;
            //load in the map file
            aFileName[fileExten] = '\0';
            strcat(aFileName, "mission.map");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (loadFileToBufferNoError(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  if (!mapLoad(pFileData, fileSize))
                  {
                        debug( LOG_NEVER, "loadgame: Fail7\n" );
                        return(false);
                  }
            }

            //load in the visibility file
            aFileName[fileExten] = '\0';
            strcat(aFileName, "misvis.bjo");

            // Load in the visibility data from the chosen file
            if (!readVisibilityData(aFileName))
            {
                  debug( LOG_NEVER, "loadgame: Fail33\n" );
                  goto error;
            }

      // reload the objects that were in the mission list
            LOADBARCALLBACK();      //          loadingScreenCallback();
            //load in the features -do before the structures
            aFileName[fileExten] = '\0';
            strcat(aFileName, "mfeat.bjo");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail14\n" );
                  goto error;
            }

            //load the data into apsFeatureLists
            if (!loadSaveFeature(pFileData, fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail16\n" );
                  goto error;
            }

            //load in the mission structures
            initStructLimits();
            aFileName[fileExten] = '\0';
            strcat(aFileName, "mstruct.bjo");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail17\n" );
                  goto error;
            }

            //load the data into apsStructLists
            if (!loadSaveStructure(pFileData, fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail19\n" );
                  goto error;
            }

            LOADBARCALLBACK();      //          loadingScreenCallback();

            if (bMultiPlayer)
            {
                  for(pl=0;pl<MAX_PLAYERS;pl++)// ajl. must do for every player to stop multiplay/pc players going gaga.
                  {
                        //reverse the structure lists so the Research Facilities are in the same order as when saved
                        reverseObjectList((BASE_OBJECT**)&apsStructLists[pl]);
                  }
            }

            LOADBARCALLBACK();      //          loadingScreenCallback();
            //load in the mission droids
            aFileName[fileExten] = '\0';

            if (saveGameVersion < VERSION_27)//V27
            {
                  strcat(aFileName, "mdroid.bjo");
            }
            else
            {
                  strcat(aFileName, "munit.bjo");
            }
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (loadFileToBufferNoError(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  //load the data into mission.apsDroidLists
                  if (!loadSaveDroid(pFileData, fileSize, apsDroidLists))
                  {
                        debug( LOG_NEVER, "loadgame: Fail12\n" );
                        goto error;
                  }
            }

            /* after we've loaded in the units we need to redo the orientation because
             * the direction may have been saved - we need to do it outside of the loop
             * whilst the current map is valid for the units
             */
            for (player = 0; player < MAX_PLAYERS; ++player)
            {
                  for (psCurr = apsDroidLists[player]; psCurr != NULL; psCurr = psCurr->psNext)
                  {
                        if (psCurr->droidType != DROID_PERSON
                        // && psCurr->droidType != DROID_CYBORG
                         && !cyborgDroid(psCurr)
                         && psCurr->droidType != DROID_TRANSPORTER
                         && psCurr->pos.x != INVALID_XY)
                        {
                              updateDroidOrientation(psCurr);
                        }
                  }
            }

            LOADBARCALLBACK();      //          loadingScreenCallback();
            //load in the flag list file
            aFileName[fileExten] = '\0';
            strcat(aFileName, "mflagstate.bjo");
            // Load in the chosen file data
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadMissionExtras: Fail 3\n" );
                  return false;
            }


            //load the flag status data
            if (pFileData)
            {
                  if (!loadSaveFlag(pFileData, fileSize))
                  {
                        debug( LOG_NEVER, "loadMissionExtras: Fail 4\n" );
                        return false;
                  }
            }
            swapMissionPointers();

            //once the mission map has been loaded reset the mission scroll limits
            if (saveGameVersion >= VERSION_29)
            {
                  mission.scrollMinX = missionScrollMinX;
                  mission.scrollMinY = missionScrollMinY;
                  mission.scrollMaxX = missionScrollMaxX;
                  mission.scrollMaxY = missionScrollMaxY;
            }
      }


      //if Campaign Expand then don't load in another map
      if (gameType != GTYPE_SCENARIO_EXPAND)
      {
            LOADBARCALLBACK();      //          loadingScreenCallback();
            psMapTiles = NULL;
            //load in the map file
            aFileName[fileExten] = '\0';
            strcat(aFileName, "game.map");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail5\n" );
                  goto error;
            }

            if (!mapLoad(pFileData, fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail7\n" );
                  return(false);
            }
      }

      //save game stuff added after map load

      LOADBARCALLBACK();      //    loadingScreenCallback();
      if (saveGameVersion >= VERSION_16)
      {
            for (inc = 0; inc < MAX_NOGO_AREAS; inc++)
            {
                  setNoGoArea(saveGameData.sLandingZone[inc].x1, saveGameData.sLandingZone[inc].y1,
                                    saveGameData.sLandingZone[inc].x2, saveGameData.sLandingZone[inc].y2, (UBYTE)inc);
            }
      }



      //adjust the scroll range for the new map or the expanded map
      setMapScroll();

      //initialise the Templates' build and power points before loading in any droids
      initTemplatePoints();

      //if user save game then load up the research BEFORE any droids or structures are loaded
      if ((gameType == GTYPE_SAVE_START) ||
            (gameType == GTYPE_SAVE_MIDMISSION))
      {
            LOADBARCALLBACK();      //          loadingScreenCallback();
            //load in the research list file
            aFileName[fileExten] = '\0';
            strcat(aFileName, "resstate.bjo");
            // Load in the chosen file data
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail32\n" );
                  goto error;
            }

            //load the research status data
            if (pFileData)
            {
                  if (!loadSaveResearch(pFileData, fileSize))
                  {
                        debug( LOG_NEVER, "loadgame: Fail33\n" );
                        goto error;
                  }
            }
      }

      if(IsScenario==true)
      {
            LOADBARCALLBACK();      //          loadingScreenCallback();
            //load in the droid initialisation file
            aFileName[fileExten] = '\0';
            strcat(aFileName, "dinit.bjo");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail8\n" );
                  goto error;
            }


            if(!loadSaveDroidInit(pFileData,fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail10\n" );
                  goto error;
            }
      }
      else
      {
            LOADBARCALLBACK();      //          loadingScreenCallback();
            //load in the droids
            aFileName[fileExten] = '\0';
            if (saveGameVersion < VERSION_27)//V27
            {
                  strcat(aFileName, "droid.bjo");
            }
            else
            {
                  strcat(aFileName, "unit.bjo");
            }
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail11\n" );
                  goto error;
            }

            //load the data into apsDroidLists
            if (!loadSaveDroid(pFileData, fileSize, apsDroidLists))
            {
                  debug( LOG_NEVER, "loadgame: Fail12\n" );
                  goto error;
            }

            /* after we've loaded in the units we need to redo the orientation because
             * the direction may have been saved - we need to do it outside of the loop
             * whilst the current map is valid for the units
             */
            for (player = 0; player < MAX_PLAYERS; ++player)
            {
                  for (psCurr = apsDroidLists[player]; psCurr != NULL; psCurr = psCurr->psNext)
                  {
                        if (psCurr->droidType != DROID_PERSON
                        // && psCurr->droidType != DROID_CYBORG
                         && !cyborgDroid(psCurr)
                         && psCurr->droidType != DROID_TRANSPORTER
                         && psCurr->pos.x != INVALID_XY)
                        {
                              updateDroidOrientation(psCurr);
                        }
                  }
            }

            LOADBARCALLBACK();      //          loadingScreenCallback();
            if (saveGameVersion >= 12)
            {
                  if (!saveGameOnMission)
                  {
                        //load in the mission droids
                        aFileName[fileExten] = '\0';
                        if (saveGameVersion < VERSION_27)//V27
                        {
                              strcat(aFileName, "mdroid.bjo");
                        }
                        else
                        {
                              strcat(aFileName, "munit.bjo");
                        }
                        /* Load in the chosen file data */
                        pFileData = fileLoadBuffer;
                        if (loadFileToBufferNoError(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize)) {
                              //load the data into mission.apsDroidLists
                              if (!loadSaveDroid(pFileData, fileSize, mission.apsDroidLists))
                              {
                                    debug( LOG_NEVER, "loadgame: Fail12\n" );
                                    goto error;
                              }
                        }

                  }
            }
      }

      LOADBARCALLBACK();      //    loadingScreenCallback();
      if (saveGameVersion >= VERSION_23)
      {
            //load in the limbo droids
            aFileName[fileExten] = '\0';
            strcat(aFileName, "limbo.bjo");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (loadFileToBufferNoError(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  //load the data into apsDroidLists
                  if (!loadSaveDroid(pFileData, fileSize, apsLimboDroids))
                  {
                        debug( LOG_NEVER, "loadgame: Fail12\n" );
                        goto error;
                  }
            }

      }

      LOADBARCALLBACK();      //    loadingScreenCallback();
      //load in the features -do before the structures
      aFileName[fileExten] = '\0';
      strcat(aFileName, "feat.bjo");
      /* Load in the chosen file data */
      pFileData = fileLoadBuffer;
      if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
      {
            debug( LOG_NEVER, "loadgame: Fail14\n" );
            goto error;
      }


      //load the data into apsFeatureLists
      if (!loadSaveFeature(pFileData, fileSize))
      {
            debug( LOG_NEVER, "loadgame: Fail16\n" );
            goto error;
      }

      //load droid templates moved from here to BEFORE any structures loaded in

      //load in the structures
      LOADBARCALLBACK();      //    loadingScreenCallback();
      initStructLimits();
      aFileName[fileExten] = '\0';
      strcat(aFileName, "struct.bjo");
      /* Load in the chosen file data */
      pFileData = fileLoadBuffer;
      if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
      {
            debug( LOG_NEVER, "loadgame: Fail17\n" );
            goto error;
      }
      //load the data into apsStructLists
      if (!loadSaveStructure(pFileData, fileSize))
      {
            debug( LOG_NEVER, "loadgame: Fail19\n" );
            goto error;
      }

      LOADBARCALLBACK();      //    loadingScreenCallback();
      if ((gameType == GTYPE_SAVE_START) ||
            (gameType == GTYPE_SAVE_MIDMISSION))
      {
            for(pl=0;pl<MAX_PLAYERS;pl++) // ajl. must do for every player to stop multiplay/pc players going gaga.
            {
                  //reverse the structure lists so the Research Facilities are in the same order as when saved
                  reverseObjectList((BASE_OBJECT**)&apsStructLists[pl]);
            }
      }

      //if user save game then load up the current level for structs and components
      if ((gameType == GTYPE_SAVE_START) ||
            (gameType == GTYPE_SAVE_MIDMISSION))
      {
            LOADBARCALLBACK();      //          loadingScreenCallback();
            //load in the component list file
            aFileName[fileExten] = '\0';
            strcat(aFileName, "compl.bjo");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail26\n" );
                  goto error;
            }


            //load the component list data
            if (pFileData)
            {
                  if (!loadSaveCompList(pFileData, fileSize))
                  {
                        debug( LOG_NEVER, "loadgame: Fail28\n" );
                        goto error;
                  }
            }
            LOADBARCALLBACK();      //          loadingScreenCallback();
            //load in the structure type list file
            aFileName[fileExten] = '\0';
            strcat(aFileName, "strtype.bjo");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail29\n" );
                  goto error;
            }


            //load the structure type list data
            if (pFileData)
            {
                  if (!loadSaveStructTypeList(pFileData, fileSize))
                  {
                        debug( LOG_NEVER, "loadgame: Fail31\n" );
                        goto error;
                  }
            }
      }

      LOADBARCALLBACK();      //    loadingScreenCallback();

      if (saveGameVersion >= VERSION_11)
      {
            //if user save game then load up the Visibility
            if ((gameType == GTYPE_SAVE_START) ||
                  (gameType == GTYPE_SAVE_MIDMISSION))
            {
                  //load in the visibility file
                  aFileName[fileExten] = '\0';
                  strcat(aFileName, "visstate.bjo");

                  // Load in the visibility data from the chosen file
                  if (!readVisibilityData(aFileName))
                  {
                        debug( LOG_NEVER, "loadgame: Fail33\n" );
                        goto error;
                  }
            }
      }

      LOADBARCALLBACK();      //    loadingScreenCallback();

      if (saveGameVersion > VERSION_12)
      {
            //if user save game then load up the Visibility
            if ((gameType == GTYPE_SAVE_START) ||
                  (gameType == GTYPE_SAVE_MIDMISSION))
            {
                  //load in the message list file
                  aFileName[fileExten] = '\0';
                  strcat(aFileName, "prodstate.bjo");
                  // Load in the chosen file data
                  pFileData = fileLoadBuffer;
                  if (loadFileToBufferNoError(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
                  {
                        //load the visibility data
                        if (pFileData)
                        {
                              if (!loadSaveProduction(pFileData, fileSize))
                              {
                                    debug( LOG_NEVER, "loadgame: Fail33\n" );
                                    goto error;
                              }
                        }
                  }

            }
      }
      LOADBARCALLBACK();      //    loadingScreenCallback();

      if (saveGameVersion > VERSION_12)
      {
            //if user save game then load up the FX
            if ((gameType == GTYPE_SAVE_START) ||
                  (gameType == GTYPE_SAVE_MIDMISSION))
            {
                  //load in the message list file
                  aFileName[fileExten] = '\0';
                  strcat(aFileName, "fxstate.tag");

                  // load the fx data from the file
                  if (!readFXData(aFileName))
                  {
                        debug(LOG_ERROR, "loadgame: Fail33");
                        goto error;
                  }
            }
      }

      LOADBARCALLBACK();      //    loadingScreenCallback();

      if (saveGameVersion >= VERSION_16)
      {
            //if user save game then load up the FX
            if ((gameType == GTYPE_SAVE_START) ||
                  (gameType == GTYPE_SAVE_MIDMISSION))
            {
                  //load in the message list file
                  aFileName[fileExten] = '\0';
                  strcat(aFileName, "score.tag");

                  // Load the fx data from the chosen file
                  if (!readScoreData(aFileName))
                  {
                        debug( LOG_NEVER, "loadgame: Fail33\n" );
                        goto error;
                  }
            }
      }

      LOADBARCALLBACK();      //    loadingScreenCallback();

      if (saveGameVersion >= VERSION_12)
      {
            //if user save game then load up the flags AFTER any droids or structures are loaded
            if ((gameType == GTYPE_SAVE_START) ||
                  (gameType == GTYPE_SAVE_MIDMISSION))
            {
                  //load in the flag list file
                  aFileName[fileExten] = '\0';
                  strcat(aFileName, "flagstate.bjo");
                  // Load in the chosen file data
                  pFileData = fileLoadBuffer;
                  if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
                  {
                        debug( LOG_NEVER, "loadMissionExtras: Fail 3\n");
                        return false;
                  }


                  //load the flag status data
                  if (pFileData)
                  {
                        if (!loadSaveFlag(pFileData, fileSize))
                        {
                              debug( LOG_NEVER, "loadMissionExtras: Fail 4\n" );
                              return false;
                        }
                  }
            }
      }

      LOADBARCALLBACK();      //    loadingScreenCallback();

      if (saveGameVersion >= VERSION_21)
      {
            //rebuild the apsCommandDesignation AFTER all droids and structures are loaded
            if ((gameType == GTYPE_SAVE_START) ||
                  (gameType == GTYPE_SAVE_MIDMISSION))
            {
                  //load in the command list file
                  aFileName[fileExten] = '\0';
                  strcat(aFileName, "firesupport.tag");

                  if (!readFiresupportDesignators(aFileName))
                  {
                        debug( LOG_NEVER, "loadMissionExtras: readFiresupportDesignators(%s) failed\n", aFileName );
                        return false;
                  }
            }
      }

      LOADBARCALLBACK();      //    loadingScreenCallback();

      if ((saveGameVersion >= VERSION_15) && UserSaveGame)
      {
            //load in the mission structures
            aFileName[fileExten] = '\0';
            strcat(aFileName, "limits.bjo");
            /* Load in the chosen file data */
            pFileData = fileLoadBuffer;
            if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail17\n" );
                  goto error;
            }

            //load the data into apsStructLists
            if (!loadSaveStructLimits(pFileData, fileSize))
            {
                  debug( LOG_NEVER, "loadgame: Fail19\n" );
                  goto error;
            }

            //set up the structure Limits
            setCurrentStructQuantity(false);
      }
      else
      {
            //set up the structure Limits
            setCurrentStructQuantity(true);
      }


      LOADBARCALLBACK();      //    loadingScreenCallback();

      //check that delivery points haven't been put down in invalid location
      checkDeliveryPoints(saveGameVersion);

      if ((gameType == GTYPE_SAVE_START) ||
            (gameType == GTYPE_SAVE_MIDMISSION))
      {
            LOADBARCALLBACK();      //    loadingScreenCallback();
            for(pl=0;pl<MAX_PLAYERS;pl++) // ajl. must do for every player to stop multiplay/pc players going gaga.
            {
                  //reverse the structure lists so the Research Facilities are in the same order as when saved
                  reverseTemplateList((DROID_TEMPLATE**)&apsDroidTemplates[pl]);
            }

            LOADBARCALLBACK();      //          loadingScreenCallback();
            for(pl=0;pl<MAX_PLAYERS;pl++)
            {
                  //reverse the droid lists so selections occur in the same order
                  reverseObjectList((BASE_OBJECT**)&apsLimboDroids[pl]);
            }

            LOADBARCALLBACK();      //          loadingScreenCallback();
            for(pl=0;pl<MAX_PLAYERS;pl++)
            {
                  //reverse the droid lists so selections occur in the same order
                  reverseObjectList((BASE_OBJECT**)&apsDroidLists[pl]);
            }

            LOADBARCALLBACK();      //          loadingScreenCallback();
            for(pl=0;pl<MAX_PLAYERS;pl++)
            {
                  //reverse the droid lists so selections occur in the same order
                  reverseObjectList((BASE_OBJECT**)&mission.apsDroidLists[pl]);
            }

            LOADBARCALLBACK();      //          loadingScreenCallback();
            for(pl=0;pl<MAX_PLAYERS;pl++)
            {
                  //reverse the struct lists so selections occur in the same order
                  reverseObjectList((BASE_OBJECT**)&mission.apsStructLists[pl]);
            }

            LOADBARCALLBACK();      //          loadingScreenCallback();
            for(pl=0;pl<MAX_PLAYERS;pl++)
            {
                  //reverse the droid lists so selections occur in the same order
                  reverseObjectList((BASE_OBJECT**)&apsFeatureLists[pl]);
            }

            LOADBARCALLBACK();      //          loadingScreenCallback();
            for(pl=0;pl<MAX_PLAYERS;pl++)
            {
                  //reverse the droid lists so selections occur in the same order
                  reverseObjectList((BASE_OBJECT**)&mission.apsFeatureLists[pl]);
            }
      }

      //turn power on for rest of game
      powerCalculated = true;

      LOADBARCALLBACK();      //    loadingScreenCallback();

      if (saveGameVersion > VERSION_12)
      {
            if (!keepObjects)//only reset the pointers if they were set
            {
                  //reset the object pointers in the droid target lists
                  loadDroidSetPointers();
            }
      }

      LOADBARCALLBACK();      //    loadingScreenCallback();

      if (saveGameVersion > VERSION_20)
      {
            if (!keepObjects)//only reset the pointers if they were set
            {
                  //reset the object pointers in the structure lists
                  loadStructSetPointers();
            }
      }

      //if user save game then reset the time - THIS SETS BOTH TIMERS - BEWARE IF YOU USE IT
      if ((gameType == GTYPE_SAVE_START) ||
            (gameType == GTYPE_SAVE_MIDMISSION))
      {
            ASSERT( gameTime == savedGameTime,"loadGame; game time modified during load" );
            gameTimeReset(savedGameTime);//added 14 may 98 JPS to solve kev's problem with no firing droids
            //need to reset the event timer too - AB 14/01/99
            eventTimeReset(savedGameTime/SCR_TICKRATE);

            //reset the objId for new objects
            if (saveGameVersion >= VERSION_17)
            {
                  objID = savedObjId;
            }
      }

      //check the research button isn't flashing unnecessarily
      //cancel first
      stopReticuleButtonFlash(IDRET_RESEARCH);
      //then see if needs to be set
      intCheckResearchButton();

      //set up the mission countdown flag
      setMissionCountDown();

      /* Start the game clock */
      gameTimeStart();

      //after the clock has been reset need to check if any res_extractors are active
      checkResExtractorsActive();

//    if (multiPlayerInUse)
//    {
//          bMultiPlayer = true;                      // reenable multi player messages.
//          multiPlayerInUse = false;
//    }
//    initViewPosition();

      //check if limbo_expand mission has changed to an expand mission for user save game (mid-mission)
      if (gameType == GTYPE_SAVE_MIDMISSION && missionLimboExpand())
      {
            /* when all the units have moved from the mission.apsDroidList then the
             * campaign has been reset to an EXPAND type - OK so there should have
             * been another flag to indicate this state has changed but its late in
             * the day excuses...excuses...excuses
             */
            if (mission.apsDroidLists[selectedPlayer] == NULL)
            {
                  //set the mission type
                  startMissionSave(LDS_EXPAND);
            }
      }

      //set this if come into a save game mid mission
      if (gameType == GTYPE_SAVE_MIDMISSION)
      {
            setScriptWinLoseVideo(PLAY_NONE);
      }

      //need to clear before setting up
      clearMissionWidgets();
      //put any widgets back on for the missions
      resetMissionWidgets();

      debug( LOG_NEVER, "loadGame: done\n" );

      return true;

error:
            debug( LOG_NEVER, "loadgame: ERROR\n" );

      /* Clear all the objects off the map and free up the map memory */
      freeAllDroids();
      freeAllStructs();
      freeAllFeatures();
      droidTemplateShutDown();
      if (psMapTiles)
      {
//          free(psMapTiles);
      }
      psMapTiles = NULL;

      /*if (!loadFile("blank.map", &pFileData, &fileSize))
      {
            return false;
      }

      if (!mapLoad(pFileData, fileSize))
      {
            return false;
      }

      free(pFileData);*/

      /* Start the game clock */
      gameTimeStart();
//    if (multiPlayerInUse)
//    {
//          bMultiPlayer = true;                      // reenable multi player messages.
//          multiPlayerInUse = false;
//    }

      return false;
}
// -----------------------------------------------------------------------------------------

// Modified by AlexL , now takes a filename, with no popup....
BOOL saveGame(char *aFileName, SDWORD saveType)
{
      UDWORD                  fileExtension;
      DROID             *psDroid, *psNext;

      ASSERT(aFileName && strlen(aFileName) > 4, "Bad savegame filename");
      if (!aFileName || strlen(aFileName) < 4)
      {
            return false;
      }

      debug(LOG_WZ, "saveGame: %s", aFileName);

      fileExtension = strlen(aFileName) - 3;
      gameTimeStop();

      /* Write the data to the file */
      if (!writeGameFile(aFileName, saveType))
      {
            debug(LOG_ERROR, "saveGame: writeGameFile(\"%s\") failed", aFileName);
            goto error;
      }

      //remove the file extension
      aFileName[strlen(aFileName) - 4] = '\0';

      //create dir will fail if directory already exists but don't care!
      (void) PHYSFS_mkdir(aFileName);

      //save the map file
      strcat(aFileName, "/game.map");
      /* Write the data to the file */
      if (!writeMapFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeMapFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the droids filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "unit.bjo");
      /*Write the current droid lists to the file*/
      if (!writeDroidFile(aFileName,apsDroidLists))
      {
            debug(LOG_ERROR, "saveGame: writeDroidFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the structures filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "struct.bjo");
      /*Write the data to the file*/
      if (!writeStructFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeStructFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the templates filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "templ.bjo");
      /*Write the data to the file*/
      if (!writeTemplateFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeTemplateFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the features filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "feat.bjo");
      /*Write the data to the file*/
      if (!writeFeatureFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeFeatureFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the terrain types filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "ttypes.ttp");
      /*Write the data to the file*/
      if (!writeTerrainTypeMapFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeTerrainTypeMapFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the strucutLimits filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "limits.bjo");
      /*Write the data to the file*/
      if (!writeStructLimitsFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeStructLimitsFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the component lists filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "compl.bjo");
      /*Write the data to the file*/
      if (!writeCompListFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeCompListFile(\"%s\") failed", aFileName);
            goto error;
      }
      //create the structure type lists filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "strtype.bjo");
      /*Write the data to the file*/
      if (!writeStructTypeListFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeStructTypeListFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the research filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "resstate.bjo");
      /*Write the data to the file*/
      if (!writeResearchFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeResearchFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the message filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "messtate.bjo");
      /*Write the data to the file*/
      if (!writeMessageFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeMessageFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the proximity message filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "proxstate.bjo");
      /*Write the data to the file*/
      if (!writeMessageFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeMessageFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the message filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "visstate.bjo");
      /*Write the data to the file*/
      if (!writeVisibilityData(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeVisibilityData(\"%s\") failed", aFileName);
            goto error;
      }

      //create the message filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "prodstate.bjo");
      /*Write the data to the file*/
      if (!writeProductionFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeProductionFile(\"%s\") failed", aFileName);
            goto error;
      }


      //create the message filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "fxstate.tag");
      /*Write the data to the file*/
      if (!writeFXData(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeFXData(\"%s\") failed", aFileName);
            goto error;
      }

      //added at V15 save
      //create the message filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "score.tag");
      /*Write the data to the file*/
      if (!writeScoreData(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeScoreData(\"%s\") failed", aFileName);
            goto error;
      }

      //create the message filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "flagstate.bjo");
      /*Write the data to the file*/
      if (!writeFlagFile(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeFlagFile(\"%s\") failed", aFileName);
            goto error;
      }

      //create the message filename
      aFileName[fileExtension] = '\0';
      strcat(aFileName, "firesupport.tag");
      /*Write the data to the file*/
      if (!writeFiresupportDesignators(aFileName))
      {
            debug(LOG_ERROR, "saveGame: writeFiresupportDesignators(\"%s\") failed", aFileName);
            goto error;
      }

      // save the script state if necessary
      if (saveType == GTYPE_SAVE_MIDMISSION)
      {
            aFileName[fileExtension-1] = '\0';
            strcat(aFileName, ".es");
            /*Write the data to the file*/
            if (!writeScriptState(aFileName))
            {
                  debug(LOG_ERROR, "saveGame: writeScriptState(\"%s\") failed", aFileName);
                  goto error;
            }
      }

      //create the droids filename
      aFileName[fileExtension-1] = '\0';
      strcat(aFileName, "/munit.bjo");
      /*Write the swapped droid lists to the file*/
      if (!writeDroidFile(aFileName, mission.apsDroidLists))
      {
            debug(LOG_ERROR, "saveGame: writeDroidFile(\"%s\") failed", aFileName);
            goto error;
      }

      //21feb now done always
      //create the limbo filename
      //clear the list
      if (saveGameVersion < VERSION_25)
      {
            for (psDroid = apsLimboDroids[selectedPlayer]; psDroid != NULL; psDroid = psNext)
            {
                  psNext = psDroid->psNext;
                  //limbo list invalidate XY
                  psDroid->pos.x = INVALID_XY;
                  psDroid->pos.y = INVALID_XY;
                  //this is mainly for VTOLs
                  setSaveDroidBase(psDroid, NULL);
                  psDroid->cluster = 0;
                  orderDroid(psDroid, DORDER_STOP);
            }
      }

      aFileName[fileExtension] = '\0';
      strcat(aFileName, "limbo.bjo");
      /*Write the swapped droid lists to the file*/
      if (!writeDroidFile(aFileName, apsLimboDroids))
      {
            debug(LOG_ERROR, "saveGame: writeDroidFile(\"%s\") failed", aFileName);
            goto error;
      }

      if (saveGameOnMission )
      {
            //mission save swap the mission pointers and save the changes
            swapMissionPointers();
            //now save the map and droids


            //save the map file
            aFileName[fileExtension] = '\0';
            strcat(aFileName, "mission.map");
            /* Write the data to the file */
            if (!writeMapFile(aFileName))
            {
                  debug(LOG_ERROR, "saveGame: writeMapFile(\"%s\") failed", aFileName);
                  goto error;
            }

            //save the map file
            aFileName[fileExtension] = '\0';
            strcat(aFileName, "misvis.bjo");
            /* Write the data to the file */
            if (!writeVisibilityData(aFileName))
            {
                  debug(LOG_ERROR, "saveGame: writeVisibilityData(\"%s\") failed", aFileName);
                  goto error;
            }

            //create the structures filename
            aFileName[fileExtension] = '\0';
            strcat(aFileName, "mstruct.bjo");
            /*Write the data to the file*/
            if (!writeStructFile(aFileName))
            {
                  debug(LOG_ERROR, "saveGame: writeStructFile(\"%s\") failed", aFileName);
                  goto error;
            }

            //create the features filename
            aFileName[fileExtension] = '\0';
            strcat(aFileName, "mfeat.bjo");
            /*Write the data to the file*/
            if (!writeFeatureFile(aFileName))
            {
                  debug(LOG_ERROR, "saveGame: writeFeatureFile(\"%s\") failed", aFileName);
                  goto error;
            }

            //create the message filename
            aFileName[fileExtension] = '\0';
            strcat(aFileName, "mflagstate.bjo");
            /*Write the data to the file*/
            if (!writeFlagFile(aFileName))
            {
                  debug(LOG_ERROR, "saveGame: writeFlagFile(\"%s\") failed", aFileName);
                  goto error;
            }

            //mission save swap back so we can restart the game
            swapMissionPointers();
      }

      /* Start the game clock */
      gameTimeStart();
      return true;

error:
      /* Start the game clock */
      gameTimeStart();

      return false;
}

// -----------------------------------------------------------------------------------------
static bool writeMapFile(const char* fileName)
{
      char* pFileData = NULL;
      UDWORD fileSize;

      /* Get the save data */
      bool status = mapSave(&pFileData, &fileSize);

      if (status)
      {
            /* Write the data to the file */
            status = saveFile(fileName, pFileData, fileSize);
      }

      if (pFileData != NULL)
      {
            free(pFileData);
      }

      return status;
}

// -----------------------------------------------------------------------------------------
static bool gameLoad(const char* fileName)
{
      GAME_SAVEHEADER fileHeader;

      PHYSFS_file* fileHandle = openLoadFile(fileName, true);
      if (!fileHandle)
      {
            // Failure to open the file is a failure to load the specified savegame
            return true;
      }

      debug(LOG_WZ, "gameLoad");

      // Read the header from the file
      if (!deserializeSaveGameHeader(fileHandle, &fileHeader))
      {
            debug(LOG_ERROR, "gameLoad: error while reading header from file (%s): %s", fileName, PHYSFS_getLastError());
            PHYSFS_close(fileHandle);
            return false;
      }

      // Check the header to see if we've been given a file of the right type
      if (fileHeader.aFileType[0] != 'g'
       || fileHeader.aFileType[1] != 'a'
       || fileHeader.aFileType[2] != 'm'
       || fileHeader.aFileType[3] != 'e')
      {
            debug(LOG_ERROR, "gameLoad: Weird file type found? Has header letters - '%c' '%c' '%c' '%c' (should be 'g' 'a' 'm' 'e')",
                  fileHeader.aFileType[0],
                  fileHeader.aFileType[1],
                  fileHeader.aFileType[2],
                  fileHeader.aFileType[3]);

            PHYSFS_close(fileHandle);
            abort();
            return false;
      }

      debug(LOG_NEVER, "gl .gam file is version %u\n", fileHeader.version);

      //set main version Id from game file
      saveGameVersion = fileHeader.version;

      /* Check the file version */
      if (fileHeader.version < VERSION_7)
      {
            debug(LOG_ERROR, "gameLoad: unsupported save format version %d", fileHeader.version);
            PHYSFS_close(fileHandle);
            abort();
            return false;
      }
      else if (fileHeader.version < VERSION_9)
      {
            bool retVal = gameLoadV7(fileHandle);
            PHYSFS_close(fileHandle);
            return retVal;
      }
      else if (fileHeader.version <= CURRENT_VERSION_NUM)
      {
            bool retVal = gameLoadV(fileHandle, fileHeader.version);
            PHYSFS_close(fileHandle);
            return retVal;
      }
      else
      {
            debug(LOG_ERROR, "gameLoad: undefined save format version %u", fileHeader.version);
            PHYSFS_close(fileHandle);
            abort();
            return false;
      }
}

// Fix endianness of a savegame
static void endian_SaveGameV(SAVE_GAME* psSaveGame, UDWORD version)
{
      unsigned int i, j;
      /* SAVE_GAME is GAME_SAVE_V33 */
      /* GAME_SAVE_V33 includes GAME_SAVE_V31 */
      if(version >= VERSION_33)
      {
            endian_udword(&psSaveGame->sGame.power);
            for(i = 0; i < MaxGames; i++) {
                  endian_sdword(&psSaveGame->sNetPlay.games[i].desc.dwSize);
                  endian_sdword(&psSaveGame->sNetPlay.games[i].desc.dwFlags);
                  endian_sdword(&psSaveGame->sNetPlay.games[i].desc.dwMaxPlayers);
                  endian_sdword(&psSaveGame->sNetPlay.games[i].desc.dwCurrentPlayers);
                  endian_sdword(&psSaveGame->sNetPlay.games[i].desc.dwUser1);
                  endian_sdword(&psSaveGame->sNetPlay.games[i].desc.dwUser2);
                  endian_sdword(&psSaveGame->sNetPlay.games[i].desc.dwUser3);
                  endian_sdword(&psSaveGame->sNetPlay.games[i].desc.dwUser4);
            }
            for(i = 0; i < MAX_PLAYERS; i++)
                  endian_udword(&psSaveGame->sNetPlay.players[i].dpid);
            endian_udword(&psSaveGame->sNetPlay.playercount);
            endian_udword(&psSaveGame->sNetPlay.dpidPlayer);
            endian_udword(&psSaveGame->savePlayer);
            for(i = 0; i < MAX_PLAYERS; i++)
                  endian_udword(&psSaveGame->sPlayer2dpid[i]);
      }
      /* GAME_SAVE_V31 includes GAME_SAVE_V30 */
      if(version >= VERSION_31) {
            endian_sdword(&psSaveGame->missionCheatTime);
      }
      /* GAME_SAVE_V30 includes GAME_SAVE_V29 */
      if(version >= VERSION_30) {
            endian_sdword(&psSaveGame->scrGameLevel);
      }
      /* GAME_SAVE_V29 includes GAME_SAVE_V27 */
      if(version >= VERSION_29) {
            endian_uword(&psSaveGame->missionScrollMinX);
            endian_uword(&psSaveGame->missionScrollMinY);
            endian_uword(&psSaveGame->missionScrollMaxX);
            endian_uword(&psSaveGame->missionScrollMaxY);
      }
      /* GAME_SAVE_V27 includes GAME_SAVE_V24 */
      if(version >= VERSION_27) {
            for(i = 0; i < MAX_PLAYERS; i++)
                  for(j = 0; j < MAX_RECYCLED_DROIDS; j++)
                        endian_uword(&psSaveGame->awDroidExperience[i][j]);
      }
      /* GAME_SAVE_V24 includes GAME_SAVE_V22 */
      if(version >= VERSION_24) {
            endian_udword(&psSaveGame->reinforceTime);
      }
      /* GAME_SAVE_V22 includes GAME_SAVE_V20 */
      if(version >= VERSION_22) {
            for(i = 0; i < MAX_PLAYERS; i++) {
                  endian_sdword(&psSaveGame->asRunData[i].sPos.x);
                  endian_sdword(&psSaveGame->asRunData[i].sPos.y);
            }
      }
      /* GAME_SAVE_V20 includes GAME_SAVE_V19 */
      if(version >= VERSION_20) {
            for(i = 0; i < MAX_PLAYERS; i++) {
                  endian_sdword(&psSaveGame->asVTOLReturnPos[i].x);
                  endian_sdword(&psSaveGame->asVTOLReturnPos[i].y);
            }
      }
      /* GAME_SAVE_V19 includes GAME_SAVE_V18 */
      if(version >= VERSION_19) {
      }
      /* GAME_SAVE_V18 includes GAME_SAVE_V17 */
      if(version >= VERSION_18) {
            endian_udword(&psSaveGame->oldestVersion);
            endian_udword(&psSaveGame->validityKey);
      }
      /* GAME_SAVE_V17 includes GAME_SAVE_V16 */
      if(version >= VERSION_17) {
            endian_udword(&psSaveGame->objId);
      }
      /* GAME_SAVE_V16 includes GAME_SAVE_V15 */
      if(version >= VERSION_16) {
      }
      /* GAME_SAVE_V15 includes GAME_SAVE_V14 */
      if(version >= VERSION_15) {
            endian_udword(&psSaveGame->RubbleTile);
            endian_udword(&psSaveGame->WaterTile);
            endian_udword(&psSaveGame->fogColour);
            endian_udword(&psSaveGame->fogState);
      }
      /* GAME_SAVE_V14 includes GAME_SAVE_V12 */
      if(version >= VERSION_14) {
            endian_sdword(&psSaveGame->missionOffTime);
            endian_sdword(&psSaveGame->missionETA);
            endian_uword(&psSaveGame->missionHomeLZ_X);
            endian_uword(&psSaveGame->missionHomeLZ_Y);
            endian_sdword(&psSaveGame->missionPlayerX);
            endian_sdword(&psSaveGame->missionPlayerY);
            for(i = 0; i < MAX_PLAYERS; i++) {
                  endian_uword(&psSaveGame->iTranspEntryTileX[i]);
                  endian_uword(&psSaveGame->iTranspEntryTileY[i]);
                  endian_uword(&psSaveGame->iTranspExitTileX[i]);
                  endian_uword(&psSaveGame->iTranspExitTileY[i]);
                  endian_udword(&psSaveGame->aDefaultSensor[i]);
                  endian_udword(&psSaveGame->aDefaultECM[i]);
                  endian_udword(&psSaveGame->aDefaultRepair[i]);
            }
      }
      /* GAME_SAVE_V12 includes GAME_SAVE_V11 */
      if(version >= VERSION_12) {
            endian_udword(&psSaveGame->missionTime);
            endian_udword(&psSaveGame->saveKey);
      }
      /* GAME_SAVE_V11 includes GAME_SAVE_V10 */
      if(version >= VERSION_11) {
            endian_sdword(&psSaveGame->currentPlayerPos.p.x);
            endian_sdword(&psSaveGame->currentPlayerPos.p.y);
            endian_sdword(&psSaveGame->currentPlayerPos.p.z);
            endian_sdword(&psSaveGame->currentPlayerPos.r.x);
            endian_sdword(&psSaveGame->currentPlayerPos.r.y);
            endian_sdword(&psSaveGame->currentPlayerPos.r.z);
      }
      /* GAME_SAVE_V10 includes GAME_SAVE_V7 */
      if(version >= VERSION_10) {
            for(i = 0; i < MAX_PLAYERS; i++) {
                  endian_udword(&psSaveGame->power[i].currentPower);
                  endian_udword(&psSaveGame->power[i].extractedPower);
            }
      }
      /* GAME_SAVE_V7 */
      if(version >= VERSION_7) {
            endian_udword(&psSaveGame->gameTime);
            endian_udword(&psSaveGame->GameType);
            endian_sdword(&psSaveGame->ScrollMinX);
            endian_sdword(&psSaveGame->ScrollMinY);
            endian_udword(&psSaveGame->ScrollMaxX);
            endian_udword(&psSaveGame->ScrollMaxY);
      }
}

// -----------------------------------------------------------------------------------------
// Get campaign number stuff is not needed in this form on the PSX (thank you very much)
static UDWORD getCampaignV(PHYSFS_file* fileHandle, unsigned int version)
{
      SAVE_GAME_V14 saveGame;

      debug(LOG_WZ, "getCampaignV: version = %u", version);

      if (version < VERSION_14)
      {
            return 0;
      }
      // We only need VERSION 12 data (saveGame.saveKey)
      else if (version <= VERSION_34)
      {
            if (PHYSFS_read(fileHandle, &saveGame, sizeof(SAVE_GAME_V14), 1) != 1)
            {
                  debug(LOG_ERROR, "getCampaignV: error while reading file: %s", PHYSFS_getLastError());
                  abort();
                  return 0;
            }

            // Convert from little-endian to native byte-order
            endian_SaveGameV((SAVE_GAME*)&saveGame, VERSION_14);
      }
      else if (version <= CURRENT_VERSION_NUM)
      {
            if (!deserializeSaveGameV14Data(fileHandle, &saveGame))
            {
                  debug(LOG_ERROR, "getCampaignV: error while reading file: %s", PHYSFS_getLastError());
                  abort();
                  return 0;
            }
      }

//    savedGameTime = saveGame.gameTime;

      return saveGame.saveKey & (SAVEKEY_ONMISSION - 1);
}

// -----------------------------------------------------------------------------------------
// Returns the campaign number  --- apparently this is does alot less than it look like
    /// it now does even less than it looks like on the psx ... cause its pc only
04147 UDWORD getCampaign(const char* fileName)
{
      GAME_SAVEHEADER fileHeader;

      PHYSFS_file* fileHandle = openLoadFile(fileName, true);
      if (!fileHandle)
      {
            // Failure to open the file is a failure to load the specified savegame
            return false;
      }

      debug(LOG_WZ, "getCampaign: %s", fileName);

      // Read the header from the file
      if (!deserializeSaveGameHeader(fileHandle, &fileHeader))
      {
            debug(LOG_ERROR, "getCampaign: error while reading header from file (%s): %s", fileName, PHYSFS_getLastError());
            PHYSFS_close(fileHandle);
            return false;
      }

      // Check the header to see if we've been given a file of the right type
      if (fileHeader.aFileType[0] != 'g'
       || fileHeader.aFileType[1] != 'a'
       || fileHeader.aFileType[2] != 'm'
       || fileHeader.aFileType[3] != 'e')
      {
            debug(LOG_ERROR, "getCampaign: Weird file type found? Has header letters - '%c' '%c' '%c' '%c' (should be 'g' 'a' 'm' 'e')",
                  fileHeader.aFileType[0],
                  fileHeader.aFileType[1],
                  fileHeader.aFileType[2],
                  fileHeader.aFileType[3]);

            PHYSFS_close(fileHandle);
            abort();
            return false;
      }

      debug(LOG_NEVER, "gl .gam file is version %d\n", fileHeader.version);

      //set main version Id from game file
      saveGameVersion = fileHeader.version;


      /* Check the file version */
      if (fileHeader.version < VERSION_14)
      {
            PHYSFS_close(fileHandle);
            return 0;
      }

      // what the arse bollocks is this
                  // the campaign number is fine prior to saving
                  // you save it out in a skirmish save and
                  // then don't bother putting it back in again
                  // when loading so it screws loads of stuff?!?
      // dont check skirmish saves.
      if (fileHeader.version <= CURRENT_VERSION_NUM)
      {
            UDWORD retVal = getCampaignV(fileHandle, fileHeader.version);
            PHYSFS_close(fileHandle);
            return retVal;
      }
      else
      {
            debug(LOG_ERROR, "getCampaign: undefined save format version %d", fileHeader.version);
            PHYSFS_close(fileHandle);
            abort();
            return 0;
      }

      PHYSFS_close(fileHandle);
      return 0;
}

// -----------------------------------------------------------------------------------------
void game_SetValidityKey(UDWORD keys)
{
      validityKey = validityKey|keys;
      return;
}

// -----------------------------------------------------------------------------------------
/* code specific to version 7 of a save game */
bool gameLoadV7(PHYSFS_file* fileHandle)
{
      SAVE_GAME_V7 saveGame;

      if (PHYSFS_read(fileHandle, &saveGame, sizeof(saveGame), 1) != 1)
      {
            debug(LOG_ERROR, "gameLoadV7: error while reading file: %s", PHYSFS_getLastError());
            abort();
            return false;
      }

      /* GAME_SAVE_V7 */
      endian_udword(&saveGame.gameTime);
      endian_udword(&saveGame.GameType);
      endian_sdword(&saveGame.ScrollMinX);
      endian_sdword(&saveGame.ScrollMinY);
      endian_udword(&saveGame.ScrollMaxX);
      endian_udword(&saveGame.ScrollMaxY);

      savedGameTime = saveGame.gameTime;

      //set the scroll varaibles
      startX = saveGame.ScrollMinX;
      startY = saveGame.ScrollMinY;
      width = saveGame.ScrollMaxX - saveGame.ScrollMinX;
      height = saveGame.ScrollMaxY - saveGame.ScrollMinY;
      gameType = saveGame.GameType;
      //set IsScenario to true if not a user saved game
      if (gameType == GTYPE_SAVE_START)
      {
            LEVEL_DATASET* psNewLevel;

            IsScenario = false;
            //copy the level name across
            sstrcpy(aLevelName, saveGame.levelName);
            //load up the level dataset
            if (!levLoadData(aLevelName, saveGameName, gameType))
            {
                  return false;
            }
            // find the level dataset
            psNewLevel = levFindDataSet(aLevelName);
            if (psNewLevel == NULL)
            {
                  debug( LOG_ERROR, "gameLoadV7: couldn't find level data" );
                  abort();
                  return false;
            }
            //check to see whether mission automatically starts
            //shouldn't be able to be any other value at the moment!
            if (psNewLevel->type == LDS_CAMSTART
             || psNewLevel->type == LDS_BETWEEN
             || psNewLevel->type == LDS_EXPAND
             || psNewLevel->type == LDS_EXPAND_LIMBO)
            {
                  launchMission();
            }

      }
      else
      {
            IsScenario = true;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
/* non specific version of a save game */
bool gameLoadV(PHYSFS_file* fileHandle, unsigned int version)
{
      unsigned int i, j;
      static      SAVE_POWER  powerSaved[MAX_PLAYERS];
      UDWORD                  player;
      char              date[MAX_STR_LENGTH];

      debug(LOG_WZ, "gameLoadV: version %u", version);

      // Version 7 and earlier are loaded separately in gameLoadV7

      //size is now variable so only check old save games
      if (version <= VERSION_10)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V10), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version == VERSION_11)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V11), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_12)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V12), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_14)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V14), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_15)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V15), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_16)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V16), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_17)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V17), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_18)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V18), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_19)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V19), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_21)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V20), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_23)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V22), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_26)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V24), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_28)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V27), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_29)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V29), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_30)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V30), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_32)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V31), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_33)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V33), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= VERSION_34)
      {
            if (PHYSFS_read(fileHandle, &saveGameData, sizeof(SAVE_GAME_V34), 1) != 1)
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading file (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else if (version <= CURRENT_VERSION_NUM)
      {
            if (!deserializeSaveGameData(fileHandle, &saveGameData))
            {
                  debug(LOG_ERROR, "gameLoadV: error while reading data from file for deserialization (with version number %u): %s", version, PHYSFS_getLastError());
                  abort();
                  return false;
            }
      }
      else
      {
            debug(LOG_ERROR, "gameLoadV: out of range version number (%u) for savegame", version);
            abort();
            return false;
      }

      // All savegames from version 34 or before are little endian so swap them. All
      // from version 35, and onward, are already swapped to the native byte-order
      // by the (de)serialization API
      if (version <= VERSION_34)
      {
            endian_SaveGameV(&saveGameData, version);
      }

      savedGameTime = saveGameData.gameTime;

      if (version >= VERSION_12)
      {
            mission.startTime = saveGameData.missionTime;
            if (saveGameData.saveKey & SAVEKEY_ONMISSION)
            {
                  saveGameOnMission = true;
            }
            else
            {
                  saveGameOnMission = false;
            }

      }
      else
      {
            saveGameOnMission = false;
      }
      //set the scroll varaibles
      startX = saveGameData.ScrollMinX;
      startY = saveGameData.ScrollMinY;
      width = saveGameData.ScrollMaxX - saveGameData.ScrollMinX;
      height = saveGameData.ScrollMaxY - saveGameData.ScrollMinY;
      gameType = saveGameData.GameType;

      if (version >= VERSION_11)
      {
            //camera position
            disp3d_setView(&saveGameData.currentPlayerPos);
      }
      else
      {
            disp3d_resetView();
      }

      //load mission data from save game these values reloaded after load game

      if (version >= VERSION_14)
      {
            //mission data
            mission.time     = saveGameData.missionOffTime;
            mission.ETA      = saveGameData.missionETA;
            mission.homeLZ_X = saveGameData.missionHomeLZ_X;
            mission.homeLZ_Y = saveGameData.missionHomeLZ_Y;
            mission.playerX  = saveGameData.missionPlayerX;
            mission.playerY  = saveGameData.missionPlayerY;

            for (player = 0; player < MAX_PLAYERS; player++)
            {
                  mission.iTranspEntryTileX[player]   = saveGameData.iTranspEntryTileX[player];
                  mission.iTranspEntryTileY[player]   = saveGameData.iTranspEntryTileY[player];
                  mission.iTranspExitTileX[player]    = saveGameData.iTranspExitTileX[player];
                  mission.iTranspExitTileY[player]    = saveGameData.iTranspExitTileY[player];
                  aDefaultSensor[player]                    = saveGameData.aDefaultSensor[player];
                  aDefaultECM[player]                             = saveGameData.aDefaultECM[player];
                  aDefaultRepair[player]                    = saveGameData.aDefaultRepair[player];
            }
      }

      if (version >= VERSION_15)
      {
            PIELIGHT colour;

            offWorldKeepLists = saveGameData.offWorldKeepLists;
            setRubbleTile(saveGameData.RubbleTile);
            setUnderwaterTile(saveGameData.WaterTile);
            if (saveGameData.fogState == 0)//no fog
            {
                  pie_EnableFog(false);
                  fogStatus = 0;
            }
            else if (saveGameData.fogState == 1)//fog using old code assume background and depth
            {
                  if (war_GetFog())
                  {
                        pie_EnableFog(true);
                  }
                  else
                  {
                        pie_EnableFog(false);
                  }
                  fogStatus = FOG_BACKGROUND + FOG_DISTANCE;
            }
            else//version 18+ fog
            {
                  if (war_GetFog())
                  {
                        pie_EnableFog(true);
                  }
                  else
                  {
                        pie_EnableFog(false);
                  }
                  fogStatus = saveGameData.fogState;
                  fogStatus &= FOG_FLAGS;
            }
            colour.rgba = saveGameData.fogColour;
            pie_SetFogColour(colour);
      }

      if (version >= VERSION_17)
      {
            objID = saveGameData.objId;// this must be done before any new Ids added
            savedObjId = saveGameData.objId;
      }

      if (version >= VERSION_18)//version 18
      {
            validityKey = saveGameData.validityKey;
            oldestSaveGameVersion = saveGameData.oldestVersion;
            if (oldestSaveGameVersion > version)
            {
                  oldestSaveGameVersion = version;
                  validityKey = validityKey|VALIDITYKEY_VERSION;
            }
            else if (oldestSaveGameVersion < version)
            {
                  validityKey = validityKey|VALIDITYKEY_VERSION;
            }

            strcpy(date,__DATE__);
            ASSERT( strlen(date)<MAX_STR_LENGTH,"BuildDate; String error" );
            if (strcmp(saveGameData.buildDate,date) != 0)
            {
//                ASSERT( gameType != GTYPE_SAVE_MIDMISSION,"Mid-game save out of date. Continue with caution." );
                  debug( LOG_NEVER, "saveGame build date differs;\nsavegame %s\n build    %s\n", saveGameData.buildDate, date );
                  validityKey = validityKey|VALIDITYKEY_DATE;
                  if (gameType == GTYPE_SAVE_MIDMISSION)
                  {
                        validityKey = validityKey|VALIDITYKEY_MID_GAME;
                  }
            }
      }
      else
      {
            debug( LOG_NEVER, "saveGame build date differs;\nsavegame pre-Version 18 (%s)\n build    %s\n", saveGameData.buildDate, date );
            oldestSaveGameVersion = 1;
            validityKey = VALIDITYKEY_DATE;
      }


      if (version >= VERSION_19)//version 19
      {
            for(i=0; i<MAX_PLAYERS; i++)
            {
                  for(j=0; j<MAX_PLAYERS; j++)
                  {
                        alliances[i][j] = saveGameData.alliances[i][j];
                  }
            }
            for(i=0; i<MAX_PLAYERS; i++)
            {
                  setPlayerColour(i,saveGameData.playerColour[i]);
            }
            SetRadarZoom(saveGameData.radarZoom);
      }

      if (version >= VERSION_20)//version 20
      {
            setDroidsToSafetyFlag(saveGameData.bDroidsToSafetyFlag);
            for (i = 0; i < MAX_PLAYERS; ++i)
            {
                  memcpy(&asVTOLReturnPos[i], &saveGameData.asVTOLReturnPos[i], sizeof(Vector2i));
            }
      }

      if (version >= VERSION_22)//version 22
      {
            for (i = 0; i < MAX_PLAYERS; ++i)
            {
                  memcpy(&asRunData[i], &saveGameData.asRunData[i], sizeof(RUN_DATA));
            }
      }

      if (saveGameVersion >= VERSION_24)//V24
      {
            missionSetReinforcementTime(saveGameData.reinforceTime);

            // horrible hack to catch savegames that were saving garbage into these fields
            if (saveGameData.bPlayCountDown <= 1)
            {
                  setPlayCountDown(saveGameData.bPlayCountDown);
            }
            if (saveGameData.bPlayerHasWon <= 1)
            {
                  setPlayerHasWon(saveGameData.bPlayerHasWon);
            }
            if (saveGameData.bPlayerHasLost <= 1)
            {
                  setPlayerHasLost(saveGameData.bPlayerHasLost);
            }
      }

    if (saveGameVersion >= VERSION_29)
    {
        mission.scrollMinX = saveGameData.missionScrollMinX;
        mission.scrollMinY = saveGameData.missionScrollMinY;
        mission.scrollMaxX = saveGameData.missionScrollMaxX;
        mission.scrollMaxY = saveGameData.missionScrollMaxY;
    }

    if (saveGameVersion >= VERSION_30)
    {
        scrGameLevel = saveGameData.scrGameLevel;
        bExtraVictoryFlag = saveGameData.bExtraVictoryFlag;
        bExtraFailFlag = saveGameData.bExtraFailFlag;
        bTrackTransporter = saveGameData.bTrackTransporter;
    }

    if (saveGameVersion >= VERSION_31)
    {
        mission.cheatTime = saveGameData.missionCheatTime;
    }

    for (player = 0; player < MAX_PLAYERS; player++)
      {
            for (i = 0; i < MAX_RECYCLED_DROIDS; ++i)
            {
                  aDroidExperience[player][i]   = 0;//clear experience before
            }
      }

      //set IsScenario to true if not a user saved game
      if ((gameType == GTYPE_SAVE_START) ||
            (gameType == GTYPE_SAVE_MIDMISSION))
      {
            for (i = 0; i < MAX_PLAYERS; ++i)
            {
                  powerSaved[i].currentPower = saveGameData.power[i].currentPower;
                  powerSaved[i].extractedPower = saveGameData.power[i].extractedPower;
            }

            for (player = 0; player < MAX_PLAYERS; player++)
            {
                  for (i = 0; i < MAX_RECYCLED_DROIDS; ++i)
                  {
                        aDroidExperience[player][i] = 0;//clear experience before building saved units
                  }
            }


            IsScenario = false;
            //copy the level name across
            sstrcpy(aLevelName, saveGameData.levelName);
            //load up the level dataset
            if (!levLoadData(aLevelName, saveGameName, gameType))
            {
                  return false;
            }
            // find the level dataset
/*          psNewLevel = levFindDataSet(aLevelName);
            if (psNewLevel == NULL)
            {
                  DBERROR(("gameLoadV6: couldn't find level data"));
                  return false;
            }
            //check to see whether mission automatically starts
            if (gameType == GTYPE_SAVE_START)
            {
//                launchMission();
                  if (!levLoadData(aLevelName, NULL, 0))
                  {
                        return false;
                  }
            }*/

            if (saveGameVersion >= VERSION_33)
            {
                  PLAYERSTATS       playerStats;

                  bMultiPlayer      = saveGameData.multiPlayer;
                  NetPlay                 = saveGameData.sNetPlay;
                  selectedPlayer    = saveGameData.savePlayer;
                  productionPlayer = selectedPlayer;
                  game              = saveGameData.sGame;
                  cmdDroidMultiExpBoost(true);
                  for(i = 0; i < MAX_PLAYERS; ++i)
                  {
                        player2dpid[i] = saveGameData.sPlayer2dpid[i];
                  }
                  if(bMultiPlayer)
                  {
                        loadMultiStats(saveGameData.sPName,&playerStats);                       // stats stuff
                        setMultiStats(NetPlay.dpidPlayer,playerStats,false);
                        setMultiStats(NetPlay.dpidPlayer,playerStats,true);
                  }
            }

      }
      else
      {
            IsScenario = true;
      }

      /* Get human and AI players names */
      if (saveGameVersion >= VERSION_34)
      {
            for(i=0;i<MAX_PLAYERS;i++)
                  (void)setPlayerName(i, saveGameData.sPlayerName[i]);
      }

    //don't adjust any power if a camStart (gameType is set to GTYPE_SCENARIO_START when a camChange saveGame is loaded)
    if (gameType != GTYPE_SCENARIO_START)
    {
          //set the players power
          for (i = 0; i < MAX_PLAYERS; ++i)
          {
            //only overwrite selectedPlayer's power on a startMission save game
            if (gameType == GTYPE_SAVE_MIDMISSION || i == selectedPlayer)
            {
                asPower[i].currentPower = powerSaved[i].currentPower;
                asPower[i].extractedPower = powerSaved[i].extractedPower;
            }
                //init the last structure
                asPower[i].psLastPowered = NULL;
          }
    }

      return true;
}


// -----------------------------------------------------------------------------------------
/*
Writes the game specifics to a file
*/
static bool writeGameFile(const char* fileName, SDWORD saveType)
{
      GAME_SAVEHEADER fileHeader;
      SAVE_GAME       saveGame;
      bool            status;
      unsigned int    i, j;

      PHYSFS_file* fileHandle = openSaveFile(fileName);
      if (!fileHandle)
      {
            debug(LOG_ERROR, "writeGameFile: openSaveFile(\"%s\") failed", fileName);
            return false;
      }

      fileHeader.aFileType[0] = 'g';
      fileHeader.aFileType[1] = 'a';
      fileHeader.aFileType[2] = 'm';
      fileHeader.aFileType[3] = 'e';

      fileHeader.version = CURRENT_VERSION_NUM;

      if (!serializeSaveGameHeader(fileHandle, &fileHeader))
      {
            debug(LOG_ERROR, "game.c:writeGameFile: could not write header to %s; PHYSFS error: %s", fileName, PHYSFS_getLastError());
            PHYSFS_close(fileHandle);
            return false;
      }

      ASSERT( saveType == GTYPE_SAVE_START ||
                  saveType == GTYPE_SAVE_MIDMISSION,
                  "writeGameFile: invalid save type" );

      // saveKeymissionIsOffworld
      saveGame.saveKey = getCampaignNumber();
      if (missionIsOffworld())
      {
            saveGame.saveKey |= SAVEKEY_ONMISSION;
            saveGameOnMission = true;
      }
      else
      {
            saveGameOnMission = false;
      }


      /* Put the save game data into the buffer */
      saveGame.gameTime = gameTime;
      saveGame.missionTime = mission.startTime;

      //put in the scroll data
      saveGame.ScrollMinX = scrollMinX;
      saveGame.ScrollMinY = scrollMinY;
      saveGame.ScrollMaxX = scrollMaxX;
      saveGame.ScrollMaxY = scrollMaxY;

      saveGame.GameType = saveType;

      //save the current level so we can load up the STARTING point of the mission
      if (strlen(aLevelName) > MAX_LEVEL_SIZE)
      {
            ASSERT( false,
                  "writeGameFile:Unable to save level name - too long (max20) - %s",
                  aLevelName );

            return false;
      }
      sstrcpy(saveGame.levelName, aLevelName);

      //save out the players power
      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            saveGame.power[i].currentPower = asPower[i].currentPower;
            saveGame.power[i].extractedPower = asPower[i].extractedPower;
      }

      //camera position
      disp3d_getView(&(saveGame.currentPlayerPos));

      //mission data
//    saveGame.missionStartTime =         mission.startTime;
      saveGame.missionOffTime =           mission.time;
      saveGame.missionETA =               mission.ETA;
    saveGame.missionCheatTime =           mission.cheatTime;
      saveGame.missionHomeLZ_X =          mission.homeLZ_X;
      saveGame.missionHomeLZ_Y =          mission.homeLZ_Y;
      saveGame.missionPlayerX =           mission.playerX;
      saveGame.missionPlayerY =           mission.playerY;
    saveGame.missionScrollMinX =     (UWORD)mission.scrollMinX;
    saveGame.missionScrollMinY =     (UWORD)mission.scrollMinY;
    saveGame.missionScrollMaxX =     (UWORD)mission.scrollMaxX;
    saveGame.missionScrollMaxY =     (UWORD)mission.scrollMaxY;

      saveGame.offWorldKeepLists = offWorldKeepLists;
      saveGame.RubbleTile     = getRubbleTileNum();
      saveGame.WaterTile      = getWaterTileNum();
      saveGame.fogColour      = pie_GetFogColour().rgba;
      saveGame.fogState = fogStatus;
      if(pie_GetFogEnabled())
      {
            saveGame.fogState = fogStatus | FOG_ENABLED;
      }

      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            saveGame.iTranspEntryTileX[i] = mission.iTranspEntryTileX[i];
            saveGame.iTranspEntryTileY[i] = mission.iTranspEntryTileY[i];
            saveGame.iTranspExitTileX[i]  = mission.iTranspExitTileX[i];
            saveGame.iTranspExitTileY[i]  = mission.iTranspExitTileY[i];
            saveGame.aDefaultSensor[i]    = aDefaultSensor[i];
            saveGame.aDefaultECM[i]       = aDefaultECM[i];
            saveGame.aDefaultRepair[i]    = aDefaultRepair[i];

            for (j = 0; j < MAX_RECYCLED_DROIDS; ++j)
            {
                  saveGame.awDroidExperience[i][j]    = aDroidExperience[i][j];
            }
      }

      for (i = 0; i < MAX_NOGO_AREAS; ++i)
      {
            LANDING_ZONE* psLandingZone = getLandingZone(i);
            saveGame.sLandingZone[i].x1   = psLandingZone->x1; // in case struct changes
            saveGame.sLandingZone[i].x2   = psLandingZone->x2;
            saveGame.sLandingZone[i].y1   = psLandingZone->y1;
            saveGame.sLandingZone[i].y2   = psLandingZone->y2;
      }

      //version 17
      saveGame.objId = objID;

      //version 18
      ASSERT(strlen(__DATE__) < MAX_STR_LENGTH, "BuildDate; String error" );
      strcpy(saveGame.buildDate, __DATE__);
      saveGame.oldestVersion = oldestSaveGameVersion;
      saveGame.validityKey = validityKey;

      //version 19
      for(i=0; i<MAX_PLAYERS; i++)
      {
            for(j=0; j<MAX_PLAYERS; j++)
            {
                  saveGame.alliances[i][j] = alliances[i][j];
            }
      }
      for(i=0; i<MAX_PLAYERS; i++)
      {
            saveGame.playerColour[i] = getPlayerColour(i);
      }
      saveGame.radarZoom = (UBYTE)GetRadarZoom();


      //version 20
      saveGame.bDroidsToSafetyFlag = (UBYTE)getDroidsToSafetyFlag();
      for (i = 0; i < MAX_PLAYERS; i++)
      {
            memcpy(&saveGame.asVTOLReturnPos[i], &asVTOLReturnPos[i], sizeof(Vector2i));
      }

      //version 22
      for (i = 0; i < MAX_PLAYERS; i++)
      {
            memcpy(&saveGame.asRunData[i], &asRunData[i], sizeof(RUN_DATA));
      }

      //version 24
      saveGame.reinforceTime = missionGetReinforcementTime();
      saveGame.bPlayCountDown = (UBYTE)getPlayCountDown();
      saveGame.bPlayerHasWon =  (UBYTE)testPlayerHasWon();
      saveGame.bPlayerHasLost = (UBYTE)testPlayerHasLost();

    //version 30
    saveGame.scrGameLevel = scrGameLevel;
    saveGame.bExtraFailFlag = (UBYTE)bExtraFailFlag;
    saveGame.bExtraVictoryFlag = (UBYTE)bExtraVictoryFlag;
    saveGame.bTrackTransporter = (UBYTE)bTrackTransporter;


      // version 33
      saveGame.sGame          = game;
      saveGame.savePlayer     = selectedPlayer;
      saveGame.multiPlayer = bMultiPlayer;
      saveGame.sNetPlay = NetPlay;
      strcpy(saveGame.sPName, getPlayerName(selectedPlayer));
      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            saveGame.sPlayer2dpid[i] = player2dpid[i];
      }

      //version 34
      for (i = 0; i < MAX_PLAYERS; ++i)
      {
            strcpy(saveGame.sPlayerName[i], getPlayerName(i));
      }

      status = serializeSaveGameData(fileHandle, &saveGame);

      // Close the file
      PHYSFS_close(fileHandle);

      // Return our success status with writing out the file!
      return status;
}

// -----------------------------------------------------------------------------------------
// Process the droid initialisation file (dinit.bjo). Creates droids for
// the scenario being loaded. This is *NEVER* called for a user save game
//
BOOL loadSaveDroidInit(char *pFileData, UDWORD filesize)
{
      DROIDINIT_SAVEHEADER          *psHeader;

      /* Check the file type */
      psHeader = (DROIDINIT_SAVEHEADER *)pFileData;
      if (psHeader->aFileType[0] != 'd' || psHeader->aFileType[1] != 'i' ||
            psHeader->aFileType[2] != 'n' || psHeader->aFileType[3] != 't')   {
            debug( LOG_ERROR, "loadSaveUnitInit: Incorrect file type" );
            abort();
            return false;
      }

      /* DROIDINIT_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += DROIDINIT_HEADER_SIZE;

      /* Check the file version */
      if (psHeader->version < VERSION_7)
      {
            debug( LOG_ERROR, "UnitInit; unsupported save format version %d", psHeader->version );
            abort();
            return false;
      }
      else if (psHeader->version <= CURRENT_VERSION_NUM)
      {
            if (!loadSaveDroidInitV2(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else
      {
            debug( LOG_ERROR, "UnitInit: undefined save format version %d", psHeader->version );
            abort();
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Used for all droids
BOOL loadSaveDroidInitV2(char *pFileData, UDWORD filesize,UDWORD quantity)
{
      SAVE_DROIDINIT *pDroidInit;
      DROID_TEMPLATE *psTemplate;
      DROID *psDroid;
      UDWORD i;
      UDWORD NumberOfSkippedDroids = 0;

      pDroidInit = (SAVE_DROIDINIT*)pFileData;

      for(i=0; i<quantity; i++)
      {
            /* SAVE_DROIDINIT is OBJECT_SAVE_V19 */
            /* OBJECT_SAVE_V19 */
            endian_udword(&pDroidInit->id);
            endian_udword(&pDroidInit->x);
            endian_udword(&pDroidInit->y);
            endian_udword(&pDroidInit->z);
            endian_udword(&pDroidInit->direction);
            endian_udword(&pDroidInit->player);
            endian_udword(&pDroidInit->burnStart);
            endian_udword(&pDroidInit->burnDamage);

            pDroidInit->player=RemapPlayerNumber(pDroidInit->player);

            if (pDroidInit->player >= MAX_PLAYERS) {
                  pDroidInit->player = MAX_PLAYERS-1; // now don't lose any droids ... force them to be the last player
                  NumberOfSkippedDroids++;
            }


            psTemplate = (DROID_TEMPLATE *)FindDroidTemplate(pDroidInit->name,pDroidInit->player);

            if(psTemplate==NULL)
            {
                  debug( LOG_NEVER, "loadSaveUnitInitV2:\nUnable to find template for %s player %d", pDroidInit->name,pDroidInit->player );
            }
            else
            {
                  ASSERT( psTemplate != NULL,
                        "loadSaveUnitInitV2: Invalid template pointer" );

// Need to set apCompList[pDroidInit->player][componenttype][compid] = AVAILABLE for each droid.

                  {

                        psDroid = buildDroid(psTemplate, (pDroidInit->x & (~TILE_MASK)) + TILE_UNITS/2, (pDroidInit->y  & (~TILE_MASK)) + TILE_UNITS/2,
                              pDroidInit->player, false);

                        if (psDroid) {
                              psDroid->id = pDroidInit->id;
                              psDroid->direction = pDroidInit->direction;
                              addDroid(psDroid, apsDroidLists);
                        }
                        else
                        {

                              debug( LOG_ERROR, "This droid cannot be built - %s", pDroidInit->name );
                              abort();
                        }
                  }
            }
            pDroidInit++;
      }
      if(NumberOfSkippedDroids) {
            debug( LOG_ERROR, "unitLoad: Bad Player number in %d unit(s)... assigned to the last player!\n", NumberOfSkippedDroids );
            abort();
      }
      return true;
}


// -----------------------------------------------------------------------------------------
DROID_TEMPLATE *FindDroidTemplate(char *name,UDWORD player)
{
      UDWORD                  TempPlayer;
      DROID_TEMPLATE *Template;
      UDWORD                  id;

      //get the name from the resource associated with it
      if (!strresGetIDNum(psStringRes, name, &id))
      {
            debug( LOG_ERROR, "Cannot find resource for template - %s", name );
            abort();
            return NULL;
      }
      //get the string from the id
      name = strresGetString(psStringRes, id);

      for(TempPlayer=0; TempPlayer<MAX_PLAYERS; TempPlayer++) {
            Template = apsDroidTemplates[TempPlayer];

            while(Template) {

                  //if(strcmp(name,Template->pName)==0) {
                  if(strcmp(name,Template->aName)==0) {
                        return Template;
                  }
                  Template = Template->psNext;
            }
      }

      return NULL;
}



// -----------------------------------------------------------------------------------------
UDWORD RemapPlayerNumber(UDWORD OldNumber)
{
      return(OldNumber);
}

// -----------------------------------------------------------------------------------------
/*This is *ALWAYS* called by a User Save Game */
BOOL loadSaveDroid(char *pFileData, UDWORD filesize, DROID **ppsCurrentDroidLists)
{
      DROID_SAVEHEADER        *psHeader;

      /* Check the file type */
      psHeader = (DROID_SAVEHEADER *)pFileData;
      if (psHeader->aFileType[0] != 'd' || psHeader->aFileType[1] != 'r' ||
            psHeader->aFileType[2] != 'o' || psHeader->aFileType[3] != 'd')
      {
            debug( LOG_ERROR, "loadSaveUnit: Incorrect file type" );
            abort();
            return false;
      }

      /* DROID_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += DROID_HEADER_SIZE;

      /* Check the file version */
      if (psHeader->version < VERSION_9)
      {
            debug( LOG_ERROR, "UnitLoad; unsupported save format version %d", psHeader->version );
            abort();
            return false;
      }
      else if (psHeader->version == VERSION_11)
      {
            if (!loadSaveDroidV11(pFileData, filesize, psHeader->quantity, psHeader->version, ppsCurrentDroidLists))
            {
                  return false;
            }
      }
      else if (psHeader->version <= VERSION_19)//old save name size
      {
            if (!loadSaveDroidV19(pFileData, filesize, psHeader->quantity, psHeader->version, ppsCurrentDroidLists))
            {
                  return false;
            }
      }
      else if (psHeader->version <= CURRENT_VERSION_NUM)
      {
            if (!loadSaveDroidV(pFileData, filesize, psHeader->quantity, psHeader->version, ppsCurrentDroidLists))
            {
                  return false;
            }
      }
      else
      {
            debug( LOG_ERROR, "UnitLoad: undefined save format version %d", psHeader->version );
            abort();
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
static DROID* buildDroidFromSaveDroidV11(SAVE_DROID_V11* psSaveDroid)
{
      DROID_TEMPLATE                *psTemplate, sTemplate;
      DROID                         *psDroid;
      BOOL                          found;
      UDWORD                              i;
      SDWORD                              compInc;
      UDWORD                              burnTime;

      psTemplate = &sTemplate;


      //set up the template
      //copy the values across

      sstrcpy(psTemplate->aName, psSaveDroid->name);
      //ignore the first comp - COMP_UNKNOWN
      found = true;
      for (i=1; i < DROID_MAXCOMP; i++)
      {

            compInc = getCompFromName(i, psSaveDroid->asBits[i].name);
            if (compInc < 0)
            {

                  debug( LOG_ERROR, "This component no longer exists - %s, the droid will be deleted", psSaveDroid->asBits[i].name );
                  abort();
                  found = false;
                  break;//continue;
            }
            psTemplate->asParts[i] = (UDWORD)compInc;
      }
      if (!found)
      {
            //ignore this record
            return NULL;
      }
      psTemplate->numWeaps = psSaveDroid->numWeaps;
      for (i=0; i < psSaveDroid->numWeaps; i++)
      {
            int weapon = getCompFromName(COMP_WEAPON, psSaveDroid->asWeaps[i].name);
            if( weapon < 0)
            {
                  ASSERT(false, "This component does not exist : %s", psSaveDroid->asWeaps[i].name );
                  return NULL;
            }
            psTemplate->asWeaps[i] = weapon;
      }

      psTemplate->buildPoints = calcTemplateBuild(psTemplate);
      psTemplate->powerPoints = calcTemplatePower(psTemplate);
      psTemplate->droidType = psSaveDroid->droidType;

      /*create the Droid */

      // ignore brains for now
      psTemplate->asParts[COMP_BRAIN] = 0;

      psDroid = buildDroid(psTemplate, psSaveDroid->x, psSaveDroid->y,
            psSaveDroid->player, false);

      //copy the droid's weapon stats
      for (i=0; i < psDroid->numWeaps; i++)
      {
            if (psDroid->asWeaps[i].nStat > 0)
            {
                  //only one weapon now
                  psDroid->asWeaps[i].hitPoints = psSaveDroid->asWeaps[i].hitPoints;
                  psDroid->asWeaps[i].ammo = psSaveDroid->asWeaps[i].ammo;
                  psDroid->asWeaps[i].lastFired = psSaveDroid->asWeaps[i].lastFired;
            }
      }
      //copy the values across
      psDroid->id = psSaveDroid->id;
      //are these going to ever change from the values set up with?
//                psDroid->pos.z = psSaveDroid->pos.z;            // use the correct map height value

      psDroid->direction = psSaveDroid->direction;
      psDroid->body = psSaveDroid->body;
      if (psDroid->body > psDroid->originalBody)
      {
            psDroid->body = psDroid->originalBody;
      }

      psDroid->inFire = psSaveDroid->inFire;
      psDroid->burnDamage = psSaveDroid->burnDamage;
      burnTime = psSaveDroid->burnStart;
      psDroid->burnStart = burnTime;

      psDroid->experience = (UWORD)psSaveDroid->numKills;
      //version 11
      for (i=0; i < psDroid->numWeaps; i++)
      {
            psDroid->turretRotation[i] = psSaveDroid->turretRotation;
            psDroid->turretPitch[i] = psSaveDroid->turretPitch;
      }


      psDroid->psGroup = NULL;
      psDroid->psGrpNext = NULL;

      return psDroid;
}

// -----------------------------------------------------------------------------------------
static DROID* buildDroidFromSaveDroidV19(SAVE_DROID_V18* psSaveDroid, UDWORD version)
{
      DROID_TEMPLATE                *psTemplate, sTemplate;
      DROID                         *psDroid;
      SAVE_DROID_V14                *psSaveDroidV14;
      BOOL                          found;
      UDWORD                              i, id;
      SDWORD                              compInc;
      UDWORD                              burnTime;

      psTemplate = &sTemplate;

      psTemplate->pName = NULL;

      //set up the template
      //copy the values across

      sstrcpy(psTemplate->aName, psSaveDroid->name);

      //ignore the first comp - COMP_UNKNOWN
      found = true;
      for (i=1; i < DROID_MAXCOMP; i++)
      {

            compInc = getCompFromName(i, psSaveDroid->asBits[i].name);

            if (compInc < 0)
            {

                  debug( LOG_ERROR, "This component no longer exists - %s, the droid will be deleted", psSaveDroid->asBits[i].name );
                  abort();

                  found = false;
                  break;//continue;
            }
            psTemplate->asParts[i] = (UDWORD)compInc;
      }
      if (!found)
      {
            //ignore this record
            ASSERT( found,"buildUnitFromSavedUnit; failed to find weapon" );
            return NULL;
      }
      psTemplate->numWeaps = psSaveDroid->numWeaps;

      if (psSaveDroid->numWeaps > 0)
      {
            for(i = 0;i < psTemplate->numWeaps;i++)
            {
                  int weapon = getCompFromName(COMP_WEAPON, psSaveDroid->asWeaps[i].name);
                  if( weapon < 0)
                  {
                        ASSERT(false, "This component does not exist : %s", psSaveDroid->asWeaps[i].name );
                        return NULL;
                  }
                  psTemplate->asWeaps[i] = weapon;
            }
      }

      psTemplate->buildPoints = calcTemplateBuild(psTemplate);
      psTemplate->powerPoints = calcTemplatePower(psTemplate);
      psTemplate->droidType = psSaveDroid->droidType;

      /*create the Droid */


      // ignore brains for now
      // not any *$&!!! more - JOHN
//    psTemplate->asParts[COMP_BRAIN] = 0;

      if(psSaveDroid->x == INVALID_XY)
      {
            psDroid = buildDroid(psTemplate, psSaveDroid->x, psSaveDroid->y,
                  psSaveDroid->player, true);
      }
      else if(psSaveDroid->saveType == DROID_ON_TRANSPORT)
      {
            psDroid = buildDroid(psTemplate, 0, 0,
                  psSaveDroid->player, true);
      }
      else
      {
            psDroid = buildDroid(psTemplate, psSaveDroid->x, psSaveDroid->y,
                  psSaveDroid->player, false);
      }

      if(psDroid == NULL)
      {
            ASSERT( false,"buildUnitFromSavedUnit; failed to build unit" );
            return NULL;
      }


      //copy the droid's weapon stats
      for (i=0; i < psDroid->numWeaps; i++)
      {
            if (psDroid->asWeaps[i].nStat > 0)
            {
                  psDroid->asWeaps[i].hitPoints = psSaveDroid->asWeaps[i].hitPoints;
                  psDroid->asWeaps[i].ammo = psSaveDroid->asWeaps[i].ammo;
                  psDroid->asWeaps[i].lastFired = psSaveDroid->asWeaps[i].lastFired;
            }
      }
      //copy the values across
      psDroid->id = psSaveDroid->id;
      //are these going to ever change from the values set up with?
//                psDroid->pos.z = psSaveDroid->pos.z;            // use the correct map height value

      psDroid->direction = psSaveDroid->direction;
    psDroid->body = psSaveDroid->body;
      if (psDroid->body > psDroid->originalBody)
      {
            psDroid->body = psDroid->originalBody;
      }

      psDroid->inFire = psSaveDroid->inFire;
      psDroid->burnDamage = psSaveDroid->burnDamage;
      burnTime = psSaveDroid->burnStart;
      psDroid->burnStart = burnTime;

      psDroid->experience = (UWORD)psSaveDroid->numKills;
      //version 14
      psDroid->resistance = droidResistance(psDroid);

      if (version >= VERSION_11)//version 11
      {
            //Watermelon:make it back-compatible with older versions of save
            for (i=0; i < psDroid->numWeaps; i++)
            {
                  psDroid->turretRotation[i] = psSaveDroid->turretRotation;
                  psDroid->turretPitch[i] = psSaveDroid->turretPitch;
            }
      }
      if (version >= VERSION_12)//version 12
      {
            psDroid->order                      = psSaveDroid->order;
            psDroid->orderX                     = psSaveDroid->orderX;
            psDroid->orderY                     = psSaveDroid->orderY;
            psDroid->orderX2                    = psSaveDroid->orderX2;
            psDroid->orderY2                    = psSaveDroid->orderY2;
            psDroid->timeLastHit                = psSaveDroid->timeLastHit;
            //rebuild the object pointer from the ID
            FIXME_CAST_ASSIGN(UDWORD, psDroid->psTarget, psSaveDroid->targetID);
            psDroid->secondaryOrder       = psSaveDroid->secondaryOrder;
            psDroid->action                     = psSaveDroid->action;
            psDroid->actionX                    = psSaveDroid->actionX;
            psDroid->actionY                    = psSaveDroid->actionY;
            //rebuild the object pointer from the ID
            FIXME_CAST_ASSIGN(UDWORD, psDroid->psActionTarget[0], psSaveDroid->actionTargetID);
            psDroid->actionStarted        = psSaveDroid->actionStarted;
            psDroid->actionPoints         = psSaveDroid->actionPoints;
            psDroid->powerAccrued         = psSaveDroid->actionHeight;
            //added for V14

            psDroid->psGroup = NULL;
            psDroid->psGrpNext = NULL;
      }
      if ((version >= VERSION_14) && (version < VERSION_18))//version 14
      {

//warning V14 - v17 only
            //current Save Droid V18+ uses larger tarStatName
            //subsequent structure elements are not aligned between the two
            psSaveDroidV14 = (SAVE_DROID_V14*)psSaveDroid;
            if (psSaveDroidV14->tarStatName[0] == 0)
            {
                  psDroid->psTarStats = NULL;
            }
            else
            {
                  id = getStructStatFromName(psSaveDroidV14->tarStatName);
                  if (id != (UDWORD)-1)
                  {
                        psDroid->psTarStats = (BASE_STATS*)&asStructureStats[id];
                  }
                  else
                  {
                        ASSERT( false,"loadUnit TargetStat not found" );
                        psDroid->psTarStats = NULL;
                orderDroid(psDroid, DORDER_STOP);
                  }
            }
//warning V14 - v17 only
            //rebuild the object pointer from the ID
            FIXME_CAST_ASSIGN(UDWORD, psDroid->psBaseStruct, psSaveDroidV14->baseStructID);
            psDroid->group = psSaveDroidV14->group;
            psDroid->selected = psSaveDroidV14->selected;
//20feb           psDroid->cluster = psSaveDroidV14->cluster;
            psDroid->died = psSaveDroidV14->died;
            psDroid->lastEmission = psSaveDroidV14->lastEmission;
//warning V14 - v17 only
            for (i=0; i < MAX_PLAYERS; i++)
            {
                  psDroid->visible[i]     = psSaveDroidV14->visible[i];
            }
//end warning V14 - v17 only
      }
      else if (version >= VERSION_18)//version 18
      {

            if (psSaveDroid->tarStatName[0] == 0)
            {
                  psDroid->psTarStats = NULL;
            }
            else
            {
                  id = getStructStatFromName(psSaveDroid->tarStatName);
                  if (id != (UDWORD)-1)
                  {
                        psDroid->psTarStats = (BASE_STATS*)&asStructureStats[id];
                  }
                  else
                  {
                        ASSERT( false,"loadUnit TargetStat not found" );
                        psDroid->psTarStats = NULL;
                  }
            }
            //rebuild the object pointer from the ID
            FIXME_CAST_ASSIGN(UDWORD, psDroid->psBaseStruct, psSaveDroid->baseStructID);
            psDroid->group = psSaveDroid->group;
            psDroid->selected = psSaveDroid->selected;
//20feb           psDroid->cluster = psSaveDroid->cluster;
            psDroid->died = psSaveDroid->died;
            psDroid->lastEmission = psSaveDroid->lastEmission;
            for (i=0; i < MAX_PLAYERS; i++)
            {
                  psDroid->visible[i]     = psSaveDroid->visible[i];
            }
      }

      return psDroid;
}

static void SaveDroidMoveControl(SAVE_DROID * const psSaveDroid, DROID const * const psDroid)
{
      unsigned int i;

      // Had to disable this one -- cannot figure out where it gets set wrong! - Per
      // ASSERT(droidOnMap(psDroid), "saved droid standing or moving off the map");

      // Copy over the endian neutral stuff (all UBYTE)
      psSaveDroid->sMove.Status    = psDroid->sMove.Status;
      psSaveDroid->sMove.Position  = psDroid->sMove.Position;
      psSaveDroid->sMove.numPoints = psDroid->sMove.numPoints;
      memcpy(&psSaveDroid->sMove.asPath, &psDroid->sMove.asPath, sizeof(psSaveDroid->sMove.asPath));

      // Little endian SDWORDs
      psSaveDroid->sMove.DestinationX = PHYSFS_swapSLE32(psDroid->sMove.DestinationX);
      psSaveDroid->sMove.DestinationY = PHYSFS_swapSLE32(psDroid->sMove.DestinationY);
      psSaveDroid->sMove.srcX         = PHYSFS_swapSLE32(psDroid->sMove.srcX);
      psSaveDroid->sMove.srcY         = PHYSFS_swapSLE32(psDroid->sMove.srcY);
      psSaveDroid->sMove.targetX      = PHYSFS_swapSLE32(psDroid->sMove.targetX);
      psSaveDroid->sMove.targetY      = PHYSFS_swapSLE32(psDroid->sMove.targetY);

      // Little endian floats
      psSaveDroid->sMove.fx           = PHYSFS_swapSLE32(psDroid->sMove.fx);
      psSaveDroid->sMove.fy           = PHYSFS_swapSLE32(psDroid->sMove.fy);
      psSaveDroid->sMove.speed        = PHYSFS_swapSLE32(psDroid->sMove.speed);
      psSaveDroid->sMove.moveDir      = PHYSFS_swapSLE32(psDroid->sMove.moveDir);
      psSaveDroid->sMove.fz           = PHYSFS_swapSLE32(psDroid->sMove.fz);

      // Little endian SWORDs
      psSaveDroid->sMove.boundX       = PHYSFS_swapSLE16(psDroid->sMove.boundX);
      psSaveDroid->sMove.boundY       = PHYSFS_swapSLE16(psDroid->sMove.boundY);
      psSaveDroid->sMove.bumpDir      = PHYSFS_swapSLE16(psDroid->sMove.bumpDir);
      psSaveDroid->sMove.iVertSpeed   = PHYSFS_swapSLE16(psDroid->sMove.iVertSpeed);

      // Little endian UDWORDs
      psSaveDroid->sMove.bumpTime     = PHYSFS_swapULE32(psDroid->sMove.bumpTime);
      psSaveDroid->sMove.shuffleStart = PHYSFS_swapULE32(psDroid->sMove.shuffleStart);

      // Array of little endian UDWORDS
      for (i = 0; i < sizeof(psDroid->sMove.iAttackRuns) / sizeof(psDroid->sMove.iAttackRuns[0]); ++i)
      {
            psSaveDroid->sMove.iAttackRuns[i] = PHYSFS_swapULE32(psDroid->sMove.iAttackRuns[i]);
      }

      // Little endian UWORDs
      psSaveDroid->sMove.lastBump     = PHYSFS_swapULE16(psDroid->sMove.lastBump);
      psSaveDroid->sMove.pauseTime    = PHYSFS_swapULE16(psDroid->sMove.pauseTime);
      psSaveDroid->sMove.bumpX        = PHYSFS_swapULE16(psDroid->sMove.bumpX);
      psSaveDroid->sMove.bumpY        = PHYSFS_swapULE16(psDroid->sMove.bumpY);

      if (psDroid->sMove.psFormation != NULL)
      {
            psSaveDroid->sMove.isInFormation = true;
            psSaveDroid->formationDir = psDroid->sMove.psFormation->dir;
            psSaveDroid->formationX   = psDroid->sMove.psFormation->x;
            psSaveDroid->formationY   = psDroid->sMove.psFormation->y;
      }
      else
      {
            psSaveDroid->sMove.isInFormation = false;
            psSaveDroid->formationDir = 0;
            psSaveDroid->formationX   = 0;
            psSaveDroid->formationY   = 0;
      }

      endian_sword(&psSaveDroid->formationDir);
      endian_sdword(&psSaveDroid->formationX);
      endian_sdword(&psSaveDroid->formationY);
}

static void LoadDroidMoveControl(DROID * const psDroid, SAVE_DROID const * const psSaveDroid)
{
      unsigned int i;

      // Copy over the endian neutral stuff (all UBYTE)
      psDroid->sMove.Status      = psSaveDroid->sMove.Status;
      psDroid->sMove.Position    = psSaveDroid->sMove.Position;
      psDroid->sMove.numPoints   = psSaveDroid->sMove.numPoints;
      memcpy(&psDroid->sMove.asPath, &psSaveDroid->sMove.asPath, sizeof(psSaveDroid->sMove.asPath));

      // Little endian SDWORDs
      psDroid->sMove.DestinationX = PHYSFS_swapSLE32(psSaveDroid->sMove.DestinationX);
      psDroid->sMove.DestinationY = PHYSFS_swapSLE32(psSaveDroid->sMove.DestinationY);
      psDroid->sMove.srcX         = PHYSFS_swapSLE32(psSaveDroid->sMove.srcX);
      psDroid->sMove.srcY         = PHYSFS_swapSLE32(psSaveDroid->sMove.srcY);
      psDroid->sMove.targetX      = PHYSFS_swapSLE32(psSaveDroid->sMove.targetX);
      psDroid->sMove.targetY      = PHYSFS_swapSLE32(psSaveDroid->sMove.targetY);

      // Little endian floats
      psDroid->sMove.fx           = PHYSFS_swapSLE32(psSaveDroid->sMove.fx);
      psDroid->sMove.fy           = PHYSFS_swapSLE32(psSaveDroid->sMove.fy);
      psDroid->sMove.speed        = PHYSFS_swapSLE32(psSaveDroid->sMove.speed);
      psDroid->sMove.moveDir      = PHYSFS_swapSLE32(psSaveDroid->sMove.moveDir);
      psDroid->sMove.fz           = PHYSFS_swapSLE32(psSaveDroid->sMove.fz);

      // Hack to fix bad droids in savegames
      if (!droidOnMap(psDroid))
      {
            psDroid->sMove.fx = 0;
            psDroid->sMove.fy = 0;
            psDroid->sMove.fz = 0;
            debug(LOG_ERROR, "%s had bad movement coordinates - fixed!", psDroid->aName);
      }
      ASSERT(droidOnMap(psDroid), "loaded droid standing or moving off the map");

      // Little endian SWORDs
      psDroid->sMove.boundX       = PHYSFS_swapSLE16(psSaveDroid->sMove.boundX);
      psDroid->sMove.boundY       = PHYSFS_swapSLE16(psSaveDroid->sMove.boundY);
      psDroid->sMove.bumpDir      = PHYSFS_swapSLE16(psSaveDroid->sMove.bumpDir);
      psDroid->sMove.iVertSpeed   = PHYSFS_swapSLE16(psSaveDroid->sMove.iVertSpeed);

      // Little endian UDWORDs
      psDroid->sMove.bumpTime     = PHYSFS_swapULE32(psSaveDroid->sMove.bumpTime);
      psDroid->sMove.shuffleStart = PHYSFS_swapULE32(psSaveDroid->sMove.shuffleStart);

      // Array of little endian UDWORDS
      for (i = 0; i < sizeof(psSaveDroid->sMove.iAttackRuns) / sizeof(psSaveDroid->sMove.iAttackRuns[0]); ++i)
      {
            psDroid->sMove.iAttackRuns[i] = PHYSFS_swapULE32(psSaveDroid->sMove.iAttackRuns[i]);
      }

      // Little endian UWORDs
      psDroid->sMove.lastBump     = PHYSFS_swapULE16(psSaveDroid->sMove.lastBump);
      psDroid->sMove.pauseTime    = PHYSFS_swapULE16(psSaveDroid->sMove.pauseTime);
      psDroid->sMove.bumpX        = PHYSFS_swapULE16(psSaveDroid->sMove.bumpX);
      psDroid->sMove.bumpY        = PHYSFS_swapULE16(psSaveDroid->sMove.bumpY);

      if (psSaveDroid->sMove.isInFormation)
      {
            psDroid->sMove.psFormation = formationFind(psSaveDroid->formationX, psSaveDroid->formationY);
//          psSaveDroid->formationDir;
//          psSaveDroid->formationX;
//          psSaveDroid->formationY;
            // join a formation if it exists at the destination
            if (psDroid->sMove.psFormation)
            {
                  formationJoin(psDroid->sMove.psFormation, psDroid);
            }
            else
            {
                  // no formation so create a new one
                  if (formationNew(&psDroid->sMove.psFormation, FT_LINE, psSaveDroid->formationX, psSaveDroid->formationY,
                              (SDWORD)psSaveDroid->formationDir))
                  {
                        formationJoin(psDroid->sMove.psFormation, psDroid);
                  }
            }
      }
}

// -----------------------------------------------------------------------------------------
//version 20 + after names change
static DROID* buildDroidFromSaveDroid(SAVE_DROID* psSaveDroid, UDWORD version)
{
      DROID_TEMPLATE                *psTemplate, sTemplate;
      DROID                         *psDroid;
      BOOL                          found;
      UDWORD                              i, id;
      SDWORD                              compInc;
      UDWORD                              burnTime;

//    version;

      psTemplate = &sTemplate;

      psTemplate->pName = NULL;

      //set up the template
      //copy the values across

      sstrcpy(psTemplate->aName, psSaveDroid->name);
      //ignore the first comp - COMP_UNKNOWN
      found = true;
      for (i=1; i < DROID_MAXCOMP; i++)
      {

            compInc = getCompFromName(i, psSaveDroid->asBits[i].name);

        //HACK to get the game to load when ECMs, Sensors or RepairUnits have been deleted
        if (compInc < 0 && (i == COMP_ECM || i == COMP_SENSOR || i == COMP_REPAIRUNIT))
        {
            //set the ECM to be the defaultECM ...
            if (i == COMP_ECM)
            {
                compInc = aDefaultECM[psSaveDroid->player];
            }
            else if (i == COMP_SENSOR)
            {
                compInc = aDefaultSensor[psSaveDroid->player];
            }
            else if (i == COMP_REPAIRUNIT)
            {
                compInc = aDefaultRepair[psSaveDroid->player];
            }
        }
            else if (compInc < 0)
            {

                  debug( LOG_ERROR, "This component no longer exists - %s, the droid will be deleted", psSaveDroid->asBits[i].name );
                  abort();

                  found = false;
                  break;//continue;
            }
            psTemplate->asParts[i] = (UDWORD)compInc;
      }
      if (!found)
      {
            //ignore this record
            ASSERT( found,"buildUnitFromSavedUnit; failed to find weapon" );
            return NULL;
      }
      psTemplate->numWeaps = psSaveDroid->numWeaps;
      if (psSaveDroid->numWeaps > 0)
      {
            for(i = 0;i < psTemplate->numWeaps;i++)
            {
                  int weapon = getCompFromName(COMP_WEAPON, psSaveDroid->asWeaps[i].name);
                  if( weapon < 0)
                  {
                        ASSERT(false, "This component does not exist : %s", psSaveDroid->asWeaps[i].name );
                        return NULL;
                  }
                  psTemplate->asWeaps[i] = weapon;
            }
      }

      psTemplate->buildPoints = calcTemplateBuild(psTemplate);
      psTemplate->powerPoints = calcTemplatePower(psTemplate);
      psTemplate->droidType = psSaveDroid->droidType;

      /*create the Droid */

      // ignore brains for now
      // not any *$&!!! more - JOHN
//    psTemplate->asParts[COMP_BRAIN] = 0;


      turnOffMultiMsg(true);

      if(psSaveDroid->x == INVALID_XY)
      {
            psDroid = buildDroid(psTemplate, psSaveDroid->x, psSaveDroid->y,
                  psSaveDroid->player, true);
      }
      else if(psSaveDroid->saveType == DROID_ON_TRANSPORT)
      {
            psDroid = buildDroid(psTemplate, 0, 0,
                  psSaveDroid->player, true);
      }
      else
      {
            psDroid = buildDroid(psTemplate, psSaveDroid->x, psSaveDroid->y,
                  psSaveDroid->player, false);
      }

      if(psDroid == NULL)
      {
            ASSERT( false,"buildUnitFromSavedUnit; failed to build unit" );
            return NULL;
      }

      turnOffMultiMsg(false);


      //copy the droid's weapon stats
      for (i=0; i < psDroid->numWeaps; i++)
      {
            if (psDroid->asWeaps[i].nStat > 0)
            {
                  psDroid->asWeaps[i].hitPoints = psSaveDroid->asWeaps[i].hitPoints;
                  psDroid->asWeaps[i].ammo = psSaveDroid->asWeaps[i].ammo;
                  psDroid->asWeaps[i].lastFired = psSaveDroid->asWeaps[i].lastFired;
            }
      }
      //copy the values across
      psDroid->id = psSaveDroid->id;
      //are these going to ever change from the values set up with?
//                psDroid->pos.z = psSaveDroid->pos.z;            // use the correct map height value

      psDroid->direction = psSaveDroid->direction;
      psDroid->body = psSaveDroid->body;
      if (psDroid->body > psDroid->originalBody)
      {
            psDroid->body = psDroid->originalBody;
      }

      psDroid->inFire = psSaveDroid->inFire;
      psDroid->burnDamage = psSaveDroid->burnDamage;
      burnTime = psSaveDroid->burnStart;
      psDroid->burnStart = burnTime;

      psDroid->experience = (UWORD)psSaveDroid->numKills;
      //version 14
      psDroid->resistance = droidResistance(psDroid);

      //version 11
      //Watermelon:make it back-compatible with older versions of save
      for (i=0; i < psDroid->numWeaps; i++)
      {
            if (version >= VERSION_24)
            {
                  psDroid->turretRotation[i] = psSaveDroid->turretRotation[i];
                  psDroid->turretPitch[i] = psSaveDroid->turretPitch[i];
            }
            else
            {
                  psDroid->turretRotation[i] = psSaveDroid->turretRotation[0];
                  psDroid->turretPitch[i] = psSaveDroid->turretPitch[0];
            }
      }
      //version 12
      psDroid->order                      = psSaveDroid->order;
      psDroid->orderX                     = psSaveDroid->orderX;
      psDroid->orderY                     = psSaveDroid->orderY;
      psDroid->orderX2                    = psSaveDroid->orderX2;
      psDroid->orderY2                    = psSaveDroid->orderY2;
      psDroid->timeLastHit                = psSaveDroid->timeLastHit;
      //rebuild the object pointer from the ID
      FIXME_CAST_ASSIGN(UDWORD, psDroid->psTarget, psSaveDroid->targetID);
      psDroid->secondaryOrder       = psSaveDroid->secondaryOrder;
      psDroid->action                     = psSaveDroid->action;
      psDroid->actionX                    = psSaveDroid->actionX;
      psDroid->actionY                    = psSaveDroid->actionY;
      //rebuild the object pointer from the ID
      FIXME_CAST_ASSIGN(UDWORD, psDroid->psActionTarget[0], psSaveDroid->actionTargetID);
      psDroid->actionStarted        = psSaveDroid->actionStarted;
      psDroid->actionPoints         = psSaveDroid->actionPoints;
      psDroid->powerAccrued         = psSaveDroid->actionHeight;
      //added for V14


      //version 18
      if (psSaveDroid->tarStatName[0] == 0)
      {
            psDroid->psTarStats = NULL;
      }
      else
      {
            id = getStructStatFromName(psSaveDroid->tarStatName);
            if (id != (UDWORD)-1)
            {
                  psDroid->psTarStats = (BASE_STATS*)&asStructureStats[id];
            }
            else
            {
                  ASSERT( false,"loadUnit TargetStat not found" );
                  psDroid->psTarStats = NULL;
            }
      }
      //rebuild the object pointer from the ID
      FIXME_CAST_ASSIGN(UDWORD, psDroid->psBaseStruct, psSaveDroid->baseStructID);
      psDroid->group = psSaveDroid->group;
      psDroid->selected = psSaveDroid->selected;
//20feb     psDroid->cluster = psSaveDroid->cluster;
      psDroid->died = psSaveDroid->died;
      psDroid->lastEmission = psSaveDroid->lastEmission;
      for (i=0; i < MAX_PLAYERS; i++)
      {
            psDroid->visible[i]     = psSaveDroid->visible[i];
      }

      if (version >= VERSION_21)//version 21
      {
            if ( (psDroid->droidType != DROID_TRANSPORTER) &&
                                     (psDroid->droidType != DROID_COMMAND) )
            {
                  //rebuild group from command id in loadDroidSetPointers
                  FIXME_CAST_ASSIGN(UDWORD, psDroid->psGroup, psSaveDroid->commandId);
                  FIXME_CAST_ASSIGN(UDWORD, psDroid->psGrpNext, NULL_ID);
            }
      }
      else
      {
            if ( (psDroid->droidType != DROID_TRANSPORTER) &&
                                     (psDroid->droidType != DROID_COMMAND) )
            {
                  //dont rebuild group from command id in loadDroidSetPointers
                  psDroid->psGroup = NULL;
                  psDroid->psGrpNext = NULL;
            }
      }

      if (version >= VERSION_24)//version 24
      {
            psDroid->resistance = (SWORD)psSaveDroid->resistance;
            LoadDroidMoveControl(psDroid, psSaveDroid);
      }
      return psDroid;
}


// -----------------------------------------------------------------------------------------
static BOOL loadDroidSetPointers(void)
{
      UDWORD            player,list;
      DROID       *psDroid, *psCommander;
      DROID       **ppsDroidLists[3], *psNext;

      ppsDroidLists[0] = apsDroidLists;
      ppsDroidLists[1] = mission.apsDroidLists;
      ppsDroidLists[2] = apsLimboDroids;

      for(list = 0; list<3; list++)
      {
            debug( LOG_NEVER, "List %d\n", list );
            for(player = 0; player<MAX_PLAYERS; player++)
            {
                  psDroid=(DROID *)ppsDroidLists[list][player];
                  while (psDroid)
                  {
                        UDWORD id;
                        //Target rebuild the object pointer from the ID
                        FIXME_CAST_ASSIGN(UDWORD, id, psDroid->psTarget);
                        if (id != NULL_ID)
                        {
                              setSaveDroidTarget(psDroid, getBaseObjFromId(id));
                              ASSERT(psDroid->psTarget != NULL, "Saved Droid psTarget getBaseObjFromId() failed");
                              if (psDroid->psTarget == NULL)
                              {
                                    psDroid->order = DORDER_NONE;
                              }
                        }
                        else
                        {
                              setSaveDroidTarget(psDroid, NULL);
                        }
                        //ActionTarget rebuild the object pointer from the ID
                        FIXME_CAST_ASSIGN(UDWORD, id, psDroid->psActionTarget[0]);
                        if (id != NULL_ID)
                        {
                              setSaveDroidActionTarget(psDroid, getBaseObjFromId(id), 0);
                              ASSERT( psDroid->psActionTarget[0] != NULL,"Saved Droid psActionTarget getBaseObjFromId() failed" );
                              if (psDroid->psActionTarget[0] == NULL)
                              {
                                    psDroid->action = DACTION_NONE;
                              }
                        }
                        else
                        {
                              setSaveDroidActionTarget(psDroid, NULL, 0);
                        }
                        //BaseStruct rebuild the object pointer from the ID
                        FIXME_CAST_ASSIGN(UDWORD, id, psDroid->psBaseStruct);
                        if (id != NULL_ID)
                        {
                              setSaveDroidBase(psDroid, (STRUCTURE*)getBaseObjFromId(id));
                              ASSERT( psDroid->psBaseStruct != NULL,"Saved Droid psBaseStruct getBaseObjFromId() failed" );
                              if (psDroid->psBaseStruct == NULL)
                              {
                                    psDroid->action = DACTION_NONE;
                              }
                        }
                        else
                        {
                              setSaveDroidBase(psDroid, NULL);//psSaveDroid->targetID
                        }
                        if (saveGameVersion > VERSION_20)
                        {
                              UDWORD _tmpid;
                              //rebuild group for droids in command group from the commander ID
                              FIXME_CAST_ASSIGN(UDWORD, id, psDroid->psGrpNext);
                              if (id == NULL_ID)
                              {
                                    FIXME_CAST_ASSIGN(UDWORD, _tmpid, psDroid->psGroup);
                                    psDroid->psGroup = NULL;
                                    psDroid->psGrpNext = NULL;
                                    if (_tmpid != NULL_ID)
                                    {
                                          psCommander = (DROID*)getBaseObjFromId(_tmpid);
                                          ASSERT( psCommander != NULL,"Saved Droid psCommander getBaseObjFromId() failed" );
                                          if (psCommander != NULL)
                                          {
                                                cmdDroidAddDroid(psCommander,psDroid);
                                          }
                                    }
                              }
                        }
                        psDroid = psDroid->psNext;
                  }
            }
      }

      /* HACK: Make sure all cargo units are properly initialized! I am not sure why we need
       * to do this, but the code in this file is too horrible to debug. - Per */
      for(list = 0; list<3; list++)
      {
            for(player = 0; player<MAX_PLAYERS; player++)
            {
                  for (psNext = (DROID *)ppsDroidLists[list][player]; psNext; psNext = psNext->psNext)
                  {
                        if (psNext->droidType == DROID_TRANSPORTER)
                        {
                              DROID *psCargo, *psTemp;

                              for (psCargo = psNext->psGroup->psList; psCargo; psCargo = psTemp)
                              {
                                    UDWORD i;

                                    psTemp = psCargo->psGrpNext;
                                    setSaveDroidTarget(psCargo, NULL);
                                    for (i = 0; i < DROID_MAXWEAPS; i++)
                                    {
                                          setSaveDroidActionTarget(psCargo, NULL, i);
                                    }
                                    setSaveDroidBase(psCargo, NULL);
                              }
                        }
                  }
            }
      }


      return true;
}

// -----------------------------------------------------------------------------------------
/* code specific to version 11 of a save droid */
BOOL loadSaveDroidV11(char *pFileData, UDWORD filesize, UDWORD numDroids, UDWORD version, DROID **ppsCurrentDroidLists)
{
      SAVE_DROID_V11                      *psSaveDroid, sSaveDroid;
//    DROID_TEMPLATE                *psTemplate, sTemplate;
      DROID                         *psDroid;
      DROID_GROUP                   *psCurrentTransGroup;
      UDWORD                              count;
      UDWORD                              NumberOfSkippedDroids=0;
      UDWORD                              sizeOfSaveDroid = 0;
      DROID_GROUP                   *psGrp;
      UBYTE i;

      psCurrentTransGroup = NULL;

      psSaveDroid = &sSaveDroid;
      if (version <= VERSION_10)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID_V9);
      }
      else if (version == VERSION_11)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID_V11);
      }
      else if (version <= CURRENT_VERSION_NUM)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID_V12);
      }

      if ((sizeOfSaveDroid * numDroids + DROID_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "unitLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the droid data */
      for (count = 0; count < numDroids; count ++, pFileData += sizeOfSaveDroid)
      {
            memcpy(psSaveDroid, pFileData, sizeOfSaveDroid);

            /* DROID_SAVE_V11 includes OBJECT_SAVE_V19, SAVE_WEAPON_V19 */
            endian_udword(&psSaveDroid->body);
            endian_udword(&psSaveDroid->numWeaps);
            endian_udword(&psSaveDroid->numKills);
            endian_uword(&psSaveDroid->turretRotation);
            endian_uword(&psSaveDroid->turretPitch);
            /* OBJECT_SAVE_V19 */
            endian_udword(&psSaveDroid->id);
            endian_udword(&psSaveDroid->x);
            endian_udword(&psSaveDroid->y);
            endian_udword(&psSaveDroid->z);
            endian_udword(&psSaveDroid->direction);
            endian_udword(&psSaveDroid->player);
            endian_udword(&psSaveDroid->burnStart);
            endian_udword(&psSaveDroid->burnDamage);
            for(i = 0; i < TEMP_DROID_MAXPROGS; i++) {
                  /* SAVE_WEAPON_V19 */
                  endian_udword(&psSaveDroid->asWeaps[i].hitPoints);
                  endian_udword(&psSaveDroid->asWeaps[i].ammo);
                  endian_udword(&psSaveDroid->asWeaps[i].lastFired);
            }

            // Here's a check that will allow us to load up save games on the playstation from the PC
            //  - It will skip data from any players after MAX_PLAYERS
            if (psSaveDroid->player >= MAX_PLAYERS)
            {
                  NumberOfSkippedDroids++;
                  psSaveDroid->player=MAX_PLAYERS-1;  // now don't lose any droids ... force them to be the last player
            }

            psDroid = buildDroidFromSaveDroidV11(psSaveDroid);

            if (psDroid == NULL)
            {
                  debug( LOG_ERROR, "unitLoad: Template not found for unit\n" );
                  abort();
            }
            else if (psSaveDroid->saveType == DROID_ON_TRANSPORT)
            {
                  //add the droid to the list
                  setSaveDroidTarget(psDroid, NULL);
                  for (i = 0; i < DROID_MAXWEAPS; i++)
                  {
                        setSaveDroidActionTarget(psDroid, NULL, i);
                  }
                  setSaveDroidBase(psDroid, NULL);
                  ASSERT( psCurrentTransGroup != NULL,"loadSaveUnitV9; Transporter unit without group " );
                  grpJoin(psCurrentTransGroup, psDroid);
            }
            else
            {
                  //add the droid to the list
                  addDroid(psDroid, ppsCurrentDroidLists);
            }

            if (psDroid != NULL)
            {
                  if (psDroid->droidType == DROID_TRANSPORTER)
                  {
                        //set current TransPorter group
                        if (!grpCreate(&psGrp))
                        {
                              debug( LOG_NEVER, "unit build: unable to create group\n" );
                              return false;
                        }
                        grpJoin(psGrp, psDroid);
                        psCurrentTransGroup = psDroid->psGroup;
                  }
            }
      }
      if (NumberOfSkippedDroids>0)
      {
            debug( LOG_ERROR, "unitLoad: Bad Player number in %d unit(s)... assigned to the last player!\n", NumberOfSkippedDroids );
            abort();
      }

      ppsCurrentDroidLists = NULL;//ensure it always gets set

      return true;
}

// -----------------------------------------------------------------------------------------
/* code specific all versions upto from 12 to 19*/
BOOL loadSaveDroidV19(char *pFileData, UDWORD filesize, UDWORD numDroids, UDWORD version, DROID **ppsCurrentDroidLists)
{
      SAVE_DROID_V18                      *psSaveDroid, sSaveDroid;
//    DROID_TEMPLATE                *psTemplate, sTemplate;
      DROID                         *psDroid;
      DROID_GROUP                   *psCurrentTransGroup;
      UDWORD                              count;
      UDWORD                              NumberOfSkippedDroids=0;
      UDWORD                              sizeOfSaveDroid = 0;
      DROID_GROUP                   *psGrp;
      UBYTE i;

      psCurrentTransGroup = NULL;

      psSaveDroid = &sSaveDroid;
      if (version <= VERSION_10)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID_V9);
      }
      else if (version == VERSION_11)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID_V11);
      }
      else if (version == VERSION_12)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID_V12);
      }
      else if (version < VERSION_18)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID_V14);
      }
      else if (version <= CURRENT_VERSION_NUM)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID_V18);
      }

      if ((sizeOfSaveDroid * numDroids + DROID_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "unitLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the droid data */
      for (count = 0; count < numDroids; count ++, pFileData += sizeOfSaveDroid)
      {
            memcpy(psSaveDroid, pFileData, sizeOfSaveDroid);

            /* DROID_SAVE_V18 includes DROID_SAVE_V12*/
            endian_udword(&psSaveDroid->baseStructID);
            endian_udword(&psSaveDroid->died);
            endian_udword(&psSaveDroid->lastEmission);
            /* DROID_SAVE_V12 includes DROID_SAVE_V9 */
            endian_uword(&psSaveDroid->turretRotation);
            endian_uword(&psSaveDroid->turretPitch);
            endian_sdword(&psSaveDroid->order);
            endian_uword(&psSaveDroid->orderX);
            endian_uword(&psSaveDroid->orderY);
            endian_uword(&psSaveDroid->orderX2);
            endian_uword(&psSaveDroid->orderY2);
            endian_udword(&psSaveDroid->timeLastHit);
            endian_udword(&psSaveDroid->targetID);
            endian_udword(&psSaveDroid->secondaryOrder);
            endian_sdword(&psSaveDroid->action);
            endian_udword(&psSaveDroid->actionX);
            endian_udword(&psSaveDroid->actionY);
            endian_udword(&psSaveDroid->actionTargetID);
            endian_udword(&psSaveDroid->actionStarted);
            endian_udword(&psSaveDroid->actionPoints);
            endian_uword(&psSaveDroid->actionHeight);
            /* DROID_SAVE_V9 includes OBJECT_SAVE_V19, SAVE_WEAPON_V19 */
            endian_udword(&psSaveDroid->body);
            endian_udword(&psSaveDroid->saveType);
            endian_udword(&psSaveDroid->numWeaps);
            endian_udword(&psSaveDroid->numKills);
            /* OBJECT_SAVE_V19 */
            endian_udword(&psSaveDroid->id);
            endian_udword(&psSaveDroid->x);
            endian_udword(&psSaveDroid->y);
            endian_udword(&psSaveDroid->z);
            endian_udword(&psSaveDroid->direction);
            endian_udword(&psSaveDroid->player);
            endian_udword(&psSaveDroid->burnStart);
            endian_udword(&psSaveDroid->burnDamage);
            for(i = 0; i < TEMP_DROID_MAXPROGS; i++) {
                  /* SAVE_WEAPON_V19 */
                  endian_udword(&psSaveDroid->asWeaps[i].hitPoints);
                  endian_udword(&psSaveDroid->asWeaps[i].ammo);
                  endian_udword(&psSaveDroid->asWeaps[i].lastFired);
            }

            // Here's a check that will allow us to load up save games on the playstation from the PC
            //  - It will skip data from any players after MAX_PLAYERS
            if (psSaveDroid->player >= MAX_PLAYERS)
            {
                  NumberOfSkippedDroids++;
                  psSaveDroid->player=MAX_PLAYERS-1;  // now don't lose any droids ... force them to be the last player
            }

            psDroid = buildDroidFromSaveDroidV19((SAVE_DROID_V18*)psSaveDroid, version);

            if (psDroid == NULL)
            {
                  ASSERT( psDroid != NULL,"unitLoad: Failed to build new unit\n" );
            }
            else if (psSaveDroid->saveType == DROID_ON_TRANSPORT)
            {
                  //add the droid to the list
                  psDroid->order = DORDER_NONE;
                  psDroid->action = DACTION_NONE;
                  setSaveDroidTarget(psDroid, NULL);
                  for (i = 0; i < DROID_MAXWEAPS; i++)
                  {
                        setSaveDroidActionTarget(psDroid, NULL, i);
                  }
                  setSaveDroidBase(psDroid, NULL);
                  //add the droid to the list
                  ASSERT( psCurrentTransGroup != NULL,"loadSaveUnitV9; Transporter unit without group " );
                  grpJoin(psCurrentTransGroup, psDroid);
            }
            else
            {
                  //add the droid to the list
                  addDroid(psDroid, ppsCurrentDroidLists);
            }

            if (psDroid != NULL)
            {
                  if (psDroid->droidType == DROID_TRANSPORTER)
                  {
                        //set current TransPorter group
                        if (!grpCreate(&psGrp))
                        {
                              debug( LOG_NEVER, "unit build: unable to create group\n" );
                              return false;
                        }
                        grpJoin(psGrp, psDroid);
                        psCurrentTransGroup = psDroid->psGroup;
                  }
            }
      }
      if (NumberOfSkippedDroids>0)
      {
            debug( LOG_ERROR, "unitLoad: Bad Player number in %d unit(s)... assigned to the last player!\n", NumberOfSkippedDroids );
            abort();
      }

      ppsCurrentDroidLists = NULL;//ensure it always gets set

      return true;
}

// -----------------------------------------------------------------------------------------
/* code for all versions after save name change v19*/
BOOL loadSaveDroidV(char *pFileData, UDWORD filesize, UDWORD numDroids, UDWORD version, DROID **ppsCurrentDroidLists)
{
      SAVE_DROID                    sSaveDroid, *psSaveDroid = &sSaveDroid;
//    DROID_TEMPLATE                *psTemplate, sTemplate;
      DROID                         *psDroid;
      DROID_GROUP                   *psCurrentTransGroup = NULL;
      UDWORD                              count;
      UDWORD                              NumberOfSkippedDroids=0;
      UDWORD                              sizeOfSaveDroid = 0;
//    DROID_GROUP                   *psGrp;
      UBYTE i;

      if (version <= VERSION_20)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID_V20);
      }
      else if (version <= VERSION_23)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID_V21);
      }
      else if (version <= CURRENT_VERSION_NUM)
      {
            sizeOfSaveDroid = sizeof(SAVE_DROID);
      }

      if ((sizeOfSaveDroid * numDroids + DROID_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "unitLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the droid data */
      for (count = 0; count < numDroids; count ++, pFileData += sizeOfSaveDroid)
      {
            memcpy(psSaveDroid, pFileData, sizeOfSaveDroid);

            /* DROID_SAVE_V24 includes DROID_SAVE_V21,
             * SAVE_MOVE_CONTROL */
            endian_sdword(&psSaveDroid->resistance);
            endian_sword(&psSaveDroid->formationDir);
            endian_sdword(&psSaveDroid->formationX);
            endian_sdword(&psSaveDroid->formationY);
            /* DROID_SAVE_V21 includes DROID_SAVE_V20 */
            endian_udword(&psSaveDroid->commandId);
            /* DROID_SAVE_V20 includes OBJECT_SAVE_V20, SAVE_WEAPON */
            endian_udword(&psSaveDroid->body);
            endian_udword(&psSaveDroid->saveType);
            endian_udword(&psSaveDroid->numWeaps);
            endian_udword(&psSaveDroid->numKills);
            //Watermelon:'hack' to make it read turretRotation,Pitch table properly
            if( version == CURRENT_VERSION_NUM )
            {
                  for(i = 0;i < psSaveDroid->numWeaps;i++)
                  {
                        endian_uword(&psSaveDroid->turretRotation[i]);
                        endian_uword(&psSaveDroid->turretPitch[i]);
                  }
            }
            else
            {
                  endian_uword(&psSaveDroid->turretRotation[0]);
                  endian_uword(&psSaveDroid->turretPitch[0]);
            }
            endian_sdword(&psSaveDroid->order);
            endian_uword(&psSaveDroid->orderX);
            endian_uword(&psSaveDroid->orderY);
            endian_uword(&psSaveDroid->orderX2);
            endian_uword(&psSaveDroid->orderY2);
            endian_udword(&psSaveDroid->timeLastHit);
            endian_udword(&psSaveDroid->targetID);
            endian_udword(&psSaveDroid->secondaryOrder);
            endian_sdword(&psSaveDroid->action);
            endian_udword(&psSaveDroid->actionX);
            endian_udword(&psSaveDroid->actionY);
            endian_udword(&psSaveDroid->actionTargetID);
            endian_udword(&psSaveDroid->actionStarted);
            endian_udword(&psSaveDroid->actionPoints);
            endian_uword(&psSaveDroid->actionHeight);
            endian_udword(&psSaveDroid->baseStructID);
            endian_udword(&psSaveDroid->died);
            endian_udword(&psSaveDroid->lastEmission);
            /* OBJECT_SAVE_V20 */
            endian_udword(&psSaveDroid->id);
            endian_udword(&psSaveDroid->x);
            endian_udword(&psSaveDroid->y);
            endian_udword(&psSaveDroid->z);
            endian_udword(&psSaveDroid->direction);
            endian_udword(&psSaveDroid->player);
            endian_udword(&psSaveDroid->burnStart);
            endian_udword(&psSaveDroid->burnDamage);
            /* SAVE_MOVE_CONTROL */
            endian_sdword(&psSaveDroid->sMove.DestinationX);
            endian_sdword(&psSaveDroid->sMove.DestinationY);
            endian_sdword(&psSaveDroid->sMove.srcX);
            endian_sdword(&psSaveDroid->sMove.srcY);
            endian_sdword(&psSaveDroid->sMove.targetX);
            endian_sdword(&psSaveDroid->sMove.targetY);
            endian_sword(&psSaveDroid->sMove.boundX);
            endian_sword(&psSaveDroid->sMove.boundY);
            endian_sword(&psSaveDroid->sMove.moveDir);
            endian_sword(&psSaveDroid->sMove.bumpDir);
            endian_udword(&psSaveDroid->sMove.bumpTime);
            endian_uword(&psSaveDroid->sMove.lastBump);
            endian_uword(&psSaveDroid->sMove.pauseTime);
            endian_uword(&psSaveDroid->sMove.bumpX);
            endian_uword(&psSaveDroid->sMove.bumpY);
            endian_udword(&psSaveDroid->sMove.shuffleStart);
            endian_sword(&psSaveDroid->sMove.iVertSpeed);
            if( version == CURRENT_VERSION_NUM )
            {
                  for(i = 0;i < psSaveDroid->numWeaps;i++)
                  {
                        endian_udword(&psSaveDroid->sMove.iAttackRuns[i]);
                  }
                  //endian_uword(&psSaveDroid->sMove.iGuardRadius);
            }
            else
            {
                  endian_udword(&psSaveDroid->sMove.iAttackRuns[0]);
            }
            for(i = 0; i < TEMP_DROID_MAXPROGS; i++) {
                  /* SAVE_WEAPON */
                  endian_udword(&psSaveDroid->asWeaps[i].hitPoints);
                  endian_udword(&psSaveDroid->asWeaps[i].ammo);
                  endian_udword(&psSaveDroid->asWeaps[i].lastFired);
            }

            // Here's a check that will allow us to load up save games on the playstation from the PC
            //  - It will skip data from any players after MAX_PLAYERS
            if (psSaveDroid->player >= MAX_PLAYERS)
            {
                  NumberOfSkippedDroids++;
                  psSaveDroid->player=MAX_PLAYERS-1;  // now don't lose any droids ... force them to be the last player
            }

            psDroid = buildDroidFromSaveDroid(psSaveDroid, version);

            if (psDroid == NULL)
            {
                  ASSERT( psDroid != NULL,"unitLoad: Failed to build new unit\n" );
            }
            else if (psSaveDroid->saveType == DROID_ON_TRANSPORT)
            {
                  //add the droid to the list
                  psDroid->order = DORDER_NONE;
                  psDroid->action = DACTION_NONE;
                  setSaveDroidTarget(psDroid, NULL);
                  for (i = 0; i < DROID_MAXWEAPS; i++)
                  {
                        setSaveDroidActionTarget(psDroid, NULL, i);
                  }
                  setSaveDroidBase(psDroid, NULL);
                  //add the droid to the list
                  psDroid->psGroup = NULL;
                  psDroid->psGrpNext = NULL;
                  ASSERT( psCurrentTransGroup != NULL,"loadSaveUnitV9; Transporter unit without group " );
                  grpJoin(psCurrentTransGroup, psDroid);
            }
            else if (psDroid->droidType == DROID_TRANSPORTER)
            {
                        //set current TransPorter group
/*set in build droid
                        if (!grpCreate(&psGrp))
                        {
                              DBPRINTF(("droid build: unable to create group\n"));
                              return false;
                        }
                        psDroid->psGroup = NULL;
                        grpJoin(psGrp, psDroid);
*/
                  psCurrentTransGroup = psDroid->psGroup;
                  addDroid(psDroid, ppsCurrentDroidLists);
            }
            else
            {
                  //add the droid to the list
                  addDroid(psDroid, ppsCurrentDroidLists);
            }
      }
      if (NumberOfSkippedDroids>0)
      {
            debug( LOG_ERROR, "unitLoad: Bad Player number in %d unit(s)... assigned to the last player!\n", NumberOfSkippedDroids );
            abort();
      }

      ppsCurrentDroidLists = NULL;//ensure it always gets set

      return true;
}

// -----------------------------------------------------------------------------------------
static BOOL buildSaveDroidFromDroid(SAVE_DROID* psSaveDroid, DROID* psCurr, DROID_SAVE_TYPE saveType)
{
      UDWORD                        i;

                  /*want to store the resource ID string for compatibilty with
                  different versions of save game - NOT HAPPENING - the name saved is
                  the translated name - old versions of save games should load because
                  templates are loaded from Access AND the save game so they should all
                  still exist*/
                  ASSERT(strlen(psCurr->aName) + 1 < sizeof(psSaveDroid->name), "Truncation of droid name occurred! Max droid length (without truncation while saving) is %zu", sizeof(psSaveDroid->name) - 1);
                  sstrcpy(psSaveDroid->name, psCurr->aName);

                  // not interested in first comp - COMP_UNKNOWN
                  for (i=1; i < DROID_MAXCOMP; i++)
                  {

                        if (!getNameFromComp(i, psSaveDroid->asBits[i].name, psCurr->asBits[i].nStat))

                        {
                              //ignore this record
                              break;
                        }
                  }
                  psSaveDroid->body = psCurr->body;
                  //Watermelon:loop thru all weapons
                  psSaveDroid->numWeaps = psCurr->numWeaps;
                  for(i = 0;i < psCurr->numWeaps;i++)
                  {
                        if (psCurr->asWeaps[i].nStat > 0)
                        {
                              //there is only one weapon now

                              if (getNameFromComp(COMP_WEAPON, psSaveDroid->asWeaps[i].name, psCurr->asWeaps[i].nStat))

                              {
                              psSaveDroid->asWeaps[i].hitPoints = psCurr->asWeaps[i].hitPoints;
                              psSaveDroid->asWeaps[i].ammo = psCurr->asWeaps[i].ammo;
                              psSaveDroid->asWeaps[i].lastFired = psCurr->asWeaps[i].lastFired;
                              }
                        }
                        else
                        {

                              psSaveDroid->asWeaps[i].name[i] = '\0';
                        }
                  }


            //save out experience level
                  psSaveDroid->numKills         = psCurr->experience;
                  //version 11
                  //Watermelon:endian_udword for new save format
                  for(i = 0;i < psCurr->numWeaps;i++)
                  {
                        psSaveDroid->turretRotation[i] = psCurr->turretRotation[i];
                        psSaveDroid->turretPitch[i]   = psCurr->turretPitch[i];
                  }
                  //version 12
                  psSaveDroid->order                  = psCurr->order;
                  psSaveDroid->orderX                 = psCurr->orderX;
                  psSaveDroid->orderY                 = psCurr->orderY;
                  psSaveDroid->orderX2          = psCurr->orderX2;
                  psSaveDroid->orderY2          = psCurr->orderY2;
                  psSaveDroid->timeLastHit      = psCurr->timeLastHit;

                  psSaveDroid->targetID = NULL_ID;
                  if (psCurr->psTarget != NULL && psCurr->psTarget->died <= 1 && checkValidId(psCurr->psTarget->id))
                  {
                        psSaveDroid->targetID = psCurr->psTarget->id;
                  }

                  psSaveDroid->secondaryOrder = psCurr->secondaryOrder;
                  psSaveDroid->action                 = psCurr->action;
                  psSaveDroid->actionX          = psCurr->actionX;
                  psSaveDroid->actionY          = psCurr->actionY;

                  psSaveDroid->actionTargetID = NULL_ID;
                  if (psCurr->psActionTarget[0] != NULL && psCurr->psActionTarget[0]->died <= 1 && checkValidId( psCurr->psActionTarget[0]->id))
                  {
                        psSaveDroid->actionTargetID = psCurr->psActionTarget[0]->id;
                  }

                  psSaveDroid->actionStarted    = psCurr->actionStarted;
                  psSaveDroid->actionPoints     = psCurr->actionPoints;
                  psSaveDroid->actionHeight     = psCurr->powerAccrued;

                  //version 14
                  if (psCurr->psTarStats != NULL)
                  {
                        ASSERT(strlen(psCurr->psTarStats->pName) < sizeof(psSaveDroid->tarStatName), "writeUnitFile; psTarStat pName Error");
                        sstrcpy(psSaveDroid->tarStatName, psCurr->psTarStats->pName);
                  }
                  else
                  {
                        strcpy(psSaveDroid->tarStatName,"");
                  }

                  psSaveDroid->baseStructID = NULL_ID;
                  if ((psCurr->psBaseStruct != NULL))
                  {
                        UDWORD _tmpid;
                        FIXME_CAST_ASSIGN(UDWORD, _tmpid, psCurr->psBaseStruct);
                        if (_tmpid != NULL_ID && psCurr->psBaseStruct->died <= 1 && checkValidId(psCurr->psBaseStruct->id))
                        {
                              psSaveDroid->baseStructID = psCurr->psBaseStruct->id;
                        }
                  }

                  psSaveDroid->group = psCurr->group;
                  psSaveDroid->selected = psCurr->selected;
//20feb                 psSaveDroid->cluster = psCurr->cluster;
                  psSaveDroid->died = psCurr->died;
                  psSaveDroid->lastEmission = psCurr->lastEmission;
                  for (i=0; i < MAX_PLAYERS; i++)
                  {
                        psSaveDroid->visible[i] = psCurr->visible[i];
                  }

                  //version 21
                  psSaveDroid->commandId = NULL_ID;
                  if (hasCommander(psCurr))
                  {
                        if (((DROID*)psCurr->psGroup->psCommander)->died <= 1)
                        {
                              psSaveDroid->commandId = ((DROID*)psCurr->psGroup->psCommander)->id;
                        }
                        ASSERT( ((DROID*)psCurr->psGroup->psCommander)->died <= 1, "SaveUnit pcCommander died" );
                        ASSERT( checkValidId(((DROID*)psCurr->psGroup->psCommander)->id), "SaveUnit pcCommander not found" );
                  }

                  //version 24
                  psSaveDroid->resistance = psCurr->resistance;

                  // Save the move control
                  SaveDroidMoveControl(psSaveDroid, psCurr);

                  psSaveDroid->id = psCurr->id;
                  psSaveDroid->x = psCurr->pos.x;
                  psSaveDroid->y = psCurr->pos.y;
                  psSaveDroid->z = psCurr->pos.z;
                  psSaveDroid->direction = psCurr->direction;
                  psSaveDroid->player = psCurr->player;
                  psSaveDroid->inFire = psCurr->inFire;
                  psSaveDroid->burnStart = psCurr->burnStart;
                  psSaveDroid->burnDamage = psCurr->burnDamage;
                  psSaveDroid->droidType = (UBYTE)psCurr->droidType;
                  psSaveDroid->saveType = (UBYTE)saveType;//identifies special load cases

                  /* SAVE_DROID is DROID_SAVE_V24 */
                  /* DROID_SAVE_V24 includes DROID_SAVE_V21 */
                  endian_sdword(&psSaveDroid->resistance);

                  // psSaveDroid->formationDir, psSaveDroid->formationX and psSaveDroid->formationY are set by SaveDroidMoveControl
                  // already, which also performs endian swapping, so we can (and should!) safely ignore those here.

                  /* DROID_SAVE_V21 includes DROID_SAVE_V20 */
                  endian_udword(&psSaveDroid->commandId);
                  /* DROID_SAVE_V20 includes OBJECT_SAVE_V20 */
                  endian_udword(&psSaveDroid->body);
                  endian_udword(&psSaveDroid->saveType);
                  for(i = 0; i < TEMP_DROID_MAXPROGS; i++) {
                        endian_udword(&psSaveDroid->asWeaps[i].hitPoints);
                        endian_udword(&psSaveDroid->asWeaps[i].ammo);
                        endian_udword(&psSaveDroid->asWeaps[i].lastFired);
                  }
                  endian_udword(&psSaveDroid->numKills);
                  //Watermelon:endian_udword for new save format
                  for(i = 0;i < psSaveDroid->numWeaps;i++)
                  {
                        endian_uword(&psSaveDroid->turretRotation[i]);
                        endian_uword(&psSaveDroid->turretPitch[i]);
                  }
                  endian_udword(&psSaveDroid->numWeaps);
                  endian_sdword(&psSaveDroid->order);
                  endian_uword(&psSaveDroid->orderX);
                  endian_uword(&psSaveDroid->orderY);
                  endian_uword(&psSaveDroid->orderX2);
                  endian_uword(&psSaveDroid->orderY2);
                  endian_udword(&psSaveDroid->timeLastHit);
                  endian_udword(&psSaveDroid->targetID);
                  endian_udword(&psSaveDroid->secondaryOrder);
                  endian_sdword(&psSaveDroid->action);
                  endian_udword(&psSaveDroid->actionX);
                  endian_udword(&psSaveDroid->actionY);
                  endian_udword(&psSaveDroid->actionTargetID);
                  endian_udword(&psSaveDroid->actionStarted);
                  endian_udword(&psSaveDroid->actionPoints);
                  endian_uword(&psSaveDroid->actionHeight);
                  endian_udword(&psSaveDroid->baseStructID);
                  endian_udword(&psSaveDroid->died);
                  endian_udword(&psSaveDroid->lastEmission);
                  /* OBJECT_SAVE_V20 */
                  endian_udword(&psSaveDroid->id);
                  endian_udword(&psSaveDroid->x);
                  endian_udword(&psSaveDroid->y);
                  endian_udword(&psSaveDroid->z);
                  endian_udword(&psSaveDroid->direction);
                  endian_udword(&psSaveDroid->player);
                  endian_udword(&psSaveDroid->burnStart);
                  endian_udword(&psSaveDroid->burnDamage);

                  return true;
}

// -----------------------------------------------------------------------------------------
/*
Writes the linked list of droids for each player to a file
*/
BOOL writeDroidFile(char *pFileName, DROID **ppsCurrentDroidLists)
{
      char *pFileData = NULL;
      UDWORD                        fileSize, player, totalDroids=0;
      DROID                   *psCurr;
      DROID                   *psTrans;
      DROID_SAVEHEADER  *psHeader;
      SAVE_DROID              *psSaveDroid;
      BOOL status = true;

      //total all the droids in the world
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for (psCurr = ppsCurrentDroidLists[player]; psCurr != NULL; psCurr = psCurr->psNext)
            {
                  totalDroids++;
                  // if transporter save any droids in the grp
                  if (psCurr->droidType == DROID_TRANSPORTER)
                  {
                        psTrans = psCurr->psGroup->psList;
// Some MSVC specific debug code to check for dangling pointers
#if defined(WZ_CC_MSVC)
                        {
                              void* dangling_ptr;

                              // Fill the memory with 0xcd, which MSVC initialises freshly
                              // allocated memory with.
                              memset(&dangling_ptr, 0xcd, sizeof(dangling_ptr));

                              if (psTrans->psGrpNext == dangling_ptr)
                              {
                                    debug( LOG_ERROR, "transporter ->psGrpNext not reset" );
                                    abort();
                              }
                        }
#endif

                        for (psTrans = psTrans->psGrpNext; psTrans != NULL; psTrans = psTrans->psGrpNext)
                        {
                              totalDroids++;
                        }
                  }
            }
      }

      /* Allocate the data buffer */
      fileSize = DROID_HEADER_SIZE + totalDroids*sizeof(SAVE_DROID);
      pFileData = (char*)malloc(fileSize);
      if (pFileData == NULL)
      {
            debug( LOG_ERROR, "Out of memory" );
            abort();
            return false;
      }

      /* Put the file header on the file */
      psHeader = (DROID_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 'd';
      psHeader->aFileType[1] = 'r';
      psHeader->aFileType[2] = 'o';
      psHeader->aFileType[3] = 'd';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = totalDroids;

      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      psSaveDroid = (SAVE_DROID*)(pFileData + DROID_HEADER_SIZE);

      /* Put the droid data into the buffer */
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for(psCurr = ppsCurrentDroidLists[player]; psCurr != NULL; psCurr = psCurr->psNext)
            {
            //always save transporter droids that are in the mission list with an INVALID_XY
            if (psCurr->droidType == DROID_TRANSPORTER &&
                ppsCurrentDroidLists[player] == mission.apsDroidLists[player])
            {
                psCurr->pos.x = INVALID_XY;
                psCurr->pos.y = INVALID_XY;
            }

                  buildSaveDroidFromDroid(psSaveDroid, psCurr, DROID_NORMAL);

                  psSaveDroid = (SAVE_DROID *)((char *)psSaveDroid + sizeof(SAVE_DROID));
                  // if transporter save any droids in the grp
                  if (psCurr->droidType == DROID_TRANSPORTER)
                  {
                        psTrans = psCurr->psGroup->psList;
                        for(psTrans = psCurr->psGroup->psList; psTrans != NULL; psTrans = psTrans->psGrpNext)
                        {
                              if (psTrans->droidType != DROID_TRANSPORTER)
                              {
                                    buildSaveDroidFromDroid(psSaveDroid, psTrans, DROID_ON_TRANSPORT);
                                    psSaveDroid = (SAVE_DROID *)((char *)psSaveDroid + sizeof(SAVE_DROID));
                              }
                        }
                  }
            }
      }

      ppsCurrentDroidLists = NULL;//ensure it always gets set

      /* Write the data to the file */
      if (pFileData != NULL) {
            status = saveFile(pFileName, pFileData, fileSize);
            free(pFileData);
            return status;
      }
      return false;
}


// -----------------------------------------------------------------------------------------
BOOL loadSaveStructure(char *pFileData, UDWORD filesize)
{
      STRUCT_SAVEHEADER       *psHeader;

      /* Check the file type */
      psHeader = (STRUCT_SAVEHEADER *)pFileData;
      if (psHeader->aFileType[0] != 's' || psHeader->aFileType[1] != 't' ||
            psHeader->aFileType[2] != 'r' || psHeader->aFileType[3] != 'u')
      {
            debug( LOG_ERROR, "loadSaveStructure: Incorrect file type" );
            abort();
            return false;
      }

      /* STRUCT_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += STRUCT_HEADER_SIZE;

      /* Check the file version */
      if (psHeader->version < VERSION_7)
      {
            debug( LOG_ERROR, "StructLoad: unsupported save format version %d", psHeader->version );
            abort();
            return false;
      }
      else if (psHeader->version < VERSION_9)
      {
            if (!loadSaveStructureV7(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else if (psHeader->version <= VERSION_19)
      {
            if (!loadSaveStructureV19(pFileData, filesize, psHeader->quantity, psHeader->version))
            {
                  return false;
            }
      }
      else if (psHeader->version <= CURRENT_VERSION_NUM)
      {
            if (!loadSaveStructureV(pFileData, filesize, psHeader->quantity, psHeader->version))
            {
                  return false;
            }
      }
      else
      {
            debug( LOG_ERROR, "StructLoad: undefined save format version %d", psHeader->version );
            abort();
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
/* code specific to version 7 of a save structure */
BOOL loadSaveStructureV7(char *pFileData, UDWORD filesize, UDWORD numStructures)
{
      SAVE_STRUCTURE_V2       *psSaveStructure, sSaveStructure;
      STRUCTURE                     *psStructure;
      REPAIR_FACILITY               *psRepair;
      STRUCTURE_STATS               *psStats = NULL;
      UDWORD                              count, statInc;
      BOOL                          found;
      UDWORD                              NumberOfSkippedStructures=0;
      UDWORD                              burnTime;

      psSaveStructure = &sSaveStructure;

      if ((sizeof(SAVE_STRUCTURE_V2) * numStructures + STRUCT_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "structureLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the structure data */
      for (count = 0; count < numStructures; count ++, pFileData += sizeof(SAVE_STRUCTURE_V2))
      {
            memcpy(psSaveStructure, pFileData, sizeof(SAVE_STRUCTURE_V2));

            /* STRUCTURE_SAVE_V2 includes OBJECT_SAVE_V19 */
            endian_sdword(&psSaveStructure->currentBuildPts);
            endian_udword(&psSaveStructure->body);
            endian_udword(&psSaveStructure->armour);
            endian_udword(&psSaveStructure->resistance);
            endian_udword(&psSaveStructure->dummy1);
            endian_udword(&psSaveStructure->subjectInc);
            endian_udword(&psSaveStructure->timeStarted);
            endian_udword(&psSaveStructure->output);
            endian_udword(&psSaveStructure->capacity);
            endian_udword(&psSaveStructure->quantity);
            /* OBJECT_SAVE_V19 */
            endian_udword(&psSaveStructure->id);
            endian_udword(&psSaveStructure->x);
            endian_udword(&psSaveStructure->y);
            endian_udword(&psSaveStructure->z);
            endian_udword(&psSaveStructure->direction);
            endian_udword(&psSaveStructure->player);
            endian_udword(&psSaveStructure->burnStart);
            endian_udword(&psSaveStructure->burnDamage);

            psSaveStructure->player=RemapPlayerNumber(psSaveStructure->player);


            if (psSaveStructure->player >= MAX_PLAYERS)
            {
                  psSaveStructure->player=MAX_PLAYERS-1;
                  NumberOfSkippedStructures++;
            }
            //get the stats for this structure
            found = false;


            if (!getSaveObjectName(psSaveStructure->name))
            {
                  continue;
            }

            for (statInc = 0; statInc < numStructureStats; statInc++)
            {
                  psStats = asStructureStats + statInc;
                  //loop until find the same name

                  if (!strcmp(psStats->pName, psSaveStructure->name))
                  {
                        found = true;
                        break;
                  }
            }
            //if haven't found the structure - ignore this record!
            if (!found)
            {
                  debug( LOG_ERROR, "This structure no longer exists - %s", getSaveStructNameV19((SAVE_STRUCTURE_V17*)psSaveStructure) );
                  abort();
                  continue;
            }

            //for modules - need to check the base structure exists
            if (IsStatExpansionModule(psStats))
            {
                  psStructure = getTileStructure(map_coord(psSaveStructure->x),
                        map_coord(psSaveStructure->y));
                  if (psStructure == NULL)
                  {
                        debug( LOG_ERROR, "No owning structure for module - %s for player - %d", getSaveStructNameV19((SAVE_STRUCTURE_V17*)psSaveStructure), psSaveStructure->player );
                        abort();
                        //ignore this module
                        continue;
                  }
            }

        //check not too near the edge
        /*if (psSaveStructure->pos.x <= TILE_UNITS || psSaveStructure->pos.y <= TILE_UNITS)
        {
                  DBERROR(("Structure being built too near the edge of the map"));
            continue;
        }*/

        //check not trying to build too near the edge
      if (map_coord(psSaveStructure->x) < TOO_NEAR_EDGE
       || map_coord(psSaveStructure->x) > mapWidth - TOO_NEAR_EDGE)
        {
                  debug( LOG_ERROR, "Structure %s, x coord too near the edge of the map. id - %d", getSaveStructNameV19((SAVE_STRUCTURE_V17*)psSaveStructure), psSaveStructure->id );
                  abort();
            continue;
        }
      if (map_coord(psSaveStructure->y) < TOO_NEAR_EDGE
       || map_coord(psSaveStructure->y) > mapHeight - TOO_NEAR_EDGE)
        {
                  debug( LOG_ERROR, "Structure %s, y coord too near the edge of the map. id - %d", getSaveStructNameV19((SAVE_STRUCTURE_V17*)psSaveStructure), psSaveStructure->id );
                  abort();
            continue;
        }

        psStructure = buildStructure(psStats, psSaveStructure->x, psSaveStructure->y,
                  psSaveStructure->player,true);
            if (!psStructure)
            {
                  ASSERT( false, "loadSaveStructure:Unable to create structure" );
                  return false;
            }

        /*The original code here didn't work and so the scriptwriters worked
        round it by using the module ID - so making it work now will screw up
        the scripts -so in ALL CASES overwrite the ID!*/
            //don't copy the module's id etc
            //if (IsStatExpansionModule(psStats)==false)
            {
                  //copy the values across
                  psStructure->id = psSaveStructure->id;
                  //are these going to ever change from the values set up with?
//                psStructure->pos.z = (UWORD)psSaveStructure->pos.z;
                  psStructure->direction = psSaveStructure->direction;
            }


            psStructure->inFire = psSaveStructure->inFire;
            psStructure->burnDamage = psSaveStructure->burnDamage;
            burnTime = psSaveStructure->burnStart;
            psStructure->burnStart = burnTime;

            psStructure->status = psSaveStructure->status;
            if (psStructure->status ==SS_BUILT)
            {
                  buildingComplete(psStructure);
            }

            //if not a save game, don't want to overwrite any of the stats so continue
            if (gameType != GTYPE_SAVE_START)
            {
                  continue;
            }

            psStructure->currentBuildPts = (SWORD)psSaveStructure->currentBuildPts;
//          psStructure->body = psSaveStructure->body;
//          psStructure->armour = psSaveStructure->armour;
//          psStructure->resistance = psSaveStructure->resistance;
//          psStructure->repair = psSaveStructure->repair;
            switch (psStructure->pStructureType->type)
            {
                  case REF_FACTORY:
                        //NOT DONE AT PRESENT
                        ((FACTORY *)psStructure->pFunctionality)->capacity = (UBYTE)psSaveStructure->
                              capacity;
                        //((FACTORY *)psStructure->pFunctionality)->productionOutput = psSaveStructure->
                        //    output;
                        //((FACTORY *)psStructure->pFunctionality)->quantity = psSaveStructure->
                        //    quantity;
                        //((FACTORY *)psStructure->pFunctionality)->timeStarted = gameTime -
                        //    savedGameTime - (psSaveStructure->timeStarted);
                        //((FACTORY*)psStructure->pFunctionality)->timeToBuild = ((DROID_TEMPLATE *)
                        //    psSaveStructure->subjectInc)->buildPoints / ((FACTORY *)psStructure->pFunctionality)->
                        //    productionOutput;

                        //adjust the module structures IMD
                        if (psSaveStructure->capacity)
                        {
                              psStructure->sDisplay.imd = factoryModuleIMDs[psSaveStructure->
                                    capacity-1][0];
                        }
                        break;
                  case REF_RESEARCH:
                        ((RESEARCH_FACILITY *)psStructure->pFunctionality)->capacity =
                              psSaveStructure->capacity;
                        ((RESEARCH_FACILITY *)psStructure->pFunctionality)->researchPoints =
                              psSaveStructure->output;
                        ((RESEARCH_FACILITY *)psStructure->pFunctionality)->timeStarted = (psSaveStructure->timeStarted);
                        if (psSaveStructure->subjectInc != (UDWORD)-1)
                        {
                              ((RESEARCH_FACILITY *)psStructure->pFunctionality)->psSubject = (BASE_STATS *)
                                    (asResearch + psSaveStructure->subjectInc);
                              ((RESEARCH_FACILITY*)psStructure->pFunctionality)->timeToResearch =
                                    (asResearch + psSaveStructure->subjectInc)->researchPoints /
                                    ((RESEARCH_FACILITY *)psStructure->pFunctionality)->
                                    researchPoints;
                        }
                        //adjust the module structures IMD
                        if (psSaveStructure->capacity)
                        {
                              psStructure->sDisplay.imd = researchModuleIMDs[psSaveStructure->
                                    capacity-1];
                        }
                        break;
                  case REF_REPAIR_FACILITY: //CODE THIS SOMETIME
                        psRepair = ((REPAIR_FACILITY *)psStructure->pFunctionality);
                        psRepair->psDeliveryPoint = NULL;
                        psRepair->psObj = NULL;
                        psRepair->currentPtsAdded = 0;
                        break;
                  default:
                        break;
            }
      }

      if (NumberOfSkippedStructures>0)
      {
            debug( LOG_ERROR, "structureLoad: invalid player number in %d structures ... assigned to the last player!\n\n", NumberOfSkippedStructures );
            abort();
      }

      return true;
}

// -----------------------------------------------------------------------------------------
//return id of a research topic based on the name
static UDWORD getResearchIdFromName(char *pName)
{
      UDWORD inc;


      for (inc=0; inc < numResearch; inc++)
      {

            if (!strcmp(asResearch[inc].pName, pName))
            {
                  return inc;
            }
      }

      debug( LOG_ERROR, "Unknown research - %s", pName );
      abort();
      return NULL_ID;
}


// -----------------------------------------------------------------------------------------
/* code for version upto 19of a save structure */
BOOL loadSaveStructureV19(char *pFileData, UDWORD filesize, UDWORD numStructures, UDWORD version)
{
      SAVE_STRUCTURE_V17                  *psSaveStructure, sSaveStructure;
      STRUCTURE                     *psStructure;
      FACTORY                             *psFactory;
      RESEARCH_FACILITY       *psResearch;
      REPAIR_FACILITY               *psRepair;
      STRUCTURE_STATS               *psStats = NULL;
    STRUCTURE_STATS                 *psModule;
      UDWORD                              capacity;
      UDWORD                              count, statInc;
      BOOL                          found;
      UDWORD                              NumberOfSkippedStructures=0;
      UDWORD                              burnTime;
      UDWORD                              i;
      UDWORD                              sizeOfSaveStruture;
      UDWORD                              researchId;

      if (version < VERSION_12)
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V2);
      }
      else if (version < VERSION_14)
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V12);
      }
      else if (version <= VERSION_14)
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V14);
      }
      else if (version <= VERSION_16)
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V15);
      }
      else
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V17);
      }

      psSaveStructure = &sSaveStructure;

      if ((sizeOfSaveStruture * numStructures + STRUCT_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "structureLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the structure data */
      for (count = 0; count < numStructures; count ++, pFileData += sizeOfSaveStruture)
      {
            memcpy(psSaveStructure, pFileData, sizeOfSaveStruture);

            /* SAVE_STRUCTURE_V17 is STRUCTURE_SAVE_V17 */
            /* STRUCTURE_SAVE_V17 includes STRUCTURE_SAVE_V15 */
            endian_sword(&psSaveStructure->currentPowerAccrued);
            /* STRUCTURE_SAVE_V15 includes STRUCTURE_SAVE_V14 */
            /* STRUCTURE_SAVE_V14 includes STRUCTURE_SAVE_V12 */
            endian_udword(&psSaveStructure->factoryInc);
            endian_udword(&psSaveStructure->powerAccrued);
            endian_udword(&psSaveStructure->dummy2);
            endian_udword(&psSaveStructure->droidTimeStarted);
            endian_udword(&psSaveStructure->timeToBuild);
            endian_udword(&psSaveStructure->timeStartHold);
            /* STRUCTURE_SAVE_V12 includes STRUCTURE_SAVE_V2 */
            endian_sdword(&psSaveStructure->currentBuildPts);
            endian_udword(&psSaveStructure->body);
            endian_udword(&psSaveStructure->armour);
            endian_udword(&psSaveStructure->resistance);
            endian_udword(&psSaveStructure->dummy1);
            endian_udword(&psSaveStructure->subjectInc);
            endian_udword(&psSaveStructure->timeStarted);
            endian_udword(&psSaveStructure->output);
            endian_udword(&psSaveStructure->capacity);
            /* STRUCTURE_SAVE_V2 includes OBJECT_SAVE_V19 */
            endian_udword(&psSaveStructure->id);
            endian_udword(&psSaveStructure->x);
            endian_udword(&psSaveStructure->y);
            endian_udword(&psSaveStructure->z);
            endian_udword(&psSaveStructure->direction);
            endian_udword(&psSaveStructure->player);
            endian_udword(&psSaveStructure->burnStart);
            endian_udword(&psSaveStructure->burnDamage);

            psSaveStructure->player=RemapPlayerNumber(psSaveStructure->player);

            if (psSaveStructure->player >= MAX_PLAYERS)
            {
                  psSaveStructure->player=MAX_PLAYERS-1;
                  NumberOfSkippedStructures++;

            }
            //get the stats for this structure
            found = false;


            if (!getSaveObjectName(psSaveStructure->name))
            {
                  continue;
            }

            for (statInc = 0; statInc < numStructureStats; statInc++)
            {
                  psStats = asStructureStats + statInc;
                  //loop until find the same name

                  if (!strcmp(psStats->pName, psSaveStructure->name))
                  {
                        found = true;
                        break;
                  }
            }
            //if haven't found the structure - ignore this record!
            if (!found)
            {
                  debug( LOG_ERROR, "This structure no longer exists - %s", getSaveStructNameV19(psSaveStructure) );
                  abort();
                  continue;
            }
            /*create the Structure */
            //for modules - need to check the base structure exists
            if (IsStatExpansionModule(psStats))
            {
                  psStructure = getTileStructure(map_coord(psSaveStructure->x),
                        map_coord(psSaveStructure->y));
                  if (psStructure == NULL)
                  {
                        debug( LOG_ERROR, "No owning structure for module - %s for player - %d", getSaveStructNameV19(psSaveStructure), psSaveStructure->player );
                        abort();
                        //ignore this module
                        continue;
                  }
            }
        //check not too near the edge
        /*if (psSaveStructure->pos.x <= TILE_UNITS || psSaveStructure->pos.y <= TILE_UNITS)
        {
                  DBERROR(("Structure being built too near the edge of the map"));
            continue;
        }*/
        //check not trying to build too near the edge
      if (map_coord(psSaveStructure->x) < TOO_NEAR_EDGE
       || map_coord(psSaveStructure->x) > mapWidth - TOO_NEAR_EDGE)
        {
                  debug( LOG_ERROR, "Structure %s, x coord too near the edge of the map. id - %d", getSaveStructNameV19((SAVE_STRUCTURE_V17*)psSaveStructure), psSaveStructure->id );
                  abort();
            continue;
        }
      if (map_coord(psSaveStructure->y) < TOO_NEAR_EDGE
       || map_coord(psSaveStructure->y) > mapHeight - TOO_NEAR_EDGE)
        {
                  debug( LOG_ERROR, "Structure %s, y coord too near the edge of the map. id - %d", getSaveStructNameV19((SAVE_STRUCTURE_V17*)psSaveStructure), psSaveStructure->id );
                  abort();
            continue;
        }

            psStructure = buildStructure(psStats, psSaveStructure->x, psSaveStructure->y,
                  psSaveStructure->player,true);
            if (!psStructure)
            {
                  ASSERT( false, "loadSaveStructure:Unable to create structure" );
                  return false;
            }

        /*The original code here didn't work and so the scriptwriters worked
        round it by using the module ID - so making it work now will screw up
        the scripts -so in ALL CASES overwrite the ID!*/
            //don't copy the module's id etc
            //if (IsStatExpansionModule(psStats)==false)
            {
                  //copy the values across
                  psStructure->id = psSaveStructure->id;
                  //are these going to ever change from the values set up with?
//                psStructure->pos.z = (UWORD)psSaveStructure->pos.z;
                  psStructure->direction = psSaveStructure->direction;
            }

            psStructure->inFire = psSaveStructure->inFire;
            psStructure->burnDamage = psSaveStructure->burnDamage;
            burnTime = psSaveStructure->burnStart;
            psStructure->burnStart = burnTime;
            if (version >= VERSION_14)
            {
                  for (i=0; i < MAX_PLAYERS; i++)
                  {
                        psStructure->visible[i] = psSaveStructure->visible[i];
                  }
            }
            psStructure->status = psSaveStructure->status;
            if (psStructure->status ==SS_BUILT)
            {
                  buildingComplete(psStructure);
            }

            //if not a save game, don't want to overwrite any of the stats so continue
            if ((gameType != GTYPE_SAVE_START) &&
                  (gameType != GTYPE_SAVE_MIDMISSION))
            {
                  continue;
            }

            if (version <= VERSION_16)
            {
                  if (psSaveStructure->currentBuildPts > SWORD_MAX)//old MAX_BODY
                  {
                        psSaveStructure->currentBuildPts = SWORD_MAX;
                  }
                  psStructure->currentBuildPts = (SWORD)psSaveStructure->currentBuildPts;
                  psStructure->currentPowerAccrued = 0;
            }
            else
            {
                  psStructure->currentBuildPts = (SWORD)psSaveStructure->currentBuildPts;
                  psStructure->currentPowerAccrued = psSaveStructure->currentPowerAccrued;
            }
//          psStructure->repair = (UWORD)psSaveStructure->repair;
            switch (psStructure->pStructureType->type)
            {
                  case REF_FACTORY:
            case REF_VTOL_FACTORY:
            case REF_CYBORG_FACTORY:
                        //if factory save the current build info
                        if (version >= VERSION_12)
                        {
                              psFactory = ((FACTORY *)psStructure->pFunctionality);
                              psFactory->capacity = 0;//capacity reset during module build (UBYTE)psSaveStructure->capacity;
                    //this is set up during module build - if the stats have changed it will also set up with the latest value
                              //psFactory->productionOutput = (UBYTE)psSaveStructure->output;
                              psFactory->quantity = (UBYTE)psSaveStructure->quantity;
                              psFactory->timeStarted = psSaveStructure->droidTimeStarted;
                              psFactory->powerAccrued = psSaveStructure->powerAccrued;
                              psFactory->timeToBuild = psSaveStructure->timeToBuild;
                              psFactory->timeStartHold = psSaveStructure->timeStartHold;

                              //adjust the module structures IMD
                              if (psSaveStructure->capacity)
                              {
                                  psModule = getModuleStat(psStructure);
                                    capacity = psSaveStructure->capacity;
                                    //build the appropriate number of modules
                                    while (capacity)
                                    {
                                buildStructure(psModule, psStructure->pos.x, psStructure->pos.y,
                                psStructure->player, false);
                            capacity--;
                                    }

                              }
                        // imd set by building modules
                        //    psStructure->sDisplay.imd = factoryModuleIMDs[psSaveStructure->capacity-1][0];

                        //if factory reset the delivery points
                              //this trashes the flag pos pointer but flag pos list is cleared when flags load
                              //assemblyCheck
                              FIXME_CAST_ASSIGN(UDWORD, psFactory->psAssemblyPoint, psSaveStructure->factoryInc);
                              //if factory was building find the template from the unique ID
                              if (psSaveStructure->subjectInc == NULL_ID)
                              {
                                    psFactory->psSubject = NULL;
                              }
                              else
                              {
                                    psFactory->psSubject = (BASE_STATS*)
                            getTemplateFromMultiPlayerID(psSaveStructure->subjectInc);
                        //if the build has started set the powerAccrued =
                        //powerRequired to sync the interface
                        if (psFactory->timeStarted != ACTION_START_TIME &&
                            psFactory->psSubject)
                        {
                            psFactory->powerAccrued = ((DROID_TEMPLATE *)psFactory->
                                psSubject)->powerPoints;
                        }
                              }
                        }
                        break;
                  case REF_RESEARCH:
                        psResearch = ((RESEARCH_FACILITY *)psStructure->pFunctionality);
                        psResearch->capacity = 0;//capacity set when module loaded psSaveStructure->capacity;
                //this is set up during module build - if the stats have changed it will also set up with the latest value
                //psResearch->researchPoints = psSaveStructure->output;
                        psResearch->powerAccrued = psSaveStructure->powerAccrued;
                        //clear subject
                        psResearch->psSubject = NULL;
                        psResearch->timeToResearch = 0;
                        psResearch->timeStarted = 0;
                        //set the subject
                        if (saveGameVersion >= VERSION_15)
                        {
                              if (psSaveStructure->subjectInc != NULL_ID)
                              {
                                    researchId = getResearchIdFromName(psSaveStructure->researchName);
                                    if (researchId != NULL_ID)
                                    {
                                          psResearch->psSubject = (BASE_STATS *)(asResearch + researchId);
                                          psResearch->timeToResearch = (asResearch + researchId)->researchPoints / psResearch->researchPoints;
                                          psResearch->timeStarted = psSaveStructure->timeStarted;
                                    }
                              }
                              else
                              {
                                    psResearch->psSubject = NULL;
                                    psResearch->timeToResearch = 0;
                                    psResearch->timeStarted = 0;
                              }
                        }
                        else
                        {
                              psResearch->timeStarted = (psSaveStructure->timeStarted);
                              if (psSaveStructure->subjectInc != NULL_ID)
                              {
                                    psResearch->psSubject = (BASE_STATS *)(asResearch + psSaveStructure->subjectInc);
                                    psResearch->timeToResearch = (asResearch + psSaveStructure->subjectInc)->researchPoints / psResearch->researchPoints;
                              }

                        }
                //if started research, set powerAccrued = powerRequired
                if (psResearch->timeStarted != ACTION_START_TIME && psResearch->
                    psSubject)
                {
                    psResearch->powerAccrued = ((RESEARCH *)psResearch->
                        psSubject)->researchPower;
                }
                        //adjust the module structures IMD
                        if (psSaveStructure->capacity)
                        {
                            psModule = getModuleStat(psStructure);
                              capacity = psSaveStructure->capacity;
                              //build the appropriate number of modules
                              buildStructure(psModule, psStructure->pos.x, psStructure->pos.y, psStructure->player, true);
                        }
                        break;
                  case REF_POWER_GEN:
                        //adjust the module structures IMD
                        if (psSaveStructure->capacity)
                        {
                            psModule = getModuleStat(psStructure);
                              capacity = psSaveStructure->capacity;
                              //build the appropriate number of modules
                              buildStructure(psModule, psStructure->pos.x, psStructure->pos.y, psStructure->player, true);
                        }
                        break;
                  case REF_RESOURCE_EXTRACTOR:
                        ((RES_EXTRACTOR *)psStructure->pFunctionality)->power = psSaveStructure->output;
                //if run out of power, then the res_extractor should be inactive
                if (psSaveStructure->output == 0)
                {
                    ((RES_EXTRACTOR *)psStructure->pFunctionality)->active = false;
                }
                        break;
                  case REF_REPAIR_FACILITY: //CODE THIS SOMETIME
                        if (version >= VERSION_19)
                        {
                              psRepair = ((REPAIR_FACILITY *)psStructure->pFunctionality);

                              psRepair->power = ((REPAIR_DROID_FUNCTION *) psStructure->pStructureType->asFuncList[0])->repairPoints;
                              psRepair->timeStarted = psSaveStructure->droidTimeStarted;
                              psRepair->powerAccrued = psSaveStructure->powerAccrued;
                    psRepair->currentPtsAdded = 0;

                              //if repair facility reset the delivery points
                              //this trashes the flag pos pointer but flag pos list is cleared when flags load
                              //assemblyCheck
                              psRepair->psDeliveryPoint = NULL;
                              //if factory was building find the template from the unique ID
                              if (psSaveStructure->subjectInc == NULL_ID)
                              {
                                    psRepair->psObj = NULL;
                              }
                              else
                              {
                                    psRepair->psObj = getBaseObjFromId(psSaveStructure->subjectInc);
                        //if the build has started set the powerAccrued =
                        //powerRequired to sync the interface
                        if (psRepair->timeStarted != ACTION_START_TIME &&
                            psRepair->psObj)
                        {
                            psRepair->powerAccrued = powerReqForDroidRepair((DROID*)psRepair->psObj);
                        }
                              }
                        }
                        else
                        {
                              psRepair = ((REPAIR_FACILITY *)psStructure->pFunctionality);
                    //init so setAssemplyPoint check will re-allocate one
                              psRepair->psObj = NULL;
                    psRepair->psDeliveryPoint = NULL;
                        }
                        break;
                  default:
                        break;
            }
            //get the base body points
            psStructure->body = (UWORD)structureBody(psStructure);
            if (psSaveStructure->body < psStructure->body)
            {
                  psStructure->body = (UWORD)psSaveStructure->body;
            }
            //set the build status from the build points
            psStructure->currentBuildPts = (SWORD)psStructure->pStructureType->buildPoints;
            if (psSaveStructure->currentBuildPts < psStructure->currentBuildPts)
            {
                  psStructure->currentBuildPts = (SWORD)psSaveStructure->currentBuildPts;
            psStructure->status = SS_BEING_BUILT;
            }
            else
            {
            psStructure->status = SS_BUILT;
//            buildingComplete(psStructure);//replaced by following switch
                  switch (psStructure->pStructureType->type)
                  {
                        case REF_POWER_GEN:
                              checkForResExtractors(psStructure);
                              if(selectedPlayer == psStructure->player)
                              {
                                    audio_PlayObjStaticTrack( (void *) psStructure, ID_SOUND_POWER_HUM );
                              }
                              break;
                        case REF_RESOURCE_EXTRACTOR:
                    //only try and connect if power left in
                    if (((RES_EXTRACTOR *)psStructure->pFunctionality)->power != 0)
                    {
                              checkForPowerGen(psStructure);
                              /* GJ HACK! - add anim to deriks */
                              if (psStructure->psCurAnim == NULL)
                              {
                                    psStructure->psCurAnim = animObj_Add(psStructure, ID_ANIM_DERIK, 0, 0);
                                  }
                    }

                              break;
                        case REF_RESEARCH:
//21feb                             intCheckResearchButton();
                              break;
                        default:
                              //do nothing for factories etc
                              break;
                  }
            }
      }

      if (NumberOfSkippedStructures>0)
      {
            debug( LOG_ERROR, "structureLoad: invalid player number in %d structures ... assigned to the last player!\n\n", NumberOfSkippedStructures );
            abort();
      }

      return true;
}


// -----------------------------------------------------------------------------------------
/* code for versions after version 20 of a save structure */
BOOL loadSaveStructureV(char *pFileData, UDWORD filesize, UDWORD numStructures, UDWORD version)
{
      SAVE_STRUCTURE                *psSaveStructure, sSaveStructure;
      STRUCTURE                     *psStructure;
      FACTORY                             *psFactory;
      RESEARCH_FACILITY       *psResearch;
      REPAIR_FACILITY               *psRepair;
      REARM_PAD                     *psReArmPad;
      STRUCTURE_STATS               *psStats = NULL;
      STRUCTURE_STATS               *psModule;
      UDWORD                              capacity;
      UDWORD                              count, statInc;
      BOOL                          found;
      UDWORD                              NumberOfSkippedStructures=0;
      UDWORD                              burnTime;
      UDWORD                              i;
      UDWORD                              sizeOfSaveStructure = 0;
      UDWORD                              researchId;

      if (version <= VERSION_20)
      {
            sizeOfSaveStructure = sizeof(SAVE_STRUCTURE_V20);
      }
      else if (version <= CURRENT_VERSION_NUM)
      {
            sizeOfSaveStructure = sizeof(SAVE_STRUCTURE);
      }

      psSaveStructure = &sSaveStructure;

      if ((sizeOfSaveStructure * numStructures + STRUCT_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "structureLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the structure data */
      for (count = 0; count < numStructures; count ++, pFileData += sizeOfSaveStructure)
      {
            memcpy(psSaveStructure, pFileData, sizeOfSaveStructure);

            /* SAVE_STRUCTURE is STRUCTURE_SAVE_V21 */
            /* STRUCTURE_SAVE_V21 includes STRUCTURE_SAVE_V20 */
            endian_udword(&psSaveStructure->commandId);
            /* STRUCTURE_SAVE_V20 includes OBJECT_SAVE_V20 */
            endian_sdword(&psSaveStructure->currentBuildPts);
            endian_udword(&psSaveStructure->body);
            endian_udword(&psSaveStructure->armour);
            endian_udword(&psSaveStructure->resistance);
            endian_udword(&psSaveStructure->dummy1);
            endian_udword(&psSaveStructure->subjectInc);
            endian_udword(&psSaveStructure->timeStarted);
            endian_udword(&psSaveStructure->output);
            endian_udword(&psSaveStructure->capacity);
            endian_udword(&psSaveStructure->quantity);
            endian_udword(&psSaveStructure->factoryInc);
            endian_udword(&psSaveStructure->powerAccrued);
            endian_udword(&psSaveStructure->dummy2);
            endian_udword(&psSaveStructure->droidTimeStarted);
            endian_udword(&psSaveStructure->timeToBuild);
            endian_udword(&psSaveStructure->timeStartHold);
            endian_sword(&psSaveStructure->currentPowerAccrued);
            /* OBJECT_SAVE_V20 */
            endian_udword(&psSaveStructure->id);
            endian_udword(&psSaveStructure->x);
            endian_udword(&psSaveStructure->y);
            endian_udword(&psSaveStructure->z);
            endian_udword(&psSaveStructure->direction);
            endian_udword(&psSaveStructure->player);
            endian_udword(&psSaveStructure->burnStart);
            endian_udword(&psSaveStructure->burnDamage);

            psSaveStructure->player=RemapPlayerNumber(psSaveStructure->player);

            if (psSaveStructure->player >= MAX_PLAYERS)
            {
                  psSaveStructure->player=MAX_PLAYERS-1;
                  NumberOfSkippedStructures++;

            }
            //get the stats for this structure
            found = false;


            if (!getSaveObjectName(psSaveStructure->name))
            {
                  continue;
            }

            for (statInc = 0; statInc < numStructureStats; statInc++)
            {
                  psStats = asStructureStats + statInc;
                  //loop until find the same name

                  if (!strcmp(psStats->pName, psSaveStructure->name))
                  {
                        found = true;
                        break;
                  }
            }
            //if haven't found the structure - ignore this record!
            if (!found)
            {
                  debug( LOG_ERROR, "This structure no longer exists - %s", getSaveStructNameV(psSaveStructure) );
                  abort();
                  continue;
            }
            /*create the Structure */
            //for modules - need to check the base structure exists
            if (IsStatExpansionModule(psStats))
            {
                  psStructure = getTileStructure(map_coord(psSaveStructure->x),
                                                 map_coord(psSaveStructure->y));
                  if (psStructure == NULL)
                  {
                        debug( LOG_ERROR, "No owning structure for module - %s for player - %d", getSaveStructNameV(psSaveStructure), psSaveStructure->player );
                        abort();
                        //ignore this module
                        continue;
                  }
            }
        //check not too near the edge
        /*if (psSaveStructure->pos.x <= TILE_UNITS || psSaveStructure->pos.y <= TILE_UNITS)
        {
                  DBERROR(("Structure being built too near the edge of the map"));
            continue;
        }*/
        //check not trying to build too near the edge
      if (map_coord(psSaveStructure->x) < TOO_NEAR_EDGE
       || map_coord(psSaveStructure->x) > mapWidth - TOO_NEAR_EDGE)
        {
                  debug( LOG_ERROR, "Structure %s, x coord too near the edge of the map. id - %d", getSaveStructNameV((SAVE_STRUCTURE*)psSaveStructure), psSaveStructure->id );
                  abort();
            continue;
        }
      if (map_coord(psSaveStructure->y) < TOO_NEAR_EDGE
       || map_coord(psSaveStructure->y) > mapHeight - TOO_NEAR_EDGE)
        {
                  debug( LOG_ERROR, "Structure %s, y coord too near the edge of the map. id - %d", getSaveStructNameV((SAVE_STRUCTURE*)psSaveStructure), psSaveStructure->id );
                  abort();
            continue;
        }

            psStructure = buildStructure(psStats, psSaveStructure->x, psSaveStructure->y,
                  psSaveStructure->player,true);
            if (!psStructure)
            {
                  ASSERT( false, "loadSaveStructure:Unable to create structure" );
                  return false;
            }

        /*The original code here didn't work and so the scriptwriters worked
        round it by using the module ID - so making it work now will screw up
        the scripts -so in ALL CASES overwrite the ID!*/
            //don't copy the module's id etc
            //if (IsStatExpansionModule(psStats)==false)
            {
                  //copy the values across
                  psStructure->id = psSaveStructure->id;
                  //are these going to ever change from the values set up with?
//                psStructure->pos.z = (UWORD)psSaveStructure->pos.z;
                  psStructure->direction = psSaveStructure->direction;
            }

            psStructure->inFire = psSaveStructure->inFire;
            psStructure->burnDamage = psSaveStructure->burnDamage;
            burnTime = psSaveStructure->burnStart;
            psStructure->burnStart = burnTime;
            for (i=0; i < MAX_PLAYERS; i++)
            {
                  psStructure->visible[i] = psSaveStructure->visible[i];
            }

            psStructure->status = psSaveStructure->status;
            if (psStructure->status ==SS_BUILT)
            {
                  buildingComplete(psStructure);
            }

            //if not a save game, don't want to overwrite any of the stats so continue
            if ((gameType != GTYPE_SAVE_START) &&
                  (gameType != GTYPE_SAVE_MIDMISSION))
            {
                  continue;
            }

            psStructure->currentBuildPts = (SWORD)psSaveStructure->currentBuildPts;
            psStructure->currentPowerAccrued = psSaveStructure->currentPowerAccrued;
        psStructure->resistance = (SWORD)psSaveStructure->resistance;
        //armour not ever adjusted...


//          psStructure->repair = (UWORD)psSaveStructure->repair;
            switch (psStructure->pStructureType->type)
            {
                  case REF_FACTORY:
                  case REF_VTOL_FACTORY:
                  case REF_CYBORG_FACTORY:
                        //if factory save the current build info
                        psFactory = ((FACTORY *)psStructure->pFunctionality);
                        psFactory->capacity = 0;//capacity reset during module build (UBYTE)psSaveStructure->capacity;
                        //this is set up during module build - if the stats have changed it will also set up with the latest value
                        //psFactory->productionOutput = (UBYTE)psSaveStructure->output;
                        psFactory->quantity = (UBYTE)psSaveStructure->quantity;
                        psFactory->timeStarted = psSaveStructure->droidTimeStarted;
                        psFactory->powerAccrued = psSaveStructure->powerAccrued;
                        psFactory->timeToBuild = psSaveStructure->timeToBuild;
                        psFactory->timeStartHold = psSaveStructure->timeStartHold;

                        //adjust the module structures IMD
                        if (psSaveStructure->capacity)
                        {
                              psModule = getModuleStat(psStructure);
                              capacity = psSaveStructure->capacity;
                              //build the appropriate number of modules
                              while (capacity)
                              {
                                    buildStructure(psModule, psStructure->pos.x, psStructure->pos.y, psStructure->player, true);
                                    capacity--;
                              }

                        }
                  // imd set by building modules
                  //    psStructure->sDisplay.imd = factoryModuleIMDs[psSaveStructure->capacity-1][0];

                  //if factory reset the delivery points
                        //this trashes the flag pos pointer but flag pos list is cleared when flags load
                        //assemblyCheck
                        FIXME_CAST_ASSIGN(UDWORD, psFactory->psAssemblyPoint, psSaveStructure->factoryInc);
                        //if factory was building find the template from the unique ID
                        if (psSaveStructure->subjectInc == NULL_ID)
                        {
                              psFactory->psSubject = NULL;
                        }
                        else
                        {
                              psFactory->psSubject = (BASE_STATS*)
                        getTemplateFromMultiPlayerID(psSaveStructure->subjectInc);
                    //if the build has started set the powerAccrued =
                    //powerRequired to sync the interface
                    if (psFactory->timeStarted != ACTION_START_TIME &&
                        psFactory->psSubject)
                    {
                        psFactory->powerAccrued = ((DROID_TEMPLATE *)psFactory->
                            psSubject)->powerPoints;
                    }
                        }
                        if (version >= VERSION_21)//version 21
                        {
                              //reset command id in loadStructSetPointers
                              FIXME_CAST_ASSIGN(UDWORD, psFactory->psCommander, psSaveStructure->commandId);
                        }
                //secondary order added - AB 22/04/99
                if (version >= VERSION_32)
                {
                    psFactory->secondaryOrder = psSaveStructure->dummy2;
                }
                        break;
                  case REF_RESEARCH:
                        psResearch = ((RESEARCH_FACILITY *)psStructure->pFunctionality);
                        psResearch->capacity = 0;//capacity set when module loaded psSaveStructure->capacity;
                        //adjust the module structures IMD
                        if (psSaveStructure->capacity)
                        {
                              psModule = getModuleStat(psStructure);
                              capacity = psSaveStructure->capacity;
                              //build the appropriate number of modules
                              buildStructure(psModule, psStructure->pos.x, psStructure->pos.y, psStructure->player, true);
                        }
                        //this is set up during module build - if the stats have changed it will also set up with the latest value
                        psResearch->powerAccrued = psSaveStructure->powerAccrued;
                        //clear subject
                        psResearch->psSubject = NULL;
                        psResearch->timeToResearch = 0;
                        psResearch->timeStarted = 0;
                        psResearch->timeStartHold = 0;
                        //set the subject
                        if (psSaveStructure->subjectInc != NULL_ID)
                        {
                              researchId = getResearchIdFromName(psSaveStructure->researchName);
                              if (researchId != NULL_ID)
                              {
                                    psResearch->psSubject = (BASE_STATS *)(asResearch + researchId);
                                    psResearch->timeToResearch = (asResearch + researchId)->researchPoints / psResearch->researchPoints;
                                    psResearch->timeStarted = psSaveStructure->timeStarted;
                                    if (saveGameVersion >= VERSION_20)
                                    {
                                          psResearch->timeStartHold = psSaveStructure->timeStartHold;
                                    }
                              }
                        }
                //if started research, set powerAccrued = powerRequired
                if (psResearch->timeStarted != ACTION_START_TIME && psResearch->
                    psSubject)
                {
                    psResearch->powerAccrued = ((RESEARCH *)psResearch->
                        psSubject)->researchPower;
                }
                        break;
                  case REF_POWER_GEN:
                        //adjust the module structures IMD
                        if (psSaveStructure->capacity)
                        {
                            psModule = getModuleStat(psStructure);
                              capacity = psSaveStructure->capacity;
                              //build the appropriate number of modules
                              buildStructure(psModule, psStructure->pos.x, psStructure->pos.y, psStructure->player, true);
                        }
                        break;
                  case REF_RESOURCE_EXTRACTOR:
                        ((RES_EXTRACTOR *)psStructure->pFunctionality)->power = psSaveStructure->output;
                //if run out of power, then the res_extractor should be inactive
                if (psSaveStructure->output == 0)
                {
                    ((RES_EXTRACTOR *)psStructure->pFunctionality)->active = false;
                }
                        break;
                  case REF_REPAIR_FACILITY: //CODE THIS SOMETIME
/*
      // The group the droids to be repaired by this facility belong to
      struct _droid_group           *psGroup;
      struct _droid                 *psGrpNext;
*/
                        psRepair = ((REPAIR_FACILITY *)psStructure->pFunctionality);

                        psRepair->power = ((REPAIR_DROID_FUNCTION *) psStructure->pStructureType->asFuncList[0])->repairPoints;
                        psRepair->timeStarted = psSaveStructure->droidTimeStarted;
                        psRepair->powerAccrued = psSaveStructure->powerAccrued;

                        //if repair facility reset the delivery points
                        //this trashes the flag pos pointer but flag pos list is cleared when flags load
                        //assemblyCheck
                        psRepair->psDeliveryPoint = NULL;
                        //if  repair facility was  repairing find the object later
                        FIXME_CAST_ASSIGN(UDWORD, psRepair->psObj, psSaveStructure->subjectInc);
                if (version < VERSION_27)
                {
                    psRepair->currentPtsAdded = 0;
                }
                else
                {
                    psRepair->currentPtsAdded = psSaveStructure->dummy2;
                }
                        break;
                  case REF_REARM_PAD:
                        if (version >= VERSION_26)//version 26
                        {
                              psReArmPad = ((REARM_PAD *)psStructure->pFunctionality);
                              psReArmPad->reArmPoints = psSaveStructure->output;//set in build structure ?
                              psReArmPad->timeStarted = psSaveStructure->droidTimeStarted;
                              //if  ReArm Pad was  rearming find the object later
                              FIXME_CAST_ASSIGN(UDWORD, psReArmPad->psObj, psSaveStructure->subjectInc);
                    if (version < VERSION_28)
                    {
                        psReArmPad->currentPtsAdded = 0;
                    }
                    else
                    {
                        psReArmPad->currentPtsAdded = psSaveStructure->dummy2;
                    }
                        }
                        else
                        {
                    psReArmPad = ((REARM_PAD *)psStructure->pFunctionality);
                              psReArmPad->timeStarted = 0;
                        }
                        break;
                  default:
                        break;
            }
            //get the base body points
            psStructure->body = (UWORD)structureBody(psStructure);
            if (psSaveStructure->body < psStructure->body)
            {
                  psStructure->body = (UWORD)psSaveStructure->body;
            }
            //set the build status from the build points
            psStructure->currentPowerAccrued = psSaveStructure->currentPowerAccrued;//22feb
            psStructure->currentBuildPts = (SWORD)psStructure->pStructureType->buildPoints;
            if (psSaveStructure->currentBuildPts < psStructure->currentBuildPts)
            {
                  psStructure->currentBuildPts = (SWORD)psSaveStructure->currentBuildPts;
            psStructure->status = SS_BEING_BUILT;
            }
            else
            {
            psStructure->status = SS_BUILT;
//            buildingComplete(psStructure);//replaced by following switch
                  switch (psStructure->pStructureType->type)
                  {
                        case REF_POWER_GEN:
                              checkForResExtractors(psStructure);
                              if(selectedPlayer == psStructure->player)
                              {
                                    audio_PlayObjStaticTrack( (void *) psStructure, ID_SOUND_POWER_HUM );
                              }
                              break;
                        case REF_RESOURCE_EXTRACTOR:
                    //only try and connect if power left in
                    if (((RES_EXTRACTOR *)psStructure->pFunctionality)->power != 0)
                    {
                              checkForPowerGen(psStructure);
                              /* GJ HACK! - add anim to deriks */
                              if (psStructure->psCurAnim == NULL)
                              {
                                    psStructure->psCurAnim = animObj_Add(psStructure, ID_ANIM_DERIK, 0, 0);
                                  }
                    }

                              break;
                        case REF_RESEARCH:
//21feb                             intCheckResearchButton();
                              break;
                        default:
                              //do nothing for factories etc
                              break;
                  }
            }
      }

      if (NumberOfSkippedStructures>0)
      {
            debug( LOG_ERROR, "structureLoad: invalid player number in %d structures ... assigned to the last player!\n\n", NumberOfSkippedStructures );
            abort();
      }

      return true;
}

// -----------------------------------------------------------------------------------------
/*
Writes the linked list of structure for each player to a file
*/
BOOL writeStructFile(char *pFileName)
{
      char *pFileData = NULL;
      UDWORD                        fileSize, player, i, totalStructs=0;
      STRUCTURE               *psCurr;
      DROID_TEMPLATE          *psSubjectTemplate;
      FACTORY                       *psFactory;
      REPAIR_FACILITY         *psRepair;
      REARM_PAD                     *psReArmPad;
      STRUCT_SAVEHEADER *psHeader;
      SAVE_STRUCTURE          *psSaveStruct;
      FLAG_POSITION           *psFlag;
      UDWORD                        researchId;
      BOOL status = true;

      //total all the structures in the world
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext)
            {
                  totalStructs++;
            }
      }

      /* Allocate the data buffer */
      fileSize = STRUCT_HEADER_SIZE + totalStructs*sizeof(SAVE_STRUCTURE);
      pFileData = (char*)malloc(fileSize);
      if (pFileData == NULL)
      {
            debug( LOG_ERROR, "Out of memory" );
            abort();
            return false;
      }

      /* Put the file header on the file */
      psHeader = (STRUCT_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 's';
      psHeader->aFileType[1] = 't';
      psHeader->aFileType[2] = 'r';
      psHeader->aFileType[3] = 'u';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = totalStructs;

      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      psSaveStruct = (SAVE_STRUCTURE*)(pFileData + STRUCT_HEADER_SIZE);

      /* Put the structure data into the buffer */
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for(psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext)
            {

                  strcpy(psSaveStruct->name, psCurr->pStructureType->pName);

                  psSaveStruct->id = psCurr->id;


                  psSaveStruct->x = psCurr->pos.x;
                  psSaveStruct->y = psCurr->pos.y;
                  psSaveStruct->z = psCurr->pos.z;

                  psSaveStruct->direction = psCurr->direction;
                  psSaveStruct->player = psCurr->player;
                  psSaveStruct->inFire = psCurr->inFire;
                  psSaveStruct->burnStart = psCurr->burnStart;
                  psSaveStruct->burnDamage = psCurr->burnDamage;
                  //version 14
                  for (i=0; i < MAX_PLAYERS; i++)
                  {
                        psSaveStruct->visible[i] = psCurr->visible[i];
                  }
                  //psSaveStruct->structureInc = psCurr->pStructureType - asStructureStats;
                  psSaveStruct->status = psCurr->status;
                  //check if body at max
                  if (psCurr->body >= structureBody(psCurr))
                  {
                        psSaveStruct->body = MAX_BODY;
                  }
                  else
                  {
                        psSaveStruct->body = psCurr->body;
                  }
                  //check if buildpts at max
                  if (psCurr->currentBuildPts >= (SWORD)psCurr->pStructureType->buildPoints)
                  {
                        psSaveStruct->currentBuildPts = MAX_BODY;
                  }
                  else
                  {
                        psSaveStruct->currentBuildPts = psCurr->currentBuildPts;
                  }
                  // no need to check power at max because it would be being built
                  psSaveStruct->currentPowerAccrued = psCurr->currentPowerAccrued;

                  psSaveStruct->armour = psCurr->armour[0][0]; // advanced armour not supported yet
                  psSaveStruct->resistance = psCurr->resistance;
                  psSaveStruct->subjectInc = NULL_ID;
                  psSaveStruct->timeStarted = 0;
                  psSaveStruct->output = 0;
                  psSaveStruct->capacity = 0;
                  psSaveStruct->quantity = 0;
                  if (psCurr->pFunctionality)
                  {
                        switch (psCurr->pStructureType->type)
                        {
                        case REF_FACTORY:
                        case REF_CYBORG_FACTORY:
                        case REF_VTOL_FACTORY:
                              psFactory = ((FACTORY *)psCurr->pFunctionality);
                              psSaveStruct->capacity  = psFactory->capacity;
                    //don't need to save this - it gets set up
                              //psSaveStruct->output              = psFactory->productionOutput;
                              psSaveStruct->quantity              = psFactory->quantity;
                              psSaveStruct->droidTimeStarted      = psFactory->timeStarted;
                              psSaveStruct->powerAccrued          = psFactory->powerAccrued;
                              psSaveStruct->timeToBuild           = psFactory->timeToBuild;
                              psSaveStruct->timeStartHold         = psFactory->timeStartHold;

                        if (psFactory->psSubject != NULL)
                              {
                                    psSubjectTemplate = (DROID_TEMPLATE *)psFactory->psSubject;
                                    psSaveStruct->subjectInc = psSubjectTemplate->multiPlayerID;
                              }
                              else
                              {
                                    psSaveStruct->subjectInc = NULL_ID;
                              }

                              psFlag = ((FACTORY *)psCurr->pFunctionality)->psAssemblyPoint;
                              if (psFlag != NULL)
                              {
                                    psSaveStruct->factoryInc = psFlag->factoryInc;
                              }
                              else
                              {
                                    psSaveStruct->factoryInc = NULL_ID;
                              }
                              //version 21
                              if (psFactory->psCommander)
                              {
                                    psSaveStruct->commandId = psFactory->psCommander->id;
                              }
                              else
                              {
                                    psSaveStruct->commandId = NULL_ID;
                              }
                    //secondary order added - AB 22/04/99
                    psSaveStruct->dummy2 = psFactory->secondaryOrder;

                              break;
                        case REF_RESEARCH:
                              psSaveStruct->capacity = ((RESEARCH_FACILITY *)psCurr->
                                    pFunctionality)->capacity;
                    //don't need to save this - it gets set up
                              //psSaveStruct->output = ((RESEARCH_FACILITY *)psCurr->
                              //    pFunctionality)->researchPoints;
                              psSaveStruct->powerAccrued = ((RESEARCH_FACILITY *)psCurr->
                                    pFunctionality)->powerAccrued;
                              psSaveStruct->timeStartHold   = ((RESEARCH_FACILITY *)psCurr->
                                    pFunctionality)->timeStartHold;
                        if (((RESEARCH_FACILITY *)psCurr->pFunctionality)->psSubject)
                              {
                                    psSaveStruct->subjectInc = 0;
                                    researchId = ((RESEARCH_FACILITY *)psCurr->pFunctionality)->
                                          psSubject->ref - REF_RESEARCH_START;
                                    ASSERT(strlen(asResearch[researchId].pName) < sizeof(psSaveStruct->researchName), "writeStructData: research name too long");
                                    sstrcpy(psSaveStruct->researchName, asResearch[researchId].pName);
                                    psSaveStruct->timeStarted = ((RESEARCH_FACILITY *)psCurr->
                                          pFunctionality)->timeStarted;
                              }
                              else
                              {
                                    psSaveStruct->subjectInc = NULL_ID;
                                    psSaveStruct->researchName[0] = 0;
                                    psSaveStruct->timeStarted = 0;
                              }
                              psSaveStruct->timeToBuild = 0;
                              break;
                        case REF_POWER_GEN:
                              psSaveStruct->capacity = ((POWER_GEN *)psCurr->
                                    pFunctionality)->capacity;
                              break;
                        case REF_RESOURCE_EXTRACTOR:
                              psSaveStruct->output = ((RES_EXTRACTOR *)psCurr->
                                    pFunctionality)->power;
                              break;
                        case REF_REPAIR_FACILITY: //CODE THIS SOMETIME
                              psRepair = ((REPAIR_FACILITY *)psCurr->pFunctionality);
                              psSaveStruct->droidTimeStarted = psRepair->timeStarted;
                              psSaveStruct->powerAccrued = psRepair->powerAccrued;
                    psSaveStruct->dummy2 = psRepair->currentPtsAdded;

                              if (psRepair->psObj != NULL)
                              {
                                    psSaveStruct->subjectInc = psRepair->psObj->id;
                              }
                              else
                              {
                                    psSaveStruct->subjectInc = NULL_ID;
                              }

                              psFlag = psRepair->psDeliveryPoint;
                              if (psFlag != NULL)
                              {
                                    psSaveStruct->factoryInc = psFlag->factoryInc;
                              }
                              else
                              {
                                    psSaveStruct->factoryInc = NULL_ID;
                              }
                              break;
                        case REF_REARM_PAD:
                              psReArmPad = ((REARM_PAD *)psCurr->pFunctionality);
                              psSaveStruct->output = psReArmPad->reArmPoints;
                              psSaveStruct->droidTimeStarted = psReArmPad->timeStarted;
                    psSaveStruct->dummy2 = psReArmPad->currentPtsAdded;
                              if (psReArmPad->psObj != NULL)
                              {
                                    psSaveStruct->subjectInc = psReArmPad->psObj->id;
                              }
                              else
                              {
                                    psSaveStruct->subjectInc = NULL_ID;
                              }
                              break;
                        default: //CODE THIS SOMETIME
                              ASSERT( false,"Structure facility not saved" );
                              break;
                        }
                  }

                  /* SAVE_STRUCTURE is STRUCTURE_SAVE_V21 */
                  /* STRUCTURE_SAVE_V21 includes STRUCTURE_SAVE_V20 */
                  endian_udword(&psSaveStruct->commandId);
                  /* STRUCTURE_SAVE_V20 includes OBJECT_SAVE_V20 */
                  endian_sdword(&psSaveStruct->currentBuildPts);
                  endian_udword(&psSaveStruct->body);
                  endian_udword(&psSaveStruct->armour);
                  endian_udword(&psSaveStruct->resistance);
                  endian_udword(&psSaveStruct->dummy1);
                  endian_udword(&psSaveStruct->subjectInc);
                  endian_udword(&psSaveStruct->timeStarted);
                  endian_udword(&psSaveStruct->output);
                  endian_udword(&psSaveStruct->capacity);
                  endian_udword(&psSaveStruct->quantity);
                  endian_udword(&psSaveStruct->factoryInc);
                  endian_udword(&psSaveStruct->powerAccrued);
                  endian_udword(&psSaveStruct->dummy2);
                  endian_udword(&psSaveStruct->droidTimeStarted);
                  endian_udword(&psSaveStruct->timeToBuild);
                  endian_udword(&psSaveStruct->timeStartHold);
                  endian_sword(&psSaveStruct->currentPowerAccrued);
                  /* OBJECT_SAVE_V20 */
                  endian_udword(&psSaveStruct->id);
                  endian_udword(&psSaveStruct->x);
                  endian_udword(&psSaveStruct->y);
                  endian_udword(&psSaveStruct->z);
                  endian_udword(&psSaveStruct->direction);
                  endian_udword(&psSaveStruct->player);
                  endian_udword(&psSaveStruct->burnStart);
                  endian_udword(&psSaveStruct->burnDamage);

                  psSaveStruct = (SAVE_STRUCTURE *)((char *)psSaveStruct + sizeof(SAVE_STRUCTURE));
            }
      }

      /* Write the data to the file */
      if (pFileData != NULL) {
            status = saveFile(pFileName, pFileData, fileSize);
            free(pFileData);
            return status;
      }
      return false;
}

// -----------------------------------------------------------------------------------------
BOOL loadStructSetPointers(void)
{
      UDWORD            player,list;
      FACTORY           *psFactory;
      REPAIR_FACILITY   *psRepair;
      REARM_PAD   *psReArmPad;
      STRUCTURE   *psStruct;
      DROID       *psCommander;
      STRUCTURE   **ppsStructLists[2];

      ppsStructLists[0] = apsStructLists;
      ppsStructLists[1] = mission.apsStructLists;

      for(list = 0; list<2; list++)
      {
            for(player = 0; player<MAX_PLAYERS; player++)
            {
                  psStruct=(STRUCTURE *)ppsStructLists[list][player];
                  while (psStruct)
                  {
                        if (psStruct->pFunctionality)
                        {
                              UDWORD _tmpid;
                              switch (psStruct->pStructureType->type)
                              {
                              case REF_FACTORY:
                              case REF_CYBORG_FACTORY:
                              case REF_VTOL_FACTORY:
                                    psFactory = ((FACTORY *)psStruct->pFunctionality);
                                    FIXME_CAST_ASSIGN(UDWORD, _tmpid, psFactory->psCommander);
                                    //there is a commander then has been temporarily removed
                                    //so put it back
                                    if (_tmpid != NULL_ID)
                                    {
                                          psCommander = (DROID*)getBaseObjFromId(_tmpid);
                                          psFactory->psCommander = NULL;
                                          ASSERT( psCommander != NULL,"loadStructSetPointers psCommander getBaseObjFromId() failed" );
                                          if (psCommander == NULL)
                                          {
                                                psFactory->psCommander = NULL;
                                          }
                                          else
                                          {
                                if (list == 1) //ie offWorld
                                {
                                    //don't need to worry about the Flag
                                    ((FACTORY *)psStruct->pFunctionality)->psCommander =
                                        psCommander;
                                }
                                else
                                {
                                                    assignFactoryCommandDroid(psStruct, psCommander);
                                }
                                          }
                                    }
                                    else
                                    {
                                          psFactory->psCommander = NULL;
                                    }
                                    break;
                              case REF_REPAIR_FACILITY:
                                    psRepair = ((REPAIR_FACILITY *)psStruct->pFunctionality);
                                    FIXME_CAST_ASSIGN(UDWORD, _tmpid, psRepair->psObj);
                                    if (_tmpid == NULL_ID)
                                    {
                                          psRepair->psObj = NULL;
                                    }
                                    else
                                    {
                                          psRepair->psObj = getBaseObjFromId(_tmpid);
                                          //if the build has started set the powerAccrued =
                                          //powerRequired to sync the interface
                                          if (psRepair->timeStarted != ACTION_START_TIME &&
                                                psRepair->psObj)
                                          {
                                                psRepair->powerAccrued = powerReqForDroidRepair((DROID*)psRepair->psObj);
                                          }
                                    }
                                    break;
                              case REF_REARM_PAD:
                                    psReArmPad = ((REARM_PAD *)psStruct->pFunctionality);
                                    if (saveGameVersion >= VERSION_26)//version 26
                                    {
                                          FIXME_CAST_ASSIGN(UDWORD, _tmpid, psReArmPad->psObj)
                                          if (_tmpid == NULL_ID)
                                          {
                                                psReArmPad->psObj = NULL;
                                          }
                                          else
                                          {
                                                psReArmPad->psObj = getBaseObjFromId(_tmpid);
                                          }
                                    }
                                    else
                                    {
                                          psReArmPad->psObj = NULL;
                                    }
                              default:
                                    break;
                              }
                        }
                        psStruct = psStruct->psNext;
                  }
            }
      }
      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveFeature(char *pFileData, UDWORD filesize)
{
      FEATURE_SAVEHEADER            *psHeader;

      /* Check the file type */
      psHeader = (FEATURE_SAVEHEADER *)pFileData;
      if (psHeader->aFileType[0] != 'f' || psHeader->aFileType[1] != 'e' ||
            psHeader->aFileType[2] != 'a' || psHeader->aFileType[3] != 't')
      {
            debug( LOG_ERROR, "loadSaveFeature: Incorrect file type" );
            abort();
            return false;
      }

      /* FEATURE_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += FEATURE_HEADER_SIZE;

      /* Check the file version */
      if (psHeader->version < VERSION_7)
      {
            debug( LOG_ERROR, "FeatLoad: unsupported save format version %d", psHeader->version );
            abort();
            return false;
      }
      else if (psHeader->version <= VERSION_19)
      {
            if (!loadSaveFeatureV14(pFileData, filesize, psHeader->quantity, psHeader->version))
            {
                  return false;
            }
      }
      else if (psHeader->version <= CURRENT_VERSION_NUM)
      {
            if (!loadSaveFeatureV(pFileData, filesize, psHeader->quantity, psHeader->version))
            {
                  return false;
            }
      }
      else
      {
            debug( LOG_ERROR, "FeatLoad: undefined save format version %d", psHeader->version );
            abort();
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
/* code for all version 8 - 14 save features */
BOOL loadSaveFeatureV14(char *pFileData, UDWORD filesize, UDWORD numFeatures, UDWORD version)
{
      SAVE_FEATURE_V14              *psSaveFeature;
      FEATURE                             *pFeature;
      UDWORD                              count, i, statInc;
      FEATURE_STATS                 *psStats = NULL;
      BOOL                          found;
      UDWORD                              sizeOfSaveFeature;

      if (version < VERSION_14)
      {
            sizeOfSaveFeature = sizeof(SAVE_FEATURE_V2);
      }
      else
      {
            sizeOfSaveFeature = sizeof(SAVE_FEATURE_V14);
      }


      if ((sizeOfSaveFeature * numFeatures + FEATURE_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "featureLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the feature data */
      for (count = 0; count < numFeatures; count ++,
                                                      pFileData += sizeOfSaveFeature)
      {
            psSaveFeature = (SAVE_FEATURE_V14*) pFileData;

            /* FEATURE_SAVE_V14 is FEATURE_SAVE_V2 */
            /* FEATURE_SAVE_V2 is OBJECT_SAVE_V19 */
            /* OBJECT_SAVE_V19 */
            endian_udword(&psSaveFeature->id);
            endian_udword(&psSaveFeature->x);
            endian_udword(&psSaveFeature->y);
            endian_udword(&psSaveFeature->z);
            endian_udword(&psSaveFeature->direction);
            endian_udword(&psSaveFeature->player);
            endian_udword(&psSaveFeature->burnStart);
            endian_udword(&psSaveFeature->burnDamage);

            /*if (psSaveFeature->featureInc > numFeatureStats)
            {
                  DBERROR(("Invalid Feature Type - unable to load save game"));
                  goto error;
            }*/
            //get the stats for this feature
            found = false;


            if (!getSaveObjectName(psSaveFeature->name))
            {
                  continue;
            }

            for (statInc = 0; statInc < numFeatureStats; statInc++)
            {
                  psStats = asFeatureStats + statInc;
                  //loop until find the same name

                  if (!strcmp(psStats->pName, psSaveFeature->name))

                  {
                        found = true;
                        break;
                  }
            }
            //if haven't found the feature - ignore this record!
            if (!found)
            {

                  debug( LOG_ERROR, "This feature no longer exists - %s", psSaveFeature->name );
                  abort();
                  continue;
            }
            //create the Feature
            //buildFeature(asFeatureStats + psSaveFeature->featureInc,
            //    psSaveFeature->pos.x, psSaveFeature->pos.y);
            pFeature = buildFeature(psStats, psSaveFeature->x, psSaveFeature->y,true);
            //will be added to the top of the linked list
            //pFeature = apsFeatureLists[0];
            if (!pFeature)
            {
                  ASSERT( false, "loadSaveFeature:Unable to create feature" );
                  return false;
            }
            //restore values
            pFeature->id = psSaveFeature->id;
            pFeature->direction = psSaveFeature->direction;
            pFeature->inFire = psSaveFeature->inFire;
            pFeature->burnDamage = psSaveFeature->burnDamage;
            if (version >= VERSION_14)
            {
                  for (i=0; i < MAX_PLAYERS; i++)
                  {
                        pFeature->visible[i] = psSaveFeature->visible[i];
                  }
            }

      }

      return true;
}

// -----------------------------------------------------------------------------------------
/* code for all post version 7 save features */
BOOL loadSaveFeatureV(char *pFileData, UDWORD filesize, UDWORD numFeatures, UDWORD version)
{
      SAVE_FEATURE                  *psSaveFeature;
      FEATURE                             *pFeature;
      UDWORD                              count, i, statInc;
      FEATURE_STATS                 *psStats = NULL;
      BOOL                          found;
      UDWORD                              sizeOfSaveFeature;

//    version;

      sizeOfSaveFeature = sizeof(SAVE_FEATURE);

      if ((sizeOfSaveFeature * numFeatures + FEATURE_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "featureLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the feature data */
      for (count = 0; count < numFeatures; count ++,
                                                      pFileData += sizeOfSaveFeature)
      {
            psSaveFeature = (SAVE_FEATURE*) pFileData;

            /* FEATURE_SAVE is FEATURE_SAVE_V20 */
            /* FEATURE_SAVE_V20 is OBJECT_SAVE_V20 */
            /* OBJECT_SAVE_V20 */
            endian_udword(&psSaveFeature->id);
            endian_udword(&psSaveFeature->x);
            endian_udword(&psSaveFeature->y);
            endian_udword(&psSaveFeature->z);
            endian_udword(&psSaveFeature->direction);
            endian_udword(&psSaveFeature->player);
            endian_udword(&psSaveFeature->burnStart);
            endian_udword(&psSaveFeature->burnDamage);

            /*if (psSaveFeature->featureInc > numFeatureStats)
            {
                  DBERROR(("Invalid Feature Type - unable to load save game"));
                  goto error;
            }*/
            //get the stats for this feature
            found = false;


            if (!getSaveObjectName(psSaveFeature->name))
            {
                  continue;
            }

            for (statInc = 0; statInc < numFeatureStats; statInc++)
            {
                  psStats = asFeatureStats + statInc;
                  //loop until find the same name

                  if (!strcmp(psStats->pName, psSaveFeature->name))
                  {
                        found = true;
                        break;
                  }
            }
            //if haven't found the feature - ignore this record!
            if (!found)
            {

                  debug( LOG_ERROR, "This feature no longer exists - %s", psSaveFeature->name );
                  abort();

                  continue;
            }
            //create the Feature
            //buildFeature(asFeatureStats + psSaveFeature->featureInc,
            //    psSaveFeature->pos.x, psSaveFeature->pos.y);
            pFeature = buildFeature(psStats, psSaveFeature->x, psSaveFeature->y,true);
            //will be added to the top of the linked list
            //pFeature = apsFeatureLists[0];
            if (!pFeature)
            {
                  ASSERT( false, "loadSaveFeature:Unable to create feature" );
                  return false;
            }
            //restore values
            pFeature->id = psSaveFeature->id;
            pFeature->direction = psSaveFeature->direction;
            pFeature->inFire = psSaveFeature->inFire;
            pFeature->burnDamage = psSaveFeature->burnDamage;
            for (i=0; i < MAX_PLAYERS; i++)
            {
                  pFeature->visible[i] = psSaveFeature->visible[i]      ;
            }
      }
      return true;
}

// -----------------------------------------------------------------------------------------
/*
Writes the linked list of features to a file
*/
BOOL writeFeatureFile(char *pFileName)
{
      char *pFileData = NULL;
      UDWORD                        fileSize, i, totalFeatures=0;
      FEATURE                       *psCurr;
      FEATURE_SAVEHEADER      *psHeader;
      SAVE_FEATURE            *psSaveFeature;
      BOOL status = true;

      //total all the features in the world
      for (psCurr = apsFeatureLists[0]; psCurr != NULL; psCurr = psCurr->psNext)
      {
            totalFeatures++;
      }

      /* Allocate the data buffer */
      fileSize = FEATURE_HEADER_SIZE + totalFeatures * sizeof(SAVE_FEATURE);
      pFileData = (char*)malloc(fileSize);
      if (pFileData == NULL)
      {
            debug( LOG_ERROR, "Out of memory" );
            abort();
            return false;
      }

      /* Put the file header on the file */
      psHeader = (FEATURE_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 'f';
      psHeader->aFileType[1] = 'e';
      psHeader->aFileType[2] = 'a';
      psHeader->aFileType[3] = 't';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = totalFeatures;

      psSaveFeature = (SAVE_FEATURE*)(pFileData + FEATURE_HEADER_SIZE);

      /* Put the feature data into the buffer */
      for(psCurr = apsFeatureLists[0]; psCurr != NULL; psCurr = psCurr->psNext)
      {

            strcpy(psSaveFeature->name, psCurr->psStats->pName);

            psSaveFeature->id = psCurr->id;

//          psSaveFeature->pos.x = psCurr->pos.x - psCurr->psStats->baseWidth * TILE_UNITS / 2;
//          psSaveFeature->pos.y = psCurr->pos.y - psCurr->psStats->baseBreadth * TILE_UNITS / 2;
//          psSaveFeature->pos.z = psCurr->pos.z;

            psSaveFeature->x = psCurr->pos.x;
            psSaveFeature->y = psCurr->pos.y;
            psSaveFeature->z = psCurr->pos.z;

            psSaveFeature->direction = psCurr->direction;
            psSaveFeature->inFire = psCurr->inFire;
            psSaveFeature->burnDamage = psCurr->burnDamage;
            for (i=0; i < MAX_PLAYERS; i++)
            {
                  psSaveFeature->visible[i] = psCurr->visible[i];
            }

//          psSaveFeature->featureInc = psCurr->psStats - asFeatureStats;

            /* SAVE_FEATURE is FEATURE_SAVE_V20 */
            /* FEATURE_SAVE_V20 includes OBJECT_SAVE_V20 */
            /* OBJECT_SAVE_V20 */
            endian_udword(&psSaveFeature->id);
            endian_udword(&psSaveFeature->x);
            endian_udword(&psSaveFeature->y);
            endian_udword(&psSaveFeature->z);
            endian_udword(&psSaveFeature->direction);
            endian_udword(&psSaveFeature->player);
            endian_udword(&psSaveFeature->burnStart);
            endian_udword(&psSaveFeature->burnDamage);

            psSaveFeature = (SAVE_FEATURE *)((char *)psSaveFeature + sizeof(SAVE_FEATURE));
      }

      /* FEATURE_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      /* Write the data to the file */
      if (pFileData != NULL) {
            status = saveFile(pFileName, pFileData, fileSize);
            free(pFileData);
            return status;
      }
      return false;
}


// -----------------------------------------------------------------------------------------
BOOL loadSaveTemplate(char *pFileData, UDWORD filesize)
{
      TEMPLATE_SAVEHEADER           *psHeader;

      /* Check the file type */
      psHeader = (TEMPLATE_SAVEHEADER *)pFileData;
      if (psHeader->aFileType[0] != 't' || psHeader->aFileType[1] != 'e' ||
            psHeader->aFileType[2] != 'm' || psHeader->aFileType[3] != 'p')
      {
            debug( LOG_ERROR, "loadSaveTemplate: Incorrect file type" );
            abort();
            return false;
      }

      /* TEMPLATE_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += TEMPLATE_HEADER_SIZE;

      /* Check the file version */
      if (psHeader->version < VERSION_7)
      {
            debug( LOG_ERROR, "TemplateLoad: unsupported save format version %d", psHeader->version );
            abort();
            return false;
      }
      else if (psHeader->version < VERSION_14)
      {
            if (!loadSaveTemplateV7(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else if (psHeader->version <= VERSION_19)
      {
            if (!loadSaveTemplateV14(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else if (psHeader->version <= CURRENT_VERSION_NUM)
      {
            if (!loadSaveTemplateV(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else
      {
            debug( LOG_ERROR, "TemplateLoad: undefined save format version %d", psHeader->version );
            abort();
            return false;
      }
      return true;
}

// -----------------------------------------------------------------------------------------
/* code specific to version 7 of a save template */
BOOL loadSaveTemplateV7(char *pFileData, UDWORD filesize, UDWORD numTemplates)
{
      SAVE_TEMPLATE_V2        *psSaveTemplate, sSaveTemplate;
      DROID_TEMPLATE                *psTemplate;
      UDWORD                              count, i;
      SDWORD                              compInc;
      BOOL                          found;

      psSaveTemplate = &sSaveTemplate;

      if ((sizeof(SAVE_TEMPLATE_V2) * numTemplates + TEMPLATE_HEADER_SIZE) > filesize)
      {
            debug( LOG_ERROR, "templateLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the template data */
      for (count = 0; count < numTemplates; count ++, pFileData += sizeof(SAVE_TEMPLATE_V2))
      {
            memcpy(psSaveTemplate, pFileData, sizeof(SAVE_TEMPLATE_V2));

            /* SAVE_TEMPLATE_V2 is TEMPLATE_SAVE_V2 */
            /* TEMPLATE_SAVE_V2 */
            endian_udword(&psSaveTemplate->ref);
            endian_udword(&psSaveTemplate->player);
            endian_udword(&psSaveTemplate->numWeaps);
            endian_udword(&psSaveTemplate->numProgs);

            if (psSaveTemplate->player != 0)
            {
                  // only load player 0 templates - the rest come from stats
                  continue;
            }

            //create the Template
            psTemplate = malloc(sizeof(DROID_TEMPLATE));
            if (psTemplate == NULL)
            {
                  debug(LOG_ERROR, "loadSaveTemplateV7: Out of memory");
                  abort();
                  goto error;
            }
            //copy the values across

            psTemplate->pName = NULL;
            sstrcpy(psTemplate->aName, psSaveTemplate->name);

            psTemplate->ref = psSaveTemplate->ref;
            psTemplate->droidType = psSaveTemplate->droidType;
            found = true;
            //for (i=0; i < DROID_MAXCOMP; i++) - not intestested in the first comp - COMP_UNKNOWN
            for (i=1; i < DROID_MAXCOMP; i++)
            {
                  //DROID_MAXCOMP has changed to remove COMP_PROGRAM so hack here to load old save games!
                  if (i == SAVE_COMP_PROGRAM)
                  {
                        break;
                  }

                  compInc = getCompFromName(i, psSaveTemplate->asParts[i]);

                  if (compInc < 0)
                  {

                        debug( LOG_ERROR, "This component no longer exists - %s, the template will be deleted", psSaveTemplate->asParts[i] );
                        abort();

                        found = false;
                        //continue;
                        break;
                  }
                  psTemplate->asParts[i] = (UDWORD)compInc;
            }
            if (!found)
            {
                  //ignore this record
                  free(psTemplate);
                  continue;
            }
            psTemplate->numWeaps = psSaveTemplate->numWeaps;
            found = true;
            for (i=0; i < psTemplate->numWeaps; i++)
            {

                  compInc = getCompFromName(COMP_WEAPON, psSaveTemplate->asWeaps[i]);

                  if (compInc < 0)
                  {

                        debug( LOG_ERROR, "This weapon no longer exists - %s, the template will be deleted", psSaveTemplate->asWeaps[i] );
                        abort();

                        found = false;
                        //continue;
                        break;
                  }
                  psTemplate->asWeaps[i] = (UDWORD)compInc;
            }
            if (!found)
            {
                  //ignore this record
                  free(psTemplate);
                  continue;
            }

            // ignore brains and programs for now
            psTemplate->asParts[COMP_BRAIN] = 0;


            //calculate the total build points
            psTemplate->buildPoints = calcTemplateBuild(psTemplate);
            psTemplate->powerPoints = calcTemplatePower(psTemplate);

            //store it in the apropriate player' list
            psTemplate->psNext = apsDroidTemplates[psSaveTemplate->player];
            apsDroidTemplates[psSaveTemplate->player] = psTemplate;



      }

      return true;

error:
      droidTemplateShutDown();
      return false;
}

// -----------------------------------------------------------------------------------------
/* none specific version of a save template */
BOOL loadSaveTemplateV14(char *pFileData, UDWORD filesize, UDWORD numTemplates)
{
      SAVE_TEMPLATE_V14             *psSaveTemplate, sSaveTemplate;
      DROID_TEMPLATE                *psTemplate, *psDestTemplate;
      UDWORD                              count, i;
      SDWORD                              compInc;
      BOOL                          found;

      psSaveTemplate = &sSaveTemplate;

      if ((sizeof(SAVE_TEMPLATE_V14) * numTemplates + TEMPLATE_HEADER_SIZE) > filesize)
      {
            debug( LOG_ERROR, "templateLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the template data */
      for (count = 0; count < numTemplates; count ++, pFileData += sizeof(SAVE_TEMPLATE_V14))
      {
            memcpy(psSaveTemplate, pFileData, sizeof(SAVE_TEMPLATE_V14));

            /* SAVE_TEMPLATE_V14 is TEMPLATE_SAVE_V14 */
            endian_udword(&psSaveTemplate->ref);
            endian_udword(&psSaveTemplate->player);
            endian_udword(&psSaveTemplate->numWeaps);
            endian_udword(&psSaveTemplate->multiPlayerID);

            //AT SOME POINT CHECK THE multiPlayerID TO SEE IF ALREADY EXISTS - IGNORE IF IT DOES

            if (psSaveTemplate->player != 0)
            {
                  // only load player 0 templates - the rest come from stats
                  continue;
            }

            //create the Template
            psTemplate = malloc(sizeof(DROID_TEMPLATE));
            if (psTemplate == NULL)
            {
                  debug(LOG_ERROR, "loadSaveTemplateV14: Out of memory");
                  abort();
                  goto error;
            }
            //copy the values across

            psTemplate->pName = NULL;
            sstrcpy(psTemplate->aName, psSaveTemplate->name);

            psTemplate->ref = psSaveTemplate->ref;
            psTemplate->droidType = psSaveTemplate->droidType;
            psTemplate->multiPlayerID = psSaveTemplate->multiPlayerID;
            found = true;
            //for (i=0; i < DROID_MAXCOMP; i++) - not intestested in the first comp - COMP_UNKNOWN
            for (i=1; i < DROID_MAXCOMP; i++)
            {
                  //DROID_MAXCOMP has changed to remove COMP_PROGRAM so hack here to load old save games!
                  if (i == SAVE_COMP_PROGRAM)
                  {
                        break;
                  }

                  compInc = getCompFromName(i, psSaveTemplate->asParts[i]);

                  if (compInc < 0)
                  {

                        debug( LOG_ERROR, "This component no longer exists - %s, the template will be deleted", psSaveTemplate->asParts[i] );
                        abort();

                        found = false;
                        //continue;
                        break;
                  }
                  psTemplate->asParts[i] = (UDWORD)compInc;
            }
            if (!found)
            {
                  //ignore this record
                  free(psTemplate);
                  continue;
            }
            psTemplate->numWeaps = psSaveTemplate->numWeaps;
            found = true;
            for (i=0; i < psTemplate->numWeaps; i++)
            {

                  compInc = getCompFromName(COMP_WEAPON, psSaveTemplate->asWeaps[i]);

                  if (compInc < 0)
                  {
                        debug( LOG_ERROR, "This weapon no longer exists - %s, the template will be deleted", psSaveTemplate->asWeaps[i] );
                        abort();

                        found = false;
                        //continue;
                        break;
                  }
                  psTemplate->asWeaps[i] = (UDWORD)compInc;
            }
            if (!found)
            {
                  //ignore this record
                  free(psTemplate);
                  continue;
            }

            // ignore brains and programs for now
            psTemplate->asParts[COMP_BRAIN] = 0;

            //calculate the total build points
            psTemplate->buildPoints = calcTemplateBuild(psTemplate);
            psTemplate->powerPoints = calcTemplatePower(psTemplate);

            //store it in the apropriate player' list
            //if a template with the same multiplayerID exists overwrite it
            //else add this template to the top of the list
            psDestTemplate = apsDroidTemplates[psSaveTemplate->player];
            while (psDestTemplate != NULL)
            {
                  if (psTemplate->multiPlayerID == psDestTemplate->multiPlayerID)
                  {
                        //whooh get rid of this one
                        break;
                  }
                  psDestTemplate = psDestTemplate->psNext;
            }

            if (psDestTemplate != NULL)
            {
                  psTemplate->psNext = psDestTemplate->psNext;//preserve the list
                  memcpy(psDestTemplate,psTemplate,sizeof(DROID_TEMPLATE));
            }
            else
            {
                  //add it to the top of the list
                  psTemplate->psNext = apsDroidTemplates[psSaveTemplate->player];
                  apsDroidTemplates[psSaveTemplate->player] = psTemplate;
            }




      }

      return true;

error:
      droidTemplateShutDown();
      return false;
}

// -----------------------------------------------------------------------------------------
/* none specific version of a save template */
BOOL loadSaveTemplateV(char *pFileData, UDWORD filesize, UDWORD numTemplates)
{
      SAVE_TEMPLATE                 *psSaveTemplate, sSaveTemplate;
      DROID_TEMPLATE                *psTemplate, *psDestTemplate;
      UDWORD                              count, i;
      SDWORD                              compInc;
      BOOL                          found;

      psSaveTemplate = &sSaveTemplate;

      if ((sizeof(SAVE_TEMPLATE) * numTemplates + TEMPLATE_HEADER_SIZE) > filesize)
      {
            debug( LOG_ERROR, "templateLoad: unexpected end of file" );
            abort();
            return false;
      }

      /* Load in the template data */
      for (count = 0; count < numTemplates; count ++, pFileData += sizeof(SAVE_TEMPLATE))
      {
            memcpy(psSaveTemplate, pFileData, sizeof(SAVE_TEMPLATE));

            /* SAVE_TEMPLATE is TEMPLATE_SAVE_V20 */
            /* TEMPLATE_SAVE_V20 */
            endian_udword(&psSaveTemplate->ref);
            endian_udword(&psSaveTemplate->player);
            endian_udword(&psSaveTemplate->numWeaps);
            endian_udword(&psSaveTemplate->multiPlayerID);

            //AT SOME POINT CHECK THE multiPlayerID TO SEE IF ALREADY EXISTS - IGNORE IF IT DOES

            if (psSaveTemplate->player != 0 && !bMultiPlayer)
            {
                  // only load player 0 templates - the rest come from stats
                  continue;
            }

            //create the Template
            psTemplate = malloc(sizeof(DROID_TEMPLATE));
            if (psTemplate == NULL)
            {
                  debug(LOG_ERROR, "loadSaveTemplateV: Out of memory");
                  abort();
                  goto error;
            }
            //copy the values across

            psTemplate->pName = NULL;
            sstrcpy(psTemplate->aName, psSaveTemplate->name);

            psTemplate->ref = psSaveTemplate->ref;
            psTemplate->droidType = psSaveTemplate->droidType;
            psTemplate->multiPlayerID = psSaveTemplate->multiPlayerID;
            found = true;
            //for (i=0; i < DROID_MAXCOMP; i++) - not intestested in the first comp - COMP_UNKNOWN
            for (i=1; i < DROID_MAXCOMP; i++)
            {
                  //DROID_MAXCOMP has changed to remove COMP_PROGRAM so hack here to load old save games!
                  if (i == SAVE_COMP_PROGRAM)
                  {
                        break;
                  }

                  compInc = getCompFromName(i, psSaveTemplate->asParts[i]);

            //HACK to get the game to load when ECMs, Sensors or RepairUnits have been deleted
            if (compInc < 0 && (i == COMP_ECM || i == COMP_SENSOR || i == COMP_REPAIRUNIT))
            {
                //set the ECM to be the defaultECM ...
                if (i == COMP_ECM)
                {
                    compInc = aDefaultECM[psSaveTemplate->player];
                }
                else if (i == COMP_SENSOR)
                {
                    compInc = aDefaultSensor[psSaveTemplate->player];
                }
                else if (i == COMP_REPAIRUNIT)
                {
                    compInc = aDefaultRepair[psSaveTemplate->player];
                }
            }
                  else if (compInc < 0)
                  {

                        debug( LOG_ERROR, "This component no longer exists - %s, the template will be deleted", psSaveTemplate->asParts[i] );
                        abort();

                        found = false;
                        //continue;
                        break;
                  }
                  psTemplate->asParts[i] = (UDWORD)compInc;
            }
            if (!found)
            {
                  //ignore this record
                  free(psTemplate);
                  continue;
            }
            psTemplate->numWeaps = psSaveTemplate->numWeaps;
            found = true;
            for (i=0; i < psTemplate->numWeaps; i++)
            {

                  compInc = getCompFromName(COMP_WEAPON, psSaveTemplate->asWeaps[i]);

                  if (compInc < 0)
                  {

                        debug( LOG_ERROR, "This weapon no longer exists - %s, the template will be deleted", psSaveTemplate->asWeaps[i] );
                        abort();


                        found = false;
                        //continue;
                        break;
                  }
                  psTemplate->asWeaps[i] = (UDWORD)compInc;
            }
            if (!found)
            {
                  //ignore this record
                  free(psTemplate);
                  continue;
            }

            //no! put brains back in 10Feb //ignore brains and programs for now
            //psTemplate->asParts[COMP_BRAIN] = 0;

            //calculate the total build points
            psTemplate->buildPoints = calcTemplateBuild(psTemplate);
            psTemplate->powerPoints = calcTemplatePower(psTemplate);

            //store it in the apropriate player' list
            //if a template with the same multiplayerID exists overwrite it
            //else add this template to the top of the list
            psDestTemplate = apsDroidTemplates[psSaveTemplate->player];
            while (psDestTemplate != NULL)
            {
                  if (psTemplate->multiPlayerID == psDestTemplate->multiPlayerID)
                  {
                        //whooh get rid of this one
                        break;
                  }
                  psDestTemplate = psDestTemplate->psNext;
            }

            if (psDestTemplate != NULL)
            {
                  psTemplate->psNext = psDestTemplate->psNext;//preserve the list
                  memcpy(psDestTemplate,psTemplate,sizeof(DROID_TEMPLATE));
            }
            else
            {
                  //add it to the top of the list
                  psTemplate->psNext = apsDroidTemplates[psSaveTemplate->player];
                  apsDroidTemplates[psSaveTemplate->player] = psTemplate;
            }




      }

      return true;

error:
      droidTemplateShutDown();
      return false;
}

// -----------------------------------------------------------------------------------------
/*
Writes the linked list of templates for each player to a file
*/
BOOL writeTemplateFile(char *pFileName)
{
      char *pFileData = NULL;
      UDWORD                        fileSize, player, totalTemplates=0;
      DROID_TEMPLATE          *psCurr;
      TEMPLATE_SAVEHEADER     *psHeader;
      SAVE_TEMPLATE           *psSaveTemplate;
      UDWORD                        i;
      BOOL status = true;

      //total all the droids in the world
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for (psCurr = apsDroidTemplates[player]; psCurr != NULL; psCurr = psCurr->psNext)
            {
                  totalTemplates++;
            }
      }

      /* Allocate the data buffer */
      fileSize = TEMPLATE_HEADER_SIZE + totalTemplates*sizeof(SAVE_TEMPLATE);
      pFileData = (char*)malloc(fileSize);
      if (pFileData == NULL)
      {
            debug( LOG_ERROR, "Out of memory" );
            abort();
            return false;
      }

      /* Put the file header on the file */
      psHeader = (TEMPLATE_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 't';
      psHeader->aFileType[1] = 'e';
      psHeader->aFileType[2] = 'm';
      psHeader->aFileType[3] = 'p';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = totalTemplates;

      psSaveTemplate = (SAVE_TEMPLATE*)(pFileData + TEMPLATE_HEADER_SIZE);

      /* Put the template data into the buffer */
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for(psCurr = apsDroidTemplates[player]; psCurr != NULL; psCurr = psCurr->psNext)
            {
                  ASSERT(strlen(psCurr->aName) + 1 < sizeof(psSaveTemplate->name), "Truncation of droid name occurred! Max droid length (without truncation while saving) is %zu", sizeof(psSaveTemplate->name) - 1);
                  sstrcpy(psSaveTemplate->name, psCurr->aName);

                  psSaveTemplate->ref = psCurr->ref;
                  psSaveTemplate->player = player;
                  psSaveTemplate->droidType = (UBYTE)psCurr->droidType;
                  psSaveTemplate->multiPlayerID = psCurr->multiPlayerID;

                  // not interested in first comp - COMP_UNKNOWN
                  for (i=1; i < DROID_MAXCOMP; i++)
                  {

                        if (!getNameFromComp(i, psSaveTemplate->asParts[i], psCurr->asParts[i]))

                        {
                              //ignore this record
                              break;
                              //continue;
                        }
                  }
                  psSaveTemplate->numWeaps = psCurr->numWeaps;
                  for (i=0; i < psCurr->numWeaps; i++)
                  {

                        if (!getNameFromComp(COMP_WEAPON, psSaveTemplate->asWeaps[i], psCurr->asWeaps[i]))

                        {
                              //ignore this record
                              //continue;
                              break;
                        }
                  }

                  /* SAVE_TEMPLATE is TEMPLATE_SAVE_V20 */
                  /* TEMPLATE_SAVE_V20 */
                  endian_udword(&psSaveTemplate->ref);
                  endian_udword(&psSaveTemplate->player);
                  endian_udword(&psSaveTemplate->numWeaps);
                  endian_udword(&psSaveTemplate->multiPlayerID);

                  psSaveTemplate = (SAVE_TEMPLATE *)((char *)psSaveTemplate + sizeof(SAVE_TEMPLATE));
            }
      }

      /* TEMPLATE_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      /* Write the data to the file */
      if (pFileData != NULL) {
            status = saveFile(pFileName, pFileData, fileSize);
            free(pFileData);
            return status;
      }
      return false;
}

// -----------------------------------------------------------------------------------------
// load up a terrain tile type map file
09475 BOOL loadTerrainTypeMap(const char *pFileData, UDWORD filesize)
{
      TILETYPE_SAVEHEADER     *psHeader;
      UDWORD                        i;
      UWORD                   *pType;

      if (filesize < TILETYPE_HEADER_SIZE)
      {
            debug( LOG_ERROR, "loadTerrainTypeMap: file too small" );
            abort();
            return false;
      }

      // Check the header
      psHeader = (TILETYPE_SAVEHEADER *)pFileData;
      if (psHeader->aFileType[0] != 't' || psHeader->aFileType[1] != 't' ||
            psHeader->aFileType[2] != 'y' || psHeader->aFileType[3] != 'p')
      {
            debug( LOG_ERROR, "loadTerrainTypeMap: Incorrect file type" );
            abort();
            return false;
      }

      /* TILETYPE_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      // reset the terrain table
      memset(terrainTypes, 0, sizeof(terrainTypes));

      // Load the terrain type mapping
      pType = (UWORD *)(pFileData + TILETYPE_HEADER_SIZE);
      endian_uword(pType);
      for(i = 0; i < psHeader->quantity; i++)
      {
            if (i >= MAX_TILE_TEXTURES)
            {
                  debug( LOG_ERROR, "loadTerrainTypeMap: too many types" );
                  abort();
                  return false;

            }
            if (*pType > TER_MAX)
            {
                  debug( LOG_ERROR, "loadTerrainTypeMap: terrain type out of range" );
                  abort();
                  return false;
            }

            terrainTypes[i] = (UBYTE)*pType;
            pType++;
            endian_uword(pType);
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Write out the terrain type map
static BOOL writeTerrainTypeMapFile(char *pFileName)
{
      TILETYPE_SAVEHEADER           *psHeader;
      char *pFileData;
      UDWORD                              fileSize, i;
      UWORD                         *pType;

      // Calculate the file size
      fileSize = TILETYPE_HEADER_SIZE + sizeof(UWORD) * MAX_TILE_TEXTURES;
      pFileData = (char*)malloc(fileSize);
      if (!pFileData)
      {
            debug( LOG_ERROR, "writeTerrainTypeMapFile: Out of memory" );
            abort();
            return false;
      }

      // Put the file header on the file
      psHeader = (TILETYPE_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 't';
      psHeader->aFileType[1] = 't';
      psHeader->aFileType[2] = 'y';
      psHeader->aFileType[3] = 'p';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = MAX_TILE_TEXTURES;

      pType = (UWORD *)(pFileData + TILETYPE_HEADER_SIZE);
      for(i=0; i<MAX_TILE_TEXTURES; i++)
      {
            *pType = terrainTypes[i];
            endian_uword(pType);
            pType += 1;
      }

      /* TILETYPE_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      if (!saveFile(pFileName, pFileData, fileSize))
      {
            return false;
      }
      free(pFileData);

      return true;
}

// -----------------------------------------------------------------------------------------
// load up component list file
BOOL loadSaveCompList(char *pFileData, UDWORD filesize)
{
      COMPLIST_SAVEHEADER           *psHeader;

      /* Check the file type */
      psHeader = (COMPLIST_SAVEHEADER *)pFileData;
      if (psHeader->aFileType[0] != 'c' || psHeader->aFileType[1] != 'm' ||
            psHeader->aFileType[2] != 'p' || psHeader->aFileType[3] != 'l')
      {
            debug( LOG_ERROR, "loadSaveCompList: Incorrect file type" );
            abort();
            return false;
      }

      /* COMPLIST_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += COMPLIST_HEADER_SIZE;

      /* Check the file version */
      if (psHeader->version < VERSION_7)
      {
            debug( LOG_ERROR, "CompLoad: unsupported save format version %d", psHeader->version );
            abort();
            return false;
      }
      else if (psHeader->version <= VERSION_19)
      {
            if (!loadSaveCompListV9(pFileData, filesize, psHeader->quantity, psHeader->version))
            {
                  return false;
            }
      }
      else if (psHeader->version <= CURRENT_VERSION_NUM)
      {
            if (!loadSaveCompListV(pFileData, filesize, psHeader->quantity, psHeader->version))
            {
                  return false;
            }
      }
      else
      {
            debug( LOG_ERROR, "CompLoad: undefined save format version %d", psHeader->version );
            abort();
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveCompListV9(char *pFileData, UDWORD filesize, UDWORD numRecords, UDWORD version)
{
      SAVE_COMPLIST_V6        *psSaveCompList;
      UDWORD                        i;
      SDWORD                        compInc;

      if ((sizeof(SAVE_COMPLIST_V6) * numRecords + COMPLIST_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "CompListLoad: unexpected end of file" );
            abort();
            return false;
      }

      // Load the data
      for (i = 0; i < numRecords; i++, pFileData += sizeof(SAVE_COMPLIST_V6))
      {
            psSaveCompList = (SAVE_COMPLIST_V6 *) pFileData;

            if (version < VERSION_9)
            {
                  //DROID_MAXCOMP has changed to remove COMP_PROGRAM so hack here to load old save games!
                  if (psSaveCompList->type == SAVE_COMP_PROGRAM)
                  {
                        //ignore this record
                        continue;
                  }
                  if (psSaveCompList->type == SAVE_COMP_WEAPON)
                  {
                        //this typeNum has to be reset for lack of COMP_PROGRAM
                        psSaveCompList->type = COMP_WEAPON;
                  }
            }

            if (psSaveCompList->type > COMP_NUMCOMPONENTS)
            {
                  //ignore this record
                  continue;
            }

            compInc = getCompFromName(psSaveCompList->type, psSaveCompList->name);

            if (compInc < 0)
            {
                  //ignore this record
                  continue;
            }
            if (psSaveCompList->state != UNAVAILABLE && psSaveCompList->state !=
                  AVAILABLE && psSaveCompList->state != FOUND)
            {
                  //ignore this record
                  continue;
            }
            if (psSaveCompList->player > MAX_PLAYERS)
            {
                  //ignore this record
                  continue;
            }
            //date is valid so set the state
            apCompLists[psSaveCompList->player][psSaveCompList->type][compInc] =
                  psSaveCompList->state;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveCompListV(char *pFileData, UDWORD filesize, UDWORD numRecords, UDWORD version)
{
      SAVE_COMPLIST           *psSaveCompList;
      UDWORD                        i;
      SDWORD                        compInc;

      if ((sizeof(SAVE_COMPLIST) * numRecords + COMPLIST_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "CompListLoad: unexpected end of file" );
            abort();
            return false;
      }

      // Load the data
      for (i = 0; i < numRecords; i++, pFileData += sizeof(SAVE_COMPLIST))
      {
            psSaveCompList = (SAVE_COMPLIST *) pFileData;

            if (psSaveCompList->type > COMP_NUMCOMPONENTS)
            {
                  //ignore this record
                  continue;
            }

            compInc = getCompFromName(psSaveCompList->type, psSaveCompList->name);

            if (compInc < 0)
            {
                  //ignore this record
                  continue;
            }
            if (psSaveCompList->state != UNAVAILABLE && psSaveCompList->state !=
                  AVAILABLE && psSaveCompList->state != FOUND)
            {
                  //ignore this record
                  continue;
            }
            if (psSaveCompList->player > MAX_PLAYERS)
            {
                  //ignore this record
                  continue;
            }
            //date is valid so set the state
            apCompLists[psSaveCompList->player][psSaveCompList->type][compInc] =
                  psSaveCompList->state;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Write out the current state of the Comp lists per player
static BOOL writeCompListFile(char *pFileName)
{
      COMPLIST_SAVEHEADER           *psHeader;
      char *pFileData;
      SAVE_COMPLIST                 *psSaveCompList;
      UDWORD                              fileSize, totalComp, player, i;
      COMP_BASE_STATS               *psStats;

      // Calculate the file size
      totalComp = (numBodyStats + numWeaponStats + numConstructStats + numECMStats +
            numPropulsionStats + numSensorStats + numRepairStats + numBrainStats) * MAX_PLAYERS;
      fileSize = COMPLIST_HEADER_SIZE + (sizeof(SAVE_COMPLIST) * totalComp);
      //allocate the buffer space
      pFileData = (char*)malloc(fileSize);
      if (!pFileData)
      {
            debug( LOG_ERROR, "writeCompListFile: Out of memory" );
            abort();
            return false;
      }

      // Put the file header on the file
      psHeader = (COMPLIST_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 'c';
      psHeader->aFileType[1] = 'm';
      psHeader->aFileType[2] = 'p';
      psHeader->aFileType[3] = 'l';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = totalComp;

      psSaveCompList = (SAVE_COMPLIST *) (pFileData + COMPLIST_HEADER_SIZE);

      //save each type of comp
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for(i = 0; i < numBodyStats; i++)
            {
                  psStats = (COMP_BASE_STATS *)(asBodyStats + i);

                  strcpy(psSaveCompList->name, psStats->pName);

                  psSaveCompList->type = COMP_BODY;
                  psSaveCompList->player = (UBYTE)player;
                  psSaveCompList->state = apCompLists[player][COMP_BODY][i];
                  psSaveCompList = (SAVE_COMPLIST *)((char *)psSaveCompList + sizeof(SAVE_COMPLIST));
            }
            for(i = 0; i < numWeaponStats; i++)
            {
                  psStats = (COMP_BASE_STATS *)(asWeaponStats + i);

                  strcpy(psSaveCompList->name, psStats->pName);

                  psSaveCompList->type = COMP_WEAPON;
                  psSaveCompList->player = (UBYTE)player;
                  psSaveCompList->state = apCompLists[player][COMP_WEAPON][i];
                  psSaveCompList = (SAVE_COMPLIST *)((char *)psSaveCompList + sizeof(SAVE_COMPLIST));
            }
            for(i = 0; i < numConstructStats; i++)
            {
                  psStats = (COMP_BASE_STATS *)(asConstructStats + i);

                  strcpy(psSaveCompList->name, psStats->pName);

                  psSaveCompList->type = COMP_CONSTRUCT;
                  psSaveCompList->player = (UBYTE)player;
                  psSaveCompList->state = apCompLists[player][COMP_CONSTRUCT][i];
                  psSaveCompList = (SAVE_COMPLIST *)((char *)psSaveCompList + sizeof(SAVE_COMPLIST));
            }
            for(i = 0; i < numECMStats; i++)
            {
                  psStats = (COMP_BASE_STATS *)(asECMStats + i);

                  strcpy(psSaveCompList->name, psStats->pName);

                  psSaveCompList->type = COMP_ECM;
                  psSaveCompList->player = (UBYTE)player;
                  psSaveCompList->state = apCompLists[player][COMP_ECM][i];
                  psSaveCompList = (SAVE_COMPLIST *)((char *)psSaveCompList + sizeof(SAVE_COMPLIST));
            }
            for(i = 0; i < numPropulsionStats; i++)
            {
                  psStats = (COMP_BASE_STATS *)(asPropulsionStats + i);

                  strcpy(psSaveCompList->name, psStats->pName);

                  psSaveCompList->type = COMP_PROPULSION;
                  psSaveCompList->player = (UBYTE)player;
                  psSaveCompList->state = apCompLists[player][COMP_PROPULSION][i];
                  psSaveCompList = (SAVE_COMPLIST *)((char *)psSaveCompList + sizeof(SAVE_COMPLIST));
            }
            for(i = 0; i < numSensorStats; i++)
            {
                  psStats = (COMP_BASE_STATS *)(asSensorStats + i);

                  strcpy(psSaveCompList->name, psStats->pName);

                  psSaveCompList->type = COMP_SENSOR;
                  psSaveCompList->player = (UBYTE)player;
                  psSaveCompList->state = apCompLists[player][COMP_SENSOR][i];
                  psSaveCompList = (SAVE_COMPLIST *)((char *)psSaveCompList + sizeof(SAVE_COMPLIST));
            }
            for(i = 0; i < numRepairStats; i++)
            {
                  psStats = (COMP_BASE_STATS *)(asRepairStats + i);

                  strcpy(psSaveCompList->name, psStats->pName);

                  psSaveCompList->type = COMP_REPAIRUNIT;
                  psSaveCompList->player = (UBYTE)player;
                  psSaveCompList->state = apCompLists[player][COMP_REPAIRUNIT][i];
                  psSaveCompList = (SAVE_COMPLIST *)((char *)psSaveCompList + sizeof(SAVE_COMPLIST));
            }
            for(i = 0; i < numBrainStats; i++)
            {
                  psStats = (COMP_BASE_STATS *)(asBrainStats + i);

                  strcpy(psSaveCompList->name, psStats->pName);

                  psSaveCompList->type = COMP_BRAIN;
                  psSaveCompList->player = (UBYTE)player;
                  psSaveCompList->state = apCompLists[player][COMP_BRAIN][i];
                  psSaveCompList = (SAVE_COMPLIST *)((char *)psSaveCompList + sizeof(SAVE_COMPLIST));
            }
      }

      /* COMPLIST_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      if (!saveFile(pFileName, pFileData, fileSize))
      {
            return false;
      }
      free(pFileData);

      return true;
}

// -----------------------------------------------------------------------------------------
// load up structure type list file
BOOL loadSaveStructTypeList(char *pFileData, UDWORD filesize)
{
      STRUCTLIST_SAVEHEADER         *psHeader;

      /* Check the file type */
      psHeader = (STRUCTLIST_SAVEHEADER*)pFileData;
      if (psHeader->aFileType[0] != 's' || psHeader->aFileType[1] != 't' ||
            psHeader->aFileType[2] != 'r' || psHeader->aFileType[3] != 'l')
      {
            debug( LOG_ERROR, "loadSaveStructTypeList: Incorrect file type" );
            abort();
            return false;
      }

      /* STRUCTLIST_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += STRUCTLIST_HEADER_SIZE;

      /* Check the file version */
      if (psHeader->version < VERSION_7)
      {
            debug( LOG_ERROR, "StructTypeLoad: unsupported save format version %d", psHeader->version );
            abort();
            return false;
      }
      else if (psHeader->version <= VERSION_19)
      {
            if (!loadSaveStructTypeListV7(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else if (psHeader->version <= CURRENT_VERSION_NUM)
      {
            if (!loadSaveStructTypeListV(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else
      {
            debug( LOG_ERROR, "StructTypeLoad: undefined save format version %d", psHeader->version );
            abort();
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveStructTypeListV7(char *pFileData, UDWORD filesize, UDWORD numRecords)
{
      SAVE_STRUCTLIST_V6            *psSaveStructList;
      UDWORD                        i, statInc;
      STRUCTURE_STATS         *psStats;
      BOOL                    found;

      if ((sizeof(SAVE_STRUCTLIST_V6) * numRecords + STRUCTLIST_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "StructListLoad: unexpected end of file" );
            abort();
            return false;
      }

      // Load the data
      for (i = 0; i < numRecords; i++, pFileData += sizeof(SAVE_STRUCTLIST_V6))
      {
            psSaveStructList = (SAVE_STRUCTLIST_V6 *) pFileData;

            found = false;

            for (statInc = 0; statInc < numStructureStats; statInc++)
            {
                  psStats = asStructureStats + statInc;
                  //loop until find the same name

                  if (!strcmp(psStats->pName, psSaveStructList->name))

                  {
                        found = true;
                        break;
                  }
            }
            if (!found)
            {
                  //ignore this record
                  continue;
            }
            if (psSaveStructList->state != UNAVAILABLE && psSaveStructList->state !=
                  AVAILABLE && psSaveStructList->state != FOUND)
            {
                  //ignore this record
                  continue;
            }
            if (psSaveStructList->player > MAX_PLAYERS)
            {
                  //ignore this record
                  continue;
            }
            //date is valid so set the state
            apStructTypeLists[psSaveStructList->player][statInc] =
                  psSaveStructList->state;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveStructTypeListV(char *pFileData, UDWORD filesize, UDWORD numRecords)
{
      SAVE_STRUCTLIST         *psSaveStructList;
      UDWORD                        i, statInc;
      STRUCTURE_STATS         *psStats;
      BOOL                    found;

      if ((sizeof(SAVE_STRUCTLIST) * numRecords + STRUCTLIST_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "StructListLoad: unexpected end of file" );
            abort();
            return false;
      }

      // Load the data
      for (i = 0; i < numRecords; i++, pFileData += sizeof(SAVE_STRUCTLIST))
      {
            psSaveStructList = (SAVE_STRUCTLIST *) pFileData;

            found = false;

            for (statInc = 0; statInc < numStructureStats; statInc++)
            {
                  psStats = asStructureStats + statInc;
                  //loop until find the same name

                  if (!strcmp(psStats->pName, psSaveStructList->name))

                  {
                        found = true;
                        break;
                  }
            }
            if (!found)
            {
                  //ignore this record
                  continue;
            }
            if (psSaveStructList->state != UNAVAILABLE && psSaveStructList->state !=
                  AVAILABLE && psSaveStructList->state != FOUND)
            {
                  //ignore this record
                  continue;
            }
            if (psSaveStructList->player > MAX_PLAYERS)
            {
                  //ignore this record
                  continue;
            }
            //date is valid so set the state
            apStructTypeLists[psSaveStructList->player][statInc] =
                  psSaveStructList->state;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Write out the current state of the Struct Type List per player
static BOOL writeStructTypeListFile(char *pFileName)
{
      STRUCTLIST_SAVEHEADER   *psHeader;
      SAVE_STRUCTLIST               *psSaveStructList;
      char *pFileData;
      UDWORD                              fileSize, player, i;
      STRUCTURE_STATS               *psStats;

      // Calculate the file size
      fileSize = STRUCTLIST_HEADER_SIZE + (sizeof(SAVE_STRUCTLIST) *
            numStructureStats * MAX_PLAYERS);

      //allocate the buffer space
      pFileData = (char*)malloc(fileSize);
      if (!pFileData)
      {
            debug( LOG_ERROR, "writeStructTypeListFile: Out of memory" );
            abort();
            return false;
      }

      // Put the file header on the file
      psHeader = (STRUCTLIST_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 's';
      psHeader->aFileType[1] = 't';
      psHeader->aFileType[2] = 'r';
      psHeader->aFileType[3] = 'l';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = numStructureStats * MAX_PLAYERS;

      psSaveStructList = (SAVE_STRUCTLIST *) (pFileData + STRUCTLIST_HEADER_SIZE);
      //save each type of struct type
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            psStats = asStructureStats;
            for(i = 0; i < numStructureStats; i++, psStats++)
            {

                  strcpy(psSaveStructList->name, psStats->pName);
                  psSaveStructList->state = apStructTypeLists[player][i];
                  psSaveStructList->player = (UBYTE)player;
                  psSaveStructList = (SAVE_STRUCTLIST *)((char *)psSaveStructList +
                        sizeof(SAVE_STRUCTLIST));
            }
      }

      /* STRUCT_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      if (!saveFile(pFileName, pFileData, fileSize))
      {
            return false;
      }
      free(pFileData);

      return true;
}


// -----------------------------------------------------------------------------------------
// load up saved research file
BOOL loadSaveResearch(char *pFileData, UDWORD filesize)
{
      RESEARCH_SAVEHEADER           *psHeader;

      /* Check the file type */
      psHeader = (RESEARCH_SAVEHEADER*)pFileData;
      if (psHeader->aFileType[0] != 'r' || psHeader->aFileType[1] != 'e' ||
            psHeader->aFileType[2] != 's' || psHeader->aFileType[3] != 'h')
      {
            debug( LOG_ERROR, "loadSaveResearch: Incorrect file type" );
            abort();
            return false;
      }

      /* RESEARCH_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += RESEARCH_HEADER_SIZE;

      /* Check the file version */
      if (psHeader->version < VERSION_8)
      {
            debug( LOG_ERROR, "ResearchLoad: unsupported save format version %d", psHeader->version );
            abort();
            return false;
      }
      else if (psHeader->version <= VERSION_19)
      {
            if (!loadSaveResearchV8(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else if (psHeader->version <= CURRENT_VERSION_NUM)
      {
            if (!loadSaveResearchV(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else
      {
            debug( LOG_ERROR, "ResearchLoad: undefined save format version %d", psHeader->version );
            abort();
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveResearchV8(char *pFileData, UDWORD filesize, UDWORD numRecords)
{
      SAVE_RESEARCH_V8        *psSaveResearch;
      UDWORD                        i, statInc;
      RESEARCH                *psStats;
      BOOL                    found;
      UBYTE                   playerInc;

      if ((sizeof(SAVE_RESEARCH_V8) * numRecords + RESEARCH_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "loadSaveResearch: unexpected end of file" );
            abort();
            return false;
      }

      // Load the data
      for (i = 0; i < numRecords; i++, pFileData += sizeof(SAVE_RESEARCH_V8))
      {
            psSaveResearch = (SAVE_RESEARCH_V8 *) pFileData;

            /* SAVE_RESEARCH_V8 is RESEARCH_SAVE_V8 */
            /* RESEARCH_SAVE_V8 */
            for(playerInc = 0; playerInc < MAX_PLAYERS; playerInc++)
                  endian_udword(&psSaveResearch->currentPoints[playerInc]);

            found = false;

            for (statInc = 0; statInc < numResearch; statInc++)
            {
                  psStats = asResearch + statInc;
                  //loop until find the same name

                  if (!strcmp(psStats->pName, psSaveResearch->name))

                  {
                        found = true;
                        break;
                  }
            }
            if (!found)
            {
                  //ignore this record
                  continue;
            }


            for (playerInc = 0; playerInc < MAX_PLAYERS; playerInc++)
            {
/* what did this do then ?
                  if (psSaveResearch->researched[playerInc] != 0 &&
                        psSaveResearch->researched[playerInc] != STARTED_RESEARCH &&
                        psSaveResearch->researched[playerInc] != CANCELLED_RESEARCH &&
                        psSaveResearch->researched[playerInc] != RESEARCHED)
                  {
                        //ignore this record
                        continue; //to next player
                  }
*/
                  PLAYER_RESEARCH *psPlRes;

                  psPlRes=&asPlayerResList[playerInc][statInc];

                  // Copy the research status
                  psPlRes->ResearchStatus=      (UBYTE)(psSaveResearch->researched[playerInc] & RESBITS);

                  if (psSaveResearch->possible[playerInc]!=0)
                        MakeResearchPossible(psPlRes);


                  psPlRes->currentPoints = psSaveResearch->currentPoints[playerInc];

                  //for any research that has been completed - perform so that upgrade values are set up
                  if (psSaveResearch->researched[playerInc] == RESEARCHED)
                  {
                        researchResult(statInc, playerInc, false, NULL);
                  }
            }
      }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveResearchV(char *pFileData, UDWORD filesize, UDWORD numRecords)
{
      SAVE_RESEARCH           *psSaveResearch;
      UDWORD                        i, statInc;
      RESEARCH                *psStats;
      BOOL                    found;
      UBYTE                   playerInc;

      if ((sizeof(SAVE_RESEARCH) * numRecords + RESEARCH_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "loadSaveResearch: unexpected end of file" );
            abort();
            return false;
      }

      // Load the data
      for (i = 0; i < numRecords; i++, pFileData += sizeof(SAVE_RESEARCH))
      {
            psSaveResearch = (SAVE_RESEARCH *) pFileData;

            /* SAVE_RESEARCH is RESEARCH_SAVE_V20 */
            /* RESEARCH_SAVE_V20 */
            for(playerInc = 0; playerInc < MAX_PLAYERS; playerInc++)
                  endian_udword(&psSaveResearch->currentPoints[playerInc]);

            found = false;

            for (statInc = 0; statInc < numResearch; statInc++)
            {
                  psStats = asResearch + statInc;
                  //loop until find the same name

                  if (!strcmp(psStats->pName, psSaveResearch->name))

                  {
                        found = true;
                        break;
                  }
            }
            if (!found)
            {
                  //ignore this record
                  continue;
            }


            for (playerInc = 0; playerInc < MAX_PLAYERS; playerInc++)
            {


                  PLAYER_RESEARCH *psPlRes;

                  psPlRes=&asPlayerResList[playerInc][statInc];

                  // Copy the research status
                  psPlRes->ResearchStatus=      (UBYTE)(psSaveResearch->researched[playerInc] & RESBITS);

                  if (psSaveResearch->possible[playerInc]!=0)
                        MakeResearchPossible(psPlRes);



                  psPlRes->currentPoints = psSaveResearch->currentPoints[playerInc];

                  //for any research that has been completed - perform so that upgrade values are set up
                  if (psSaveResearch->researched[playerInc] == RESEARCHED)
                  {
                        researchResult(statInc, playerInc, false, NULL);
                  }
            }
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Write out the current state of the Research per player
static BOOL writeResearchFile(char *pFileName)
{
      RESEARCH_SAVEHEADER           *psHeader;
      SAVE_RESEARCH                 *psSaveResearch;
      char *pFileData;
      UDWORD                              fileSize, player, i;
      RESEARCH                      *psStats;

      // Calculate the file size
      fileSize = RESEARCH_HEADER_SIZE + (sizeof(SAVE_RESEARCH) *
            numResearch);

      //allocate the buffer space
      pFileData = (char*)malloc(fileSize);
      if (!pFileData)
      {
            debug( LOG_ERROR, "writeResearchFile: Out of memory" );
            abort();
            return false;
      }

      // Put the file header on the file
      psHeader = (RESEARCH_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 'r';
      psHeader->aFileType[1] = 'e';
      psHeader->aFileType[2] = 's';
      psHeader->aFileType[3] = 'h';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = numResearch;

      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      psSaveResearch = (SAVE_RESEARCH *) (pFileData + RESEARCH_HEADER_SIZE);
      //save each type of reesearch
      psStats = asResearch;
      for(i = 0; i < numResearch; i++, psStats++)
      {

            strcpy(psSaveResearch->name, psStats->pName);

            for (player = 0; player < MAX_PLAYERS; player++)
            {
                  psSaveResearch->possible[player] = IsResearchPossible(&asPlayerResList[player][i]);
                  psSaveResearch->researched[player] = (UBYTE)(asPlayerResList[player][i].ResearchStatus&RESBITS);
                  psSaveResearch->currentPoints[player] = asPlayerResList[player][i].currentPoints;
            }

            for(player = 0; player < MAX_PLAYERS; player++)
                  endian_udword(&psSaveResearch->currentPoints[player]);

            psSaveResearch = (SAVE_RESEARCH *)((char *)psSaveResearch +
                  sizeof(SAVE_RESEARCH));
      }

      if (!saveFile(pFileName, pFileData, fileSize))
      {
            return false;
      }
      free(pFileData);

      return true;
}


// -----------------------------------------------------------------------------------------
// load up saved message file
BOOL loadSaveMessage(char *pFileData, UDWORD filesize, SWORD levelType)
{
      MESSAGE_SAVEHEADER            *psHeader;

      /* Check the file type */
      psHeader = (MESSAGE_SAVEHEADER*)pFileData;
      if (psHeader->aFileType[0] != 'm' || psHeader->aFileType[1] != 'e' ||
            psHeader->aFileType[2] != 's' || psHeader->aFileType[3] != 's')
      {
            debug( LOG_ERROR, "loadSaveMessage: Incorrect file type" );
            abort();
            return false;
      }

      /* MESSAGE_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += MESSAGE_HEADER_SIZE;

      /* Check the file version */
      if(psHeader->version <= VERSION_35)
      {
            if (!loadSaveMessageV(pFileData, filesize, psHeader->quantity, psHeader->version, levelType))
            {
                  return false;
            }
      }
      else
      {
            if (!loadSaveMessage36(pFileData, filesize, psHeader->quantity, psHeader->version, levelType))
            {
                  return false;
            }
      }
      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveMessageV(char *pFileData, UDWORD filesize, UDWORD numMessages, UDWORD version, SWORD levelType)
{
      SAVE_MESSAGE      *psSaveMessage;
      MESSAGE                 *psMessage;
      VIEWDATA          *psViewData = NULL;
      UDWORD                  i, height;

      //clear any messages put in during level loads
      //freeMessages();

    //only clear the messages if its a mid save game
      if (gameType == GTYPE_SAVE_MIDMISSION)
    {
        freeMessages();
    }
    else if (gameType == GTYPE_SAVE_START)
    {
        //if we're loading in a CamStart or a CamChange then we're not interested in any saved messages
        if (levelType == LDS_CAMSTART || levelType == LDS_CAMCHANGE)
        {
            return true;
        }

    }

      //check file
      if ((sizeof(SAVE_MESSAGE) * numMessages + MESSAGE_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "loadSaveMessage: unexpected end of file" );
            abort();
            return false;
      }

      // Load the data
      for (i = 0; i < numMessages; i++, pFileData += sizeof(SAVE_MESSAGE))
      {
            psSaveMessage = (SAVE_MESSAGE *) pFileData;

            /* SAVE_MESSAGE */
            endian_sdword((SDWORD*)&psSaveMessage->type);   /* FIXME: enum may not be this type! */
            endian_udword(&psSaveMessage->objId);
            endian_udword(&psSaveMessage->player);

            if (psSaveMessage->type == MSG_PROXIMITY)
            {
            //only load proximity if a mid-mission save game
            if (gameType == GTYPE_SAVE_MIDMISSION)
            {
                      if (psSaveMessage->bObj)
                      {
                            //proximity object so create get the obj from saved idy
                            psMessage = addMessage(psSaveMessage->type, true, psSaveMessage->player);
                            if (psMessage)
                            {
                                  psMessage->pViewData = (MSG_VIEWDATA *)getBaseObjFromId(psSaveMessage->objId);
                            }
                      }
                      else
                      {
                            //proximity position so get viewdata pointer from the name
                            psMessage = addMessage(psSaveMessage->type, false, psSaveMessage->player);
                            if (psMessage)
                            {
                                  psViewData = (VIEWDATA *)getViewData(psSaveMessage->name);
                        if (psViewData == NULL)
                        {
                            //skip this message
                            continue;
                        }
                                  psMessage->pViewData = (MSG_VIEWDATA *)psViewData;
                            }
                            //check the z value is at least the height of the terrain
                            height = map_Height(((VIEW_PROXIMITY *)psViewData->pData)->x,
                                  ((VIEW_PROXIMITY *)psViewData->pData)->y);
                            if (((VIEW_PROXIMITY *)psViewData->pData)->z < height)
                            {
                                  ((VIEW_PROXIMITY *)psViewData->pData)->z = height;
                            }
                      }
            }
            }
            else
            {
            //only load Campaign/Mission if a mid-mission save game
            if (psSaveMessage->type == MSG_CAMPAIGN || psSaveMessage->type == MSG_MISSION)
            {
                if (gameType == GTYPE_SAVE_MIDMISSION)
                {
                      // Research message // Campaign message // Mission Report messages
                  psMessage = addMessage(psSaveMessage->type, false, psSaveMessage->player);
                  if (psMessage)
                      {
                            psMessage->pViewData = (MSG_VIEWDATA *)getViewData(psSaveMessage->name);
                          }
                }
            }
            else
            {
                  // Research message
            psMessage = addMessage(psSaveMessage->type, false, psSaveMessage->player);
                if (psMessage)
                  {
                        psMessage->pViewData = (MSG_VIEWDATA *)getViewData(psSaveMessage->name);
                      }
            }
            }
      }

      return true;
}

BOOL loadSaveMessage36(char *pFileData, UDWORD filesize, UDWORD numMessages, UDWORD version, SWORD levelType)
{
      SAVE_MESSAGE_36   *psSaveMessage;
      MESSAGE                 *psMessage;
      VIEWDATA          *psViewData = NULL;
      UDWORD                  i, height;

      //clear any messages put in during level loads
      //freeMessages();

    //only clear the messages if its a mid save game
      if (gameType == GTYPE_SAVE_MIDMISSION)
    {
        freeMessages();
    }
    else if (gameType == GTYPE_SAVE_START)
    {
        //if we're loading in a CamStart or a CamChange then we're not interested in any saved messages
        if (levelType == LDS_CAMSTART || levelType == LDS_CAMCHANGE)
        {
            return true;
        }

    }

      //check file
      if ((sizeof(SAVE_MESSAGE_36) * numMessages + MESSAGE_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "loadSaveMessage: unexpected end of file" );
            abort();
            return false;
      }

      // Load the data
      for (i = 0; i < numMessages; i++, pFileData += sizeof(SAVE_MESSAGE_36))
      {
            psSaveMessage = (SAVE_MESSAGE_36 *) pFileData;

            /* SAVE_MESSAGE_36 */
            endian_sdword((SDWORD*)&psSaveMessage->type);   /* FIXME: enum may not be this type! */
            endian_sdword((SDWORD*)&psSaveMessage->dataType);
            endian_udword(&psSaveMessage->objId);
            endian_udword(&psSaveMessage->player);

            if (psSaveMessage->type == MSG_PROXIMITY)
            {
            //only load proximity if a mid-mission save game
            if (gameType == GTYPE_SAVE_MIDMISSION)
            {
                      if (psSaveMessage->bObj)
                      {
                            //proximity object so create get the obj from saved idy
                            psMessage = addMessage(psSaveMessage->type, true, psSaveMessage->player);
                            if (psMessage)
                            {
                                  psMessage->pViewData = (MSG_VIEWDATA *)getBaseObjFromId(psSaveMessage->objId);
                            }
                      }
                      else
                      {
                            //proximity position so get viewdata pointer from the name
                              psMessage = addMessage(psSaveMessage->type, false, psSaveMessage->player);

                              if (psMessage)
                            {
                                    if(psSaveMessage->dataType == MSG_DATA_BEACON)
                                    {
                                          UDWORD locX,locY;
                                          SDWORD sender;

                                          endian_udword(&psSaveMessage->locX);
                                          locX = psSaveMessage->locX;
                                          endian_udword(&psSaveMessage->locY);
                                          locY = psSaveMessage->locY;
                                          endian_sdword(&psSaveMessage->sender);
                                          sender = psSaveMessage->sender;

                                          psViewData = CreateBeaconViewData(sender, locX, locY);
                                    }
                                    else
                                    {
                                          psViewData = (VIEWDATA *)getViewData(psSaveMessage->name);
                                    }

                                    if (psViewData == NULL)
                        {
                            //skip this message
                            continue;
                        }
                                  psMessage->pViewData = (MSG_VIEWDATA *)psViewData;
                            }
                            //check the z value is at least the height of the terrain
                            height = map_Height(((VIEW_PROXIMITY *)psViewData->pData)->x,
                                  ((VIEW_PROXIMITY *)psViewData->pData)->y);
                            if (((VIEW_PROXIMITY *)psViewData->pData)->z < height)
                            {
                                  ((VIEW_PROXIMITY *)psViewData->pData)->z = height;
                            }
                      }
            }
            }
            else
            {
            //only load Campaign/Mission if a mid-mission save game
            if (psSaveMessage->type == MSG_CAMPAIGN || psSaveMessage->type == MSG_MISSION)
            {
                if (gameType == GTYPE_SAVE_MIDMISSION)
                {
                      // Research message // Campaign message // Mission Report messages
                  psMessage = addMessage(psSaveMessage->type, false, psSaveMessage->player);
                  if (psMessage)
                      {
                            psMessage->pViewData = (MSG_VIEWDATA *)getViewData(psSaveMessage->name);
                          }
                }
            }
            else
            {
                  // Research message
            psMessage = addMessage(psSaveMessage->type, false, psSaveMessage->player);
                if (psMessage)
                  {
                        psMessage->pViewData = (MSG_VIEWDATA *)getViewData(psSaveMessage->name);
                      }
            }
            }
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Write out the current messages per player
static BOOL writeMessageFile(char *pFileName)
{
      MESSAGE_SAVEHEADER            *psHeader;
      SAVE_MESSAGE_36               *psSaveMessage;
      char *pFileData;
      UDWORD                              fileSize, player;
      MESSAGE                             *psMessage;
      PROXIMITY_DISPLAY       *psProx;
      BASE_OBJECT                   *psObj;
      UDWORD                              numMessages = 0;
      VIEWDATA                      *pViewData;

      // Calculate the file size
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for(psMessage = apsMessages[player]; psMessage != NULL;psMessage = psMessage->psNext)
            {
                  numMessages++;
            }

      }

      fileSize = MESSAGE_HEADER_SIZE + (sizeof(SAVE_MESSAGE_36) *
            numMessages);


      //allocate the buffer space
      pFileData = (char*)malloc(fileSize);
      if (!pFileData)
      {
            debug( LOG_ERROR, "writeMessageFile: Out of memory" );
            abort();
            return false;
      }

      // Put the file header on the file
      psHeader = (MESSAGE_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 'm';
      psHeader->aFileType[1] = 'e';
      psHeader->aFileType[2] = 's';
      psHeader->aFileType[3] = 's';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = numMessages;

      psSaveMessage = (SAVE_MESSAGE_36 *) (pFileData + MESSAGE_HEADER_SIZE);
      //save each type of reesearch
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            psMessage = apsMessages[player];
            for(psMessage = apsMessages[player]; psMessage != NULL;psMessage = psMessage->psNext)
            {
                  psSaveMessage->type = psMessage->type;    //The type of message
                  psSaveMessage->dataType = psMessage->dataType;

                  if (psMessage->type == MSG_PROXIMITY)
                  {
                        //get the matching proximity message
                        for(psProx = apsProxDisp[player]; psProx != NULL; psProx = psProx->psNext)
                        {
                              //compare the pointers
                              if (psProx->psMessage == psMessage)
                              {
                                    break;
                              }
                        }
                        ASSERT( psProx != NULL,"Save message; proximity display not found for message" );

                        if (psProx->type == POS_PROXDATA)
                        {
                              //message has viewdata so store the name
                              psSaveMessage->bObj = false;
                              pViewData = (VIEWDATA*)psMessage->pViewData;
                              ASSERT( strlen(pViewData->pName) < MAX_GAME_STR_SIZE,"writeMessageFile; viewdata pName Error" );
                              strcpy(psSaveMessage->name,pViewData->pName);   //Pointer to view data - if any - should be some!

                              // save beacon data
                              if(psMessage->dataType == MSG_DATA_BEACON)
                              {
                                    VIEW_PROXIMITY * viewData = (VIEW_PROXIMITY *)((VIEWDATA *)psMessage->pViewData)->pData;

                                    psSaveMessage->locX =  viewData->x;
                                    endian_udword(&psSaveMessage->locX);
                                    psSaveMessage->locY =  viewData->y;
                                    endian_udword(&psSaveMessage->locY);
                                    psSaveMessage->sender = viewData->sender;
                                    endian_sdword(&psSaveMessage->sender);
                              }
                        }
                        else
                        {
                              //message has object so store ObjectId
                              psSaveMessage->bObj = true;
                              psObj = (BASE_OBJECT*)psMessage->pViewData;
                              psSaveMessage->objId = psObj->id;//should be unique for these objects
                        }
                  }
                  else
                  {
                        psSaveMessage->bObj = false;
                        pViewData = (VIEWDATA*)psMessage->pViewData;
                        ASSERT( strlen(pViewData->pName) < MAX_GAME_STR_SIZE,"writeMessageFile; viewdata pName Error" );
                        strcpy(psSaveMessage->name,pViewData->pName);   //Pointer to view data - if any - should be some!
                  }
                  psSaveMessage->read = psMessage->read;                //flag to indicate whether message has been read
                  psSaveMessage->player = psMessage->player;            //which player this message belongs to

                  endian_sdword((SDWORD*)&psSaveMessage->type); /* FIXME: enum may be different type! */
                  endian_sdword((SDWORD*)&psSaveMessage->dataType);
                  endian_udword(&psSaveMessage->objId);
                  endian_udword(&psSaveMessage->player);

                  psSaveMessage = (SAVE_MESSAGE_36 *)((char *)psSaveMessage +       sizeof(SAVE_MESSAGE_36));
            }
      }

      /* MESSAGE_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      if (!saveFile(pFileName, pFileData, fileSize))
      {
            return false;
      }
      free(pFileData);

      return true;
}

// -----------------------------------------------------------------------------------------
// load up saved flag file
BOOL loadSaveFlag(char *pFileData, UDWORD filesize)
{
      FLAG_SAVEHEADER         *psHeader;

      /* Check the file type */
      psHeader = (FLAG_SAVEHEADER*)pFileData;
      if (psHeader->aFileType[0] != 'f' || psHeader->aFileType[1] != 'l' ||
            psHeader->aFileType[2] != 'a' || psHeader->aFileType[3] != 'g')
      {
            debug( LOG_ERROR, "loadSaveflag: Incorrect file type" );
            abort();
            return false;
      }

      /* FLAG_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += FLAG_HEADER_SIZE;

      /* Check the file version */
      if (!loadSaveFlagV(pFileData, filesize, psHeader->quantity, psHeader->version))
      {
            return false;
      }
      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveFlagV(char *pFileData, UDWORD filesize, UDWORD numflags, UDWORD version)
{
      SAVE_FLAG         *psSaveflag;
      FLAG_POSITION     *psflag;
      UDWORD                  i;
      STRUCTURE*        psStruct;
      UDWORD                  factoryToFind = 0;
      UDWORD                  sizeOfSaveFlag;

      //clear any flags put in during level loads
      freeAllFlagPositions();
      initFactoryNumFlag();//clear the factory masks, we will find the factories from their assembly points


      //check file
      if (version <= VERSION_18)
      {
            sizeOfSaveFlag = sizeof(SAVE_FLAG_V18);
      }
      else
      {
            sizeOfSaveFlag = sizeof(SAVE_FLAG);
      }

      if ((sizeOfSaveFlag * numflags + FLAG_HEADER_SIZE) > filesize)
      {
            debug( LOG_ERROR, "loadSaveflag: unexpected end of file" );
            abort();
            return false;
      }

      // Load the data
      for (i = 0; i < numflags; i++, pFileData += sizeOfSaveFlag)
      {
            psSaveflag = (SAVE_FLAG *) pFileData;

            /* SAVE_FLAG */
            endian_sdword((SDWORD*) &psSaveflag->type); /* FIXME: enum may not be this type! */
            endian_udword(&psSaveflag->frameNumber);
            endian_udword(&psSaveflag->screenX);
            endian_udword(&psSaveflag->screenY);
            endian_udword(&psSaveflag->screenR);
            endian_udword(&psSaveflag->player);
            endian_sdword(&psSaveflag->coords.x);
            endian_sdword(&psSaveflag->coords.y);
            endian_sdword(&psSaveflag->coords.z);
            endian_udword(&psSaveflag->repairId);

            createFlagPosition(&psflag, psSaveflag->player);
            psflag->type = psSaveflag->type;                      //The type of flag
            psflag->frameNumber = psSaveflag->frameNumber;  //when the Position was last drawn
            psflag->screenX = psSaveflag->screenX;                //screen coords and radius of Position imd
            psflag->screenY = psSaveflag->screenY;
            psflag->screenR = psSaveflag->screenR;
            psflag->player = psSaveflag->player;                  //which player the Position belongs to
            psflag->selected = psSaveflag->selected;        //flag to indicate whether the Position
            psflag->coords = psSaveflag->coords;                  //the world coords of the Position
            psflag->factoryInc = psSaveflag->factoryInc;    //indicates whether the first, second etc factory
            psflag->factoryType = psSaveflag->factoryType;  //indicates whether standard, cyborg or vtol factory

            if ((psflag->type == POS_DELIVERY) || (psflag->type == POS_TEMPDELIVERY))
            {
                  if (psflag->factoryType == FACTORY_FLAG)
                  {
                        factoryToFind = REF_FACTORY;
                  }
                  else if (psflag->factoryType == CYBORG_FLAG)
                  {
                        factoryToFind = REF_CYBORG_FACTORY;
                  }
                  else if (psflag->factoryType == VTOL_FLAG)
                  {
                        factoryToFind = REF_VTOL_FACTORY;
                  }
                  else if (psflag->factoryType == REPAIR_FLAG)
                  {
                        factoryToFind = REF_REPAIR_FACILITY;
                  }
                  else
                  {
                        ASSERT( false,"loadSaveFlagV delivery flag type not recognised?" );
                  }

                  if (factoryToFind == REF_REPAIR_FACILITY)
                  {
                        if (version > VERSION_18)
                        {
                              psStruct = (STRUCTURE*)getBaseObjFromId(psSaveflag->repairId);
                              if (psStruct != NULL)
                              {
                                    if (psStruct->type != OBJ_STRUCTURE)
                                    {
                                          ASSERT( false,"loadFlag found duplicate Id for repair facility" );
                                    }
                                    else if (psStruct->pStructureType->type == REF_REPAIR_FACILITY)
                                    {
                                          if (psStruct->pFunctionality != NULL)
                                          {
                                                //this is the one so set it
                                                ((REPAIR_FACILITY *)psStruct->pFunctionality)->psDeliveryPoint = psflag;
                                          }
                                    }
                              }
                        }
                  }
                  else
                  {
                        //okay find the player factory with this inc and set this as the assembly point
                        for (psStruct = apsStructLists[psflag->player]; psStruct != NULL; psStruct = psStruct->psNext)
                        {
                              if (psStruct->pStructureType->type == factoryToFind)
                              {
                                    if ((UDWORD)((FACTORY *)psStruct->pFunctionality)->psAssemblyPoint == psflag->factoryInc)
                                    {
                                          //this is the one so set it
                                          ((FACTORY *)psStruct->pFunctionality)->psAssemblyPoint = psflag;
                                    }
                              }
                        }
                  }
            }

            if (psflag->type == POS_DELIVERY)//dont add POS_TEMPDELIVERYs
            {
                  addFlagPosition(psflag);
            }
            else if (psflag->type == POS_TEMPDELIVERY)//but make them real flags
            {
                  psflag->type = POS_DELIVERY;
            }
      }

      resetFactoryNumFlag();//set the new numbers into the masks

      return true;
}

// -----------------------------------------------------------------------------------------
// Write out the current flags per player
static BOOL writeFlagFile(char *pFileName)
{
      FLAG_SAVEHEADER         *psHeader;
      SAVE_FLAG               *psSaveflag;
      STRUCTURE               *psStruct;
      FACTORY                       *psFactory;
      char                    *pFileData;
      UDWORD                        fileSize, player;
      FLAG_POSITION           *psflag;
      UDWORD                        numflags = 0;


      // Calculate the file size
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for(psflag = apsFlagPosLists[player]; psflag != NULL;psflag = psflag->psNext)
            {
                  numflags++;
            }

      }
      //and add the delivery points not in the list
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for(psStruct = apsStructLists[player]; psStruct != NULL; psStruct = psStruct->psNext)
            {
                  if (psStruct->pFunctionality)
                  {
                        switch (psStruct->pStructureType->type)
                        {
                        case REF_FACTORY:
                        case REF_CYBORG_FACTORY:
                        case REF_VTOL_FACTORY:
                              psFactory = ((FACTORY *)psStruct->pFunctionality);
                              //if there is a commander then has been temporarily removed
                              //so put it back
                              if (psFactory->psCommander)
                              {
                                    psflag = psFactory->psAssemblyPoint;
                                    if (psflag != NULL)
                                    {
                                          numflags++;
                                    }
                              }
                              break;
                        default:
                              break;
                        }
                  }
            }
      }

      fileSize = FLAG_HEADER_SIZE + (sizeof(SAVE_FLAG) *
            numflags);


      //allocate the buffer space
      pFileData = (char*)malloc(fileSize);
      if (!pFileData)
      {
            debug( LOG_ERROR, "writeflagFile: Out of memory" );
            abort();
            return false;
      }

      // Put the file header on the file
      psHeader = (FLAG_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 'f';
      psHeader->aFileType[1] = 'l';
      psHeader->aFileType[2] = 'a';
      psHeader->aFileType[3] = 'g';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = numflags;

      psSaveflag = (SAVE_FLAG *) (pFileData + FLAG_HEADER_SIZE);
      //save each type of reesearch
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            psflag = apsFlagPosLists[player];
            for(psflag = apsFlagPosLists[player]; psflag != NULL;psflag = psflag->psNext)
            {
                  psSaveflag->type = psflag->type;                      //The type of flag
                  psSaveflag->frameNumber = psflag->frameNumber;  //when the Position was last drawn
                  psSaveflag->screenX = psflag->screenX;                //screen coords and radius of Position imd
                  psSaveflag->screenY = psflag->screenY;
                  psSaveflag->screenR = psflag->screenR;
                  psSaveflag->player = psflag->player;                  //which player the Position belongs to
                  psSaveflag->selected = psflag->selected;        //flag to indicate whether the Position
                  psSaveflag->coords = psflag->coords;                  //the world coords of the Position
                  psSaveflag->factoryInc = psflag->factoryInc;    //indicates whether the first, second etc factory
                  psSaveflag->factoryType = psflag->factoryType;  //indicates whether standard, cyborg or vtol factory
                  if (psflag->factoryType == REPAIR_FLAG)
                  {
                        //get repair facility id
                        psSaveflag->repairId = getRepairIdFromFlag(psflag);
                  }

                  /* SAVE_FLAG */
                  endian_sdword((SDWORD*)&psSaveflag->type); /* FIXME: enum may be different type! */
                  endian_udword(&psSaveflag->frameNumber);
                  endian_udword(&psSaveflag->screenX);
                  endian_udword(&psSaveflag->screenY);
                  endian_udword(&psSaveflag->screenR);
                  endian_udword(&psSaveflag->player);
                  endian_sdword(&psSaveflag->coords.x);
                  endian_sdword(&psSaveflag->coords.y);
                  endian_sdword(&psSaveflag->coords.z);
                  endian_udword(&psSaveflag->repairId);

                  psSaveflag = (SAVE_FLAG *)((char *)psSaveflag +       sizeof(SAVE_FLAG));
            }
      }
      //and add the delivery points not in the list
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            for(psStruct = apsStructLists[player]; psStruct != NULL; psStruct = psStruct->psNext)
            {
                  if (psStruct->pFunctionality)
                  {
                        switch (psStruct->pStructureType->type)
                        {
                        case REF_FACTORY:
                        case REF_CYBORG_FACTORY:
                        case REF_VTOL_FACTORY:
                              psFactory = ((FACTORY *)psStruct->pFunctionality);
                              //if there is a commander then has been temporarily removed
                              //so put it back
                              if (psFactory->psCommander)
                              {
                                    psflag = psFactory->psAssemblyPoint;
                                    if (psflag != NULL)
                                    {
                                          psSaveflag->type = POS_TEMPDELIVERY;                        //The type of flag
                                          psSaveflag->frameNumber = psflag->frameNumber;  //when the Position was last drawn
                                          psSaveflag->screenX = psflag->screenX;                //screen coords and radius of Position imd
                                          psSaveflag->screenY = psflag->screenY;
                                          psSaveflag->screenR = psflag->screenR;
                                          psSaveflag->player = psflag->player;                  //which player the Position belongs to
                                          psSaveflag->selected = psflag->selected;        //flag to indicate whether the Position
                                          psSaveflag->coords = psflag->coords;                  //the world coords of the Position
                                          psSaveflag->factoryInc = psflag->factoryInc;    //indicates whether the first, second etc factory
                                          psSaveflag->factoryType = psflag->factoryType;  //indicates whether standard, cyborg or vtol factory
                                          if (psflag->factoryType == REPAIR_FLAG)
                                          {
                                                //get repair facility id
                                                psSaveflag->repairId = getRepairIdFromFlag(psflag);
                                          }

                                          /* SAVE_FLAG */
                                          endian_sdword((SDWORD*)&psSaveflag->type); /* FIXME: enum may be different type! */
                                          endian_udword(&psSaveflag->frameNumber);
                                          endian_udword(&psSaveflag->screenX);
                                          endian_udword(&psSaveflag->screenY);
                                          endian_udword(&psSaveflag->screenR);
                                          endian_udword(&psSaveflag->player);
                                          endian_sdword(&psSaveflag->coords.x);
                                          endian_sdword(&psSaveflag->coords.y);
                                          endian_sdword(&psSaveflag->coords.z);
                                          endian_udword(&psSaveflag->repairId);

                                          psSaveflag = (SAVE_FLAG *)((char *)psSaveflag +       sizeof(SAVE_FLAG));
                                    }
                              }
                              break;
                        default:
                              break;
                        }
                  }
            }
      }

      /* FLAG_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      if (!saveFile(pFileName, pFileData, fileSize))
      {
            return false;
      }
      free(pFileData);

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveProduction(char *pFileData, UDWORD filesize)
{
      PRODUCTION_SAVEHEADER         *psHeader;

      /* Check the file type */
      psHeader = (PRODUCTION_SAVEHEADER*)pFileData;
      if (psHeader->aFileType[0] != 'p' || psHeader->aFileType[1] != 'r' ||
            psHeader->aFileType[2] != 'o' || psHeader->aFileType[3] != 'd')
      {
            debug( LOG_ERROR, "loadSaveProduction: Incorrect file type" );
            abort();
            return false;
      }

      /* PRODUCTION_SAVEHEADER */
      endian_udword(&psHeader->version);

      //increment to the start of the data
      pFileData += PRODUCTION_HEADER_SIZE;

      /* Check the file version */
      if (!loadSaveProductionV(pFileData, filesize, psHeader->version))
      {
            return false;
      }
      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveProductionV(char *pFileData, UDWORD filesize, UDWORD version)
{
      SAVE_PRODUCTION   *psSaveProduction;
      PRODUCTION_RUN    *psCurrentProd;
      UDWORD                  factoryType,factoryNum,runNum;

      //check file
      if ((sizeof(SAVE_PRODUCTION) * NUM_FACTORY_TYPES * MAX_FACTORY * MAX_PROD_RUN + PRODUCTION_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "loadSaveProduction: unexpected end of file" );
            abort();
            return false;
      }

      //save each production run
      for (factoryType = 0; factoryType < NUM_FACTORY_TYPES; factoryType++)
      {
            for (factoryNum = 0; factoryNum < MAX_FACTORY; factoryNum++)
            {
                  for (runNum = 0; runNum < MAX_PROD_RUN; runNum++)
                  {
                        psSaveProduction = (SAVE_PRODUCTION *)pFileData;
                        psCurrentProd = &asProductionRun[factoryType][factoryNum][runNum];

                        /* SAVE_PRODUCTION */
                        endian_udword(&psSaveProduction->multiPlayerID);

                        psCurrentProd->quantity = psSaveProduction->quantity;
                        psCurrentProd->built = psSaveProduction->built;
                        if (psSaveProduction->multiPlayerID != NULL_ID)
                        {
                              psCurrentProd->psTemplate = getTemplateFromMultiPlayerID(psSaveProduction->multiPlayerID);
                        }
                        else
                        {
                              psCurrentProd->psTemplate = NULL;
                        }
                        pFileData += sizeof(SAVE_PRODUCTION);
                  }
            }
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Write out the current production figures for factories
static BOOL writeProductionFile(char *pFileName)
{
      PRODUCTION_SAVEHEADER   *psHeader;
      SAVE_PRODUCTION               *psSaveProduction;
      char                    *pFileData;
      UDWORD                        fileSize;
      PRODUCTION_RUN    *psCurrentProd;
      UDWORD                        factoryType,factoryNum,runNum;

      fileSize = PRODUCTION_HEADER_SIZE + (sizeof(SAVE_PRODUCTION) *
            NUM_FACTORY_TYPES * MAX_FACTORY * MAX_PROD_RUN);

      //allocate the buffer space
      pFileData = (char*)malloc(fileSize);
      if (!pFileData)
      {
            debug( LOG_ERROR, "writeProductionFile: Out of memory" );
            abort();
            return false;
      }

      // Put the file header on the file
      psHeader = (PRODUCTION_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 'p';
      psHeader->aFileType[1] = 'r';
      psHeader->aFileType[2] = 'o';
      psHeader->aFileType[3] = 'd';
      psHeader->version = CURRENT_VERSION_NUM;

      psSaveProduction = (SAVE_PRODUCTION *) (pFileData + PRODUCTION_HEADER_SIZE);
      //save each production run
      for (factoryType = 0; factoryType < NUM_FACTORY_TYPES; factoryType++)
      {
            for (factoryNum = 0; factoryNum < MAX_FACTORY; factoryNum++)
            {
                  for (runNum = 0; runNum < MAX_PROD_RUN; runNum++)
                  {
                        psCurrentProd = &asProductionRun[factoryType][factoryNum][runNum];
                        psSaveProduction->quantity = psCurrentProd->quantity;
                        psSaveProduction->built = psCurrentProd->built;
                        psSaveProduction->multiPlayerID = NULL_ID;
                        if (psCurrentProd->psTemplate != NULL)
                        {
                              psSaveProduction->multiPlayerID = psCurrentProd->psTemplate->multiPlayerID;
                        }

                        /* SAVE_PRODUCTION */
                        endian_udword(&psSaveProduction->multiPlayerID);

                        psSaveProduction = (SAVE_PRODUCTION *)((char *)psSaveProduction +       sizeof(SAVE_PRODUCTION));
                  }
            }
      }

      /* PRODUCTION_SAVEHEADER */
      endian_udword(&psHeader->version);

      if (!saveFile(pFileName, pFileData, fileSize))
      {
            return false;
      }
      free(pFileData);

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveStructLimits(char *pFileData, UDWORD filesize)
{
      STRUCTLIMITS_SAVEHEADER       *psHeader;

      // Check the file type
      psHeader = (STRUCTLIMITS_SAVEHEADER *)pFileData;
      if (psHeader->aFileType[0] != 'l' || psHeader->aFileType[1] != 'm' ||
            psHeader->aFileType[2] != 't' || psHeader->aFileType[3] != 's')
      {
            debug( LOG_ERROR, "loadSaveStructLimits: Incorrect file type" );
            abort();
            return false;
      }

      //increment to the start of the data
      pFileData += STRUCTLIMITS_HEADER_SIZE;

      /* STRUCTLIMITS_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      // Check the file version
      if ((psHeader->version >= VERSION_15) && (psHeader->version <= VERSION_19))
      {
            if (!loadSaveStructLimitsV19(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else if (psHeader->version <= CURRENT_VERSION_NUM)
      {
            if (!loadSaveStructLimitsV(pFileData, filesize, psHeader->quantity))
            {
                  return false;
            }
      }
      else
      {
            debug( LOG_ERROR, "loadSaveStructLimits: Incorrect file format version" );
            abort();
            return false;
      }
      return true;
}

// -----------------------------------------------------------------------------------------
/* code specific to version 2 of saveStructLimits */
BOOL loadSaveStructLimitsV19(char *pFileData, UDWORD filesize, UDWORD numLimits)
{
      SAVE_STRUCTLIMITS_V2    *psSaveLimits;
      UDWORD                              count, statInc;
      BOOL                          found;
      STRUCTURE_STATS               *psStats;
      int SkippedRecords=0;

      psSaveLimits = (SAVE_STRUCTLIMITS_V2 *) malloc(sizeof(SAVE_STRUCTLIMITS_V2));
      if (!psSaveLimits)
      {
            debug( LOG_ERROR, "Out of memory" );
            abort();
            return false;
      }

      if ((sizeof(SAVE_STRUCTLIMITS_V2) * numLimits + STRUCTLIMITS_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "loadSaveStructLimits: unexpected end of file" );
            abort();
            return false;
      }

      // Load in the data
      for (count = 0; count < numLimits; count ++,
                                                      pFileData += sizeof(SAVE_STRUCTLIMITS_V2))
      {
            memcpy(psSaveLimits, pFileData, sizeof(SAVE_STRUCTLIMITS_V2));

            //get the stats for this structure
            found = false;
            for (statInc = 0; statInc < numStructureStats; statInc++)
            {
                  psStats = asStructureStats + statInc;
                  //loop until find the same name
                  if (!strcmp(psStats->pName, psSaveLimits->name))
                  {
                        found = true;
                        break;
                  }
            }
            //if haven't found the structure - ignore this record!
            if (!found)
            {
                  debug( LOG_ERROR, "The structure no longer exists. The limits have not been set! - %s", psSaveLimits->name );
                  abort();
                  continue;
            }

            if (psSaveLimits->player < MAX_PLAYERS)
            {
                  asStructLimits[psSaveLimits->player][statInc].limit = psSaveLimits->limit;
            }
            else
            {
                  SkippedRecords++;
            }

      }

      if (SkippedRecords>0)
      {
            debug( LOG_ERROR, "Skipped %d records in structure limits due to bad player number\n", SkippedRecords );
            abort();
      }
      free(psSaveLimits);
      return true;
}

// -----------------------------------------------------------------------------------------
BOOL loadSaveStructLimitsV(char *pFileData, UDWORD filesize, UDWORD numLimits)
{
      SAVE_STRUCTLIMITS       *psSaveLimits;
      UDWORD                              count, statInc;
      BOOL                          found;
      STRUCTURE_STATS               *psStats;
      int SkippedRecords=0;

      psSaveLimits = (SAVE_STRUCTLIMITS*) malloc(sizeof(SAVE_STRUCTLIMITS));
      if (!psSaveLimits)
      {
            debug( LOG_ERROR, "Out of memory" );
            abort();
            return false;
      }

      if ((sizeof(SAVE_STRUCTLIMITS) * numLimits + STRUCTLIMITS_HEADER_SIZE) >
            filesize)
      {
            debug( LOG_ERROR, "loadSaveStructLimits: unexpected end of file" );
            abort();
            return false;
      }

      // Load in the data
      for (count = 0; count < numLimits; count ++,
                                                      pFileData += sizeof(SAVE_STRUCTLIMITS))
      {
            memcpy(psSaveLimits, pFileData, sizeof(SAVE_STRUCTLIMITS));

            //get the stats for this structure
            found = false;
            for (statInc = 0; statInc < numStructureStats; statInc++)
            {
                  psStats = asStructureStats + statInc;
                  //loop until find the same name
                  if (!strcmp(psStats->pName, psSaveLimits->name))
                  {
                        found = true;
                        break;
                  }
            }
            //if haven't found the structure - ignore this record!
            if (!found)
            {
                  debug( LOG_ERROR, "The structure no longer exists. The limits have not been set! - %s", psSaveLimits->name );
                  abort();
                  continue;
            }

            if (psSaveLimits->player < MAX_PLAYERS)
            {
                  asStructLimits[psSaveLimits->player][statInc].limit = psSaveLimits->limit;
            }
            else
            {
                  SkippedRecords++;
            }

      }

      if (SkippedRecords>0)
      {
            debug( LOG_ERROR, "Skipped %d records in structure limits due to bad player number\n", SkippedRecords );
            abort();
      }
      free(psSaveLimits);
      return true;
}

// -----------------------------------------------------------------------------------------
/*
Writes the list of structure limits to a file
*/
BOOL writeStructLimitsFile(char *pFileName)
{
      char *pFileData = NULL;
      UDWORD                                    fileSize, totalLimits=0, i, player;
      STRUCTLIMITS_SAVEHEADER       *psHeader;
      SAVE_STRUCTLIMITS             *psSaveLimit;
      STRUCTURE_STATS                     *psStructStats;
      BOOL status = true;

      totalLimits = numStructureStats * MAX_PLAYERS;

      // Allocate the data buffer
      fileSize = STRUCTLIMITS_HEADER_SIZE + (totalLimits * (sizeof(SAVE_STRUCTLIMITS)));
      pFileData = (char*)malloc(fileSize);
      if (pFileData == NULL)
      {
            debug( LOG_ERROR, "Out of memory" );
            abort();
            return false;
      }

      // Put the file header on the file
      psHeader = (STRUCTLIMITS_SAVEHEADER *)pFileData;
      psHeader->aFileType[0] = 'l';
      psHeader->aFileType[1] = 'm';
      psHeader->aFileType[2] = 't';
      psHeader->aFileType[3] = 's';
      psHeader->version = CURRENT_VERSION_NUM;
      psHeader->quantity = totalLimits;

      psSaveLimit = (SAVE_STRUCTLIMITS*)(pFileData + STRUCTLIMITS_HEADER_SIZE);

      // Put the data into the buffer
      for (player = 0; player < MAX_PLAYERS ; player++)
      {
            psStructStats = asStructureStats;
            for(i = 0; i < numStructureStats; i++, psStructStats++)
            {
                  strcpy(psSaveLimit->name, psStructStats->pName);
                  psSaveLimit->limit = asStructLimits[player][i].limit;
                  psSaveLimit->player = (UBYTE)player;
                  psSaveLimit = (SAVE_STRUCTLIMITS *)((char *)psSaveLimit + sizeof(SAVE_STRUCTLIMITS));
            }
      }

      /* STRUCTLIMITS_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      // Write the data to the file
      if (pFileData != NULL) {
            status = saveFile(pFileName, pFileData, fileSize);
            free(pFileData);
            return status;
      }
      return false;
}

static const char FireSupport_tag_definition[] = "tagdefinitions/savegame/firesupport.def";
static const char FireSupport_file_identifier[] = "FIRESUPPORT";

/*!
 * Load the current fire-support designated commanders (the one who has fire-support enabled)
 */
BOOL readFiresupportDesignators(char *pFileName)
{
      unsigned int numPlayers, player;
      char formatIdentifier[12] = "";

      if (!tagOpenRead(FireSupport_tag_definition, pFileName))
      {
            debug(LOG_ERROR, "readFiresupportDesignators: Failed to open savegame %s", pFileName);
            return false;
      }
      debug(LOG_MAP, "Reading tagged savegame %s with definition %s:", pFileName, FireSupport_tag_definition);

      tagReadString(0x01, 12, formatIdentifier);
      if (strcmp(formatIdentifier, FireSupport_file_identifier) != 0)
      {
            debug(LOG_ERROR, "readFiresupportDesignators: Incompatble %s, 'FIRESUPPORT' expected", pFileName);
            return false;
      }

      numPlayers = tagReadEnter(0x02);
      for (player = 0; player < numPlayers; player++)
      {
            uint32_t id = tagRead(0x01);
            if (id != NULL_ID)
            {
                  cmdDroidSetDesignator((DROID*)getBaseObjFromId(id));
            }
            tagReadNext();
      }
      tagReadLeave(0x02);

      tagClose();

      return true;
}


/*!
 * Save the current fire-support designated commanders (the one who has fire-support enabled)
 */
BOOL writeFiresupportDesignators(char *pFileName)
{
      unsigned int player;

      if (!tagOpenWrite(FireSupport_tag_definition, pFileName))
      {
            debug(LOG_ERROR, "writeFiresupportDesignators: Failed to create savegame %s", pFileName);
            return false;
      }
      debug(LOG_MAP, "Creating tagged savegame %s with definition %s:", pFileName, FireSupport_tag_definition);

      tagWriteString(0x01, FireSupport_file_identifier);
      tagWriteEnter(0x02, MAX_PLAYERS);
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            DROID * psDroid = cmdDroidGetDesignator(player);
            if (psDroid != NULL)
            {
                  tagWrite(0x01, psDroid->id);
            }
            else
            {
                  tagWrite(0x01, NULL_ID);
            }
            tagWriteNext();
      }
      tagWriteLeave(0x02);

      tagClose();

      return true;
}


// -----------------------------------------------------------------------------------------
// write the event state to a file on disk
static BOOL writeScriptState(char *pFileName)
{
      char  *pBuffer;
      UDWORD      fileSize;

      if (!eventSaveState(3, &pBuffer, &fileSize))
      {
            return false;
      }

      if (!saveFile(pFileName, pBuffer, fileSize))
      {
            return false;
      }
      free(pBuffer);
      return true;
}

// -----------------------------------------------------------------------------------------
// load the script state given a .gam name
BOOL loadScriptState(char *pFileName)
{
      char  *pFileData;
      UDWORD      fileSize;
      BOOL bHashed = false;

      // change the file extension
      pFileName[strlen(pFileName)-4] = (char)0;
      strcat(pFileName, ".es");

      pFileData = fileLoadBuffer;
      if (!loadFileToBuffer(pFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
      {
            debug( LOG_ERROR, "loadScriptState: couldn't load %s", pFileName );
            abort();
            return false;
      }

      if (saveGameVersion > VERSION_12)
      {
            bHashed = true;
      }

      if (!eventLoadState(pFileData, fileSize, bHashed))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
/* set the global scroll values to use for the save game */
static void setMapScroll(void)
{
      //if loading in a pre version5 then scroll values will not have been set up so set to max poss
      if (width == 0 && height == 0)
      {
            scrollMinX = 0;
            scrollMaxX = mapWidth;
            scrollMinY = 0;
            scrollMaxY = mapHeight;
            return;
      }
      scrollMinX = startX;
      scrollMinY = startY;
      scrollMaxX = startX + width;
      scrollMaxY = startY + height;
      //check not going beyond width/height of map
      if (scrollMaxX > (SDWORD)mapWidth)
      {
            scrollMaxX = mapWidth;
            debug( LOG_NEVER, "scrollMaxX was too big It has been set to map width" );
      }
      if (scrollMaxY > (SDWORD)mapHeight)
      {
            scrollMaxY = mapHeight;
            debug( LOG_NEVER, "scrollMaxY was too big It has been set to map height" );
      }
}


// -----------------------------------------------------------------------------------------
BOOL getSaveObjectName(char *pName)
{
      return true;
}

// -----------------------------------------------------------------------------------------
/*returns the current type of save game being loaded*/
UDWORD getSaveGameType(void)
{
      return gameType;
}


// -----------------------------------------------------------------------------------------
//copies a Stat name into a destination string for a given stat type and index
static BOOL getNameFromComp(UDWORD compType, char *pDest, UDWORD compIndex)
{
      BASE_STATS  *psStats;

      //allocate the stats pointer
      switch (compType)
      {
      case COMP_BODY:
            psStats = (BASE_STATS *)(asBodyStats + compIndex);
            break;
      case COMP_BRAIN:
            psStats = (BASE_STATS *)(asBrainStats + compIndex);
            break;
      case COMP_PROPULSION:
            psStats = (BASE_STATS *)(asPropulsionStats + compIndex);
            break;
      case COMP_REPAIRUNIT:
            psStats = (BASE_STATS*)(asRepairStats  + compIndex);
            break;
      case COMP_ECM:
            psStats = (BASE_STATS*)(asECMStats + compIndex);
            break;
      case COMP_SENSOR:
            psStats = (BASE_STATS*)(asSensorStats + compIndex);
            break;
      case COMP_CONSTRUCT:
            psStats = (BASE_STATS*)(asConstructStats + compIndex);
            break;
      /*case COMP_PROGRAM:
            psStats = (BASE_STATS*)(asProgramStats + compIndex);
            break;*/
      case COMP_WEAPON:
            psStats = (BASE_STATS*)(asWeaponStats + compIndex);
            break;
      default:
            debug( LOG_ERROR, "Invalid component type - game.c" );
            abort();
            return false;
      }

      //copy the name into the destination string
      strcpy(pDest, psStats->pName);
      return true;
}
// -----------------------------------------------------------------------------------------
// END

//======================================================
//draws stuff into our newer bitmap.
BOOL plotStructurePreview16(char *backDropSprite, UBYTE scale, UDWORD offX, UDWORD offY)
{
      SAVE_STRUCTURE                      sSave;  // close eyes now.
      SAVE_STRUCTURE                      *psSaveStructure = &sSave; // assumes save_struct is larger than all previous ones...
      SAVE_STRUCTURE_V2             *psSaveStructure2 = (SAVE_STRUCTURE_V2*)&sSave;
      SAVE_STRUCTURE_V12                  *psSaveStructure12= (SAVE_STRUCTURE_V12*)&sSave;
      SAVE_STRUCTURE_V14                  *psSaveStructure14= (SAVE_STRUCTURE_V14*)&sSave;
      SAVE_STRUCTURE_V15                  *psSaveStructure15= (SAVE_STRUCTURE_V15*)&sSave;
      SAVE_STRUCTURE_V17                  *psSaveStructure17= (SAVE_STRUCTURE_V17*)&sSave;
      SAVE_STRUCTURE_V20                  *psSaveStructure20= (SAVE_STRUCTURE_V20*)&sSave;
                                                            // ok you can open them again..

      STRUCT_SAVEHEADER       *psHeader;
      char              aFileName[256];
      UDWORD                  xx,yy,x,y,count,fileSize,sizeOfSaveStruture;
      UDWORD      playerid =0;
      char              *pFileData = NULL;
      LEVEL_DATASET     *psLevel;
      PIELIGHT color = WZCOL_BLACK ;

      psLevel = levFindDataSet(game.map);
      strcpy(aFileName,psLevel->apDataFiles[0]);
      aFileName[strlen(aFileName)-4] = '\0';
      strcat(aFileName, "/struct.bjo");

      pFileData = fileLoadBuffer;
      if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
      {
            debug( LOG_NEVER, "plotStructurePreview16: Fail1\n" );
      }

      /* Check the file type */
      psHeader = (STRUCT_SAVEHEADER *)pFileData;
      if (psHeader->aFileType[0] != 's' || psHeader->aFileType[1] != 't' ||
            psHeader->aFileType[2] != 'r' || psHeader->aFileType[3] != 'u')
      {
            debug( LOG_ERROR, "plotStructurePreview16: Incorrect file type" );
            abort();
            return false;
      }

      /* STRUCT_SAVEHEADER */
      endian_udword(&psHeader->version);
      endian_udword(&psHeader->quantity);

      //increment to the start of the data
      pFileData += STRUCT_HEADER_SIZE;

      if (psHeader->version < VERSION_12)
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V2);
      }
      else if (psHeader->version < VERSION_14)
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V12);
      }
      else if (psHeader->version <= VERSION_14)
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V14);
      }
      else if (psHeader->version <= VERSION_16)
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V15);
      }
      else if (psHeader->version <= VERSION_19)
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V17);
      }
      else if (psHeader->version <= VERSION_20)
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE_V20);
      }
      else
      {
            sizeOfSaveStruture = sizeof(SAVE_STRUCTURE);
      }


      /* Load in the structure data */
      for (count = 0; count < psHeader-> quantity; count ++, pFileData += sizeOfSaveStruture)
      {
            if (psHeader->version < VERSION_12)
            {
                  memcpy(psSaveStructure2, pFileData, sizeOfSaveStruture);

                  /* STRUCTURE_SAVE_V2 includes OBJECT_SAVE_V19 */
                  endian_sdword(&psSaveStructure2->currentBuildPts);
                  endian_udword(&psSaveStructure2->body);
                  endian_udword(&psSaveStructure2->armour);
                  endian_udword(&psSaveStructure2->resistance);
                  endian_udword(&psSaveStructure2->dummy1);
                  endian_udword(&psSaveStructure2->subjectInc);
                  endian_udword(&psSaveStructure2->timeStarted);
                  endian_udword(&psSaveStructure2->output);
                  endian_udword(&psSaveStructure2->capacity);
                  endian_udword(&psSaveStructure2->quantity);
                  /* OBJECT_SAVE_V19 */
                  endian_udword(&psSaveStructure2->id);
                  endian_udword(&psSaveStructure2->x);
                  endian_udword(&psSaveStructure2->y);
                  endian_udword(&psSaveStructure2->z);
                  endian_udword(&psSaveStructure2->direction);
                  endian_udword(&psSaveStructure2->player);
                  endian_udword(&psSaveStructure2->burnStart);
                  endian_udword(&psSaveStructure2->burnDamage);

                  xx = map_coord(psSaveStructure2->x);
                  yy = map_coord(psSaveStructure2->y);
                  playerid = psSaveStructure2->player;
            }
            else if (psHeader->version < VERSION_14)
            {
                  memcpy(psSaveStructure12, pFileData, sizeOfSaveStruture);

                  /* STRUCTURE_SAVE_V12 includes STRUCTURE_SAVE_V2 */
                  endian_udword(&psSaveStructure12->factoryInc);
                  endian_udword(&psSaveStructure12->powerAccrued);
                  endian_udword(&psSaveStructure12->droidTimeStarted);
                  endian_udword(&psSaveStructure12->timeToBuild);
                  endian_udword(&psSaveStructure12->timeStartHold);
                  /* STRUCTURE_SAVE_V2 includes OBJECT_SAVE_V19 */
                  endian_sdword(&psSaveStructure12->currentBuildPts);
                  endian_udword(&psSaveStructure12->body);
                  endian_udword(&psSaveStructure12->armour);
                  endian_udword(&psSaveStructure12->resistance);
                  endian_udword(&psSaveStructure12->dummy1);
                  endian_udword(&psSaveStructure12->subjectInc);
                  endian_udword(&psSaveStructure12->timeStarted);
                  endian_udword(&psSaveStructure12->output);
                  endian_udword(&psSaveStructure12->capacity);
                  endian_udword(&psSaveStructure12->quantity);
                  /* OBJECT_SAVE_V19 */
                  endian_udword(&psSaveStructure12->id);
                  endian_udword(&psSaveStructure12->x);
                  endian_udword(&psSaveStructure12->y);
                  endian_udword(&psSaveStructure12->z);
                  endian_udword(&psSaveStructure12->direction);
                  endian_udword(&psSaveStructure12->player);
                  endian_udword(&psSaveStructure12->burnStart);
                  endian_udword(&psSaveStructure12->burnDamage);

                  xx = map_coord(psSaveStructure12->x);
                  yy = map_coord(psSaveStructure12->y);
                  playerid = psSaveStructure12->player;
            }
            else if (psHeader->version <= VERSION_14)
            {
                  memcpy(psSaveStructure14, pFileData, sizeOfSaveStruture);

                  /* STRUCTURE_SAVE_V14 includes STRUCTURE_SAVE_V12 */
                  /* STRUCTURE_SAVE_V12 includes STRUCTURE_SAVE_V2 */
                  endian_udword(&psSaveStructure14->factoryInc);
                  endian_udword(&psSaveStructure14->powerAccrued);
                  endian_udword(&psSaveStructure14->droidTimeStarted);
                  endian_udword(&psSaveStructure14->timeToBuild);
                  endian_udword(&psSaveStructure14->timeStartHold);
                  /* STRUCTURE_SAVE_V2 includes OBJECT_SAVE_V19 */
                  endian_sdword(&psSaveStructure14->currentBuildPts);
                  endian_udword(&psSaveStructure14->body);
                  endian_udword(&psSaveStructure14->armour);
                  endian_udword(&psSaveStructure14->resistance);
                  endian_udword(&psSaveStructure14->dummy1);
                  endian_udword(&psSaveStructure14->subjectInc);
                  endian_udword(&psSaveStructure14->timeStarted);
                  endian_udword(&psSaveStructure14->output);
                  endian_udword(&psSaveStructure14->capacity);
                  endian_udword(&psSaveStructure14->quantity);
                  /* OBJECT_SAVE_V19 */
                  endian_udword(&psSaveStructure14->id);
                  endian_udword(&psSaveStructure14->x);
                  endian_udword(&psSaveStructure14->y);
                  endian_udword(&psSaveStructure14->z);
                  endian_udword(&psSaveStructure14->direction);
                  endian_udword(&psSaveStructure14->player);
                  endian_udword(&psSaveStructure14->burnStart);
                  endian_udword(&psSaveStructure14->burnDamage);

                  xx = map_coord(psSaveStructure14->x);
                  yy = map_coord(psSaveStructure14->y);
                  playerid = psSaveStructure14->player;
            }
            else if (psHeader->version <= VERSION_16)
            {
                  memcpy(psSaveStructure15, pFileData, sizeOfSaveStruture);

                  /* STRUCTURE_SAVE_V15 includes STRUCTURE_SAVE_V14 */
                  /* STRUCTURE_SAVE_V14 includes STRUCTURE_SAVE_V12 */
                  /* STRUCTURE_SAVE_V12 includes STRUCTURE_SAVE_V2 */
                  endian_udword(&psSaveStructure15->factoryInc);
                  endian_udword(&psSaveStructure15->powerAccrued);
                  endian_udword(&psSaveStructure15->droidTimeStarted);
                  endian_udword(&psSaveStructure15->timeToBuild);
                  endian_udword(&psSaveStructure15->timeStartHold);
                  /* STRUCTURE_SAVE_V2 includes OBJECT_SAVE_V19 */
                  endian_sdword(&psSaveStructure15->currentBuildPts);
                  endian_udword(&psSaveStructure15->body);
                  endian_udword(&psSaveStructure15->armour);
                  endian_udword(&psSaveStructure15->resistance);
                  endian_udword(&psSaveStructure15->dummy1);
                  endian_udword(&psSaveStructure15->subjectInc);
                  endian_udword(&psSaveStructure15->timeStarted);
                  endian_udword(&psSaveStructure15->output);
                  endian_udword(&psSaveStructure15->capacity);
                  endian_udword(&psSaveStructure15->quantity);
                  /* OBJECT_SAVE_V19 */
                  endian_udword(&psSaveStructure15->id);
                  endian_udword(&psSaveStructure15->x);
                  endian_udword(&psSaveStructure15->y);
                  endian_udword(&psSaveStructure15->z);
                  endian_udword(&psSaveStructure15->direction);
                  endian_udword(&psSaveStructure15->player);
                  endian_udword(&psSaveStructure15->burnStart);
                  endian_udword(&psSaveStructure15->burnDamage);

                  xx = map_coord(psSaveStructure15->x);
                  yy = map_coord(psSaveStructure15->y);
                  playerid = psSaveStructure15->player;
            }
            else if (psHeader->version <= VERSION_19)
            {
                  memcpy(psSaveStructure17, pFileData, sizeOfSaveStruture);

                  /* STRUCTURE_SAVE_V17 includes STRUCTURE_SAVE_V15 */
                  endian_sword(&psSaveStructure17->currentPowerAccrued);
                  /* STRUCTURE_SAVE_V15 includes STRUCTURE_SAVE_V14 */
                  /* STRUCTURE_SAVE_V14 includes STRUCTURE_SAVE_V12 */
                  /* STRUCTURE_SAVE_V12 includes STRUCTURE_SAVE_V2 */
                  endian_udword(&psSaveStructure17->factoryInc);
                  endian_udword(&psSaveStructure17->powerAccrued);
                  endian_udword(&psSaveStructure17->droidTimeStarted);
                  endian_udword(&psSaveStructure17->timeToBuild);
                  endian_udword(&psSaveStructure17->timeStartHold);
                  /* STRUCTURE_SAVE_V2 includes OBJECT_SAVE_V19 */
                  endian_sdword(&psSaveStructure17->currentBuildPts);
                  endian_udword(&psSaveStructure17->body);
                  endian_udword(&psSaveStructure17->armour);
                  endian_udword(&psSaveStructure17->resistance);
                  endian_udword(&psSaveStructure17->dummy1);
                  endian_udword(&psSaveStructure17->subjectInc);
                  endian_udword(&psSaveStructure17->timeStarted);
                  endian_udword(&psSaveStructure17->output);
                  endian_udword(&psSaveStructure17->capacity);
                  endian_udword(&psSaveStructure17->quantity);
                  /* OBJECT_SAVE_V19 */
                  endian_udword(&psSaveStructure17->id);
                  endian_udword(&psSaveStructure17->x);
                  endian_udword(&psSaveStructure17->y);
                  endian_udword(&psSaveStructure17->z);
                  endian_udword(&psSaveStructure17->direction);
                  endian_udword(&psSaveStructure17->player);
                  endian_udword(&psSaveStructure17->burnStart);
                  endian_udword(&psSaveStructure17->burnDamage);

                  xx = map_coord(psSaveStructure17->x);
                  yy = map_coord(psSaveStructure17->y);
                  playerid = psSaveStructure17->player;
            }
            else if (psHeader->version <= VERSION_20)
            {
                  memcpy(psSaveStructure20, pFileData, sizeOfSaveStruture);

                  /* STRUCTURE_SAVE_V20 includes OBJECT_SAVE_V20 */
                  endian_sdword(&psSaveStructure20->currentBuildPts);
                  endian_udword(&psSaveStructure20->body);
                  endian_udword(&psSaveStructure20->armour);
                  endian_udword(&psSaveStructure20->resistance);
                  endian_udword(&psSaveStructure20->dummy1);
                  endian_udword(&psSaveStructure20->subjectInc);
                  endian_udword(&psSaveStructure20->timeStarted);
                  endian_udword(&psSaveStructure20->output);
                  endian_udword(&psSaveStructure20->capacity);
                  endian_udword(&psSaveStructure20->quantity);
                  endian_udword(&psSaveStructure20->factoryInc);
                  endian_udword(&psSaveStructure20->powerAccrued);
                  endian_udword(&psSaveStructure20->dummy2);
                  endian_udword(&psSaveStructure20->droidTimeStarted);
                  endian_udword(&psSaveStructure20->timeToBuild);
                  endian_udword(&psSaveStructure20->timeStartHold);
                  endian_sword(&psSaveStructure20->currentPowerAccrued);
                  /* OBJECT_SAVE_V20 */
                  endian_udword(&psSaveStructure20->id);
                  endian_udword(&psSaveStructure20->x);
                  endian_udword(&psSaveStructure20->y);
                  endian_udword(&psSaveStructure20->z);
                  endian_udword(&psSaveStructure20->direction);
                  endian_udword(&psSaveStructure20->player);
                  endian_udword(&psSaveStructure20->burnStart);
                  endian_udword(&psSaveStructure20->burnDamage);

                  xx = map_coord(psSaveStructure20->x);
                  yy = map_coord(psSaveStructure20->y);
                  playerid = psSaveStructure20->player;
            }
            else
            {
                  memcpy(psSaveStructure, pFileData, sizeOfSaveStruture);

                  /* SAVE_STRUCTURE is STRUCTURE_SAVE_V21 */
                  /* STRUCTURE_SAVE_V21 includes STRUCTURE_SAVE_V20 */
                  endian_udword(&psSaveStructure->commandId);
                  /* STRUCTURE_SAVE_V20 includes OBJECT_SAVE_V20 */
                  endian_sdword(&psSaveStructure->currentBuildPts);
                  endian_udword(&psSaveStructure->body);
                  endian_udword(&psSaveStructure->armour);
                  endian_udword(&psSaveStructure->resistance);
                  endian_udword(&psSaveStructure->dummy1);
                  endian_udword(&psSaveStructure->subjectInc);
                  endian_udword(&psSaveStructure->timeStarted);
                  endian_udword(&psSaveStructure->output);
                  endian_udword(&psSaveStructure->capacity);
                  endian_udword(&psSaveStructure->quantity);
                  endian_udword(&psSaveStructure->factoryInc);
                  endian_udword(&psSaveStructure->powerAccrued);
                  endian_udword(&psSaveStructure->dummy2);
                  endian_udword(&psSaveStructure->droidTimeStarted);
                  endian_udword(&psSaveStructure->timeToBuild);
                  endian_udword(&psSaveStructure->timeStartHold);
                  endian_sword(&psSaveStructure->currentPowerAccrued);
                  /* OBJECT_SAVE_V20 */
                  endian_udword(&psSaveStructure->id);
                  endian_udword(&psSaveStructure->x);
                  endian_udword(&psSaveStructure->y);
                  endian_udword(&psSaveStructure->z);
                  endian_udword(&psSaveStructure->direction);
                  endian_udword(&psSaveStructure->player);
                  endian_udword(&psSaveStructure->burnStart);
                  endian_udword(&psSaveStructure->burnDamage);

                  xx = map_coord(psSaveStructure->x);
                  yy = map_coord(psSaveStructure->y);
                  playerid = psSaveStructure->player;
            }
            // if human player, then use clan color.  If AI, then use something else.
            if( isHumanPlayer(playerid) )
            {
                  playerid = (UDWORD) getPlayerColour(playerid);
                  color.rgba = clanColours[playerid].rgba;
                  // kludge to fix black, so you can see it on some maps.
                  if ( playerid == 3 )    // in this case 3 = pallete entry for black.
                  {     
      
                        color = WZCOL_GREY;
                  }
            }
            else
            {     // Use a dark green color for the AI
                  color = WZCOL_MAP_PREVIEW_AIPLAYER ;
            }

            for(x = (xx*scale);x < (xx*scale)+scale ;x++)
            {
                  for(y = (yy*scale);y< (yy*scale)+scale ;y++)
                  {
                        backDropSprite[3 * (((offY + y) * BACKDROP_HACK_WIDTH) + x + offX)] = color.byte.r;  
                        backDropSprite[3 * (((offY + y) * BACKDROP_HACK_WIDTH) + x + offX) + 1] = color.byte.g;
                        backDropSprite[3 * (((offY + y) * BACKDROP_HACK_WIDTH) + x + offX) + 2] = color.byte.b;
                  }
            }
      }
      return true;

}

Generated by  Doxygen 1.6.0   Back to index