ref: 58bf0e9f39e85fbd3238458bf18403c41275ab7b
dir: /sv_save.c/
//************************************************************************** //** //** sv_save.c : Heretic 2 : Raven Software, Corp. //** //** $Revision: 522 $ //** $Date: 2009-06-08 11:45:17 +0300 (Mon, 08 Jun 2009) $ //** //** Games are always saved Little Endian, with 32 bit offsets. //** The saved games then can be properly read on 64 bit and/or //** Big Endian machines all the same. //** See the file SAVEGAME for notes and/or issues. //** //************************************************************************** // HEADER FILES ------------------------------------------------------------ #include "h2stdinc.h" #include "h2def.h" #include "p_local.h" #include "sv_save.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 (*mangleFunc)(void *, void *); void (*restoreFunc)(void *, void *); size_t realsize, savesize; } 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 void MangleMobj(mobj_t *mobj, save_mobj_t *temp); static void RestoreMobj(mobj_t *mobj, save_mobj_t *temp); static int32_t GetMobjNum(mobj_t *mobj); static mobj_t *GetMobjPtr(int32_t archiveNum, intptr_t *target); static void MangleFloorMove(void *arg1, void *arg2); static void RestoreFloorMove(void *arg1, void *arg2); static void MangleLight(void *arg1, void *arg2); static void RestoreLight(void *arg1, void *arg2); static void MangleVerticalDoor(void *arg1, void *arg2); static void RestoreVerticalDoor(void *arg1, void *arg2); static void ManglePhase(void *arg1, void *arg2); static void RestorePhase(void *arg1, void *arg2); static void ManglePillar(void *arg1, void *arg2); static void RestorePillar(void *arg1, void *arg2); static void MangleFloorWaggle(void *arg1, void *arg2); static void RestoreFloorWaggle(void *arg1, void *arg2); static void ManglePolyEvent(void *arg1, void *arg2); static void RestorePolyEvent(void *arg1, void *arg2); static void ManglePolyDoor(void *arg1, void *arg2); static void RestorePolyDoor(void *arg1, void *arg2); static void MangleScript(void *arg1, void *arg2); static void RestoreScript(void *arg1, void *arg2); static void ManglePlatRaise(void *arg1, void *arg2); static void RestorePlatRaise(void *arg1, void *arg2); static void MangleMoveCeiling(void *arg1, void *arg2); static void RestoreMoveCeiling(void *arg1, void *arg2); static void AssertSegment(gameArchiveSegment_t segType); static void ClearSaveSlot(int slot); static void CopySaveSlot(int sourceSlot, int destSlot); static void CopyFile(const char *sourceName, const char *destName); static boolean ExistingFile(const char *name); static void OpenStreamOut(const char *fileName); static void CloseStreamOut(void); static void StreamOutBuffer(const void *buffer, size_t size); static void StreamOutByte(byte val); static void StreamOutWord(uint16_t val); static void StreamOutLong(uint32_t val); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern int ACScriptCount; extern byte *ActionCodeBase; extern acsInfo_t *ACSInfo; // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int MobjCount; static mobj_t **MobjList; static intptr_t **TargetPlayerAddrs; static int TargetPlayerCount; static void *SaveBuffer; static boolean SavingPlayers; static byte *SavePtr; static FILE *SavingFP; // This list has been prioritized using frequency estimates static thinkInfo_t ThinkerInfo[] = { { TC_MOVE_FLOOR, T_MoveFloor, MangleFloorMove, RestoreFloorMove, sizeof(floormove_t), sizeof(save_floormove_t) }, { TC_PLAT_RAISE, T_PlatRaise, ManglePlatRaise, RestorePlatRaise, sizeof(plat_t), sizeof(save_plat_t) }, { TC_MOVE_CEILING, T_MoveCeiling, MangleMoveCeiling, RestoreMoveCeiling, sizeof(ceiling_t), sizeof(save_ceiling_t) }, { TC_LIGHT, T_Light, MangleLight, RestoreLight, sizeof(light_t), sizeof(save_light_t) }, { TC_VERTICAL_DOOR, T_VerticalDoor, MangleVerticalDoor, RestoreVerticalDoor, sizeof(vldoor_t), sizeof(save_vldoor_t) }, { TC_PHASE, T_Phase, ManglePhase, RestorePhase, sizeof(phase_t), sizeof(save_phase_t) }, { TC_INTERPRET_ACS, T_InterpretACS, MangleScript, RestoreScript, sizeof(acs_t), sizeof(save_acs_t) }, { TC_ROTATE_POLY, T_RotatePoly, ManglePolyEvent, RestorePolyEvent, sizeof(polyevent_t), sizeof(save_polyevent_t) }, { TC_BUILD_PILLAR, T_BuildPillar, ManglePillar, RestorePillar, sizeof(pillar_t), sizeof(save_pillar_t) }, { TC_MOVE_POLY, T_MovePoly, ManglePolyEvent, RestorePolyEvent, sizeof(polyevent_t), sizeof(save_polyevent_t) }, { TC_POLY_DOOR, T_PolyDoor, ManglePolyDoor, RestorePolyDoor, sizeof(polydoor_t), sizeof(save_polydoor_t) }, { TC_FLOOR_WAGGLE, T_FloorWaggle, MangleFloorWaggle, RestoreFloorWaggle, sizeof(floorWaggle_t), sizeof(save_floorWaggle_t) }, { // Terminator TC_NULL, NULL, NULL, NULL, 0 } }; // CODE -------------------------------------------------------------------- static byte GET_BYTE (void) { return *SavePtr++; } static int16_t GET_WORD (void) { uint16_t val = READ_INT16(SavePtr); INCR_INT16(SavePtr); return (int16_t) val; } static int32_t GET_LONG (void) { uint32_t val = READ_INT32(SavePtr); INCR_INT32(SavePtr); return (int32_t) val; } //========================================================================== // // SV_SaveGame // //========================================================================== void SV_SaveGame(int slot, const char *description) { int i; char fileName[MAX_OSPATH]; char versionText[HXS_VERSION_TEXT_LENGTH]; // Open the output file snprintf(fileName, sizeof(fileName), "%shex6.hxs", basePath); OpenStreamOut(fileName); // Write game save description StreamOutBuffer(description, HXS_DESCRIPTION_LENGTH); // Write version info memset(versionText, 0, HXS_VERSION_TEXT_LENGTH); strcpy(versionText, HXS_VERSION_TEXT); StreamOutBuffer(versionText, HXS_VERSION_TEXT_LENGTH); // Place a header marker StreamOutLong(ASEG_GAME_HEADER); // Write current map and difficulty StreamOutByte(gamemap); StreamOutByte(gameskill); // Write global script info for (i = 0; i < MAX_ACS_WORLD_VARS; i++) { StreamOutLong(WorldVars[i]); } for (i = 0; i <= MAX_ACS_STORE; i++) { StreamOutLong(ACSStore[i].map); StreamOutLong(ACSStore[i].script); StreamOutBuffer(ACSStore[i].args, 4); } ArchivePlayers(); // Place a termination marker StreamOutLong(ASEG_END); // Close the output file CloseStreamOut(); // 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[MAX_OSPATH]; SavingPlayers = savePlayers; // Open the output file snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", basePath, gamemap); OpenStreamOut(fileName); // Place a header marker StreamOutLong(ASEG_MAP_HEADER); // Write the level timer StreamOutLong(leveltime); // Set the mobj archive numbers SetMobjArchiveNums(); ArchiveWorld(); ArchivePolyobjs(); ArchiveMobjs(); ArchiveThinkers(); ArchiveScripts(); ArchiveSounds(); ArchiveMisc(); // Place a termination marker StreamOutLong(ASEG_END); // Close the output file CloseStreamOut(); } //========================================================================== // // SV_LoadGame // //========================================================================== void SV_LoadGame(int slot) { int i; char fileName[MAX_OSPATH]; 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 snprintf(fileName, sizeof(fileName), "%shex6.hxs", basePath); // Load the file M_ReadFile(fileName, &SaveBuffer); // Set the save pointer and skip the description field SavePtr = (byte *)SaveBuffer + HXS_DESCRIPTION_LENGTH; // Check the version text if (strcmp((char *)SavePtr, HXS_VERSION_TEXT)) { // Bad version return; } SavePtr += HXS_VERSION_TEXT_LENGTH; AssertSegment(ASEG_GAME_HEADER); gameepisode = 1; gamemap = GET_BYTE(); gameskill = GET_BYTE(); // Read global script info memcpy(WorldVars, SavePtr, sizeof(WorldVars)); SavePtr += sizeof(WorldVars); memcpy(ACSStore, SavePtr, sizeof(ACSStore)); SavePtr += sizeof(ACSStore); for (i = 0; i < MAX_ACS_WORLD_VARS; i++) { WorldVars[i] = (int) LONG(WorldVars[i]); } for (i = 0; i <= MAX_ACS_STORE; i++) { ACSStore[i].map = (int) LONG(ACSStore[i].map); ACSStore[i].script = (int) LONG(ACSStore[i].script); } // Read the player structures UnarchivePlayers(); AssertSegment(ASEG_END); Z_Free(SaveBuffer); // Save player structs for (i = 0; i < MAXPLAYERS; i++) { playerBackup[i] = players[i]; } // Load the current map SV_LoadMap(); // Don't need the player mobj relocation info for load game Z_Free(TargetPlayerAddrs); TargetPlayerAddrs = NULL; // 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[MAX_OSPATH]; player_t playerBackup[MAXPLAYERS]; mobj_t *mobj; mobj_t *targetPlayerMobj; int inventoryPtr; int currentInvPos; boolean rClass; boolean playerWasReborn; boolean oldWeaponowned[NUMWEAPONS]; int oldKeys = 0; /* jim added initialiser */ int oldPieces = 0; /* jim added initialiser */ 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; gamemap = map; snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", basePath, 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] = (intptr_t)targetPlayerMobj; } Z_Free(TargetPlayerAddrs); TargetPlayerAddrs = NULL; } // 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[MAX_OSPATH]; snprintf(fileName, sizeof(fileName), "%shex%d.hxs", basePath, REBORN_SLOT); return ExistingFile(fileName); } //========================================================================== // // SV_LoadMap // //========================================================================== void SV_LoadMap(void) { char fileName[MAX_OSPATH]; // Load a base level G_InitNew(gameskill, gameepisode, gamemap); // Remove all thinkers RemoveAllThinkers(); // Create the name snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", basePath, gamemap); // Load the file M_ReadFile(fileName, &SaveBuffer); SavePtr = (byte *) SaveBuffer; AssertSegment(ASEG_MAP_HEADER); // Read the level timer leveltime = GET_LONG(); UnarchiveWorld(); UnarchivePolyobjs(); UnarchiveMobjs(); UnarchiveThinkers(); UnarchiveScripts(); UnarchiveSounds(); UnarchiveMisc(); AssertSegment(ASEG_END); // Free mobj list and save buffer Z_Free(MobjList); Z_Free(SaveBuffer); } //========================================================================== // // SV_InitBaseSlot // //========================================================================== void SV_InitBaseSlot(void) { ClearSaveSlot(BASE_SLOT); } //========================================================================== // // ArchivePlayers // //========================================================================== static void ArchivePlayers(void) { int i, j; save_player_t savp; player_t *p; StreamOutLong(ASEG_PLAYERS); for (i = 0; i < MAXPLAYERS; i++) { StreamOutByte(playeringame[i]); } for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) { continue; } StreamOutByte(PlayerClasses[i]); p = &players[i]; savp.mo_idx = 0; savp.poisoner_idx = 0; savp.attacker_idx = 0; savp.playerstate = (int) LONG(p->playerstate); savp.cmd.forwardmove = p->cmd.forwardmove; savp.cmd.sidemove = p->cmd.sidemove; savp.cmd.angleturn = (short) SHORT(p->cmd.angleturn); savp.cmd.consistancy = (short) SHORT(p->cmd.consistancy); savp.cmd.chatchar = p->cmd.chatchar; savp.cmd.buttons = p->cmd.buttons; savp.cmd.lookfly = p->cmd.lookfly; savp.cmd.arti = p->cmd.arti; savp.playerclass = (int) LONG(p->playerclass); savp.viewz = (fixed_t) LONG(p->viewz); savp.viewheight = (fixed_t) LONG(p->viewheight); savp.deltaviewheight = (fixed_t) LONG(p->deltaviewheight); savp.bob = (fixed_t) LONG(p->bob); savp.flyheight = (int) LONG(p->flyheight); savp.lookdir = (int) LONG(p->lookdir); savp.centering = (int) LONG(p->centering); savp.health = (int) LONG(p->health); for (j = 0; j < NUMARMOR; j++) { savp.armorpoints[j] = (int) LONG(p->armorpoints[j]); } for (j = 0; j < NUMINVENTORYSLOTS; j++) { savp.inventory[j].type = (int) LONG(p->inventory[j].type); savp.inventory[j].count = (int) LONG(p->inventory[j].count); } savp.readyArtifact = (int) LONG(p->readyArtifact); savp.inventorySlotNum = (int) LONG(p->inventorySlotNum); savp.artifactCount = (int) LONG(p->artifactCount); for (j = 0; j < NUMPOWERS; j++) { savp.powers[j] = (int) LONG(p->powers[j]); } savp.keys = (int) LONG(p->keys); savp.pieces = (int) LONG(p->pieces); for (j = 0; j < MAXPLAYERS; j++) { savp.frags[j] = (signed int) LONG(p->frags[j]); } savp.readyweapon = (int) LONG(p->readyweapon); savp.pendingweapon = (int) LONG(p->pendingweapon); for (j = 0; j < NUMWEAPONS; j++) { savp.weaponowned[j] = (int) LONG(p->weaponowned[j]); } for (j = 0; j < NUMMANA; j++) { savp.mana[j] = (int) LONG(p->mana[j]); } savp.attackdown = (int) LONG(p->attackdown); savp.usedown = (int) LONG(p->usedown); savp.cheats = (int) LONG(p->cheats); savp.refire = (int) LONG(p->refire); savp.killcount = (int) LONG(p->killcount); savp.itemcount = (int) LONG(p->itemcount); savp.secretcount = (int) LONG(p->secretcount); memcpy (savp.message, p->message, 80); savp.messageTics = (int) LONG(p->messageTics); savp.ultimateMessage = (short) SHORT(p->ultimateMessage); savp.yellowMessage = (short) SHORT(p->yellowMessage); savp.damagecount = (int) LONG(p->damagecount); savp.bonuscount = (int) LONG(p->bonuscount); savp.poisoncount = (int) LONG(p->poisoncount); savp.extralight = (int) LONG(p->extralight); savp.fixedcolormap = (int) LONG(p->fixedcolormap); savp.colormap = (int) LONG(p->colormap); savp.morphTics = (int) LONG(p->morphTics); savp.jumpTics = (unsigned int) LONG(p->jumpTics); savp.worldTimer = (unsigned int) LONG(p->worldTimer); for (j = 0; j < NUMPSPRITES; j++) { savp.psprites[j].tics = (int) LONG(p->psprites[j].tics); savp.psprites[j].sx = (fixed_t) LONG(p->psprites[j].sx); savp.psprites[j].sy = (fixed_t) LONG(p->psprites[j].sy); if (p->psprites[j].state) { savp.psprites[j].state_idx = LONG((int32_t) (p->psprites[j].state - states)); } else { savp.psprites[j].state_idx = 0; } } StreamOutBuffer(&savp, sizeof(save_player_t)); } } //========================================================================== // // UnarchivePlayers // //========================================================================== static void UnarchivePlayers(void) { int i, j; save_player_t savp; player_t *p; AssertSegment(ASEG_PLAYERS); for (i = 0; i < MAXPLAYERS; i++) { playeringame[i] = GET_BYTE(); } for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) { continue; } PlayerClasses[i] = GET_BYTE(); memcpy(&savp, SavePtr, sizeof(save_player_t)); SavePtr += sizeof(save_player_t); p = &players[i]; p->mo = NULL; // Will be set when unarc thinker p->attacker = NULL; p->poisoner = NULL; p->playerstate = (playerstate_t) LONG(savp.playerstate); p->cmd.forwardmove = savp.cmd.forwardmove; p->cmd.sidemove = savp.cmd.sidemove; p->cmd.angleturn = (short) SHORT(savp.cmd.angleturn); p->cmd.consistancy = (short) SHORT(savp.cmd.consistancy); p->cmd.chatchar = savp.cmd.chatchar; p->cmd.buttons = savp.cmd.buttons; p->cmd.lookfly = savp.cmd.lookfly; p->cmd.arti = savp.cmd.arti; p->playerclass = (pclass_t) LONG(savp.playerclass); p->viewz = (fixed_t) LONG(savp.viewz); p->viewheight = (fixed_t) LONG(savp.viewheight); p->deltaviewheight = (fixed_t) LONG(savp.deltaviewheight); p->bob = (fixed_t) LONG(savp.bob); p->flyheight = (int) LONG(savp.flyheight); p->lookdir = (int) LONG(savp.lookdir); p->centering = !!(LONG(savp.centering)); p->health = (int) LONG(savp.health); for (j = 0; j < NUMARMOR; j++) { p->armorpoints[j] = (int) LONG(savp.armorpoints[j]); } for (j = 0; j < NUMINVENTORYSLOTS; j++) { p->inventory[j].type = (int) LONG(savp.inventory[j].type); p->inventory[j].count = (int) LONG(savp.inventory[j].count); } p->readyArtifact = (artitype_t) LONG(savp.readyArtifact); p->artifactCount = (int) LONG(savp.artifactCount); p->inventorySlotNum = (int) LONG(savp.inventorySlotNum); for (j = 0; j < NUMPOWERS; j++) { p->powers[j] = (int) LONG(savp.powers[j]); } p->keys = (int) LONG(savp.keys); p->pieces = (int) LONG(savp.pieces); for (j = 0; j < MAXPLAYERS; j++) { p->frags[j] = (signed int) LONG(savp.frags[j]); } p->readyweapon = (weapontype_t) LONG(savp.readyweapon); p->pendingweapon = (weapontype_t) LONG(savp.pendingweapon); for (j = 0; j < NUMWEAPONS; j++) { p->weaponowned[j] = !!(LONG(savp.weaponowned[j])); } for (j = 0; j < NUMMANA; j++) { p->mana[j] = (int) LONG(savp.mana[j]); } p->attackdown = (int) LONG(savp.attackdown); p->usedown = (int) LONG(savp.usedown); p->cheats = (int) LONG(savp.cheats); p->refire = (int) LONG(savp.refire); p->killcount = (int) LONG(savp.killcount); p->itemcount = (int) LONG(savp.itemcount); p->secretcount = (int) LONG(savp.secretcount); memcpy (p->message, savp.message, 80); p->messageTics = (int) LONG(savp.messageTics); p->ultimateMessage = (short) SHORT(savp.ultimateMessage); p->yellowMessage = (short) SHORT(savp.yellowMessage); p->damagecount = (int) LONG(savp.damagecount); p->bonuscount = (int) LONG(savp.bonuscount); p->poisoncount = (int) LONG(savp.poisoncount); p->extralight = (int) LONG(savp.extralight); p->fixedcolormap = (int) LONG(savp.fixedcolormap); p->colormap = (int) LONG(savp.colormap); p->morphTics = (int) LONG(savp.morphTics); p->jumpTics = (unsigned int) LONG(savp.jumpTics); p->worldTimer = (unsigned int) LONG(savp.worldTimer); P_ClearMessage(p); for (j = 0; j < NUMPSPRITES; j++) { p->psprites[j].tics = (int) LONG(savp.psprites[j].tics); p->psprites[j].sx = (fixed_t) LONG(savp.psprites[j].sx); p->psprites[j].sy = (fixed_t) LONG(savp.psprites[j].sy); if (savp.psprites[j].state_idx) { savp.psprites[j].state_idx = LONG(savp.psprites[j].state_idx); p->psprites[j].state = &states[savp.psprites[j].state_idx]; } else { p->psprites[j].state = NULL; } } } } //========================================================================== // // ArchiveWorld // //========================================================================== static void ArchiveWorld(void) { int i; int j; sector_t *sec; line_t *li; side_t *si; StreamOutLong(ASEG_WORLD); for (i = 0, sec = sectors; i < numsectors; i++, sec++) { StreamOutWord(sec->floorheight>>FRACBITS); StreamOutWord(sec->ceilingheight>>FRACBITS); StreamOutWord(sec->floorpic); StreamOutWord(sec->ceilingpic); StreamOutWord(sec->lightlevel); StreamOutWord(sec->special); StreamOutWord(sec->tag); StreamOutWord(sec->seqType); } for (i = 0, li = lines; i < numlines; i++, li++) { StreamOutWord(li->flags); StreamOutByte(li->special); StreamOutByte(li->arg1); StreamOutByte(li->arg2); StreamOutByte(li->arg3); StreamOutByte(li->arg4); StreamOutByte(li->arg5); for (j = 0; j < 2; j++) { if (li->sidenum[j] == -1) { continue; } si = &sides[li->sidenum[j]]; StreamOutWord(si->textureoffset>>FRACBITS); StreamOutWord(si->rowoffset>>FRACBITS); StreamOutWord(si->toptexture); StreamOutWord(si->bottomtexture); StreamOutWord(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 = ((fixed_t) GET_WORD())<<FRACBITS; sec->ceilingheight = ((fixed_t) GET_WORD())<<FRACBITS; sec->floorpic = GET_WORD(); sec->ceilingpic = GET_WORD(); sec->lightlevel = GET_WORD(); sec->special = GET_WORD(); sec->tag = GET_WORD(); sec->seqType = GET_WORD(); sec->specialdata = NULL; sec->soundtarget = NULL; } for (i = 0, li = lines; i < numlines; i++, li++) { li->flags = GET_WORD(); li->special = GET_BYTE(); li->arg1 = GET_BYTE(); li->arg2 = GET_BYTE(); li->arg3 = GET_BYTE(); li->arg4 = GET_BYTE(); li->arg5 = GET_BYTE(); for (j = 0; j < 2; j++) { if (li->sidenum[j] == -1) { continue; } si = &sides[li->sidenum[j]]; si->textureoffset = ((fixed_t) GET_WORD())<<FRACBITS; si->rowoffset = ((fixed_t) GET_WORD())<<FRACBITS; si->toptexture = GET_WORD(); si->bottomtexture = GET_WORD(); si->midtexture = GET_WORD(); } } } //========================================================================== // // 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; save_mobj_t tempMobj; mobj_t *mobj; StreamOutLong(ASEG_MOBJS); StreamOutLong(MobjCount); count = 0; for (thinker = thinkercap.next; thinker != &thinkercap; thinker = thinker->next) { if (thinker->function != P_MobjThinker) { // Not a mobj thinker continue; } mobj = (mobj_t *)thinker; if (mobj->player && !SavingPlayers) { // Skipping player mobjs continue; } count++; memset(&tempMobj, 0, sizeof(save_mobj_t)); MangleMobj(mobj, &tempMobj); StreamOutBuffer(&tempMobj, sizeof(save_mobj_t)); } if (count != MobjCount) { I_Error("ArchiveMobjs: bad mobj count"); } } //========================================================================== // // UnarchiveMobjs // //========================================================================== static void UnarchiveMobjs(void) { int i; save_mobj_t tempMobj; mobj_t *mobj; AssertSegment(ASEG_MOBJS); TargetPlayerAddrs = (intptr_t **) Z_Malloc(MAX_TARGET_PLAYERS*sizeof(intptr_t *), PU_STATIC, NULL); TargetPlayerCount = 0; MobjCount = GET_LONG(); MobjList = (mobj_t **) Z_Malloc(MobjCount*sizeof(mobj_t *), PU_STATIC, NULL); for (i = 0; i < MobjCount; i++) { MobjList[i] = (mobj_t *) Z_Malloc(sizeof(mobj_t), PU_LEVEL, NULL); } for (i = 0; i < MobjCount; i++) { mobj = MobjList[i]; memset(&tempMobj, 0, sizeof(save_mobj_t)); memset(mobj, 0, sizeof(mobj_t)); memcpy(&tempMobj, SavePtr, sizeof(save_mobj_t)); SavePtr += sizeof(save_mobj_t); RestoreMobj(mobj, &tempMobj); P_AddThinker(&mobj->thinker); } P_CreateTIDList(); P_InitCreatureCorpseQueue(true); // true = scan for corpses } //========================================================================== // // MangleMobj // //========================================================================== static void MangleMobj(mobj_t *mobj, save_mobj_t *temp) { boolean corpse; temp->x = (fixed_t) LONG(mobj->x); temp->y = (fixed_t) LONG(mobj->y); temp->z = (fixed_t) LONG(mobj->z); temp->angle = (angle_t) LONG(mobj->angle); temp->sprite = (int) LONG(mobj->sprite); temp->frame = (int) LONG(mobj->frame); temp->floorpic = (fixed_t) LONG(mobj->floorpic); temp->radius = (fixed_t) LONG(mobj->radius); temp->height = (fixed_t) LONG(mobj->height); temp->momx = (fixed_t) LONG(mobj->momx); temp->momy = (fixed_t) LONG(mobj->momy); temp->momz = (fixed_t) LONG(mobj->momz); temp->validcount = (int) LONG(mobj->validcount); temp->type = (int) LONG(mobj->type); temp->tics = (int) LONG(mobj->tics); temp->damage = (int) LONG(mobj->damage); temp->flags = (int) LONG(mobj->flags); temp->flags2 = (int) LONG(mobj->flags2); temp->health = (int) LONG(mobj->health); temp->movedir = (int) LONG(mobj->movedir); temp->movecount = (int) LONG(mobj->movecount); temp->reactiontime = (int) LONG(mobj->reactiontime); temp->threshold = (int) LONG(mobj->threshold); temp->lastlook = (int) LONG(mobj->lastlook); temp->floorclip = (fixed_t) LONG(mobj->floorclip); temp->archiveNum = (int) LONG(mobj->archiveNum); temp->tid = (short) SHORT(mobj->tid); temp->special = mobj->special; temp->args[0] = mobj->args[0]; temp->args[1] = mobj->args[1]; temp->args[2] = mobj->args[2]; temp->args[3] = mobj->args[3]; temp->args[4] = mobj->args[4]; corpse = mobj->flags & MF_CORPSE; temp->state_idx = LONG((int32_t)(mobj->state - states)); if (mobj->player) { temp->player_idx = LONG((int32_t)((mobj->player - players) + 1)); } if (corpse) { temp->target_idx = (int32_t) LONG(MOBJ_NULL); } else { temp->target_idx = (int32_t) LONG(GetMobjNum(mobj->target)); } 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) { temp->special1 = (int32_t) LONG(MOBJ_NULL); } else { temp->special1 = (int32_t) LONG(GetMobjNum((mobj_t *)mobj->special1)); } break; // Just special2 case MT_LIGHTNING_FLOOR: case MT_LIGHTNING_ZAP: if (corpse) { temp->special2 = (int32_t) LONG(MOBJ_NULL); } else { temp->special2 = (int32_t) LONG(GetMobjNum((mobj_t *)mobj->special2)); } break; // Both special1 and special2 case MT_HOLY_TAIL: case MT_LIGHTNING_CEILING: if (corpse) { temp->special1 = (int32_t) LONG(MOBJ_NULL); temp->special2 = (int32_t) LONG(MOBJ_NULL); } else { temp->special1 = (int32_t) LONG(GetMobjNum((mobj_t *)mobj->special1)); temp->special2 = (int32_t) LONG(GetMobjNum((mobj_t *)mobj->special2)); } break; // Miscellaneous case MT_KORAX: temp->special1 = 0; // Searching index break; default: break; } } //========================================================================== // // GetMobjNum // //========================================================================== static int32_t GetMobjNum(mobj_t *mobj) { if (mobj == NULL) { return MOBJ_NULL; } if (mobj->player && !SavingPlayers) { return MOBJ_XX_PLAYER; } return mobj->archiveNum; } //========================================================================== // // RestoreMobj // //========================================================================== static void RestoreMobj(mobj_t *mobj, save_mobj_t *temp) { mobj->x = (fixed_t) LONG(temp->x); mobj->y = (fixed_t) LONG(temp->y); mobj->z = (fixed_t) LONG(temp->z); mobj->angle = (angle_t) LONG(temp->angle); mobj->sprite = (spritenum_t) LONG(temp->sprite); mobj->frame = (int) LONG(temp->frame); mobj->floorpic = (fixed_t) LONG(temp->floorpic); mobj->radius = (fixed_t) LONG(temp->radius); mobj->height = (fixed_t) LONG(temp->height); mobj->momx = (fixed_t) LONG(temp->momx); mobj->momy = (fixed_t) LONG(temp->momy); mobj->momz = (fixed_t) LONG(temp->momz); mobj->validcount = (int) LONG(temp->validcount); mobj->type = (mobjtype_t) LONG(temp->type); mobj->tics = (int) LONG(temp->tics); mobj->damage = (int) LONG(temp->damage); mobj->flags = (int) LONG(temp->flags); mobj->flags2 = (int) LONG(temp->flags2); mobj->health = (int) LONG(temp->health); mobj->movedir = (int) LONG(temp->movedir); mobj->movecount = (int) LONG(temp->movecount); mobj->reactiontime = (int) LONG(temp->reactiontime); mobj->threshold = (int) LONG(temp->threshold); mobj->lastlook = (int) LONG(temp->lastlook); mobj->floorclip = (fixed_t) LONG(temp->floorclip); mobj->archiveNum = (int) LONG(temp->archiveNum); mobj->tid = (short) SHORT(temp->tid); mobj->special = temp->special; mobj->args[0] = temp->args[0]; mobj->args[1] = temp->args[1]; mobj->args[2] = temp->args[2]; mobj->args[3] = temp->args[3]; mobj->args[4] = temp->args[4]; temp->state_idx = (int32_t) LONG(temp->state_idx); temp->player_idx = (int32_t) LONG(temp->player_idx); temp->target_idx = (int32_t) LONG(temp->target_idx); temp->special1 = (int32_t) LONG(temp->special1); temp->special2 = (int32_t) LONG(temp->special2); mobj->thinker.function = P_MobjThinker; mobj->state = &states[temp->state_idx]; if (temp->player_idx) { mobj->player = &players[temp->player_idx - 1]; mobj->player->mo = mobj; } P_SetThingPosition(mobj); mobj->info = &mobjinfo[mobj->type]; mobj->floorz = mobj->subsector->sector->floorheight; mobj->ceilingz = mobj->subsector->sector->ceilingheight; mobj->target = GetMobjPtr(temp->target_idx, (intptr_t *)(void *)&mobj->target); 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: mobj->special1 = (intptr_t) GetMobjPtr(temp->special1, &mobj->special1); break; // Just special2 case MT_LIGHTNING_FLOOR: case MT_LIGHTNING_ZAP: mobj->special2 = (intptr_t) GetMobjPtr(temp->special2, &mobj->special2); break; // Both special1 and special2 case MT_HOLY_TAIL: case MT_LIGHTNING_CEILING: mobj->special1 = (intptr_t) GetMobjPtr(temp->special1, &mobj->special1); mobj->special2 = (intptr_t) GetMobjPtr(temp->special2, &mobj->special2); break; default: break; } } //========================================================================== // // GetMobjPtr // //========================================================================== static mobj_t *GetMobjPtr(int32_t archiveNum, intptr_t *target) { if (archiveNum == MOBJ_NULL) { return NULL; } if (archiveNum == MOBJ_XX_PLAYER) { if (TargetPlayerCount == MAX_TARGET_PLAYERS) { I_Error("RestoreMobj: exceeded MAX_TARGET_PLAYERS"); } TargetPlayerAddrs[TargetPlayerCount++] = target; return NULL; } return MobjList[archiveNum]; } //========================================================================== // // ArchiveThinkers // //========================================================================== static void ArchiveThinkers(void) { thinker_t *thinker; thinkInfo_t *info; byte buffer[MAX_THINKER_SIZE]; StreamOutLong(ASEG_THINKERS); for (thinker = thinkercap.next; thinker != &thinkercap; thinker = thinker->next) { for (info = ThinkerInfo; info->tClass != TC_NULL; info++) { if (thinker->function == info->thinkerFunc) { StreamOutByte(info->tClass); memset(buffer, 0, sizeof(buffer)); info->mangleFunc(thinker, buffer); StreamOutBuffer(buffer, info->savesize); break; } } } // Add a termination marker StreamOutByte(TC_NULL); } //========================================================================== // // UnarchiveThinkers // //========================================================================== static void UnarchiveThinkers(void) { int tClass; thinker_t *thinker; thinkInfo_t *info; byte buffer[MAX_THINKER_SIZE]; AssertSegment(ASEG_THINKERS); while ((tClass = GET_BYTE()) != TC_NULL) { for (info = ThinkerInfo; info->tClass != TC_NULL; info++) { if (tClass == info->tClass) { thinker = (thinker_t *) Z_Malloc(info->realsize, PU_LEVEL, NULL); memset(thinker, 0, info->realsize); memset(buffer, 0, sizeof(buffer)); memcpy(buffer, SavePtr, info->savesize); SavePtr += info->savesize; thinker->function = info->thinkerFunc; info->restoreFunc(thinker, buffer); P_AddThinker(thinker); break; } } if (info->tClass == TC_NULL) { I_Error("UnarchiveThinkers: Unknown tClass %d in savegame", tClass); } } } //========================================================================== // // MangleFloorMove // //========================================================================== static void MangleFloorMove(void *arg1, void *arg2) { floormove_t *fm = (floormove_t *) arg1; save_floormove_t *temp = (save_floormove_t *) arg2; temp->sector_idx = LONG((int32_t)(fm->sector - sectors)); temp->type = (int) LONG(fm->type); temp->crush = (int) LONG(fm->crush); temp->direction = (int) LONG(fm->direction); temp->newspecial = (int) LONG(fm->newspecial); temp->texture = (short) SHORT(fm->texture); temp->floordestheight = (fixed_t) LONG(fm->floordestheight); temp->speed = (fixed_t) LONG(fm->speed); temp->delayCount = (int) LONG(fm->delayCount); temp->delayTotal = (int) LONG(fm->delayTotal); temp->stairsDelayHeight = (fixed_t) LONG(fm->stairsDelayHeight); temp->stairsDelayHeightDelta = (fixed_t) LONG(fm->stairsDelayHeightDelta); temp->resetHeight = (fixed_t) LONG(fm->resetHeight); temp->resetDelay = (short) SHORT(fm->resetDelay); temp->resetDelayCount = (short) SHORT(fm->resetDelayCount); temp->textureChange = fm->textureChange; } //========================================================================== // // RestoreFloorMove // //========================================================================== static void RestoreFloorMove(void *arg1, void *arg2) { floormove_t *fm = (floormove_t *) arg1; save_floormove_t *temp = (save_floormove_t *) arg2; temp->sector_idx = LONG(temp->sector_idx); fm->sector = §ors[temp->sector_idx]; fm->sector->specialdata = T_MoveFloor; //fm->thinker.function fm->type = (floor_e) LONG(temp->type); fm->crush = (int) LONG(temp->crush); fm->direction = (int) LONG(temp->direction); fm->newspecial = (int) LONG(temp->newspecial); fm->texture = (short) SHORT(temp->texture); fm->floordestheight = (fixed_t) LONG(temp->floordestheight); fm->speed = (fixed_t) LONG(temp->speed); fm->delayCount = (int) LONG(temp->delayCount); fm->delayTotal = (int) LONG(temp->delayTotal); fm->stairsDelayHeight = (fixed_t) LONG(temp->stairsDelayHeight); fm->stairsDelayHeightDelta = (fixed_t) LONG(temp->stairsDelayHeightDelta); fm->resetHeight = (fixed_t) LONG(temp->resetHeight); fm->resetDelay = (short) SHORT(temp->resetDelay); fm->resetDelayCount = (short) SHORT(temp->resetDelayCount); fm->textureChange = temp->textureChange; } //========================================================================== // // MangleLight // //========================================================================== static void MangleLight(void *arg1, void *arg2) { light_t *light = (light_t *) arg1; save_light_t *temp = (save_light_t *) arg2; temp->sector_idx = LONG((int32_t)(light->sector - sectors)); temp->type = (int) LONG(light->type); temp->value1 = (int) LONG(light->value1); temp->value2 = (int) LONG(light->value2); temp->tics1 = (int) LONG(light->tics1); temp->tics2 = (int) LONG(light->tics2); temp->count = (int) LONG(light->count); } //========================================================================== // // RestoreLight // //========================================================================== static void RestoreLight(void *arg1, void *arg2) { light_t *light = (light_t *) arg1; save_light_t *temp = (save_light_t *) arg2; temp->sector_idx = LONG(temp->sector_idx); light->sector = §ors[temp->sector_idx]; light->type = (lighttype_t) LONG(temp->type); light->value1 = (int) LONG(temp->value1); light->value2 = (int) LONG(temp->value2); light->tics1 = (int) LONG(temp->tics1); light->tics2 = (int) LONG(temp->tics2); light->count = (int) LONG(temp->count); } //========================================================================== // // MangleVerticalDoor // //========================================================================== static void MangleVerticalDoor(void *arg1, void *arg2) { vldoor_t *vldoor = (vldoor_t *) arg1; save_vldoor_t *temp = (save_vldoor_t *) arg2; temp->sector_idx = LONG((int32_t)(vldoor->sector - sectors)); temp->type = (int) LONG(vldoor->type); temp->topheight = (fixed_t) LONG(vldoor->topheight); temp->speed = (fixed_t) LONG(vldoor->speed); temp->direction = (int) LONG(vldoor->direction); temp->topwait = (int) LONG(vldoor->topwait); temp->topcountdown = (int) LONG(vldoor->topcountdown); } //========================================================================== // // RestoreVerticalDoor // //========================================================================== static void RestoreVerticalDoor(void *arg1, void *arg2) { vldoor_t *vldoor = (vldoor_t *) arg1; save_vldoor_t *temp = (save_vldoor_t *) arg2; temp->sector_idx = LONG(temp->sector_idx); vldoor->sector = §ors[temp->sector_idx]; vldoor->sector->specialdata = T_VerticalDoor; //vldoor->thinker.function vldoor->type = (vldoor_e) LONG(temp->type); vldoor->topheight = (fixed_t) LONG(temp->topheight); vldoor->speed = (fixed_t) LONG(temp->speed); vldoor->direction = (int) LONG(temp->direction); vldoor->topwait = (int) LONG(temp->topwait); vldoor->topcountdown = (int) LONG(temp->topcountdown); } //========================================================================== // // ManglePhase // //========================================================================== static void ManglePhase(void *arg1, void *arg2) { phase_t *phase = (phase_t *) arg1; save_phase_t *temp = (save_phase_t *) arg2; temp->sector_idx = LONG((int32_t)(phase->sector - sectors)); temp->index = (int) LONG(phase->index); temp->base = (int) LONG(phase->base); } //========================================================================== // // RestorePhase // //========================================================================== static void RestorePhase(void *arg1, void *arg2) { phase_t *phase = (phase_t *) arg1; save_phase_t *temp = (save_phase_t *) arg2; temp->sector_idx = LONG(temp->sector_idx); phase->sector = §ors[temp->sector_idx]; phase->index = (int) LONG(temp->index); phase->base = (int) LONG(temp->base); } //========================================================================== // // ManglePillar // //========================================================================== static void ManglePillar(void *arg1, void *arg2) { pillar_t *pillar = (pillar_t *) arg1; save_pillar_t *temp = (save_pillar_t *) arg2; temp->sector_idx = LONG((int32_t)(pillar->sector - sectors)); temp->ceilingSpeed = (int) LONG(pillar->ceilingSpeed); temp->floorSpeed = (int) LONG(pillar->floorSpeed); temp->floordest = (int) LONG(pillar->floordest); temp->ceilingdest = (int) LONG(pillar->ceilingdest); temp->direction = (int) LONG(pillar->direction); temp->crush = (int) LONG(pillar->crush); } //========================================================================== // // RestorePillar // //========================================================================== static void RestorePillar(void *arg1, void *arg2) { pillar_t *pillar = (pillar_t *) arg1; save_pillar_t *temp = (save_pillar_t *) arg2; temp->sector_idx = LONG(temp->sector_idx); pillar->sector = §ors[temp->sector_idx]; pillar->sector->specialdata = T_BuildPillar; //pillar->thinker.function pillar->ceilingSpeed = (int) LONG(temp->ceilingSpeed); pillar->floorSpeed = (int) LONG(temp->floorSpeed); pillar->floordest = (int) LONG(temp->floordest); pillar->ceilingdest = (int) LONG(temp->ceilingdest); pillar->direction = (int) LONG(temp->direction); pillar->crush = (int) LONG(temp->crush); } //========================================================================== // // MangleFloorWaggle // //========================================================================== static void MangleFloorWaggle(void *arg1, void *arg2) { floorWaggle_t *fw = (floorWaggle_t *) arg1; save_floorWaggle_t *temp = (save_floorWaggle_t *) arg2; temp->sector_idx = LONG((int32_t)(fw->sector - sectors)); temp->originalHeight = (fixed_t) LONG(fw->originalHeight); temp->accumulator = (fixed_t) LONG(fw->accumulator); temp->accDelta = (fixed_t) LONG(fw->accDelta); temp->targetScale = (fixed_t) LONG(fw->targetScale); temp->scale = (fixed_t) LONG(fw->scale); temp->scaleDelta = (fixed_t) LONG(fw->scaleDelta); temp->ticker = (int) LONG(fw->ticker); temp->state = (int) LONG(fw->state); } //========================================================================== // // RestoreFloorWaggle // //========================================================================== static void RestoreFloorWaggle(void *arg1, void *arg2) { floorWaggle_t *fw = (floorWaggle_t *) arg1; save_floorWaggle_t *temp = (save_floorWaggle_t *) arg2; temp->sector_idx = LONG(temp->sector_idx); fw->sector = §ors[temp->sector_idx]; fw->sector->specialdata = T_FloorWaggle; //fw->thinker.function fw->originalHeight = (fixed_t) LONG(temp->originalHeight); fw->accumulator = (fixed_t) LONG(temp->accumulator); fw->accDelta = (fixed_t) LONG(temp->accDelta); fw->targetScale = (fixed_t) LONG(temp->targetScale); fw->scale = (fixed_t) LONG(temp->scale); fw->scaleDelta = (fixed_t) LONG(temp->scaleDelta); fw->ticker = (int) LONG(temp->ticker); fw->state = (int) LONG(temp->state); } //========================================================================== // // ManglePolyEvent // //========================================================================== static void ManglePolyEvent(void *arg1, void *arg2) { polyevent_t *pe = (polyevent_t *) arg1; save_polyevent_t *temp = (save_polyevent_t *) arg2; temp->polyobj = LONG(pe->polyobj); temp->speed = LONG(pe->speed); temp->dist = LONG(pe->dist); temp->angle = LONG(pe->angle); temp->xSpeed = LONG(pe->xSpeed); temp->ySpeed = LONG(pe->ySpeed); } //========================================================================== // // RestorePolyEvent // //========================================================================== static void RestorePolyEvent(void *arg1, void *arg2) { polyevent_t *pe = (polyevent_t *) arg1; save_polyevent_t *temp = (save_polyevent_t *) arg2; pe->polyobj = (int) LONG(temp->polyobj); pe->speed = (int) LONG(temp->speed); pe->dist = (unsigned int) LONG(temp->dist); pe->angle = (int) LONG(temp->angle); pe->xSpeed = (fixed_t) LONG(temp->xSpeed); pe->ySpeed = (fixed_t) LONG(temp->ySpeed); } //========================================================================== // // ManglePolyDoor // //========================================================================== static void ManglePolyDoor(void *arg1, void *arg2) { polydoor_t *pd = (polydoor_t *) arg1; save_polydoor_t *temp = (save_polydoor_t *) arg2; temp->polyobj = (int) LONG(pd->polyobj); temp->speed = (int) LONG(pd->speed); temp->dist = (int) LONG(pd->dist); temp->totalDist = (int) LONG(pd->totalDist); temp->direction = (int) LONG(pd->direction); temp->xSpeed = (fixed_t) LONG(pd->xSpeed); temp->ySpeed = (fixed_t) LONG(pd->ySpeed); temp->tics = (int) LONG(pd->tics); temp->waitTics = (int) LONG(pd->waitTics); temp->type = (int) LONG(pd->type); temp->close = (int) LONG(pd->close); } //========================================================================== // // RestorePolyEvent // //========================================================================== static void RestorePolyDoor(void *arg1, void *arg2) { polydoor_t *pd = (polydoor_t *) arg1; save_polydoor_t *temp = (save_polydoor_t *) arg2; pd->polyobj = (int) LONG(temp->polyobj); pd->speed = (int) LONG(temp->speed); pd->dist = (int) LONG(temp->dist); pd->totalDist = (int) LONG(temp->totalDist); pd->direction = (int) LONG(temp->direction); pd->xSpeed = (fixed_t) LONG(temp->xSpeed); pd->ySpeed = (fixed_t) LONG(temp->ySpeed); pd->tics = (int) LONG(temp->tics); pd->waitTics = (int) LONG(temp->waitTics); pd->type = (podoortype_t) LONG(temp->type); pd->close = !!(LONG(temp->close)); } //========================================================================== // // MangleScript // //========================================================================== static void MangleScript(void *arg1, void *arg2) { int i; acs_t *script = (acs_t *) arg1; save_acs_t *temp = (save_acs_t *) arg2; temp->ip_idx = LONG((int32_t)((intptr_t)(script->ip) - (intptr_t)ActionCodeBase)); temp->line_idx = script->line ? LONG((int32_t)(script->line - lines)) : (int32_t)LONG(-1); temp->activator_idx = (int32_t) LONG(GetMobjNum(script->activator)); temp->side = (int) LONG(script->side); temp->number = (int) LONG(script->number); temp->infoIndex = (int) LONG(script->infoIndex); temp->delayCount = (int) LONG(script->delayCount); temp->stackPtr = (int) LONG(script->stackPtr); for (i = 0; i < ACS_STACK_DEPTH; i++) { temp->stack[i] = (int) LONG(script->stack[i]); } for (i = 0; i < MAX_ACS_SCRIPT_VARS; i++) { temp->vars[i] = (int) LONG(script->vars[i]); } } //========================================================================== // // RestoreScript // //========================================================================== static void RestoreScript(void *arg1, void *arg2) { int i; acs_t *script = (acs_t *) arg1; save_acs_t *temp = (save_acs_t *) arg2; temp->ip_idx = (int32_t) LONG(temp->ip_idx); temp->line_idx = (int32_t) LONG(temp->line_idx); temp->activator_idx = (int32_t) LONG(temp->activator_idx); script->ip = ActionCodeBase + temp->ip_idx; if (temp->line_idx == -1) { script->line = NULL; } else { script->line = &lines[temp->line_idx]; } script->activator = GetMobjPtr(temp->activator_idx, (intptr_t *)(void *)&script->activator); script->side = (int) LONG(temp->side); script->number = (int) LONG(temp->number); script->infoIndex = (int) LONG(temp->infoIndex); script->delayCount = (int) LONG(temp->delayCount); script->stackPtr = (int) LONG(temp->stackPtr); for (i = 0; i < ACS_STACK_DEPTH; i++) { script->stack[i] = (int) LONG(temp->stack[i]); } for (i = 0; i < MAX_ACS_SCRIPT_VARS; i++) { script->vars[i] = (int) LONG(temp->vars[i]); } } //========================================================================== // // ManglePlatRaise // //========================================================================== static void ManglePlatRaise(void *arg1, void *arg2) { plat_t *plat = (plat_t *) arg1; save_plat_t *temp = (save_plat_t *) arg2; temp->sector_idx = LONG((int32_t)(plat->sector - sectors)); temp->speed = (fixed_t) LONG(plat->speed); temp->low = (fixed_t) LONG(plat->low); temp->high = (fixed_t) LONG(plat->high); temp->wait = (int) LONG(plat->wait); temp->count = (int) LONG(plat->count); temp->status = (int) LONG(plat->status); temp->oldstatus = (int) LONG(plat->oldstatus); temp->crush = (int) LONG(plat->crush); temp->tag = (int) LONG(plat->tag); temp->type = (int) LONG(plat->type); } //========================================================================== // // RestorePlatRaise // //========================================================================== static void RestorePlatRaise(void *arg1, void *arg2) { plat_t *plat = (plat_t *) arg1; save_plat_t *temp = (save_plat_t *) arg2; temp->sector_idx = LONG(temp->sector_idx); plat->sector = §ors[temp->sector_idx]; plat->sector->specialdata = T_PlatRaise; plat->speed = (fixed_t) LONG(temp->speed); plat->low = (fixed_t) LONG(temp->low); plat->high = (fixed_t) LONG(temp->high); plat->wait = (int) LONG(temp->wait); plat->count = (int) LONG(temp->count); plat->status = (plat_e) LONG(temp->status); plat->oldstatus = (plat_e) LONG(temp->oldstatus); plat->crush = (int) LONG(temp->crush); plat->tag = (int) LONG(temp->tag); plat->type = (plattype_e) LONG(temp->type); P_AddActivePlat(plat); } //========================================================================== // // MangleMoveCeiling // //========================================================================== static void MangleMoveCeiling(void *arg1, void *arg2) { ceiling_t *ceiling = (ceiling_t *) arg1; save_ceiling_t *temp = (save_ceiling_t *) arg2; temp->sector_idx = LONG((int32_t)(ceiling->sector - sectors)); temp->type = (int) LONG(ceiling->type); temp->bottomheight = (fixed_t) LONG(ceiling->bottomheight); temp->topheight = (fixed_t) LONG(ceiling->topheight); temp->speed = (fixed_t) LONG(ceiling->speed); temp->crush = (int) LONG(ceiling->crush); temp->direction = (int) LONG(ceiling->direction); temp->tag = (int) LONG(ceiling->tag); temp->olddirection = (int) LONG(ceiling->olddirection); } //========================================================================== // // RestoreMoveCeiling // //========================================================================== static void RestoreMoveCeiling(void *arg1, void *arg2) { ceiling_t *ceiling = (ceiling_t *) arg1; save_ceiling_t *temp = (save_ceiling_t *) arg2; temp->sector_idx = LONG(temp->sector_idx); ceiling->sector = §ors[temp->sector_idx]; ceiling->sector->specialdata = T_MoveCeiling; ceiling->type = (ceiling_e) LONG(temp->type); ceiling->bottomheight = (fixed_t) LONG(temp->bottomheight); ceiling->topheight = (fixed_t) LONG(temp->topheight); ceiling->speed = (fixed_t) LONG(temp->speed); ceiling->crush = (int) LONG(temp->crush); ceiling->direction = (int) LONG(temp->direction); ceiling->tag = (int) LONG(temp->tag); ceiling->olddirection = (int) LONG(temp->olddirection); P_AddActiveCeiling(ceiling); } //========================================================================== // // ArchiveScripts // //========================================================================== static void ArchiveScripts(void) { int i; StreamOutLong(ASEG_SCRIPTS); for (i = 0; i < ACScriptCount; i++) { StreamOutWord(ACSInfo[i].state); StreamOutWord(ACSInfo[i].waitValue); } for (i = 0; i < MAX_ACS_MAP_VARS; i++) { StreamOutLong(MapVars[i]); } } //========================================================================== // // UnarchiveScripts // //========================================================================== static void UnarchiveScripts(void) { int i; AssertSegment(ASEG_SCRIPTS); for (i = 0; i < ACScriptCount; i++) { ACSInfo[i].state = GET_WORD(); ACSInfo[i].waitValue = GET_WORD(); } for (i = 0; i < MAX_ACS_MAP_VARS; i++) { MapVars[i] = GET_LONG(); } } //========================================================================== // // ArchiveMisc // //========================================================================== static void ArchiveMisc(void) { int ix; StreamOutLong(ASEG_MISC); for (ix = 0; ix < MAXPLAYERS; ix++) { StreamOutLong(localQuakeHappening[ix]); } } //========================================================================== // // UnarchiveMisc // //========================================================================== static void UnarchiveMisc(void) { int ix; AssertSegment(ASEG_MISC); for (ix = 0; ix < MAXPLAYERS; ix++) { localQuakeHappening[ix] = GET_LONG(); } } //========================================================================== // // 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; StreamOutLong(ASEG_SOUNDS); // Save the sound sequences StreamOutLong(ActiveSequences); for (node = SequenceListHead; node; node = node->next) { StreamOutLong(node->sequence); StreamOutLong(node->delayTics); StreamOutLong(node->volume); StreamOutLong(SN_GetSequenceOffset(node->sequence, node->sequencePtr)); StreamOutLong(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 *)§ors[0]) / sizeof(sector_t)); StreamOutLong(0); // 0 -- sector sound origin } else { StreamOutLong(1); // 1 -- polyobj sound origin difference = i; } StreamOutLong(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 = GET_LONG(); i = 0; while (i < numSequences) { sequence = GET_LONG(); delayTics = GET_LONG(); volume = GET_LONG(); seqOffset = GET_LONG(); soundID = GET_LONG(); polySnd = GET_LONG(); secNum = GET_LONG(); if (!polySnd) { sndMobj = (mobj_t *)(void *)§ors[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; StreamOutLong(ASEG_POLYOBJS); StreamOutLong(po_NumPolyobjs); for (i = 0; i < po_NumPolyobjs; i++) { StreamOutLong(polyobjs[i].tag); StreamOutLong(polyobjs[i].angle); StreamOutLong(polyobjs[i].startSpot.x); StreamOutLong(polyobjs[i].startSpot.y); } } //========================================================================== // // UnarchivePolyobjs // //========================================================================== static void UnarchivePolyobjs(void) { int i; fixed_t deltaX; fixed_t deltaY; AssertSegment(ASEG_POLYOBJS); if (GET_LONG() != po_NumPolyobjs) { I_Error("UnarchivePolyobjs: Bad polyobj count"); } for (i = 0; i < po_NumPolyobjs; i++) { if (GET_LONG() != polyobjs[i].tag) { I_Error("UnarchivePolyobjs: Invalid polyobj tag"); } PO_RotatePolyobj(polyobjs[i].tag, (angle_t)GET_LONG()); deltaX = GET_LONG() - polyobjs[i].startSpot.x; deltaY = GET_LONG() - polyobjs[i].startSpot.y; PO_MovePolyobj(polyobjs[i].tag, deltaX, deltaY); } } //========================================================================== // // AssertSegment // //========================================================================== static void AssertSegment(gameArchiveSegment_t segType) { if (GET_LONG() != 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[MAX_OSPATH]; for (i = 0; i < MAX_MAPS; i++) { snprintf(fileName, sizeof(fileName), "%shex%d%02d.hxs", basePath, slot, i); remove(fileName); } snprintf(fileName, sizeof(fileName), "%shex%d.hxs", basePath, 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[MAX_OSPATH]; char destName[MAX_OSPATH]; for (i = 0; i < MAX_MAPS; i++) { snprintf(sourceName, sizeof(sourceName), "%shex%d%02d.hxs", basePath,sourceSlot, i); if (ExistingFile(sourceName)) { snprintf(destName, sizeof(destName), "%shex%d%02d.hxs", basePath,destSlot, i); CopyFile(sourceName, destName); } } snprintf(sourceName, sizeof(sourceName), "%shex%d.hxs", basePath, sourceSlot); if (ExistingFile(sourceName)) { snprintf(destName, sizeof(destName), "%shex%d.hxs", basePath, destSlot); CopyFile(sourceName, destName); } } //========================================================================== // // CopyFile // //========================================================================== static void CopyFile(const char *sourceName, const char *destName) { int length; void *buffer; length = M_ReadFile(sourceName, &buffer); M_WriteFile(destName, buffer, length); Z_Free(buffer); } //========================================================================== // // ExistingFile // //========================================================================== static boolean ExistingFile(const char *name) { FILE *fp; if ((fp = fopen(name, "rb")) != NULL) { fclose(fp); return true; } else { return false; } } //========================================================================== // // OpenStreamOut // //========================================================================== static void OpenStreamOut(const char *fileName) { SavingFP = fopen(fileName, "wb"); } //========================================================================== // // CloseStreamOut // //========================================================================== static void CloseStreamOut(void) { if (SavingFP) { fclose(SavingFP); } } //========================================================================== // // StreamOutBuffer // //========================================================================== static void StreamOutBuffer(const void *buffer, size_t size) { fwrite(buffer, size, 1, SavingFP); } //========================================================================== // // StreamOutByte // //========================================================================== static void StreamOutByte(byte val) { fwrite(&val, sizeof(byte), 1, SavingFP); } //========================================================================== // // StreamOutWord // //========================================================================== static void StreamOutWord(uint16_t val) { uint16_t tmp = (uint16_t) SHORT(val); fwrite(&tmp, sizeof(uint16_t), 1, SavingFP); } //========================================================================== // // StreamOutLong // //========================================================================== static void StreamOutLong(uint32_t val) { uint32_t tmp = (uint32_t) LONG(val); fwrite(&tmp, sizeof(uint32_t), 1, SavingFP); }