1 /*
2 ** p_saveg.cpp
3 ** Code for serializing the world state in an archive
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 #include "i_system.h"
36 #include "p_local.h"
37 
38 // State.
39 #include "dobject.h"
40 #include "doomstat.h"
41 #include "r_state.h"
42 #include "m_random.h"
43 #include "p_saveg.h"
44 #include "s_sndseq.h"
45 #include "v_palette.h"
46 #include "a_sharedglobal.h"
47 #include "r_data/r_interpolate.h"
48 #include "g_level.h"
49 #include "po_man.h"
50 #include "p_setup.h"
51 #include "r_data/colormaps.h"
52 #include "farchive.h"
53 #include "p_lnspec.h"
54 #include "p_acs.h"
55 #include "p_terrain.h"
56 
57 static void CopyPlayer (player_t *dst, player_t *src, const char *name);
58 static void ReadOnePlayer (FArchive &arc, bool skipload);
59 static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload);
60 static void SpawnExtraPlayers ();
61 
operator <<(FArchive & arc,FLinkedSector & link)62 inline FArchive &operator<< (FArchive &arc, FLinkedSector &link)
63 {
64 	arc << link.Sector << link.Type;
65 	return arc;
66 }
67 
68 //
69 // P_ArchivePlayers
70 //
P_SerializePlayers(FArchive & arc,bool skipload)71 void P_SerializePlayers (FArchive &arc, bool skipload)
72 {
73 	BYTE numPlayers, numPlayersNow;
74 	int i;
75 
76 	// Count the number of players present right now.
77 	for (numPlayersNow = 0, i = 0; i < MAXPLAYERS; ++i)
78 	{
79 		if (playeringame[i])
80 		{
81 			++numPlayersNow;
82 		}
83 	}
84 
85 	if (arc.IsStoring())
86 	{
87 		// Record the number of players in this save.
88 		arc << numPlayersNow;
89 
90 		// Record each player's name, followed by their data.
91 		for (i = 0; i < MAXPLAYERS; ++i)
92 		{
93 			if (playeringame[i])
94 			{
95 				arc.WriteString (players[i].userinfo.GetName());
96 				players[i].Serialize (arc);
97 			}
98 		}
99 	}
100 	else
101 	{
102 		arc << numPlayers;
103 
104 		// If there is only one player in the game, they go to the
105 		// first player present, no matter what their name.
106 		if (numPlayers == 1)
107 		{
108 			ReadOnePlayer (arc, skipload);
109 		}
110 		else
111 		{
112 			ReadMultiplePlayers (arc, numPlayers, numPlayersNow, skipload);
113 		}
114 		if (!skipload && numPlayersNow > numPlayers)
115 		{
116 			SpawnExtraPlayers ();
117 		}
118 		// Redo pitch limits, since the spawned player has them at 0.
119 		players[consoleplayer].SendPitchLimits();
120 	}
121 }
122 
ReadOnePlayer(FArchive & arc,bool skipload)123 static void ReadOnePlayer (FArchive &arc, bool skipload)
124 {
125 	int i;
126 	char *name = NULL;
127 	bool didIt = false;
128 
129 	arc << name;
130 
131 	for (i = 0; i < MAXPLAYERS; ++i)
132 	{
133 		if (playeringame[i])
134 		{
135 			if (!didIt)
136 			{
137 				didIt = true;
138 				player_t playerTemp;
139 				playerTemp.Serialize (arc);
140 				if (!skipload)
141 				{
142 					CopyPlayer (&players[i], &playerTemp, name);
143 				}
144 			}
145 			else
146 			{
147 				if (players[i].mo != NULL)
148 				{
149 					players[i].mo->Destroy();
150 					players[i].mo = NULL;
151 				}
152 			}
153 		}
154 	}
155 	delete[] name;
156 }
157 
ReadMultiplePlayers(FArchive & arc,int numPlayers,int numPlayersNow,bool skipload)158 static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload)
159 {
160 	// For two or more players, read each player into a temporary array.
161 	int i, j;
162 	char **nametemp = new char *[numPlayers];
163 	player_t *playertemp = new player_t[numPlayers];
164 	BYTE *tempPlayerUsed = new BYTE[numPlayers];
165 	BYTE playerUsed[MAXPLAYERS];
166 
167 	for (i = 0; i < numPlayers; ++i)
168 	{
169 		nametemp[i] = NULL;
170 		arc << nametemp[i];
171 		playertemp[i].Serialize (arc);
172 		tempPlayerUsed[i] = 0;
173 	}
174 	for (i = 0; i < MAXPLAYERS; ++i)
175 	{
176 		playerUsed[i] = playeringame[i] ? 0 : 2;
177 	}
178 
179 	if (!skipload)
180 	{
181 		// Now try to match players from the savegame with players present
182 		// based on their names. If two players in the savegame have the
183 		// same name, then they are assigned to players in the current game
184 		// on a first-come, first-served basis.
185 		for (i = 0; i < numPlayers; ++i)
186 		{
187 			for (j = 0; j < MAXPLAYERS; ++j)
188 			{
189 				if (playerUsed[j] == 0 && stricmp(players[j].userinfo.GetName(), nametemp[i]) == 0)
190 				{ // Found a match, so copy our temp player to the real player
191 					Printf ("Found player %d (%s) at %d\n", i, nametemp[i], j);
192 					CopyPlayer (&players[j], &playertemp[i], nametemp[i]);
193 					playerUsed[j] = 1;
194 					tempPlayerUsed[i] = 1;
195 					break;
196 				}
197 			}
198 		}
199 
200 		// Any players that didn't have matching names are assigned to existing
201 		// players on a first-come, first-served basis.
202 		for (i = 0; i < numPlayers; ++i)
203 		{
204 			if (tempPlayerUsed[i] == 0)
205 			{
206 				for (j = 0; j < MAXPLAYERS; ++j)
207 				{
208 					if (playerUsed[j] == 0)
209 					{
210 						Printf ("Assigned player %d (%s) to %d (%s)\n", i, nametemp[i], j, players[j].userinfo.GetName());
211 						CopyPlayer (&players[j], &playertemp[i], nametemp[i]);
212 						playerUsed[j] = 1;
213 						tempPlayerUsed[i] = 1;
214 						break;
215 					}
216 				}
217 			}
218 		}
219 
220 		// Make sure any extra players don't have actors spawned yet. Happens if the players
221 		// present now got the same slots as they had in the save, but there are not as many
222 		// as there were in the save.
223 		for (j = 0; j < MAXPLAYERS; ++j)
224 		{
225 			if (playerUsed[j] == 0)
226 			{
227 				if (players[j].mo != NULL)
228 				{
229 					players[j].mo->Destroy();
230 					players[j].mo = NULL;
231 				}
232 			}
233 		}
234 
235 		// Remove any temp players that were not used. Happens if there are fewer players
236 		// than there were in the save, and they got shuffled.
237 		for (i = 0; i < numPlayers; ++i)
238 		{
239 			if (tempPlayerUsed[i] == 0)
240 			{
241 				playertemp[i].mo->Destroy();
242 				playertemp[i].mo = NULL;
243 			}
244 		}
245 	}
246 
247 	delete[] tempPlayerUsed;
248 	delete[] playertemp;
249 	for (i = 0; i < numPlayers; ++i)
250 	{
251 		delete[] nametemp[i];
252 	}
253 	delete[] nametemp;
254 }
255 
CopyPlayer(player_t * dst,player_t * src,const char * name)256 static void CopyPlayer (player_t *dst, player_t *src, const char *name)
257 {
258 	// The userinfo needs to be saved for real players, but it
259 	// needs to come from the save for bots.
260 	userinfo_t uibackup;
261 	userinfo_t uibackup2;
262 
263 	uibackup.TransferFrom(dst->userinfo);
264 	uibackup2.TransferFrom(src->userinfo);
265 
266 	int chasecam = dst->cheats & CF_CHASECAM;	// Remember the chasecam setting
267 	bool attackdown = dst->attackdown;
268 	bool usedown = dst->usedown;
269 
270 
271 	*dst = *src;		// To avoid memory leaks at this point the userinfo in src must be empty which is taken care of by the TransferFrom call above.
272 
273 	dst->cheats |= chasecam;
274 
275 	if (dst->Bot != NULL)
276 	{
277 		botinfo_t *thebot = bglobal.botinfo;
278 		while (thebot && stricmp (name, thebot->name))
279 		{
280 			thebot = thebot->next;
281 		}
282 		if (thebot)
283 		{
284 			thebot->inuse = BOTINUSE_Yes;
285 		}
286 		bglobal.botnum++;
287 		dst->userinfo.TransferFrom(uibackup2);
288 	}
289 	else
290 	{
291 		dst->userinfo.TransferFrom(uibackup);
292 	}
293 	// Validate the skin
294 	dst->userinfo.SkinNumChanged(R_FindSkin(skins[dst->userinfo.GetSkin()].name, dst->CurrentPlayerClass));
295 
296 	// Make sure the player pawn points to the proper player struct.
297 	if (dst->mo != NULL)
298 	{
299 		dst->mo->player = dst;
300 	}
301 	// These 2 variables may not be overwritten.
302 	dst->attackdown = attackdown;
303 	dst->usedown = usedown;
304 }
305 
SpawnExtraPlayers()306 static void SpawnExtraPlayers ()
307 {
308 	// If there are more players now than there were in the savegame,
309 	// be sure to spawn the extra players.
310 	int i;
311 
312 	if (deathmatch)
313 	{
314 		return;
315 	}
316 
317 	for (i = 0; i < MAXPLAYERS; ++i)
318 	{
319 		if (playeringame[i] && players[i].mo == NULL)
320 		{
321 			players[i].playerstate = PST_ENTER;
322 			P_SpawnPlayer(&playerstarts[i], i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
323 		}
324 	}
325 }
326 
327 //
328 // P_ArchiveWorld
329 //
P_SerializeWorld(FArchive & arc)330 void P_SerializeWorld (FArchive &arc)
331 {
332 	int i, j;
333 	sector_t *sec;
334 	line_t *li;
335 	zone_t *zn;
336 
337 	// do sectors
338 	for (i = 0, sec = sectors; i < numsectors; i++, sec++)
339 	{
340 		arc << sec->floorplane
341 			<< sec->ceilingplane;
342 		if (SaveVersion < 3223)
343 		{
344 			BYTE bytelight;
345 			arc << bytelight;
346 			sec->lightlevel = bytelight;
347 		}
348 		else
349 		{
350 			arc << sec->lightlevel;
351 		}
352 		arc << sec->special;
353 		if (SaveVersion < 4523)
354 		{
355 			short tag;
356 			arc << tag;
357 		}
358 		arc << sec->soundtraversed
359 			<< sec->seqType
360 			<< sec->friction
361 			<< sec->movefactor
362 			<< sec->floordata
363 			<< sec->ceilingdata
364 			<< sec->lightingdata
365 			<< sec->stairlock
366 			<< sec->prevsec
367 			<< sec->nextsec
368 			<< sec->planes[sector_t::floor]
369 			<< sec->planes[sector_t::ceiling]
370 			<< sec->heightsec
371 			<< sec->bottommap << sec->midmap << sec->topmap
372 			<< sec->gravity;
373 		if (SaveVersion >= 4530)
374 		{
375 			P_SerializeTerrain(arc, sec->terrainnum[0]);
376 			P_SerializeTerrain(arc, sec->terrainnum[1]);
377 		}
378 		if (SaveVersion >= 4529)
379 		{
380 			arc << sec->damageamount;
381 		}
382 		else
383 		{
384 			short dmg;
385 			arc << dmg;
386 			sec->damageamount = dmg;
387 		}
388 		if (SaveVersion >= 4528)
389 		{
390 			arc << sec->damageinterval
391 				<< sec->leakydamage
392 				<< sec->damagetype;
393 		}
394 		else
395 		{
396 			short damagemod;
397 			arc << damagemod;
398 			sec->damagetype = MODtoDamageType(damagemod);
399 			if (sec->damageamount < 20)
400 			{
401 				sec->leakydamage = 0;
402 				sec->damageinterval = 32;
403 			}
404 			else if (sec->damageamount < 50)
405 			{
406 				sec->leakydamage = 5;
407 				sec->damageinterval = 32;
408 			}
409 			else
410 			{
411 				sec->leakydamage = 256;
412 				sec->damageinterval = 1;
413 			}
414 		}
415 
416 		arc << sec->SoundTarget
417 			<< sec->SecActTarget
418 			<< sec->sky
419 			<< sec->MoreFlags
420 			<< sec->Flags
421 			<< sec->SkyBoxes[sector_t::floor] << sec->SkyBoxes[sector_t::ceiling]
422 			<< sec->ZoneNumber;
423 		if (SaveVersion < 4529)
424 		{
425 			short secretsector;
426 			arc << secretsector;
427 			if (secretsector) sec->Flags |= SECF_WASSECRET;
428 			P_InitSectorSpecial(sec, sec->special, true);
429 		}
430 		arc	<< sec->interpolations[0]
431 			<< sec->interpolations[1]
432 			<< sec->interpolations[2]
433 			<< sec->interpolations[3]
434 			<< sec->SeqName;
435 
436 		sec->e->Serialize(arc);
437 		if (arc.IsStoring ())
438 		{
439 			arc << sec->ColorMap->Color
440 				<< sec->ColorMap->Fade;
441 			BYTE sat = sec->ColorMap->Desaturate;
442 			arc << sat;
443 		}
444 		else
445 		{
446 			PalEntry color, fade;
447 			BYTE desaturate;
448 			arc << color << fade
449 				<< desaturate;
450 			sec->ColorMap = GetSpecialLights (color, fade, desaturate);
451 		}
452 	}
453 
454 	// do lines
455 	for (i = 0, li = lines; i < numlines; i++, li++)
456 	{
457 		arc << li->flags
458 			<< li->activation
459 			<< li->special
460 			<< li->Alpha;
461 
462 		if (SaveVersion < 4523)
463 		{
464 			int id;
465 			arc << id;
466 		}
467 		if (P_IsACSSpecial(li->special))
468 		{
469 			P_SerializeACSScriptNumber(arc, li->args[0], false);
470 		}
471 		else
472 		{
473 			arc << li->args[0];
474 		}
475 		arc << li->args[1] << li->args[2] << li->args[3] << li->args[4];
476 
477 		for (j = 0; j < 2; j++)
478 		{
479 			if (li->sidedef[j] == NULL)
480 				continue;
481 
482 			side_t *si = li->sidedef[j];
483 			arc << si->textures[side_t::top]
484 				<< si->textures[side_t::mid]
485 				<< si->textures[side_t::bottom]
486 				<< si->Light
487 				<< si->Flags
488 				<< si->LeftSide
489 				<< si->RightSide
490 				<< si->Index;
491 			DBaseDecal::SerializeChain (arc, &si->AttachedDecals);
492 		}
493 	}
494 
495 	// do zones
496 	arc << numzones;
497 
498 	if (arc.IsLoading())
499 	{
500 		if (zones != NULL)
501 		{
502 			delete[] zones;
503 		}
504 		zones = new zone_t[numzones];
505 	}
506 
507 	for (i = 0, zn = zones; i < numzones; ++i, ++zn)
508 	{
509 		arc << zn->Environment;
510 	}
511 }
512 
Serialize(FArchive & arc)513 void extsector_t::Serialize(FArchive &arc)
514 {
515 	arc << FakeFloor.Sectors
516 		<< Midtex.Floor.AttachedLines
517 		<< Midtex.Floor.AttachedSectors
518 		<< Midtex.Ceiling.AttachedLines
519 		<< Midtex.Ceiling.AttachedSectors
520 		<< Linked.Floor.Sectors
521 		<< Linked.Ceiling.Sectors;
522 }
523 
operator <<(FArchive & arc,side_t::part & p)524 FArchive &operator<< (FArchive &arc, side_t::part &p)
525 {
526 	arc << p.xoffset << p.yoffset << p.interpolation << p.texture
527 		<< p.xscale << p.yscale;// << p.Light;
528 	return arc;
529 }
530 
operator <<(FArchive & arc,sector_t::splane & p)531 FArchive &operator<< (FArchive &arc, sector_t::splane &p)
532 {
533 	arc << p.xform.xoffs << p.xform.yoffs << p.xform.xscale << p.xform.yscale
534 		<< p.xform.angle << p.xform.base_yoffs << p.xform.base_angle
535 		<< p.Flags << p.Light << p.Texture << p.TexZ << p.alpha;
536 	return arc;
537 }
538 
539 
540 //
541 // Thinkers
542 //
543 
544 //
545 // P_ArchiveThinkers
546 //
547 
P_SerializeThinkers(FArchive & arc,bool hubLoad)548 void P_SerializeThinkers (FArchive &arc, bool hubLoad)
549 {
550 	DImpactDecal::SerializeTime (arc);
551 	DThinker::SerializeAll (arc, hubLoad);
552 }
553 
554 //==========================================================================
555 //
556 // ArchiveSounds
557 //
558 //==========================================================================
559 
P_SerializeSounds(FArchive & arc)560 void P_SerializeSounds (FArchive &arc)
561 {
562 	S_SerializeSounds (arc);
563 	DSeqNode::SerializeSequences (arc);
564 	char *name = NULL;
565 	BYTE order;
566 
567 	if (arc.IsStoring ())
568 	{
569 		order = S_GetMusic (&name);
570 	}
571 	arc << name << order;
572 	if (arc.IsLoading ())
573 	{
574 		if (!S_ChangeMusic (name, order))
575 			if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid))
576 				S_ChangeMusic (level.Music, level.musicorder);
577 	}
578 	delete[] name;
579 }
580 
581 //==========================================================================
582 //
583 // ArchivePolyobjs
584 //
585 //==========================================================================
586 #define ASEG_POLYOBJS	104
587 
P_SerializePolyobjs(FArchive & arc)588 void P_SerializePolyobjs (FArchive &arc)
589 {
590 	int i;
591 	FPolyObj *po;
592 
593 	if (arc.IsStoring ())
594 	{
595 		int seg = ASEG_POLYOBJS;
596 		arc << seg << po_NumPolyobjs;
597 		for(i = 0, po = polyobjs; i < po_NumPolyobjs; i++, po++)
598 		{
599 			arc << po->tag << po->angle << po->StartSpot.x <<
600 				po->StartSpot.y << po->interpolation;
601   		}
602 	}
603 	else
604 	{
605 		int data;
606 		angle_t angle;
607 		fixed_t deltaX, deltaY;
608 
609 		arc << data;
610 		if (data != ASEG_POLYOBJS)
611 			I_Error ("Polyobject marker missing");
612 
613 		arc << data;
614 		if (data != po_NumPolyobjs)
615 		{
616 			I_Error ("UnarchivePolyobjs: Bad polyobj count");
617 		}
618 		for (i = 0, po = polyobjs; i < po_NumPolyobjs; i++, po++)
619 		{
620 			arc << data;
621 			if (data != po->tag)
622 			{
623 				I_Error ("UnarchivePolyobjs: Invalid polyobj tag");
624 			}
625 			arc << angle;
626 			po->RotatePolyobj (angle, true);
627 			arc << deltaX << deltaY << po->interpolation;
628 			deltaX -= po->StartSpot.x;
629 			deltaY -= po->StartSpot.y;
630 			po->MovePolyobj (deltaX, deltaY, true);
631 		}
632 	}
633 }
634 
635 //==========================================================================
636 //
637 // RecalculateDrawnSubsectors
638 //
639 // In case the subsector data is unusable this function tries to reconstruct
640 // if from the linedefs' ML_MAPPED info.
641 //
642 //==========================================================================
643 
RecalculateDrawnSubsectors()644 void RecalculateDrawnSubsectors()
645 {
646 	for(int i=0;i<numsubsectors;i++)
647 	{
648 		subsector_t *sub = &subsectors[i];
649 		for(unsigned int j=0;j<sub->numlines;j++)
650 		{
651 			if (sub->firstline[j].linedef != NULL &&
652 				(sub->firstline[j].linedef->flags & ML_MAPPED))
653 			{
654 				sub->flags |= SSECF_DRAWN;
655 			}
656 		}
657 	}
658 }
659 
660 //==========================================================================
661 //
662 // ArchiveSubsectors
663 //
664 //==========================================================================
665 
P_SerializeSubsectors(FArchive & arc)666 void P_SerializeSubsectors(FArchive &arc)
667 {
668 	int num_verts, num_subs, num_nodes;
669 	BYTE by;
670 
671 	if (arc.IsStoring())
672 	{
673 		if (hasglnodes)
674 		{
675 			arc << numvertexes << numsubsectors << numnodes;	// These are only for verification
676 			for(int i=0;i<numsubsectors;i+=8)
677 			{
678 				by = 0;
679 				for(int j=0;j<8;j++)
680 				{
681 					if (i+j<numsubsectors && (subsectors[i+j].flags & SSECF_DRAWN))
682 					{
683 						by |= (1<<j);
684 					}
685 				}
686 				arc << by;
687 			}
688 		}
689 		else
690 		{
691 			int v = 0;
692 			arc << v << v << v;
693 		}
694 	}
695 	else
696 	{
697 		arc << num_verts << num_subs << num_nodes;
698 		if (num_verts != numvertexes ||
699 			num_subs != numsubsectors ||
700 			num_nodes != numnodes)
701 		{
702 			// Nodes don't match - we can't use this info
703 			for(int i=0;i<num_subs;i+=8)
704 			{
705 				// Skip the subsector info.
706 				arc << by;
707 			}
708 			if (hasglnodes)
709 			{
710 				RecalculateDrawnSubsectors();
711 			}
712 			return;
713 		}
714 		else
715 		{
716 			for(int i=0;i<numsubsectors;i+=8)
717 			{
718 				arc << by;
719 				for(int j=0;j<8;j++)
720 				{
721 					if ((by & (1<<j)) && i+j<numsubsectors)
722 					{
723 						subsectors[i+j].flags |= SSECF_DRAWN;
724 					}
725 				}
726 			}
727 		}
728 	}
729 }
730