ref: a901b7a5b6c230eda0896fc98d908d9e520b2d69
dir: /src/hexen/sv_save.c/
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// HEADER FILES ------------------------------------------------------------
#include "h2def.h"
#include "i_system.h"
#include "m_misc.h"
#include "i_swap.h"
#include "p_local.h"
// MACROS ------------------------------------------------------------------
#define MAX_TARGET_PLAYERS 512
#define MOBJ_NULL -1
#define MOBJ_XX_PLAYER -2
#define MAX_MAPS 99
#define BASE_SLOT 6
#define REBORN_SLOT 7
#define REBORN_DESCRIPTION "TEMP GAME"
#define MAX_THINKER_SIZE 256
// TYPES -------------------------------------------------------------------
typedef enum
{
ASEG_GAME_HEADER = 101,
ASEG_MAP_HEADER,
ASEG_WORLD,
ASEG_POLYOBJS,
ASEG_MOBJS,
ASEG_THINKERS,
ASEG_SCRIPTS,
ASEG_PLAYERS,
ASEG_SOUNDS,
ASEG_MISC,
ASEG_END
} gameArchiveSegment_t;
typedef enum
{
TC_NULL,
TC_MOVE_CEILING,
TC_VERTICAL_DOOR,
TC_MOVE_FLOOR,
TC_PLAT_RAISE,
TC_INTERPRET_ACS,
TC_FLOOR_WAGGLE,
TC_LIGHT,
TC_PHASE,
TC_BUILD_PILLAR,
TC_ROTATE_POLY,
TC_MOVE_POLY,
TC_POLY_DOOR
} thinkClass_t;
typedef struct
{
thinkClass_t tClass;
think_t thinkerFunc;
void (*writeFunc)();
void (*readFunc)();
void (*restoreFunc) ();
size_t size;
} thinkInfo_t;
typedef struct
{
thinker_t thinker;
sector_t *sector;
} ssthinker_t;
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void P_SpawnPlayer(mapthing_t * mthing);
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void ArchiveWorld(void);
static void UnarchiveWorld(void);
static void ArchivePolyobjs(void);
static void UnarchivePolyobjs(void);
static void ArchiveMobjs(void);
static void UnarchiveMobjs(void);
static void ArchiveThinkers(void);
static void UnarchiveThinkers(void);
static void ArchiveScripts(void);
static void UnarchiveScripts(void);
static void ArchivePlayers(void);
static void UnarchivePlayers(void);
static void ArchiveSounds(void);
static void UnarchiveSounds(void);
static void ArchiveMisc(void);
static void UnarchiveMisc(void);
static void SetMobjArchiveNums(void);
static void RemoveAllThinkers(void);
static int GetMobjNum(mobj_t * mobj);
static void SetMobjPtr(mobj_t **ptr, unsigned int archiveNum);
static void RestoreSSThinker(ssthinker_t * sst);
static void RestorePlatRaise(plat_t * plat);
static void RestoreMoveCeiling(ceiling_t * ceiling);
static void AssertSegment(gameArchiveSegment_t segType);
static void ClearSaveSlot(int slot);
static void CopySaveSlot(int sourceSlot, int destSlot);
static void CopyFile(char *sourceName, char *destName);
static boolean ExistingFile(char *name);
static void SV_OpenRead(char *fileName);
static void SV_OpenWrite(char *fileName);
static void SV_Close(void);
static void SV_Read(void *buffer, int size);
static byte SV_ReadByte(void);
static uint16_t SV_ReadWord(void);
static uint32_t SV_ReadLong(void);
static void *SV_ReadPtr(void);
static void SV_Write(const void *buffer, int size);
static void SV_WriteByte(byte val);
static void SV_WriteWord(unsigned short val);
static void SV_WriteLong(unsigned int val);
static void SV_WritePtr(void *ptr);
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
#define DEFAULT_SAVEPATH "hexndata/"
char *SavePath = DEFAULT_SAVEPATH;
int vanilla_savegame_limit = 1;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static int MobjCount;
static mobj_t **MobjList;
static mobj_t ***TargetPlayerAddrs;
static int TargetPlayerCount;
static boolean SavingPlayers;
static FILE *SavingFP;
// CODE --------------------------------------------------------------------
// Autogenerated functions for reading/writing structs:
//
// acsstore_t
//
static void StreamIn_acsstore_t(acsstore_t *str)
{
int i;
// int map;
str->map = SV_ReadLong();
// int script;
str->script = SV_ReadLong();
// byte args[4];
for (i=0; i<4; ++i)
{
str->args[i] = SV_ReadByte();
}
}
static void StreamOut_acsstore_t(acsstore_t *str)
{
int i;
// int map;
SV_WriteLong(str->map);
// int script;
SV_WriteLong(str->script);
// byte args[4];
for (i=0; i<4; ++i)
{
SV_WriteByte(str->args[i]);
}
}
//
// ticcmd_t
// (this is based on the Vanilla definition of the struct)
//
static void StreamIn_ticcmd_t(ticcmd_t *str)
{
// char forwardmove;
str->forwardmove = SV_ReadByte();
// char sidemove;
str->sidemove = SV_ReadByte();
// short angleturn;
str->angleturn = SV_ReadWord();
// short consistancy;
str->consistancy = SV_ReadWord();
// byte chatchar;
str->chatchar = SV_ReadByte();
// byte buttons;
str->buttons = SV_ReadByte();
// byte lookfly;
str->lookfly = SV_ReadByte();
// byte arti;
str->arti = SV_ReadByte();
}
static void StreamOut_ticcmd_t(ticcmd_t *str)
{
// char forwardmove;
SV_WriteByte(str->forwardmove);
// char sidemove;
SV_WriteByte(str->sidemove);
// short angleturn;
SV_WriteWord(str->angleturn);
// short consistancy;
SV_WriteWord(str->consistancy);
// byte chatchar;
SV_WriteByte(str->chatchar);
// byte buttons;
SV_WriteByte(str->buttons);
// byte lookfly;
SV_WriteByte(str->lookfly);
// byte arti;
SV_WriteByte(str->arti);
}
//
// inventory_t
//
static void StreamIn_inventory_t(inventory_t *str)
{
// int type;
str->type = SV_ReadLong();
// int count;
str->count = SV_ReadLong();
}
static void StreamOut_inventory_t(inventory_t *str)
{
// int type;
SV_WriteLong(str->type);
// int count;
SV_WriteLong(str->count);
}
//
// pspdef_t
//
static void StreamIn_pspdef_t(pspdef_t *str)
{
int state_num;
// state_t *state;
// This is a pointer; it is stored as an index into the states table.
state_num = SV_ReadLong();
if (state_num != 0)
{
str->state = states + state_num;
}
else
{
str->state = NULL;
}
// int tics;
str->tics = SV_ReadLong();
// fixed_t sx, sy;
str->sx = SV_ReadLong();
str->sy = SV_ReadLong();
}
static void StreamOut_pspdef_t(pspdef_t *str)
{
// state_t *state;
// This is a pointer; store the index in the states table,
// rather than the pointer itself.
if (str->state != NULL)
{
SV_WriteLong(str->state - states);
}
else
{
SV_WriteLong(0);
}
// int tics;
SV_WriteLong(str->tics);
// fixed_t sx, sy;
SV_WriteLong(str->sx);
SV_WriteLong(str->sy);
}
//
// player_t
//
static void StreamIn_player_t(player_t *str)
{
int i;
// mobj_t *mo;
// Pointer value is reset on load.
str->mo = SV_ReadPtr();
str->mo = NULL;
// playerstate_t playerstate;
str->playerstate = SV_ReadLong();
// ticcmd_t cmd;
StreamIn_ticcmd_t(&str->cmd);
// pclass_t class;
str->class = SV_ReadLong();
// fixed_t viewz;
str->viewz = SV_ReadLong();
// fixed_t viewheight;
str->viewheight = SV_ReadLong();
// fixed_t deltaviewheight;
str->deltaviewheight = SV_ReadLong();
// fixed_t bob;
str->bob = SV_ReadLong();
// int flyheight;
str->flyheight = SV_ReadLong();
// int lookdir;
str->lookdir = SV_ReadLong();
// boolean centering;
str->centering = SV_ReadLong();
// int health;
str->health = SV_ReadLong();
// int armorpoints[NUMARMOR];
for (i=0; i<NUMARMOR; ++i)
{
str->armorpoints[i] = SV_ReadLong();
}
// inventory_t inventory[NUMINVENTORYSLOTS];
for (i=0; i<NUMINVENTORYSLOTS; ++i)
{
StreamIn_inventory_t(&str->inventory[i]);
}
// artitype_t readyArtifact;
str->readyArtifact = SV_ReadLong();
// int artifactCount;
str->artifactCount = SV_ReadLong();
// int inventorySlotNum;
str->inventorySlotNum = SV_ReadLong();
// int powers[NUMPOWERS];
for (i=0; i<NUMPOWERS; ++i)
{
str->powers[i] = SV_ReadLong();
}
// int keys;
str->keys = SV_ReadLong();
// int pieces;
str->pieces = SV_ReadLong();
// signed int frags[MAXPLAYERS];
for (i=0; i<maxplayers; ++i)
{
str->frags[i] = SV_ReadLong();
}
// weapontype_t readyweapon;
str->readyweapon = SV_ReadLong();
// weapontype_t pendingweapon;
str->pendingweapon = SV_ReadLong();
// boolean weaponowned[NUMWEAPONS];
for (i=0; i<NUMWEAPONS; ++i)
{
str->weaponowned[i] = SV_ReadLong();
}
// int mana[NUMMANA];
for (i=0; i<NUMMANA; ++i)
{
str->mana[i] = SV_ReadLong();
}
// int attackdown, usedown;
str->attackdown = SV_ReadLong();
str->usedown = SV_ReadLong();
// int cheats;
str->cheats = SV_ReadLong();
// int refire;
str->refire = SV_ReadLong();
// int killcount, itemcount, secretcount;
str->killcount = SV_ReadLong();
str->itemcount = SV_ReadLong();
str->secretcount = SV_ReadLong();
// char message[80];
for (i=0; i<80; ++i)
{
str->message[i] = SV_ReadByte();
}
// int messageTics;
str->messageTics = SV_ReadLong();
// short ultimateMessage;
str->ultimateMessage = SV_ReadWord();
// short yellowMessage;
str->yellowMessage = SV_ReadWord();
// int damagecount, bonuscount;
str->damagecount = SV_ReadLong();
str->bonuscount = SV_ReadLong();
// int poisoncount;
str->poisoncount = SV_ReadLong();
// mobj_t *poisoner;
// Pointer value is reset.
str->poisoner = SV_ReadPtr();
str->poisoner = NULL;
// mobj_t *attacker;
// Pointer value is reset.
str->attacker = SV_ReadPtr();
str->attacker = NULL;
// int extralight;
str->extralight = SV_ReadLong();
// int fixedcolormap;
str->fixedcolormap = SV_ReadLong();
// int colormap;
str->colormap = SV_ReadLong();
// pspdef_t psprites[NUMPSPRITES];
for (i=0; i<NUMPSPRITES; ++i)
{
StreamIn_pspdef_t(&str->psprites[i]);
}
// int morphTics;
str->morphTics = SV_ReadLong();
// unsigned int jumpTics;
str->jumpTics = SV_ReadLong();
// unsigned int worldTimer;
str->worldTimer = SV_ReadLong();
}
static void StreamOut_player_t(player_t *str)
{
int i;
// mobj_t *mo;
SV_WritePtr(str->mo);
// playerstate_t playerstate;
SV_WriteLong(str->playerstate);
// ticcmd_t cmd;
StreamOut_ticcmd_t(&str->cmd);
// pclass_t class;
SV_WriteLong(str->class);
// fixed_t viewz;
SV_WriteLong(str->viewz);
// fixed_t viewheight;
SV_WriteLong(str->viewheight);
// fixed_t deltaviewheight;
SV_WriteLong(str->deltaviewheight);
// fixed_t bob;
SV_WriteLong(str->bob);
// int flyheight;
SV_WriteLong(str->flyheight);
// int lookdir;
SV_WriteLong(str->lookdir);
// boolean centering;
SV_WriteLong(str->centering);
// int health;
SV_WriteLong(str->health);
// int armorpoints[NUMARMOR];
for (i=0; i<NUMARMOR; ++i)
{
SV_WriteLong(str->armorpoints[i]);
}
// inventory_t inventory[NUMINVENTORYSLOTS];
for (i=0; i<NUMINVENTORYSLOTS; ++i)
{
StreamOut_inventory_t(&str->inventory[i]);
}
// artitype_t readyArtifact;
SV_WriteLong(str->readyArtifact);
// int artifactCount;
SV_WriteLong(str->artifactCount);
// int inventorySlotNum;
SV_WriteLong(str->inventorySlotNum);
// int powers[NUMPOWERS];
for (i=0; i<NUMPOWERS; ++i)
{
SV_WriteLong(str->powers[i]);
}
// int keys;
SV_WriteLong(str->keys);
// int pieces;
SV_WriteLong(str->pieces);
// signed int frags[MAXPLAYERS];
for (i=0; i<maxplayers; ++i)
{
SV_WriteLong(str->frags[i]);
}
// weapontype_t readyweapon;
SV_WriteLong(str->readyweapon);
// weapontype_t pendingweapon;
SV_WriteLong(str->pendingweapon);
// boolean weaponowned[NUMWEAPONS];
for (i=0; i<NUMWEAPONS; ++i)
{
SV_WriteLong(str->weaponowned[i]);
}
// int mana[NUMMANA];
for (i=0; i<NUMMANA; ++i)
{
SV_WriteLong(str->mana[i]);
}
// int attackdown, usedown;
SV_WriteLong(str->attackdown);
SV_WriteLong(str->usedown);
// int cheats;
SV_WriteLong(str->cheats);
// int refire;
SV_WriteLong(str->refire);
// int killcount, itemcount, secretcount;
SV_WriteLong(str->killcount);
SV_WriteLong(str->itemcount);
SV_WriteLong(str->secretcount);
// char message[80];
for (i=0; i<80; ++i)
{
SV_WriteByte(str->message[i]);
}
// int messageTics;
SV_WriteLong(str->messageTics);
// short ultimateMessage;
SV_WriteWord(str->ultimateMessage);
// short yellowMessage;
SV_WriteWord(str->yellowMessage);
// int damagecount, bonuscount;
SV_WriteLong(str->damagecount);
SV_WriteLong(str->bonuscount);
// int poisoncount;
SV_WriteLong(str->poisoncount);
// mobj_t *poisoner;
SV_WritePtr(str->poisoner);
// mobj_t *attacker;
SV_WritePtr(str->attacker);
// int extralight;
SV_WriteLong(str->extralight);
// int fixedcolormap;
SV_WriteLong(str->fixedcolormap);
// int colormap;
SV_WriteLong(str->colormap);
// pspdef_t psprites[NUMPSPRITES];
for (i=0; i<NUMPSPRITES; ++i)
{
StreamOut_pspdef_t(&str->psprites[i]);
}
// int morphTics;
SV_WriteLong(str->morphTics);
// unsigned int jumpTics;
SV_WriteLong(str->jumpTics);
// unsigned int worldTimer;
SV_WriteLong(str->worldTimer);
}
//
// thinker_t
//
static void StreamIn_thinker_t(thinker_t *str)
{
// struct thinker_s *prev, *next;
// Pointers are discarded:
str->prev = SV_ReadPtr();
str->prev = NULL;
str->next = SV_ReadPtr();
str->next = NULL;
// think_t function;
// Function pointer is discarded:
str->function = SV_ReadPtr();
str->function = NULL;
}
static void StreamOut_thinker_t(thinker_t *str)
{
// struct thinker_s *prev, *next;
SV_WritePtr(str->prev);
SV_WritePtr(str->next);
// think_t function;
SV_WritePtr(&str->function);
}
//
// mobj_t
//
static void StreamInMobjSpecials(mobj_t *mobj)
{
unsigned int special1, special2;
special1 = SV_ReadLong();
special2 = SV_ReadLong();
mobj->special1.i = special1;
mobj->special2.i = special2;
switch (mobj->type)
{
// Just special1
case MT_BISH_FX:
case MT_HOLY_FX:
case MT_DRAGON:
case MT_THRUSTFLOOR_UP:
case MT_THRUSTFLOOR_DOWN:
case MT_MINOTAUR:
case MT_SORCFX1:
SetMobjPtr(&mobj->special1.m, special1);
break;
// Just special2
case MT_LIGHTNING_FLOOR:
case MT_LIGHTNING_ZAP:
SetMobjPtr(&mobj->special2.m, special2);
break;
// Both special1 and special2
case MT_HOLY_TAIL:
case MT_LIGHTNING_CEILING:
SetMobjPtr(&mobj->special1.m, special1);
SetMobjPtr(&mobj->special2.m, special2);
break;
default:
break;
}
}
static void StreamIn_mobj_t(mobj_t *str)
{
unsigned int i;
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// fixed_t x, y, z;
str->x = SV_ReadLong();
str->y = SV_ReadLong();
str->z = SV_ReadLong();
// struct mobj_s *snext, *sprev;
// Pointer values are discarded:
str->snext = SV_ReadPtr();
str->snext = NULL;
str->sprev = SV_ReadPtr();
str->sprev = NULL;
// angle_t angle;
str->angle = SV_ReadLong();
// spritenum_t sprite;
str->sprite = SV_ReadLong();
// int frame;
str->frame = SV_ReadLong();
// struct mobj_s *bnext, *bprev;
// Values are read but discarded; this will be restored when the thing's
// position is set.
str->bnext = SV_ReadPtr();
str->bnext = NULL;
str->bprev = SV_ReadPtr();
str->bprev = NULL;
// struct subsector_s *subsector;
// Read but discard: pointer will be restored when thing position is set.
str->subsector = SV_ReadPtr();
str->subsector = NULL;
// fixed_t floorz, ceilingz;
str->floorz = SV_ReadLong();
str->ceilingz = SV_ReadLong();
// fixed_t floorpic;
str->floorpic = SV_ReadLong();
// fixed_t radius, height;
str->radius = SV_ReadLong();
str->height = SV_ReadLong();
// fixed_t momx, momy, momz;
str->momx = SV_ReadLong();
str->momy = SV_ReadLong();
str->momz = SV_ReadLong();
// int validcount;
str->validcount = SV_ReadLong();
// mobjtype_t type;
str->type = SV_ReadLong();
// mobjinfo_t *info;
// Pointer value is read but discarded.
str->info = SV_ReadPtr();
str->info = NULL;
// int tics;
str->tics = SV_ReadLong();
// state_t *state;
// Restore as index into states table.
i = SV_ReadLong();
str->state = &states[i];
// int damage;
str->damage = SV_ReadLong();
// int flags;
str->flags = SV_ReadLong();
// int flags2;
str->flags2 = SV_ReadLong();
// specialval_t special1;
// specialval_t special2;
// Read in special values: there are special cases to deal with with
// mobj pointers.
StreamInMobjSpecials(str);
// int health;
str->health = SV_ReadLong();
// int movedir;
str->movedir = SV_ReadLong();
// int movecount;
str->movecount = SV_ReadLong();
// struct mobj_s *target;
i = SV_ReadLong();
SetMobjPtr(&str->target, i);
// int reactiontime;
str->reactiontime = SV_ReadLong();
// int threshold;
str->threshold = SV_ReadLong();
// struct player_s *player;
// Saved as player number.
i = SV_ReadLong();
if (i == 0)
{
str->player = NULL;
}
else
{
str->player = &players[i - 1];
str->player->mo = str;
}
// int lastlook;
str->lastlook = SV_ReadLong();
// fixed_t floorclip;
str->floorclip = SV_ReadLong();
// int archiveNum;
str->archiveNum = SV_ReadLong();
// short tid;
str->tid = SV_ReadWord();
// byte special;
str->special = SV_ReadByte();
// byte args[5];
for (i=0; i<5; ++i)
{
str->args[i] = SV_ReadByte();
}
}
static void StreamOutMobjSpecials(mobj_t *mobj)
{
unsigned int special1, special2;
boolean corpse;
corpse = (mobj->flags & MF_CORPSE) != 0;
special1 = mobj->special1.i;
special2 = mobj->special2.i;
switch (mobj->type)
{
// Just special1
case MT_BISH_FX:
case MT_HOLY_FX:
case MT_DRAGON:
case MT_THRUSTFLOOR_UP:
case MT_THRUSTFLOOR_DOWN:
case MT_MINOTAUR:
case MT_SORCFX1:
case MT_MSTAFF_FX2:
if (corpse)
{
special1 = MOBJ_NULL;
}
else
{
special1 = GetMobjNum(mobj->special1.m);
}
break;
// Just special2
case MT_LIGHTNING_FLOOR:
case MT_LIGHTNING_ZAP:
if (corpse)
{
special2 = MOBJ_NULL;
}
else
{
special2 = GetMobjNum(mobj->special2.m);
}
break;
// Both special1 and special2
case MT_HOLY_TAIL:
case MT_LIGHTNING_CEILING:
if (corpse)
{
special1 = MOBJ_NULL;
special2 = MOBJ_NULL;
}
else
{
special1 = GetMobjNum(mobj->special1.m);
special2 = GetMobjNum(mobj->special2.m);
}
break;
// Miscellaneous
case MT_KORAX:
special1 = 0; // Searching index
break;
default:
break;
}
// Write special values to savegame file.
SV_WriteLong(special1);
SV_WriteLong(special2);
}
static void StreamOut_mobj_t(mobj_t *str)
{
int i;
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// fixed_t x, y, z;
SV_WriteLong(str->x);
SV_WriteLong(str->y);
SV_WriteLong(str->z);
// struct mobj_s *snext, *sprev;
SV_WritePtr(str->snext);
SV_WritePtr(str->sprev);
// angle_t angle;
SV_WriteLong(str->angle);
// spritenum_t sprite;
SV_WriteLong(str->sprite);
// int frame;
SV_WriteLong(str->frame);
// struct mobj_s *bnext, *bprev;
SV_WritePtr(str->bnext);
SV_WritePtr(str->bprev);
// struct subsector_s *subsector;
SV_WritePtr(str->subsector);
// fixed_t floorz, ceilingz;
SV_WriteLong(str->floorz);
SV_WriteLong(str->ceilingz);
// fixed_t floorpic;
SV_WriteLong(str->floorpic);
// fixed_t radius, height;
SV_WriteLong(str->radius);
SV_WriteLong(str->height);
// fixed_t momx, momy, momz;
SV_WriteLong(str->momx);
SV_WriteLong(str->momy);
SV_WriteLong(str->momz);
// int validcount;
SV_WriteLong(str->validcount);
// mobjtype_t type;
SV_WriteLong(str->type);
// mobjinfo_t *info;
SV_WritePtr(str->info);
// int tics;
SV_WriteLong(str->tics);
// state_t *state;
// Save as index into the states table.
SV_WriteLong(str->state - states);
// int damage;
SV_WriteLong(str->damage);
// int flags;
SV_WriteLong(str->flags);
// int flags2;
SV_WriteLong(str->flags2);
// specialval_t special1;
// specialval_t special2;
// There are lots of special cases for the special values:
StreamOutMobjSpecials(str);
// int health;
SV_WriteLong(str->health);
// int movedir;
SV_WriteLong(str->movedir);
// int movecount;
SV_WriteLong(str->movecount);
// struct mobj_s *target;
if ((str->flags & MF_CORPSE) != 0)
{
SV_WriteLong(MOBJ_NULL);
}
else
{
SV_WriteLong(GetMobjNum(str->target));
}
// int reactiontime;
SV_WriteLong(str->reactiontime);
// int threshold;
SV_WriteLong(str->threshold);
// struct player_s *player;
// Stored as index into players[] array, if there is a player pointer.
if (str->player != NULL)
{
SV_WriteLong(str->player - players + 1);
}
else
{
SV_WriteLong(0);
}
// int lastlook;
SV_WriteLong(str->lastlook);
// fixed_t floorclip;
SV_WriteLong(str->floorclip);
// int archiveNum;
SV_WriteLong(str->archiveNum);
// short tid;
SV_WriteWord(str->tid);
// byte special;
SV_WriteByte(str->special);
// byte args[5];
for (i=0; i<5; ++i)
{
SV_WriteByte(str->args[i]);
}
}
//
// floormove_t
//
static void StreamIn_floormove_t(floormove_t *str)
{
int i;
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// sector_t *sector;
i = SV_ReadLong();
str->sector = sectors + i;
// floor_e type;
str->type = SV_ReadLong();
// int crush;
str->crush = SV_ReadLong();
// int direction;
str->direction = SV_ReadLong();
// int newspecial;
str->newspecial = SV_ReadLong();
// short texture;
str->texture = SV_ReadWord();
// fixed_t floordestheight;
str->floordestheight = SV_ReadLong();
// fixed_t speed;
str->speed = SV_ReadLong();
// int delayCount;
str->delayCount = SV_ReadLong();
// int delayTotal;
str->delayTotal = SV_ReadLong();
// fixed_t stairsDelayHeight;
str->stairsDelayHeight = SV_ReadLong();
// fixed_t stairsDelayHeightDelta;
str->stairsDelayHeightDelta = SV_ReadLong();
// fixed_t resetHeight;
str->resetHeight = SV_ReadLong();
// short resetDelay;
str->resetDelay = SV_ReadWord();
// short resetDelayCount;
str->resetDelayCount = SV_ReadWord();
// byte textureChange;
str->textureChange = SV_ReadByte();
}
static void StreamOut_floormove_t(floormove_t *str)
{
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// sector_t *sector;
SV_WriteLong(str->sector - sectors);
// floor_e type;
SV_WriteLong(str->type);
// int crush;
SV_WriteLong(str->crush);
// int direction;
SV_WriteLong(str->direction);
// int newspecial;
SV_WriteLong(str->newspecial);
// short texture;
SV_WriteWord(str->texture);
// fixed_t floordestheight;
SV_WriteLong(str->floordestheight);
// fixed_t speed;
SV_WriteLong(str->speed);
// int delayCount;
SV_WriteLong(str->delayCount);
// int delayTotal;
SV_WriteLong(str->delayTotal);
// fixed_t stairsDelayHeight;
SV_WriteLong(str->stairsDelayHeight);
// fixed_t stairsDelayHeightDelta;
SV_WriteLong(str->stairsDelayHeightDelta);
// fixed_t resetHeight;
SV_WriteLong(str->resetHeight);
// short resetDelay;
SV_WriteWord(str->resetDelay);
// short resetDelayCount;
SV_WriteWord(str->resetDelayCount);
// byte textureChange;
SV_WriteByte(str->textureChange);
}
//
// plat_t
//
static void StreamIn_plat_t(plat_t *str)
{
int i;
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// sector_t *sector;
i = SV_ReadLong();
str->sector = sectors + i;
// fixed_t speed;
str->speed = SV_ReadLong();
// fixed_t low;
str->low = SV_ReadLong();
// fixed_t high;
str->high = SV_ReadLong();
// int wait;
str->wait = SV_ReadLong();
// int count;
str->count = SV_ReadLong();
// plat_e status;
str->status = SV_ReadLong();
// plat_e oldstatus;
str->oldstatus = SV_ReadLong();
// int crush;
str->crush = SV_ReadLong();
// int tag;
str->tag = SV_ReadLong();
// plattype_e type;
str->type = SV_ReadLong();
}
static void StreamOut_plat_t(plat_t *str)
{
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// sector_t *sector;
SV_WriteLong(str->sector - sectors);
// fixed_t speed;
SV_WriteLong(str->speed);
// fixed_t low;
SV_WriteLong(str->low);
// fixed_t high;
SV_WriteLong(str->high);
// int wait;
SV_WriteLong(str->wait);
// int count;
SV_WriteLong(str->count);
// plat_e status;
SV_WriteLong(str->status);
// plat_e oldstatus;
SV_WriteLong(str->oldstatus);
// int crush;
SV_WriteLong(str->crush);
// int tag;
SV_WriteLong(str->tag);
// plattype_e type;
SV_WriteLong(str->type);
}
//
// ceiling_t
//
static void StreamIn_ceiling_t(ceiling_t *str)
{
int i;
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// sector_t *sector;
i = SV_ReadLong();
str->sector = sectors + i;
// ceiling_e type;
str->type = SV_ReadLong();
// fixed_t bottomheight, topheight;
str->bottomheight = SV_ReadLong();
str->topheight = SV_ReadLong();
// fixed_t speed;
str->speed = SV_ReadLong();
// int crush;
str->crush = SV_ReadLong();
// int direction;
str->direction = SV_ReadLong();
// int tag;
str->tag = SV_ReadLong();
// int olddirection;
str->olddirection = SV_ReadLong();
}
static void StreamOut_ceiling_t(ceiling_t *str)
{
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// sector_t *sector;
SV_WriteLong(str->sector - sectors);
// ceiling_e type;
SV_WriteLong(str->type);
// fixed_t bottomheight, topheight;
SV_WriteLong(str->bottomheight);
SV_WriteLong(str->topheight);
// fixed_t speed;
SV_WriteLong(str->speed);
// int crush;
SV_WriteLong(str->crush);
// int direction;
SV_WriteLong(str->direction);
// int tag;
SV_WriteLong(str->tag);
// int olddirection;
SV_WriteLong(str->olddirection);
}
//
// light_t
//
static void StreamIn_light_t(light_t *str)
{
int i;
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// sector_t *sector;
i = SV_ReadLong();
str->sector = sectors + i;
// lighttype_t type;
str->type = SV_ReadLong();
// int value1;
str->value1 = SV_ReadLong();
// int value2;
str->value2 = SV_ReadLong();
// int tics1;
str->tics1 = SV_ReadLong();
// int tics2;
str->tics2 = SV_ReadLong();
// int count;
str->count = SV_ReadLong();
}
static void StreamOut_light_t(light_t *str)
{
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// sector_t *sector;
SV_WriteLong(str->sector - sectors);
// lighttype_t type;
SV_WriteLong(str->type);
// int value1;
SV_WriteLong(str->value1);
// int value2;
SV_WriteLong(str->value2);
// int tics1;
SV_WriteLong(str->tics1);
// int tics2;
SV_WriteLong(str->tics2);
// int count;
SV_WriteLong(str->count);
}
//
// vldoor_t
//
static void StreamIn_vldoor_t(vldoor_t *str)
{
int i;
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// sector_t *sector;
i = SV_ReadLong();
str->sector = §ors[i];
// vldoor_e type;
str->type = SV_ReadLong();
// fixed_t topheight;
str->topheight = SV_ReadLong();
// fixed_t speed;
str->speed = SV_ReadLong();
// int direction;
str->direction = SV_ReadLong();
// int topwait;
str->topwait = SV_ReadLong();
// int topcountdown;
str->topcountdown = SV_ReadLong();
}
static void StreamOut_vldoor_t(vldoor_t *str)
{
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// sector_t *sector;
SV_WriteLong(str->sector - sectors);
// vldoor_e type;
SV_WriteLong(str->type);
// fixed_t topheight;
SV_WriteLong(str->topheight);
// fixed_t speed;
SV_WriteLong(str->speed);
// int direction;
SV_WriteLong(str->direction);
// int topwait;
SV_WriteLong(str->topwait);
// int topcountdown;
SV_WriteLong(str->topcountdown);
}
//
// phase_t
//
static void StreamIn_phase_t(phase_t *str)
{
int i;
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// sector_t *sector;
i = SV_ReadLong();
str->sector = §ors[i];
// int index;
str->index = SV_ReadLong();
// int base;
str->base = SV_ReadLong();
}
static void StreamOut_phase_t(phase_t *str)
{
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// sector_t *sector;
SV_WriteLong(str->sector - sectors);
// int index;
SV_WriteLong(str->index);
// int base;
SV_WriteLong(str->base);
}
//
// acs_t
//
static void StreamIn_acs_t(acs_t *str)
{
int i;
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// mobj_t *activator;
i = SV_ReadLong();
SetMobjPtr(&str->activator, i);
// line_t *line;
i = SV_ReadLong();
if (i != -1)
{
str->line = &lines[i];
}
else
{
str->line = NULL;
}
// int side;
str->side = SV_ReadLong();
// int number;
str->number = SV_ReadLong();
// int infoIndex;
str->infoIndex = SV_ReadLong();
// int delayCount;
str->delayCount = SV_ReadLong();
// int stack[ACS_STACK_DEPTH];
for (i=0; i<ACS_STACK_DEPTH; ++i)
{
str->stack[i] = SV_ReadLong();
}
// int stackPtr;
str->stackPtr = SV_ReadLong();
// int vars[MAX_ACS_SCRIPT_VARS];
for (i=0; i<MAX_ACS_SCRIPT_VARS; ++i)
{
str->vars[i] = SV_ReadLong();
}
// int *ip;
str->ip = SV_ReadLong();
}
static void StreamOut_acs_t(acs_t *str)
{
int i;
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// mobj_t *activator;
SV_WriteLong(GetMobjNum(str->activator));
// line_t *line;
if (str->line != NULL)
{
SV_WriteLong(str->line - lines);
}
else
{
SV_WriteLong(-1);
}
// int side;
SV_WriteLong(str->side);
// int number;
SV_WriteLong(str->number);
// int infoIndex;
SV_WriteLong(str->infoIndex);
// int delayCount;
SV_WriteLong(str->delayCount);
// int stack[ACS_STACK_DEPTH];
for (i=0; i<ACS_STACK_DEPTH; ++i)
{
SV_WriteLong(str->stack[i]);
}
// int stackPtr;
SV_WriteLong(str->stackPtr);
// int vars[MAX_ACS_SCRIPT_VARS];
for (i=0; i<MAX_ACS_SCRIPT_VARS; ++i)
{
SV_WriteLong(str->vars[i]);
}
// int *ip;
SV_WriteLong(str->ip);
}
//
// polyevent_t
//
static void StreamIn_polyevent_t(polyevent_t *str)
{
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// int polyobj;
str->polyobj = SV_ReadLong();
// int speed;
str->speed = SV_ReadLong();
// unsigned int dist;
str->dist = SV_ReadLong();
// int angle;
str->angle = SV_ReadLong();
// fixed_t xSpeed;
str->xSpeed = SV_ReadLong();
// fixed_t ySpeed;
str->ySpeed = SV_ReadLong();
}
static void StreamOut_polyevent_t(polyevent_t *str)
{
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// int polyobj;
SV_WriteLong(str->polyobj);
// int speed;
SV_WriteLong(str->speed);
// unsigned int dist;
SV_WriteLong(str->dist);
// int angle;
SV_WriteLong(str->angle);
// fixed_t xSpeed;
SV_WriteLong(str->xSpeed);
// fixed_t ySpeed;
SV_WriteLong(str->ySpeed);
}
//
// pillar_t
//
static void StreamIn_pillar_t(pillar_t *str)
{
int i;
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// sector_t *sector;
i = SV_ReadLong();
str->sector = §ors[i];
// int ceilingSpeed;
str->ceilingSpeed = SV_ReadLong();
// int floorSpeed;
str->floorSpeed = SV_ReadLong();
// int floordest;
str->floordest = SV_ReadLong();
// int ceilingdest;
str->ceilingdest = SV_ReadLong();
// int direction;
str->direction = SV_ReadLong();
// int crush;
str->crush = SV_ReadLong();
}
static void StreamOut_pillar_t(pillar_t *str)
{
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// sector_t *sector;
SV_WriteLong(str->sector - sectors);
// int ceilingSpeed;
SV_WriteLong(str->ceilingSpeed);
// int floorSpeed;
SV_WriteLong(str->floorSpeed);
// int floordest;
SV_WriteLong(str->floordest);
// int ceilingdest;
SV_WriteLong(str->ceilingdest);
// int direction;
SV_WriteLong(str->direction);
// int crush;
SV_WriteLong(str->crush);
}
//
// polydoor_t
//
static void StreamIn_polydoor_t(polydoor_t *str)
{
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// int polyobj;
str->polyobj = SV_ReadLong();
// int speed;
str->speed = SV_ReadLong();
// int dist;
str->dist = SV_ReadLong();
// int totalDist;
str->totalDist = SV_ReadLong();
// int direction;
str->direction = SV_ReadLong();
// fixed_t xSpeed, ySpeed;
str->xSpeed = SV_ReadLong();
str->ySpeed = SV_ReadLong();
// int tics;
str->tics = SV_ReadLong();
// int waitTics;
str->waitTics = SV_ReadLong();
// podoortype_t type;
str->type = SV_ReadLong();
// boolean close;
str->close = SV_ReadLong();
}
static void StreamOut_polydoor_t(polydoor_t *str)
{
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// int polyobj;
SV_WriteLong(str->polyobj);
// int speed;
SV_WriteLong(str->speed);
// int dist;
SV_WriteLong(str->dist);
// int totalDist;
SV_WriteLong(str->totalDist);
// int direction;
SV_WriteLong(str->direction);
// fixed_t xSpeed, ySpeed;
SV_WriteLong(str->xSpeed);
SV_WriteLong(str->ySpeed);
// int tics;
SV_WriteLong(str->tics);
// int waitTics;
SV_WriteLong(str->waitTics);
// podoortype_t type;
SV_WriteLong(str->type);
// boolean close;
SV_WriteLong(str->close);
}
//
// floorWaggle_t
//
static void StreamIn_floorWaggle_t(floorWaggle_t *str)
{
int i;
// thinker_t thinker;
StreamIn_thinker_t(&str->thinker);
// sector_t *sector;
i = SV_ReadLong();
str->sector = §ors[i];
// fixed_t originalHeight;
str->originalHeight = SV_ReadLong();
// fixed_t accumulator;
str->accumulator = SV_ReadLong();
// fixed_t accDelta;
str->accDelta = SV_ReadLong();
// fixed_t targetScale;
str->targetScale = SV_ReadLong();
// fixed_t scale;
str->scale = SV_ReadLong();
// fixed_t scaleDelta;
str->scaleDelta = SV_ReadLong();
// int ticker;
str->ticker = SV_ReadLong();
// int state;
str->state = SV_ReadLong();
}
static void StreamOut_floorWaggle_t(floorWaggle_t *str)
{
// thinker_t thinker;
StreamOut_thinker_t(&str->thinker);
// sector_t *sector;
SV_WriteLong(str->sector - sectors);
// fixed_t originalHeight;
SV_WriteLong(str->originalHeight);
// fixed_t accumulator;
SV_WriteLong(str->accumulator);
// fixed_t accDelta;
SV_WriteLong(str->accDelta);
// fixed_t targetScale;
SV_WriteLong(str->targetScale);
// fixed_t scale;
SV_WriteLong(str->scale);
// fixed_t scaleDelta;
SV_WriteLong(str->scaleDelta);
// int ticker;
SV_WriteLong(str->ticker);
// int state;
SV_WriteLong(str->state);
}
//==========================================================================
//
// SV_SaveGame
//
//==========================================================================
void SV_SaveGame(int slot, const char *description)
{
char fileName[100];
char versionText[HXS_VERSION_TEXT_LENGTH];
unsigned int i;
// Open the output file
M_snprintf(fileName, sizeof(fileName), "%shex6.hxs", SavePath);
SV_OpenWrite(fileName);
// Write game save description
SV_Write(description, HXS_DESCRIPTION_LENGTH);
// Write version info
memset(versionText, 0, HXS_VERSION_TEXT_LENGTH);
M_StringCopy(versionText, HXS_VERSION_TEXT, HXS_VERSION_TEXT_LENGTH);
SV_Write(versionText, HXS_VERSION_TEXT_LENGTH);
// Place a header marker
SV_WriteLong(ASEG_GAME_HEADER);
// Write current map and difficulty
SV_WriteByte(gamemap);
SV_WriteByte(gameskill);
// Write global script info
for (i = 0; i < MAX_ACS_WORLD_VARS; ++i)
{
SV_WriteLong(WorldVars[i]);
}
for (i = 0; i < MAX_ACS_STORE + 1; ++i)
{
StreamOut_acsstore_t(&ACSStore[i]);
}
ArchivePlayers();
// Place a termination marker
SV_WriteLong(ASEG_END);
// Close the output file
SV_Close();
// Save out the current map
SV_SaveMap(true); // true = save player info
// Clear all save files at destination slot
ClearSaveSlot(slot);
// Copy base slot to destination slot
CopySaveSlot(BASE_SLOT, slot);
}
//==========================================================================
//
// SV_SaveMap
//
//==========================================================================
void SV_SaveMap(boolean savePlayers)
{
char fileName[100];
SavingPlayers = savePlayers;
// Open the output file
M_snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap);
SV_OpenWrite(fileName);
// Place a header marker
SV_WriteLong(ASEG_MAP_HEADER);
// Write the level timer
SV_WriteLong(leveltime);
// Set the mobj archive numbers
SetMobjArchiveNums();
ArchiveWorld();
ArchivePolyobjs();
ArchiveMobjs();
ArchiveThinkers();
ArchiveScripts();
ArchiveSounds();
ArchiveMisc();
// Place a termination marker
SV_WriteLong(ASEG_END);
// Close the output file
SV_Close();
}
//==========================================================================
//
// SV_LoadGame
//
//==========================================================================
void SV_LoadGame(int slot)
{
int i;
char fileName[100];
char version_text[HXS_VERSION_TEXT_LENGTH];
player_t playerBackup[MAXPLAYERS];
mobj_t *mobj;
// Copy all needed save files to the base slot
if (slot != BASE_SLOT)
{
ClearSaveSlot(BASE_SLOT);
CopySaveSlot(slot, BASE_SLOT);
}
// Create the name
M_snprintf(fileName, sizeof(fileName), "%shex6.hxs", SavePath);
// Load the file
SV_OpenRead(fileName);
// Set the save pointer and skip the description field
fseek(SavingFP, HXS_DESCRIPTION_LENGTH, SEEK_CUR);
// Check the version text
for (i = 0; i < sizeof(version_text); ++i)
{
version_text[i] = SV_ReadByte();
}
if (strncmp(version_text, HXS_VERSION_TEXT, HXS_VERSION_TEXT_LENGTH) != 0)
{ // Bad version
return;
}
AssertSegment(ASEG_GAME_HEADER);
gameepisode = 1;
gamemap = SV_ReadByte();
gameskill = SV_ReadByte();
// Read global script info
for (i = 0; i < MAX_ACS_WORLD_VARS; ++i)
{
WorldVars[i] = SV_ReadLong();
}
for (i = 0; i < MAX_ACS_STORE + 1; ++i)
{
StreamIn_acsstore_t(&ACSStore[i]);
}
// Read the player structures
UnarchivePlayers();
AssertSegment(ASEG_END);
// Save player structs
for (i = 0; i < maxplayers; i++)
{
playerBackup[i] = players[i];
}
SV_Close();
// Load the current map
SV_LoadMap();
// Don't need the player mobj relocation info for load game
Z_Free(TargetPlayerAddrs);
// Restore player structs
inv_ptr = 0;
curpos = 0;
for (i = 0; i < maxplayers; i++)
{
mobj = players[i].mo;
players[i] = playerBackup[i];
players[i].mo = mobj;
if (i == consoleplayer)
{
players[i].readyArtifact = players[i].inventory[inv_ptr].type;
}
}
}
//==========================================================================
//
// SV_UpdateRebornSlot
//
// Copies the base slot to the reborn slot.
//
//==========================================================================
void SV_UpdateRebornSlot(void)
{
ClearSaveSlot(REBORN_SLOT);
CopySaveSlot(BASE_SLOT, REBORN_SLOT);
}
//==========================================================================
//
// SV_ClearRebornSlot
//
//==========================================================================
void SV_ClearRebornSlot(void)
{
ClearSaveSlot(REBORN_SLOT);
}
//==========================================================================
//
// SV_MapTeleport
//
//==========================================================================
void SV_MapTeleport(int map, int position)
{
int i;
int j;
char fileName[100];
player_t playerBackup[MAXPLAYERS];
mobj_t *targetPlayerMobj;
mobj_t *mobj;
int inventoryPtr;
int currentInvPos;
boolean rClass;
boolean playerWasReborn;
boolean oldWeaponowned[NUMWEAPONS];
int oldKeys = 0;
int oldPieces = 0;
int bestWeapon;
if (!deathmatch)
{
if (P_GetMapCluster(gamemap) == P_GetMapCluster(map))
{ // Same cluster - save map without saving player mobjs
SV_SaveMap(false);
}
else
{ // Entering new cluster - clear base slot
ClearSaveSlot(BASE_SLOT);
}
}
// Store player structs for later
rClass = randomclass;
randomclass = false;
for (i = 0; i < maxplayers; i++)
{
playerBackup[i] = players[i];
}
// Save some globals that get trashed during the load
inventoryPtr = inv_ptr;
currentInvPos = curpos;
// Only SV_LoadMap() uses TargetPlayerAddrs, so it's NULLed here
// for the following check (player mobj redirection)
TargetPlayerAddrs = NULL;
gamemap = map;
M_snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap);
if (!deathmatch && ExistingFile(fileName))
{ // Unarchive map
SV_LoadMap();
}
else
{ // New map
G_InitNew(gameskill, gameepisode, gamemap);
// Destroy all freshly spawned players
for (i = 0; i < maxplayers; i++)
{
if (playeringame[i])
{
P_RemoveMobj(players[i].mo);
}
}
}
// Restore player structs
targetPlayerMobj = NULL;
for (i = 0; i < maxplayers; i++)
{
if (!playeringame[i])
{
continue;
}
players[i] = playerBackup[i];
P_ClearMessage(&players[i]);
players[i].attacker = NULL;
players[i].poisoner = NULL;
if (netgame)
{
if (players[i].playerstate == PST_DEAD)
{ // In a network game, force all players to be alive
players[i].playerstate = PST_REBORN;
}
if (!deathmatch)
{ // Cooperative net-play, retain keys and weapons
oldKeys = players[i].keys;
oldPieces = players[i].pieces;
for (j = 0; j < NUMWEAPONS; j++)
{
oldWeaponowned[j] = players[i].weaponowned[j];
}
}
}
playerWasReborn = (players[i].playerstate == PST_REBORN);
if (deathmatch)
{
memset(players[i].frags, 0, sizeof(players[i].frags));
mobj = P_SpawnMobj(playerstarts[0][i].x << 16,
playerstarts[0][i].y << 16, 0,
MT_PLAYER_FIGHTER);
players[i].mo = mobj;
G_DeathMatchSpawnPlayer(i);
P_RemoveMobj(mobj);
}
else
{
P_SpawnPlayer(&playerstarts[position][i]);
}
if (playerWasReborn && netgame && !deathmatch)
{ // Restore keys and weapons when reborn in co-op
players[i].keys = oldKeys;
players[i].pieces = oldPieces;
for (bestWeapon = 0, j = 0; j < NUMWEAPONS; j++)
{
if (oldWeaponowned[j])
{
bestWeapon = j;
players[i].weaponowned[j] = true;
}
}
players[i].mana[MANA_1] = 25;
players[i].mana[MANA_2] = 25;
if (bestWeapon)
{ // Bring up the best weapon
players[i].pendingweapon = bestWeapon;
}
}
if (targetPlayerMobj == NULL)
{ // The poor sap
targetPlayerMobj = players[i].mo;
}
}
randomclass = rClass;
// Redirect anything targeting a player mobj
if (TargetPlayerAddrs)
{
for (i = 0; i < TargetPlayerCount; i++)
{
*TargetPlayerAddrs[i] = targetPlayerMobj;
}
Z_Free(TargetPlayerAddrs);
}
// Destroy all things touching players
for (i = 0; i < maxplayers; i++)
{
if (playeringame[i])
{
P_TeleportMove(players[i].mo, players[i].mo->x, players[i].mo->y);
}
}
// Restore trashed globals
inv_ptr = inventoryPtr;
curpos = currentInvPos;
// Launch waiting scripts
if (!deathmatch)
{
P_CheckACSStore();
}
// For single play, save immediately into the reborn slot
if (!netgame)
{
SV_SaveGame(REBORN_SLOT, REBORN_DESCRIPTION);
}
}
//==========================================================================
//
// SV_GetRebornSlot
//
//==========================================================================
int SV_GetRebornSlot(void)
{
return (REBORN_SLOT);
}
//==========================================================================
//
// SV_RebornSlotAvailable
//
// Returns true if the reborn slot is available.
//
//==========================================================================
boolean SV_RebornSlotAvailable(void)
{
char fileName[100];
M_snprintf(fileName, sizeof(fileName), "%shex%d.hxs", SavePath, REBORN_SLOT);
return ExistingFile(fileName);
}
//==========================================================================
//
// SV_LoadMap
//
//==========================================================================
void SV_LoadMap(void)
{
char fileName[100];
// Load a base level
G_InitNew(gameskill, gameepisode, gamemap);
// Remove all thinkers
RemoveAllThinkers();
// Create the name
M_snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap);
// Load the file
SV_OpenRead(fileName);
AssertSegment(ASEG_MAP_HEADER);
// Read the level timer
leveltime = SV_ReadLong();
UnarchiveWorld();
UnarchivePolyobjs();
UnarchiveMobjs();
UnarchiveThinkers();
UnarchiveScripts();
UnarchiveSounds();
UnarchiveMisc();
AssertSegment(ASEG_END);
// Free mobj list and save buffer
Z_Free(MobjList);
SV_Close();
}
//==========================================================================
//
// SV_InitBaseSlot
//
//==========================================================================
void SV_InitBaseSlot(void)
{
ClearSaveSlot(BASE_SLOT);
}
//==========================================================================
//
// ArchivePlayers
//
//==========================================================================
static void ArchivePlayers(void)
{
int i;
SV_WriteLong(ASEG_PLAYERS);
for (i = 0; i < maxplayers; i++)
{
SV_WriteByte(playeringame[i]);
}
for (i = 0; i < maxplayers; i++)
{
if (!playeringame[i])
{
continue;
}
SV_WriteByte(PlayerClass[i]);
StreamOut_player_t(&players[i]);
}
}
//==========================================================================
//
// UnarchivePlayers
//
//==========================================================================
static void UnarchivePlayers(void)
{
int i;
AssertSegment(ASEG_PLAYERS);
for (i = 0; i < maxplayers; i++)
{
playeringame[i] = SV_ReadByte();
}
for (i = 0; i < maxplayers; i++)
{
if (!playeringame[i])
{
continue;
}
PlayerClass[i] = SV_ReadByte();
StreamIn_player_t(&players[i]);
P_ClearMessage(&players[i]);
}
}
//==========================================================================
//
// ArchiveWorld
//
//==========================================================================
static void ArchiveWorld(void)
{
int i;
int j;
sector_t *sec;
line_t *li;
side_t *si;
SV_WriteLong(ASEG_WORLD);
for (i = 0, sec = sectors; i < numsectors; i++, sec++)
{
SV_WriteWord(sec->floorheight >> FRACBITS);
SV_WriteWord(sec->ceilingheight >> FRACBITS);
SV_WriteWord(sec->floorpic);
SV_WriteWord(sec->ceilingpic);
SV_WriteWord(sec->lightlevel);
SV_WriteWord(sec->special);
SV_WriteWord(sec->tag);
SV_WriteWord(sec->seqType);
}
for (i = 0, li = lines; i < numlines; i++, li++)
{
SV_WriteWord(li->flags);
SV_WriteByte(li->special);
SV_WriteByte(li->arg1);
SV_WriteByte(li->arg2);
SV_WriteByte(li->arg3);
SV_WriteByte(li->arg4);
SV_WriteByte(li->arg5);
for (j = 0; j < 2; j++)
{
if (li->sidenum[j] == -1)
{
continue;
}
si = &sides[li->sidenum[j]];
SV_WriteWord(si->textureoffset >> FRACBITS);
SV_WriteWord(si->rowoffset >> FRACBITS);
SV_WriteWord(si->toptexture);
SV_WriteWord(si->bottomtexture);
SV_WriteWord(si->midtexture);
}
}
}
//==========================================================================
//
// UnarchiveWorld
//
//==========================================================================
static void UnarchiveWorld(void)
{
int i;
int j;
sector_t *sec;
line_t *li;
side_t *si;
AssertSegment(ASEG_WORLD);
for (i = 0, sec = sectors; i < numsectors; i++, sec++)
{
sec->floorheight = SV_ReadWord() << FRACBITS;
sec->ceilingheight = SV_ReadWord() << FRACBITS;
sec->floorpic = SV_ReadWord();
sec->ceilingpic = SV_ReadWord();
sec->lightlevel = SV_ReadWord();
sec->special = SV_ReadWord();
sec->tag = SV_ReadWord();
sec->seqType = SV_ReadWord();
sec->specialdata = 0;
sec->soundtarget = 0;
}
for (i = 0, li = lines; i < numlines; i++, li++)
{
li->flags = SV_ReadWord();
li->special = SV_ReadByte();
li->arg1 = SV_ReadByte();
li->arg2 = SV_ReadByte();
li->arg3 = SV_ReadByte();
li->arg4 = SV_ReadByte();
li->arg5 = SV_ReadByte();
for (j = 0; j < 2; j++)
{
if (li->sidenum[j] == -1)
{
continue;
}
si = &sides[li->sidenum[j]];
si->textureoffset = SV_ReadWord() << FRACBITS;
si->rowoffset = SV_ReadWord() << FRACBITS;
si->toptexture = SV_ReadWord();
si->bottomtexture = SV_ReadWord();
si->midtexture = SV_ReadWord();
}
}
}
//==========================================================================
//
// SetMobjArchiveNums
//
// Sets the archive numbers in all mobj structs. Also sets the MobjCount
// global. Ignores player mobjs if SavingPlayers is false.
//
//==========================================================================
static void SetMobjArchiveNums(void)
{
mobj_t *mobj;
thinker_t *thinker;
MobjCount = 0;
for (thinker = thinkercap.next; thinker != &thinkercap;
thinker = thinker->next)
{
if (thinker->function == P_MobjThinker)
{
mobj = (mobj_t *) thinker;
if (mobj->player && !SavingPlayers)
{ // Skipping player mobjs
continue;
}
mobj->archiveNum = MobjCount++;
}
}
}
//==========================================================================
//
// ArchiveMobjs
//
//==========================================================================
static void ArchiveMobjs(void)
{
int count;
thinker_t *thinker;
SV_WriteLong(ASEG_MOBJS);
SV_WriteLong(MobjCount);
count = 0;
for (thinker = thinkercap.next; thinker != &thinkercap;
thinker = thinker->next)
{
if (thinker->function != P_MobjThinker)
{ // Not a mobj thinker
continue;
}
if (((mobj_t *) thinker)->player && !SavingPlayers)
{ // Skipping player mobjs
continue;
}
count++;
StreamOut_mobj_t((mobj_t *) thinker);
}
if (count != MobjCount)
{
I_Error("ArchiveMobjs: bad mobj count");
}
}
//==========================================================================
//
// UnarchiveMobjs
//
//==========================================================================
static void UnarchiveMobjs(void)
{
int i;
mobj_t *mobj;
AssertSegment(ASEG_MOBJS);
TargetPlayerAddrs = Z_Malloc(MAX_TARGET_PLAYERS * sizeof(mobj_t **),
PU_STATIC, NULL);
TargetPlayerCount = 0;
MobjCount = SV_ReadLong();
MobjList = Z_Malloc(MobjCount * sizeof(mobj_t *), PU_STATIC, NULL);
for (i = 0; i < MobjCount; i++)
{
MobjList[i] = Z_Malloc(sizeof(mobj_t), PU_LEVEL, NULL);
}
for (i = 0; i < MobjCount; i++)
{
mobj = MobjList[i];
StreamIn_mobj_t(mobj);
// Restore broken pointers.
mobj->info = &mobjinfo[mobj->type];
P_SetThingPosition(mobj);
mobj->floorz = mobj->subsector->sector->floorheight;
mobj->ceilingz = mobj->subsector->sector->ceilingheight;
mobj->thinker.function = P_MobjThinker;
P_AddThinker(&mobj->thinker);
}
P_CreateTIDList();
P_InitCreatureCorpseQueue(true); // true = scan for corpses
}
//==========================================================================
//
// GetMobjNum
//
//==========================================================================
static int GetMobjNum(mobj_t * mobj)
{
if (mobj == NULL)
{
return MOBJ_NULL;
}
if (mobj->player && !SavingPlayers)
{
return MOBJ_XX_PLAYER;
}
return mobj->archiveNum;
}
//==========================================================================
//
// SetMobjPtr
//
//==========================================================================
static void SetMobjPtr(mobj_t **ptr, unsigned int archiveNum)
{
if (archiveNum == MOBJ_NULL)
{
*ptr = NULL;
}
else if (archiveNum == MOBJ_XX_PLAYER)
{
if (TargetPlayerCount == MAX_TARGET_PLAYERS)
{
I_Error("RestoreMobj: exceeded MAX_TARGET_PLAYERS");
}
TargetPlayerAddrs[TargetPlayerCount++] = ptr;
*ptr = NULL;
}
else
{
*ptr = MobjList[archiveNum];
}
}
//==========================================================================
//
// Thinker types list.
//
// This is used by ArchiveThinkers and UnarchiveThinkers, below.
//
// Original comment:
// "This list has been prioritized using frequency estimates"
//
//==========================================================================
static thinkInfo_t ThinkerInfo[] = {
{
TC_MOVE_FLOOR,
T_MoveFloor,
StreamOut_floormove_t,
StreamIn_floormove_t,
RestoreSSThinker,
sizeof(floormove_t)
},
{
TC_PLAT_RAISE,
T_PlatRaise,
StreamOut_plat_t,
StreamIn_plat_t,
RestorePlatRaise,
sizeof(plat_t)
},
{
TC_MOVE_CEILING,
T_MoveCeiling,
StreamOut_ceiling_t,
StreamIn_ceiling_t,
RestoreMoveCeiling,
sizeof(ceiling_t)
},
{
TC_LIGHT,
T_Light,
StreamOut_light_t,
StreamIn_light_t,
NULL,
sizeof(light_t)
},
{
TC_VERTICAL_DOOR,
T_VerticalDoor,
StreamOut_vldoor_t,
StreamIn_vldoor_t,
RestoreSSThinker,
sizeof(vldoor_t)
},
{
TC_PHASE,
T_Phase,
StreamOut_phase_t,
StreamIn_phase_t,
NULL,
sizeof(phase_t)
},
{
TC_INTERPRET_ACS,
T_InterpretACS,
StreamOut_acs_t,
StreamIn_acs_t,
NULL,
sizeof(acs_t)
},
{
TC_ROTATE_POLY,
T_RotatePoly,
StreamOut_polyevent_t,
StreamIn_polyevent_t,
NULL,
sizeof(polyevent_t)
},
{
TC_BUILD_PILLAR,
T_BuildPillar,
StreamOut_pillar_t,
StreamIn_pillar_t,
RestoreSSThinker,
sizeof(pillar_t)
},
{
TC_MOVE_POLY,
T_MovePoly,
StreamOut_polyevent_t,
StreamIn_polyevent_t,
NULL,
sizeof(polyevent_t)
},
{
TC_POLY_DOOR,
T_PolyDoor,
StreamOut_polydoor_t,
StreamIn_polydoor_t,
NULL,
sizeof(polydoor_t)
},
{
TC_FLOOR_WAGGLE,
T_FloorWaggle,
StreamOut_floorWaggle_t,
StreamIn_floorWaggle_t,
RestoreSSThinker,
sizeof(floorWaggle_t)
},
{ TC_NULL, NULL, NULL, NULL, NULL, 0},
};
//==========================================================================
//
// ArchiveThinkers
//
//==========================================================================
static void ArchiveThinkers(void)
{
thinker_t *thinker;
thinkInfo_t *info;
SV_WriteLong(ASEG_THINKERS);
for (thinker = thinkercap.next; thinker != &thinkercap;
thinker = thinker->next)
{
for (info = ThinkerInfo; info->tClass != TC_NULL; info++)
{
if (thinker->function == info->thinkerFunc)
{
SV_WriteByte(info->tClass);
info->writeFunc(thinker);
break;
}
}
}
// Add a termination marker
SV_WriteByte(TC_NULL);
}
//==========================================================================
//
// UnarchiveThinkers
//
//==========================================================================
static void UnarchiveThinkers(void)
{
int tClass;
thinker_t *thinker;
thinkInfo_t *info;
AssertSegment(ASEG_THINKERS);
while ((tClass = SV_ReadByte()) != TC_NULL)
{
for (info = ThinkerInfo; info->tClass != TC_NULL; info++)
{
if (tClass == info->tClass)
{
thinker = Z_Malloc(info->size, PU_LEVEL, NULL);
info->readFunc(thinker);
thinker->function = info->thinkerFunc;
if (info->restoreFunc)
{
info->restoreFunc(thinker);
}
P_AddThinker(thinker);
break;
}
}
if (info->tClass == TC_NULL)
{
I_Error("UnarchiveThinkers: Unknown tClass %d in "
"savegame", tClass);
}
}
}
//==========================================================================
//
// RestoreSSThinker
//
//==========================================================================
static void RestoreSSThinker(ssthinker_t *sst)
{
sst->sector->specialdata = sst->thinker.function;
}
//==========================================================================
//
// RestorePlatRaise
//
//==========================================================================
static void RestorePlatRaise(plat_t *plat)
{
plat->sector->specialdata = T_PlatRaise;
P_AddActivePlat(plat);
}
//==========================================================================
//
// RestoreMoveCeiling
//
//==========================================================================
static void RestoreMoveCeiling(ceiling_t *ceiling)
{
ceiling->sector->specialdata = T_MoveCeiling;
P_AddActiveCeiling(ceiling);
}
//==========================================================================
//
// ArchiveScripts
//
//==========================================================================
static void ArchiveScripts(void)
{
int i;
SV_WriteLong(ASEG_SCRIPTS);
for (i = 0; i < ACScriptCount; i++)
{
SV_WriteWord(ACSInfo[i].state);
SV_WriteWord(ACSInfo[i].waitValue);
}
for (i = 0; i< MAX_ACS_MAP_VARS; ++i)
{
SV_WriteLong(MapVars[i]);
}
}
//==========================================================================
//
// UnarchiveScripts
//
//==========================================================================
static void UnarchiveScripts(void)
{
int i;
AssertSegment(ASEG_SCRIPTS);
for (i = 0; i < ACScriptCount; i++)
{
ACSInfo[i].state = SV_ReadWord();
ACSInfo[i].waitValue = SV_ReadWord();
}
for (i = 0; i < MAX_ACS_MAP_VARS; ++i)
{
MapVars[i] = SV_ReadLong();
}
}
//==========================================================================
//
// ArchiveMisc
//
//==========================================================================
static void ArchiveMisc(void)
{
int ix;
SV_WriteLong(ASEG_MISC);
for (ix = 0; ix < maxplayers; ix++)
{
SV_WriteLong(localQuakeHappening[ix]);
}
}
//==========================================================================
//
// UnarchiveMisc
//
//==========================================================================
static void UnarchiveMisc(void)
{
int ix;
AssertSegment(ASEG_MISC);
for (ix = 0; ix < maxplayers; ix++)
{
localQuakeHappening[ix] = SV_ReadLong();
}
}
//==========================================================================
//
// RemoveAllThinkers
//
//==========================================================================
static void RemoveAllThinkers(void)
{
thinker_t *thinker;
thinker_t *nextThinker;
thinker = thinkercap.next;
while (thinker != &thinkercap)
{
nextThinker = thinker->next;
if (thinker->function == P_MobjThinker)
{
P_RemoveMobj((mobj_t *) thinker);
}
else
{
Z_Free(thinker);
}
thinker = nextThinker;
}
P_InitThinkers();
}
//==========================================================================
//
// ArchiveSounds
//
//==========================================================================
static void ArchiveSounds(void)
{
seqnode_t *node;
sector_t *sec;
int difference;
int i;
SV_WriteLong(ASEG_SOUNDS);
// Save the sound sequences
SV_WriteLong(ActiveSequences);
for (node = SequenceListHead; node; node = node->next)
{
SV_WriteLong(node->sequence);
SV_WriteLong(node->delayTics);
SV_WriteLong(node->volume);
SV_WriteLong(SN_GetSequenceOffset(node->sequence,
node->sequencePtr));
SV_WriteLong(node->currentSoundID);
for (i = 0; i < po_NumPolyobjs; i++)
{
if (node->mobj == (mobj_t *) & polyobjs[i].startSpot)
{
break;
}
}
if (i == po_NumPolyobjs)
{ // Sound is attached to a sector, not a polyobj
sec = R_PointInSubsector(node->mobj->x, node->mobj->y)->sector;
difference = (int) ((byte *) sec
- (byte *) & sectors[0]) / sizeof(sector_t);
SV_WriteLong(0); // 0 -- sector sound origin
}
else
{
SV_WriteLong(1); // 1 -- polyobj sound origin
difference = i;
}
SV_WriteLong(difference);
}
}
//==========================================================================
//
// UnarchiveSounds
//
//==========================================================================
static void UnarchiveSounds(void)
{
int i;
int numSequences;
int sequence;
int delayTics;
int volume;
int seqOffset;
int soundID;
int polySnd;
int secNum;
mobj_t *sndMobj;
AssertSegment(ASEG_SOUNDS);
// Reload and restart all sound sequences
numSequences = SV_ReadLong();
i = 0;
while (i < numSequences)
{
sequence = SV_ReadLong();
delayTics = SV_ReadLong();
volume = SV_ReadLong();
seqOffset = SV_ReadLong();
soundID = SV_ReadLong();
polySnd = SV_ReadLong();
secNum = SV_ReadLong();
if (!polySnd)
{
sndMobj = (mobj_t *) & sectors[secNum].soundorg;
}
else
{
sndMobj = (mobj_t *) & polyobjs[secNum].startSpot;
}
SN_StartSequence(sndMobj, sequence);
SN_ChangeNodeData(i, seqOffset, delayTics, volume, soundID);
i++;
}
}
//==========================================================================
//
// ArchivePolyobjs
//
//==========================================================================
static void ArchivePolyobjs(void)
{
int i;
SV_WriteLong(ASEG_POLYOBJS);
SV_WriteLong(po_NumPolyobjs);
for (i = 0; i < po_NumPolyobjs; i++)
{
SV_WriteLong(polyobjs[i].tag);
SV_WriteLong(polyobjs[i].angle);
SV_WriteLong(polyobjs[i].startSpot.x);
SV_WriteLong(polyobjs[i].startSpot.y);
}
}
//==========================================================================
//
// UnarchivePolyobjs
//
//==========================================================================
static void UnarchivePolyobjs(void)
{
int i;
fixed_t deltaX;
fixed_t deltaY;
AssertSegment(ASEG_POLYOBJS);
if (SV_ReadLong() != po_NumPolyobjs)
{
I_Error("UnarchivePolyobjs: Bad polyobj count");
}
for (i = 0; i < po_NumPolyobjs; i++)
{
if (SV_ReadLong() != polyobjs[i].tag)
{
I_Error("UnarchivePolyobjs: Invalid polyobj tag");
}
PO_RotatePolyobj(polyobjs[i].tag, (angle_t) SV_ReadLong());
deltaX = SV_ReadLong() - polyobjs[i].startSpot.x;
deltaY = SV_ReadLong() - polyobjs[i].startSpot.y;
PO_MovePolyobj(polyobjs[i].tag, deltaX, deltaY);
}
}
//==========================================================================
//
// AssertSegment
//
//==========================================================================
static void AssertSegment(gameArchiveSegment_t segType)
{
if (SV_ReadLong() != segType)
{
I_Error("Corrupt save game: Segment [%d] failed alignment check",
segType);
}
}
//==========================================================================
//
// ClearSaveSlot
//
// Deletes all save game files associated with a slot number.
//
//==========================================================================
static void ClearSaveSlot(int slot)
{
int i;
char fileName[100];
for (i = 0; i < MAX_MAPS; i++)
{
M_snprintf(fileName, sizeof(fileName),
"%shex%d%02d.hxs", SavePath, slot, i);
remove(fileName);
}
M_snprintf(fileName, sizeof(fileName), "%shex%d.hxs", SavePath, slot);
remove(fileName);
}
//==========================================================================
//
// CopySaveSlot
//
// Copies all the save game files from one slot to another.
//
//==========================================================================
static void CopySaveSlot(int sourceSlot, int destSlot)
{
int i;
char sourceName[100];
char destName[100];
for (i = 0; i < MAX_MAPS; i++)
{
M_snprintf(sourceName, sizeof(sourceName),
"%shex%d%02d.hxs", SavePath, sourceSlot, i);
if (ExistingFile(sourceName))
{
M_snprintf(destName, sizeof(destName),
"%shex%d%02d.hxs", SavePath, destSlot, i);
CopyFile(sourceName, destName);
}
}
M_snprintf(sourceName, sizeof(sourceName),
"%shex%d.hxs", SavePath, sourceSlot);
if (ExistingFile(sourceName))
{
M_snprintf(destName, sizeof(destName),
"%shex%d.hxs", SavePath, destSlot);
CopyFile(sourceName, destName);
}
else
{
I_Error("Could not load savegame %s", sourceName);
}
}
//==========================================================================
//
// CopyFile
//
// This function was rewritten to copy files with minimal strain on zone
// allocation and allow for big maps that technically work in vanilla to
// save without error.
//==========================================================================
static void CopyFile(char *source_name, char *dest_name)
{
const int BUFFER_CHUNK_SIZE = 0x10000;
byte *buffer;
int file_length, file_remaining;
FILE *read_handle, *write_handle;
int buf_count, read_count, write_count;
read_handle = fopen(source_name, "rb");
if (read_handle == NULL)
{
I_Error ("Couldn't read file %s", source_name);
}
file_length = file_remaining = M_FileLength(read_handle);
// Vanilla savegame emulation.
//
// CopyFile() typically calls M_ReadFile() which stores the entire file
// in memory: Chocolate Hexen should force an allocation error here
// whenever it's appropriate.
if (vanilla_savegame_limit)
{
buffer = Z_Malloc(file_length, PU_STATIC, NULL);
Z_Free(buffer);
}
write_handle = fopen(dest_name, "wb");
if (write_handle == NULL)
{
I_Error ("Couldn't read file %s", dest_name);
}
buffer = Z_Malloc (BUFFER_CHUNK_SIZE, PU_STATIC, NULL);
do
{
buf_count = BUFFER_CHUNK_SIZE;
if( file_remaining < BUFFER_CHUNK_SIZE)
{
buf_count = file_remaining;
}
read_count = fread(buffer, 1, buf_count, read_handle);
if (read_count < buf_count)
{
I_Error ("Couldn't read file %s", source_name);
}
write_count = fwrite(buffer, 1, buf_count, write_handle);
if (write_count < buf_count)
{
I_Error ("Couldn't write to file %s", dest_name);
}
file_remaining -= buf_count;
} while (file_remaining > 0);
Z_Free(buffer);
fclose(read_handle);
fclose(write_handle);
}
//==========================================================================
//
// ExistingFile
//
//==========================================================================
static boolean ExistingFile(char *name)
{
FILE *fp;
if ((fp = fopen(name, "rb")) != NULL)
{
fclose(fp);
return true;
}
else
{
return false;
}
}
//==========================================================================
//
// SV_Open
//
//==========================================================================
static void SV_OpenRead(char *fileName)
{
SavingFP = fopen(fileName, "rb");
// Should never happen, only if hex6.hxs cannot ever be created.
if (SavingFP == NULL)
{
I_Error("Could not load savegame %s", fileName);
}
}
static void SV_OpenWrite(char *fileName)
{
SavingFP = fopen(fileName, "wb");
}
//==========================================================================
//
// SV_Close
//
//==========================================================================
static void SV_Close(void)
{
if (SavingFP)
{
fclose(SavingFP);
}
}
//==========================================================================
//
// SV_Read
//
//==========================================================================
static void SV_Read(void *buffer, int size)
{
int retval = fread(buffer, 1, size, SavingFP);
if (retval != size)
{
I_Error("Incomplete read in SV_Read: Expected %d, got %d bytes",
size, retval);
}
}
static byte SV_ReadByte(void)
{
byte result;
SV_Read(&result, sizeof(byte));
return result;
}
static uint16_t SV_ReadWord(void)
{
uint16_t result;
SV_Read(&result, sizeof(unsigned short));
return SHORT(result);
}
static uint32_t SV_ReadLong(void)
{
uint32_t result;
SV_Read(&result, sizeof(int));
return LONG(result);
}
static void *SV_ReadPtr(void)
{
return (void *) (intptr_t) SV_ReadLong();
}
//==========================================================================
//
// SV_Write
//
//==========================================================================
static void SV_Write(const void *buffer, int size)
{
fwrite(buffer, size, 1, SavingFP);
}
static void SV_WriteByte(byte val)
{
fwrite(&val, sizeof(byte), 1, SavingFP);
}
static void SV_WriteWord(unsigned short val)
{
val = SHORT(val);
fwrite(&val, sizeof(unsigned short), 1, SavingFP);
}
static void SV_WriteLong(unsigned int val)
{
val = LONG(val);
fwrite(&val, sizeof(int), 1, SavingFP);
}
static void SV_WritePtr(void *val)
{
long ptr;
// Write a pointer value. In Vanilla Hexen pointers are 32-bit but
// nowadays they might be larger. Whatever value we write here isn't
// going to be much use when we reload the game.
ptr = (long)(intptr_t) val;
SV_WriteLong((unsigned int) (ptr & 0xffffffff));
}