2 //**************************************************************************
4 //** sv_save.c : Heretic 2 : Raven Software, Corp.
11 //**************************************************************************
13 // HEADER FILES ------------------------------------------------------------
18 // MACROS ------------------------------------------------------------------
20 #define MAX_TARGET_PLAYERS 512
22 #define MOBJ_XX_PLAYER -2
23 #define GET_BYTE (*SavePtr.b++)
24 #define GET_WORD (*SavePtr.w++)
25 #define GET_LONG (*SavePtr.l++)
29 #define REBORN_DESCRIPTION "TEMP GAME"
30 #define MAX_THINKER_SIZE 256
32 // TYPES -------------------------------------------------------------------
36 ASEG_GAME_HEADER = 101,
47 } gameArchiveSegment_t;
71 void (*restoreFunc)();
81 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
83 void P_SpawnPlayer(mapthing_t *mthing);
85 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
87 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
89 static void ArchiveWorld(void);
90 static void UnarchiveWorld(void);
91 static void ArchivePolyobjs(void);
92 static void UnarchivePolyobjs(void);
93 static void ArchiveMobjs(void);
94 static void UnarchiveMobjs(void);
95 static void ArchiveThinkers(void);
96 static void UnarchiveThinkers(void);
97 static void ArchiveScripts(void);
98 static void UnarchiveScripts(void);
99 static void ArchivePlayers(void);
100 static void UnarchivePlayers(void);
101 static void ArchiveSounds(void);
102 static void UnarchiveSounds(void);
103 static void ArchiveMisc(void);
104 static void UnarchiveMisc(void);
105 static void SetMobjArchiveNums(void);
106 static void RemoveAllThinkers(void);
107 static void MangleMobj(mobj_t *mobj);
108 static void RestoreMobj(mobj_t *mobj);
109 static int GetMobjNum(mobj_t *mobj);
110 static void SetMobjPtr(int *archiveNum);
111 static void MangleSSThinker(ssthinker_t *sst);
112 static void RestoreSSThinker(ssthinker_t *sst);
113 static void RestoreSSThinkerNoSD(ssthinker_t *sst);
114 static void MangleScript(acs_t *script);
115 static void RestoreScript(acs_t *script);
116 static void RestorePlatRaise(plat_t *plat);
117 static void RestoreMoveCeiling(ceiling_t *ceiling);
118 static void AssertSegment(gameArchiveSegment_t segType);
119 static void ClearSaveSlot(int slot);
120 static void CopySaveSlot(int sourceSlot, int destSlot);
121 static void CopyFile(char *sourceName, char *destName);
122 static boolean ExistingFile(char *name);
123 static void OpenStreamOut(char *fileName);
124 static void CloseStreamOut(void);
125 static void StreamOutBuffer(void *buffer, int size);
126 static void StreamOutByte(byte val);
127 static void StreamOutWord(unsigned short val);
128 static void StreamOutLong(unsigned int val);
130 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
132 extern int ACScriptCount;
133 extern byte *ActionCodeBase;
134 extern acsInfo_t *ACSInfo;
136 // PUBLIC DATA DEFINITIONS -------------------------------------------------
138 extern char *basePath;
140 // PRIVATE DATA DEFINITIONS ------------------------------------------------
142 static int MobjCount;
143 static mobj_t **MobjList;
144 static int **TargetPlayerAddrs;
145 static int TargetPlayerCount;
146 static byte *SaveBuffer;
147 static boolean SavingPlayers;
154 static FILE *SavingFP;
156 // This list has been prioritized using frequency estimates
157 static thinkInfo_t ThinkerInfo[] =
184 RestoreSSThinkerNoSD,
198 RestoreSSThinkerNoSD,
241 sizeof(floorWaggle_t)
244 TC_NULL, NULL, NULL, NULL, 0
248 // CODE --------------------------------------------------------------------
250 //==========================================================================
254 //==========================================================================
256 void SV_SaveGame(int slot, char *description)
259 char versionText[HXS_VERSION_TEXT_LENGTH];
261 // Open the output file
262 snprintf(fileName, 100, "%shex6.hxs", basePath);
263 OpenStreamOut(fileName);
265 // Write game save description
266 StreamOutBuffer(description, HXS_DESCRIPTION_LENGTH);
268 // Write version info
269 memset(versionText, 0, HXS_VERSION_TEXT_LENGTH);
270 strcpy(versionText, HXS_VERSION_TEXT);
271 StreamOutBuffer(versionText, HXS_VERSION_TEXT_LENGTH);
273 // Place a header marker
274 StreamOutLong(ASEG_GAME_HEADER);
276 // Write current map and difficulty
277 StreamOutByte(gamemap);
278 StreamOutByte(gameskill);
280 // Write global script info
281 StreamOutBuffer(WorldVars, sizeof(WorldVars));
282 StreamOutBuffer(ACSStore, sizeof(ACSStore));
286 // Place a termination marker
287 StreamOutLong(ASEG_END);
289 // Close the output file
292 // Save out the current map
293 SV_SaveMap(true); // true = save player info
295 // Clear all save files at destination slot
298 // Copy base slot to destination slot
299 CopySaveSlot(BASE_SLOT, slot);
302 //==========================================================================
306 //==========================================================================
308 void SV_SaveMap(boolean savePlayers)
312 SavingPlayers = savePlayers;
314 // Open the output file
315 snprintf(fileName, 100, "%shex6%02d.hxs", basePath, gamemap);
316 OpenStreamOut(fileName);
318 // Place a header marker
319 StreamOutLong(ASEG_MAP_HEADER);
321 // Write the level timer
322 StreamOutLong(leveltime);
324 // Set the mobj archive numbers
325 SetMobjArchiveNums();
335 // Place a termination marker
336 StreamOutLong(ASEG_END);
338 // Close the output file
342 //==========================================================================
346 //==========================================================================
348 void SV_LoadGame(int slot)
352 player_t playerBackup[MAXPLAYERS];
355 // Copy all needed save files to the base slot
356 if(slot != BASE_SLOT)
358 ClearSaveSlot(BASE_SLOT);
359 CopySaveSlot(slot, BASE_SLOT);
363 snprintf(fileName, 100, "%shex6.hxs", basePath);
366 M_ReadFile(fileName, &SaveBuffer);
368 // Set the save pointer and skip the description field
369 SavePtr.b = SaveBuffer+HXS_DESCRIPTION_LENGTH;
371 // Check the version text
372 if(strcmp(SavePtr.b, HXS_VERSION_TEXT))
376 SavePtr.b += HXS_VERSION_TEXT_LENGTH;
378 AssertSegment(ASEG_GAME_HEADER);
382 gameskill = GET_BYTE;
384 // Read global script info
385 memcpy(WorldVars, SavePtr.b, sizeof(WorldVars));
386 SavePtr.b += sizeof(WorldVars);
387 memcpy(ACSStore, SavePtr.b, sizeof(ACSStore));
388 SavePtr.b += sizeof(ACSStore);
390 // Read the player structures
393 AssertSegment(ASEG_END);
397 // Save player structs
398 for(i = 0; i < MAXPLAYERS; i++)
400 playerBackup[i] = players[i];
403 // Load the current map
406 // Don't need the player mobj relocation info for load game
407 Z_Free(TargetPlayerAddrs);
409 // Restore player structs
412 for(i = 0; i < MAXPLAYERS; i++)
414 mobj = players[i].mo;
415 players[i] = playerBackup[i];
416 players[i].mo = mobj;
417 if(i == consoleplayer)
419 players[i].readyArtifact = players[i].inventory[inv_ptr].type;
424 //==========================================================================
426 // SV_UpdateRebornSlot
428 // Copies the base slot to the reborn slot.
430 //==========================================================================
432 void SV_UpdateRebornSlot(void)
434 ClearSaveSlot(REBORN_SLOT);
435 CopySaveSlot(BASE_SLOT, REBORN_SLOT);
438 //==========================================================================
440 // SV_ClearRebornSlot
442 //==========================================================================
444 void SV_ClearRebornSlot(void)
446 ClearSaveSlot(REBORN_SLOT);
449 //==========================================================================
453 //==========================================================================
455 void SV_MapTeleport(int map, int position)
460 player_t playerBackup[MAXPLAYERS];
461 mobj_t *targetPlayerMobj;
466 boolean playerWasReborn;
467 boolean oldWeaponowned[NUMWEAPONS];
468 int oldKeys = 0; /* jim added initialiser */
469 int oldPieces = 0; /* jim added initialiser */
474 if(P_GetMapCluster(gamemap) == P_GetMapCluster(map))
475 { // Same cluster - save map without saving player mobjs
479 { // Entering new cluster - clear base slot
480 ClearSaveSlot(BASE_SLOT);
484 // Store player structs for later
485 rClass = randomclass;
487 for(i = 0; i < MAXPLAYERS; i++)
489 playerBackup[i] = players[i];
492 // Save some globals that get trashed during the load
493 inventoryPtr = inv_ptr;
494 currentInvPos = curpos;
496 // Only SV_LoadMap() uses TargetPlayerAddrs, so it's NULLed here
497 // for the following check (player mobj redirection)
498 TargetPlayerAddrs = NULL;
501 snprintf(fileName, 100, "%shex6%02d.hxs", basePath, gamemap);
502 if(!deathmatch && ExistingFile(fileName))
508 G_InitNew(gameskill, gameepisode, gamemap);
510 // Destroy all freshly spawned players
511 for(i = 0; i < MAXPLAYERS; i++)
515 P_RemoveMobj(players[i].mo);
520 // Restore player structs
521 targetPlayerMobj = NULL;
522 for(i = 0; i < MAXPLAYERS; i++)
528 players[i] = playerBackup[i];
529 P_ClearMessage(&players[i]);
530 players[i].attacker = NULL;
531 players[i].poisoner = NULL;
535 if(players[i].playerstate == PST_DEAD)
536 { // In a network game, force all players to be alive
537 players[i].playerstate = PST_REBORN;
540 { // Cooperative net-play, retain keys and weapons
541 oldKeys = players[i].keys;
542 oldPieces = players[i].pieces;
543 for(j = 0; j < NUMWEAPONS; j++)
545 oldWeaponowned[j] = players[i].weaponowned[j];
549 playerWasReborn = (players[i].playerstate == PST_REBORN);
552 memset(players[i].frags, 0, sizeof(players[i].frags));
553 mobj = P_SpawnMobj(playerstarts[0][i].x<<16,
554 playerstarts[0][i].y<<16, 0, MT_PLAYER_FIGHTER);
555 players[i].mo = mobj;
556 G_DeathMatchSpawnPlayer(i);
561 P_SpawnPlayer(&playerstarts[position][i]);
564 if(playerWasReborn && netgame && !deathmatch)
565 { // Restore keys and weapons when reborn in co-op
566 players[i].keys = oldKeys;
567 players[i].pieces = oldPieces;
568 for(bestWeapon = 0, j = 0; j < NUMWEAPONS; j++)
570 if(oldWeaponowned[j])
573 players[i].weaponowned[j] = true;
576 players[i].mana[MANA_1] = 25;
577 players[i].mana[MANA_2] = 25;
579 { // Bring up the best weapon
580 players[i].pendingweapon = bestWeapon;
584 if(targetPlayerMobj == NULL)
586 targetPlayerMobj = players[i].mo;
589 randomclass = rClass;
591 // Redirect anything targeting a player mobj
592 if(TargetPlayerAddrs)
594 for(i = 0; i < TargetPlayerCount; i++)
596 *TargetPlayerAddrs[i] = (int)targetPlayerMobj;
598 Z_Free(TargetPlayerAddrs);
601 // Destroy all things touching players
602 for(i = 0; i < MAXPLAYERS; i++)
606 P_TeleportMove(players[i].mo, players[i].mo->x,
611 // Restore trashed globals
612 inv_ptr = inventoryPtr;
613 curpos = currentInvPos;
615 // Launch waiting scripts
621 // For single play, save immediately into the reborn slot
624 SV_SaveGame(REBORN_SLOT, REBORN_DESCRIPTION);
628 //==========================================================================
632 //==========================================================================
634 int SV_GetRebornSlot(void)
639 //==========================================================================
641 // SV_RebornSlotAvailable
643 // Returns true if the reborn slot is available.
645 //==========================================================================
647 boolean SV_RebornSlotAvailable(void)
651 snprintf(fileName, 100, "%shex%d.hxs", basePath, REBORN_SLOT);
652 return ExistingFile(fileName);
655 //==========================================================================
659 //==========================================================================
661 void SV_LoadMap(void)
666 G_InitNew(gameskill, gameepisode, gamemap);
668 // Remove all thinkers
672 snprintf(fileName, 100, "%shex6%02d.hxs", basePath, gamemap);
675 M_ReadFile(fileName, &SaveBuffer);
676 SavePtr.b = SaveBuffer;
678 AssertSegment(ASEG_MAP_HEADER);
680 // Read the level timer
681 leveltime = GET_LONG;
691 AssertSegment(ASEG_END);
693 // Free mobj list and save buffer
698 //==========================================================================
702 //==========================================================================
704 void SV_InitBaseSlot(void)
706 ClearSaveSlot(BASE_SLOT);
709 //==========================================================================
713 //==========================================================================
715 static void ArchivePlayers(void)
721 StreamOutLong(ASEG_PLAYERS);
722 for(i = 0; i < MAXPLAYERS; i++)
724 StreamOutByte(playeringame[i]);
726 for(i = 0; i < MAXPLAYERS; i++)
732 StreamOutByte(PlayerClass[i]);
733 tempPlayer = players[i];
734 for(j = 0; j < NUMPSPRITES; j++)
736 if(tempPlayer.psprites[j].state)
738 tempPlayer.psprites[j].state =
739 (state_t *)(tempPlayer.psprites[j].state-states);
742 StreamOutBuffer(&tempPlayer, sizeof(player_t));
746 //==========================================================================
750 //==========================================================================
752 static void UnarchivePlayers(void)
756 AssertSegment(ASEG_PLAYERS);
757 for(i = 0; i < MAXPLAYERS; i++)
759 playeringame[i] = GET_BYTE;
761 for(i = 0; i < MAXPLAYERS; i++)
767 PlayerClass[i] = GET_BYTE;
768 memcpy(&players[i], SavePtr.b, sizeof(player_t));
769 SavePtr.b += sizeof(player_t);
770 players[i].mo = NULL; // Will be set when unarc thinker
771 P_ClearMessage(&players[i]);
772 players[i].attacker = NULL;
773 players[i].poisoner = NULL;
774 for(j = 0; j < NUMPSPRITES; j++)
776 if(players[i].psprites[j].state)
778 players[i].psprites[j].state =
779 &states[(int)players[i].psprites[j].state];
785 //==========================================================================
789 //==========================================================================
791 static void ArchiveWorld(void)
799 StreamOutLong(ASEG_WORLD);
800 for(i = 0, sec = sectors; i < numsectors; i++, sec++)
802 StreamOutWord(sec->floorheight>>FRACBITS);
803 StreamOutWord(sec->ceilingheight>>FRACBITS);
804 StreamOutWord(sec->floorpic);
805 StreamOutWord(sec->ceilingpic);
806 StreamOutWord(sec->lightlevel);
807 StreamOutWord(sec->special);
808 StreamOutWord(sec->tag);
809 StreamOutWord(sec->seqType);
811 for(i = 0, li = lines; i < numlines; i++, li++)
813 StreamOutWord(li->flags);
814 StreamOutByte(li->special);
815 StreamOutByte(li->arg1);
816 StreamOutByte(li->arg2);
817 StreamOutByte(li->arg3);
818 StreamOutByte(li->arg4);
819 StreamOutByte(li->arg5);
820 for(j = 0; j < 2; j++)
822 if(li->sidenum[j] == -1)
826 si = &sides[li->sidenum[j]];
827 StreamOutWord(si->textureoffset>>FRACBITS);
828 StreamOutWord(si->rowoffset>>FRACBITS);
829 StreamOutWord(si->toptexture);
830 StreamOutWord(si->bottomtexture);
831 StreamOutWord(si->midtexture);
836 //==========================================================================
840 //==========================================================================
842 static void UnarchiveWorld(void)
850 AssertSegment(ASEG_WORLD);
851 for(i = 0, sec = sectors; i < numsectors; i++, sec++)
853 sec->floorheight = GET_WORD<<FRACBITS;
854 sec->ceilingheight = GET_WORD<<FRACBITS;
855 sec->floorpic = GET_WORD;
856 sec->ceilingpic = GET_WORD;
857 sec->lightlevel = GET_WORD;
858 sec->special = GET_WORD;
860 sec->seqType = GET_WORD;
861 sec->specialdata = 0;
862 sec->soundtarget = 0;
864 for(i = 0, li = lines; i < numlines; i++, li++)
866 li->flags = GET_WORD;
867 li->special = GET_BYTE;
873 for(j = 0; j < 2; j++)
875 if(li->sidenum[j] == -1)
879 si = &sides[li->sidenum[j]];
880 si->textureoffset = GET_WORD<<FRACBITS;
881 si->rowoffset = GET_WORD<<FRACBITS;
882 si->toptexture = GET_WORD;
883 si->bottomtexture = GET_WORD;
884 si->midtexture = GET_WORD;
889 //==========================================================================
891 // SetMobjArchiveNums
893 // Sets the archive numbers in all mobj structs. Also sets the MobjCount
894 // global. Ignores player mobjs if SavingPlayers is false.
896 //==========================================================================
898 static void SetMobjArchiveNums(void)
904 for(thinker = thinkercap.next; thinker != &thinkercap;
905 thinker = thinker->next)
907 if(thinker->function == P_MobjThinker)
909 mobj = (mobj_t *)thinker;
910 if(mobj->player && !SavingPlayers)
911 { // Skipping player mobjs
914 mobj->archiveNum = MobjCount++;
919 //==========================================================================
923 //==========================================================================
925 static void ArchiveMobjs(void)
931 StreamOutLong(ASEG_MOBJS);
932 StreamOutLong(MobjCount);
934 for(thinker = thinkercap.next; thinker != &thinkercap;
935 thinker = thinker->next)
937 if(thinker->function != P_MobjThinker)
938 { // Not a mobj thinker
941 if(((mobj_t *)thinker)->player && !SavingPlayers)
942 { // Skipping player mobjs
946 memcpy(&tempMobj, thinker, sizeof(mobj_t));
947 MangleMobj(&tempMobj);
948 StreamOutBuffer(&tempMobj, sizeof(mobj_t));
950 if(count != MobjCount)
952 I_Error("ArchiveMobjs: bad mobj count");
956 //==========================================================================
960 //==========================================================================
962 static void UnarchiveMobjs(void)
967 AssertSegment(ASEG_MOBJS);
968 TargetPlayerAddrs = Z_Malloc(MAX_TARGET_PLAYERS*sizeof(int *),
970 TargetPlayerCount = 0;
971 MobjCount = GET_LONG;
972 MobjList = Z_Malloc(MobjCount*sizeof(mobj_t *), PU_STATIC, NULL);
973 for(i = 0; i < MobjCount; i++)
975 MobjList[i] = Z_Malloc(sizeof(mobj_t), PU_LEVEL, NULL);
977 for(i = 0; i < MobjCount; i++)
980 memcpy(mobj, SavePtr.b, sizeof(mobj_t));
981 SavePtr.b += sizeof(mobj_t);
982 mobj->thinker.function = P_MobjThinker;
984 P_AddThinker(&mobj->thinker);
987 P_InitCreatureCorpseQueue(true); // true = scan for corpses
990 //==========================================================================
994 //==========================================================================
996 static void MangleMobj(mobj_t *mobj)
1000 corpse = mobj->flags&MF_CORPSE;
1001 mobj->state = (state_t *)(mobj->state-states);
1004 mobj->player = (player_t *)((mobj->player-players)+1);
1008 mobj->target = (mobj_t *)MOBJ_NULL;
1012 mobj->target = (mobj_t *)GetMobjNum(mobj->target);
1020 case MT_THRUSTFLOOR_UP:
1021 case MT_THRUSTFLOOR_DOWN:
1027 mobj->special1 = MOBJ_NULL;
1031 mobj->special1 = GetMobjNum((mobj_t *)mobj->special1);
1036 case MT_LIGHTNING_FLOOR:
1037 case MT_LIGHTNING_ZAP:
1040 mobj->special2 = MOBJ_NULL;
1044 mobj->special2 = GetMobjNum((mobj_t *)mobj->special2);
1048 // Both special1 and special2
1050 case MT_LIGHTNING_CEILING:
1053 mobj->special1 = MOBJ_NULL;
1054 mobj->special2 = MOBJ_NULL;
1058 mobj->special1 = GetMobjNum((mobj_t *)mobj->special1);
1059 mobj->special2 = GetMobjNum((mobj_t *)mobj->special2);
1065 mobj->special1 = 0; // Searching index
1073 //==========================================================================
1077 //==========================================================================
1079 static int GetMobjNum(mobj_t *mobj)
1085 if(mobj->player && !SavingPlayers)
1087 return MOBJ_XX_PLAYER;
1089 return mobj->archiveNum;
1092 //==========================================================================
1096 //==========================================================================
1098 static void RestoreMobj(mobj_t *mobj)
1100 mobj->state = &states[(int)mobj->state];
1103 mobj->player = &players[(int)mobj->player-1];
1104 mobj->player->mo = mobj;
1106 P_SetThingPosition(mobj);
1107 mobj->info = &mobjinfo[mobj->type];
1108 mobj->floorz = mobj->subsector->sector->floorheight;
1109 mobj->ceilingz = mobj->subsector->sector->ceilingheight;
1110 SetMobjPtr((int *)&mobj->target);
1117 case MT_THRUSTFLOOR_UP:
1118 case MT_THRUSTFLOOR_DOWN:
1121 SetMobjPtr(&mobj->special1);
1125 case MT_LIGHTNING_FLOOR:
1126 case MT_LIGHTNING_ZAP:
1127 SetMobjPtr(&mobj->special2);
1130 // Both special1 and special2
1132 case MT_LIGHTNING_CEILING:
1133 SetMobjPtr(&mobj->special1);
1134 SetMobjPtr(&mobj->special2);
1142 //==========================================================================
1146 //==========================================================================
1148 static void SetMobjPtr(int *archiveNum)
1150 if(*archiveNum == MOBJ_NULL)
1155 if(*archiveNum == MOBJ_XX_PLAYER)
1157 if(TargetPlayerCount == MAX_TARGET_PLAYERS)
1159 I_Error("RestoreMobj: exceeded MAX_TARGET_PLAYERS");
1161 TargetPlayerAddrs[TargetPlayerCount++] = archiveNum;
1165 *archiveNum = (int)MobjList[*archiveNum];
1168 //==========================================================================
1172 //==========================================================================
1174 static void ArchiveThinkers(void)
1178 byte buffer[MAX_THINKER_SIZE];
1180 StreamOutLong(ASEG_THINKERS);
1181 for(thinker = thinkercap.next; thinker != &thinkercap;
1182 thinker = thinker->next)
1184 for(info = ThinkerInfo; info->tClass != TC_NULL; info++)
1186 if(thinker->function == info->thinkerFunc)
1188 StreamOutByte(info->tClass);
1189 memcpy(buffer, thinker, info->size);
1190 if(info->mangleFunc)
1192 info->mangleFunc(buffer);
1194 StreamOutBuffer(buffer, info->size);
1199 // Add a termination marker
1200 StreamOutByte(TC_NULL);
1203 //==========================================================================
1205 // UnarchiveThinkers
1207 //==========================================================================
1209 static void UnarchiveThinkers(void)
1215 AssertSegment(ASEG_THINKERS);
1216 while((tClass = GET_BYTE) != TC_NULL)
1218 for(info = ThinkerInfo; info->tClass != TC_NULL; info++)
1220 if(tClass == info->tClass)
1222 thinker = Z_Malloc(info->size, PU_LEVEL, NULL);
1223 memcpy(thinker, SavePtr.b, info->size);
1224 SavePtr.b += info->size;
1225 thinker->function = info->thinkerFunc;
1226 if(info->restoreFunc)
1228 info->restoreFunc(thinker);
1230 P_AddThinker(thinker);
1234 if(info->tClass == TC_NULL)
1236 I_Error("UnarchiveThinkers: Unknown tClass %d in "
1237 "savegame", tClass);
1242 //==========================================================================
1246 //==========================================================================
1248 static void MangleSSThinker(ssthinker_t *sst)
1250 sst->sector = (sector_t *)(sst->sector-sectors);
1253 //==========================================================================
1257 //==========================================================================
1259 static void RestoreSSThinker(ssthinker_t *sst)
1261 sst->sector = §ors[(int)sst->sector];
1262 sst->sector->specialdata = sst->thinker.function;
1265 //==========================================================================
1267 // RestoreSSThinkerNoSD
1269 //==========================================================================
1271 static void RestoreSSThinkerNoSD(ssthinker_t *sst)
1273 sst->sector = §ors[(int)sst->sector];
1276 //==========================================================================
1280 //==========================================================================
1282 static void MangleScript(acs_t *script)
1284 script->ip = (int *)((int)(script->ip)-(int)ActionCodeBase);
1285 script->line = script->line ?
1286 (line_t *)(script->line-lines) : (line_t *)-1;
1287 script->activator = (mobj_t *)GetMobjNum(script->activator);
1290 //==========================================================================
1294 //==========================================================================
1296 static void RestoreScript(acs_t *script)
1298 script->ip = (int *)(ActionCodeBase+(int)script->ip);
1299 if((int)script->line == -1)
1301 script->line = NULL;
1305 script->line = &lines[(int)script->line];
1307 SetMobjPtr((int *)&script->activator);
1310 //==========================================================================
1314 //==========================================================================
1316 static void RestorePlatRaise(plat_t *plat)
1318 plat->sector = §ors[(int)plat->sector];
1319 plat->sector->specialdata = T_PlatRaise;
1320 P_AddActivePlat(plat);
1323 //==========================================================================
1325 // RestoreMoveCeiling
1327 //==========================================================================
1329 static void RestoreMoveCeiling(ceiling_t *ceiling)
1331 ceiling->sector = §ors[(int)ceiling->sector];
1332 ceiling->sector->specialdata = T_MoveCeiling;
1333 P_AddActiveCeiling(ceiling);
1336 //==========================================================================
1340 //==========================================================================
1342 static void ArchiveScripts(void)
1346 StreamOutLong(ASEG_SCRIPTS);
1347 for(i = 0; i < ACScriptCount; i++)
1349 StreamOutWord(ACSInfo[i].state);
1350 StreamOutWord(ACSInfo[i].waitValue);
1352 StreamOutBuffer(MapVars, sizeof(MapVars));
1355 //==========================================================================
1359 //==========================================================================
1361 static void UnarchiveScripts(void)
1365 AssertSegment(ASEG_SCRIPTS);
1366 for(i = 0; i < ACScriptCount; i++)
1368 ACSInfo[i].state = GET_WORD;
1369 ACSInfo[i].waitValue = GET_WORD;
1371 memcpy(MapVars, SavePtr.b, sizeof(MapVars));
1372 SavePtr.b += sizeof(MapVars);
1375 //==========================================================================
1379 //==========================================================================
1381 static void ArchiveMisc(void)
1385 StreamOutLong(ASEG_MISC);
1386 for (ix=0; ix<MAXPLAYERS; ix++)
1388 StreamOutLong(localQuakeHappening[ix]);
1392 //==========================================================================
1396 //==========================================================================
1398 static void UnarchiveMisc(void)
1402 AssertSegment(ASEG_MISC);
1403 for (ix=0; ix<MAXPLAYERS; ix++)
1405 localQuakeHappening[ix] = GET_LONG;
1409 //==========================================================================
1411 // RemoveAllThinkers
1413 //==========================================================================
1415 static void RemoveAllThinkers(void)
1418 thinker_t *nextThinker;
1420 thinker = thinkercap.next;
1421 while(thinker != &thinkercap)
1423 nextThinker = thinker->next;
1424 if(thinker->function == P_MobjThinker)
1426 P_RemoveMobj((mobj_t *)thinker);
1432 thinker = nextThinker;
1437 //==========================================================================
1441 //==========================================================================
1443 static void ArchiveSounds(void)
1450 StreamOutLong(ASEG_SOUNDS);
1452 // Save the sound sequences
1453 StreamOutLong(ActiveSequences);
1454 for(node = SequenceListHead; node; node = node->next)
1456 StreamOutLong(node->sequence);
1457 StreamOutLong(node->delayTics);
1458 StreamOutLong(node->volume);
1459 StreamOutLong(SN_GetSequenceOffset(node->sequence,
1460 node->sequencePtr));
1461 StreamOutLong(node->currentSoundID);
1462 for(i = 0; i < po_NumPolyobjs; i++)
1464 if(node->mobj == (mobj_t *)&polyobjs[i].startSpot)
1469 if(i == po_NumPolyobjs)
1470 { // Sound is attached to a sector, not a polyobj
1471 sec = R_PointInSubsector(node->mobj->x, node->mobj->y)->sector;
1472 difference = (int)((byte *)sec
1473 -(byte *)§ors[0])/sizeof(sector_t);
1474 StreamOutLong(0); // 0 -- sector sound origin
1478 StreamOutLong(1); // 1 -- polyobj sound origin
1481 StreamOutLong(difference);
1485 //==========================================================================
1489 //==========================================================================
1491 static void UnarchiveSounds(void)
1504 AssertSegment(ASEG_SOUNDS);
1506 // Reload and restart all sound sequences
1507 numSequences = GET_LONG;
1509 while(i < numSequences)
1511 sequence = GET_LONG;
1512 delayTics = GET_LONG;
1514 seqOffset = GET_LONG;
1521 sndMobj = (mobj_t *)§ors[secNum].soundorg;
1525 sndMobj = (mobj_t *)&polyobjs[secNum].startSpot;
1527 SN_StartSequence(sndMobj, sequence);
1528 SN_ChangeNodeData(i, seqOffset, delayTics, volume, soundID);
1533 //==========================================================================
1537 //==========================================================================
1539 static void ArchivePolyobjs(void)
1543 StreamOutLong(ASEG_POLYOBJS);
1544 StreamOutLong(po_NumPolyobjs);
1545 for(i = 0; i < po_NumPolyobjs; i++)
1547 StreamOutLong(polyobjs[i].tag);
1548 StreamOutLong(polyobjs[i].angle);
1549 StreamOutLong(polyobjs[i].startSpot.x);
1550 StreamOutLong(polyobjs[i].startSpot.y);
1554 //==========================================================================
1556 // UnarchivePolyobjs
1558 //==========================================================================
1560 static void UnarchivePolyobjs(void)
1566 AssertSegment(ASEG_POLYOBJS);
1567 if(GET_LONG != po_NumPolyobjs)
1569 I_Error("UnarchivePolyobjs: Bad polyobj count");
1571 for(i = 0; i < po_NumPolyobjs; i++)
1573 if(GET_LONG != polyobjs[i].tag)
1575 I_Error("UnarchivePolyobjs: Invalid polyobj tag");
1577 PO_RotatePolyobj(polyobjs[i].tag, (angle_t)GET_LONG);
1578 deltaX = GET_LONG-polyobjs[i].startSpot.x;
1579 deltaY = GET_LONG-polyobjs[i].startSpot.y;
1580 PO_MovePolyobj(polyobjs[i].tag, deltaX, deltaY);
1584 //==========================================================================
1588 //==========================================================================
1590 static void AssertSegment(gameArchiveSegment_t segType)
1592 if(GET_LONG != segType)
1594 I_Error("Corrupt save game: Segment [%d] failed alignment check",
1599 //==========================================================================
1603 // Deletes all save game files associated with a slot number.
1605 //==========================================================================
1607 static void ClearSaveSlot(int slot)
1612 for(i = 0; i < MAX_MAPS; i++)
1614 snprintf(fileName, 100, "%shex%d%02d.hxs", basePath, slot, i);
1617 snprintf(fileName, 100, "%shex%d.hxs", basePath, slot);
1621 //==========================================================================
1625 // Copies all the save game files from one slot to another.
1627 //==========================================================================
1629 static void CopySaveSlot(int sourceSlot, int destSlot)
1632 char sourceName[100];
1635 for(i = 0; i < MAX_MAPS; i++)
1637 snprintf(sourceName,100, "%shex%d%02d.hxs", basePath,sourceSlot, i);
1638 if(ExistingFile(sourceName))
1640 snprintf(destName,100, "%shex%d%02d.hxs", basePath,destSlot, i);
1641 CopyFile(sourceName, destName);
1644 snprintf(sourceName, 100,"%shex%d.hxs", basePath, sourceSlot);
1645 if(ExistingFile(sourceName))
1647 snprintf(destName, 100,"%shex%d.hxs", basePath, destSlot);
1648 CopyFile(sourceName, destName);
1652 //==========================================================================
1656 //==========================================================================
1658 static void CopyFile(char *sourceName, char *destName)
1663 length = M_ReadFile(sourceName, &buffer);
1664 M_WriteFile(destName, buffer, length);
1668 //==========================================================================
1672 //==========================================================================
1674 static boolean ExistingFile(char *name)
1678 if((fp = fopen(name, "rb")) != NULL)
1689 //==========================================================================
1693 //==========================================================================
1695 static void OpenStreamOut(char *fileName)
1697 SavingFP = fopen(fileName, "wb");
1700 //==========================================================================
1704 //==========================================================================
1706 static void CloseStreamOut(void)
1714 //==========================================================================
1718 //==========================================================================
1720 static void StreamOutBuffer(void *buffer, int size)
1722 fwrite(buffer, size, 1, SavingFP);
1725 //==========================================================================
1729 //==========================================================================
1731 static void StreamOutByte(byte val)
1733 fwrite(&val, sizeof(byte), 1, SavingFP);
1736 //==========================================================================
1740 //==========================================================================
1742 static void StreamOutWord(unsigned short val)
1744 fwrite(&val, sizeof(unsigned short), 1, SavingFP);
1747 //==========================================================================
1751 //==========================================================================
1753 static void StreamOutLong(unsigned int val)
1755 fwrite(&val, sizeof(int), 1, SavingFP);