1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file  g_demo.c
12 /// \brief Demo recording and playback
13 
14 #include "doomdef.h"
15 #include "console.h"
16 #include "d_main.h"
17 #include "d_player.h"
18 #include "d_clisrv.h"
19 #include "p_setup.h"
20 #include "i_system.h"
21 #include "m_random.h"
22 #include "p_local.h"
23 #include "r_draw.h"
24 #include "r_main.h"
25 #include "g_game.h"
26 #include "g_demo.h"
27 #include "m_misc.h"
28 #include "m_menu.h"
29 #include "m_argv.h"
30 #include "hu_stuff.h"
31 #include "z_zone.h"
32 #include "i_video.h"
33 #include "byteptr.h"
34 #include "i_joy.h"
35 #include "r_local.h"
36 #include "r_skins.h"
37 #include "y_inter.h"
38 #include "v_video.h"
39 #include "lua_hook.h"
40 #include "md5.h" // demo checksums
41 
42 boolean timingdemo; // if true, exit with report on completion
43 boolean nodrawers; // for comparative timing purposes
44 boolean noblit; // for comparative timing purposes
45 tic_t demostarttime; // for comparative timing purposes
46 
47 static char demoname[64];
48 boolean demorecording;
49 boolean demoplayback;
50 boolean titledemo; // Title Screen demo can be cancelled by any key
51 static UINT8 *demobuffer = NULL;
52 static UINT8 *demo_p, *demotime_p;
53 static UINT8 *demoend;
54 static UINT8 demoflags;
55 static UINT16 demoversion;
56 boolean singledemo; // quit after playing a demo from cmdline
57 boolean demo_start; // don't start playing demo right away
58 boolean demosynced = true; // console warning message
59 
60 boolean metalrecording; // recording as metal sonic
61 mobj_t *metalplayback;
62 static UINT8 *metalbuffer = NULL;
63 static UINT8 *metal_p;
64 static UINT16 metalversion;
65 
66 // extra data stuff (events registered this frame while recording)
67 static struct {
68 	UINT8 flags; // EZT flags
69 
70 	// EZT_COLOR
71 	UINT16 color, lastcolor;
72 
73 	// EZT_SCALE
74 	fixed_t scale, lastscale;
75 
76 	// EZT_HIT
77 	UINT16 hits;
78 	mobj_t **hitlist;
79 } ghostext;
80 
81 // Your naming conventions are stupid and useless.
82 // There is no conflict here.
83 typedef struct demoghost {
84 	UINT8 checksum[16];
85 	UINT8 *buffer, *p, fadein;
86 	UINT16 color;
87 	UINT16 version;
88 	mobj_t oldmo, *mo;
89 	struct demoghost *next;
90 } demoghost;
91 demoghost *ghosts = NULL;
92 
93 //
94 // DEMO RECORDING
95 //
96 
97 #define DEMOVERSION 0x000e
98 #define DEMOHEADER  "\xF0" "SRB2Replay" "\x0F"
99 
100 #define DF_GHOST        0x01 // This demo contains ghost data too!
101 #define DF_RECORDATTACK 0x02 // This demo is from record attack and contains its final completion time, score, and rings!
102 #define DF_NIGHTSATTACK 0x04 // This demo is from NiGHTS attack and contains its time left, score, and mares!
103 #define DF_ATTACKMASK   0x06 // This demo is from ??? attack and contains ???
104 #define DF_ATTACKSHIFT  1
105 
106 // For demos
107 #define ZT_FWD     0x01
108 #define ZT_SIDE    0x02
109 #define ZT_ANGLE   0x04
110 #define ZT_BUTTONS 0x08
111 #define ZT_AIMING  0x10
112 #define DEMOMARKER 0x80 // demoend
113 #define METALDEATH 0x44
114 #define METALSNICE 0x69
115 
116 static ticcmd_t oldcmd;
117 
118 // For Metal Sonic and time attack ghosts
119 #define GZT_XYZ    0x01
120 #define GZT_MOMXY  0x02
121 #define GZT_MOMZ   0x04
122 #define GZT_ANGLE  0x08
123 #define GZT_FRAME  0x10 // Animation frame
124 #define GZT_SPR2   0x20 // Player animations
125 #define GZT_EXTRA  0x40
126 #define GZT_FOLLOW 0x80 // Followmobj
127 
128 // GZT_EXTRA flags
129 #define EZT_THOK   0x01 // Spawned a thok object
130 #define EZT_SPIN   0x02 // Because one type of thok object apparently wasn't enough
131 #define EZT_REV    0x03 // And two types wasn't enough either yet
132 #define EZT_THOKMASK 0x03
133 #define EZT_COLOR  0x04 // Changed color (Super transformation, Mario fireflowers/invulnerability, etc.)
134 #define EZT_FLIP   0x08 // Reversed gravity
135 #define EZT_SCALE  0x10 // Changed size
136 #define EZT_HIT    0x20 // Damaged a mobj
137 #define EZT_SPRITE 0x40 // Changed sprite set completely out of PLAY (NiGHTS, SOCs, whatever)
138 #define EZT_HEIGHT 0x80 // Changed height
139 
140 // GZT_FOLLOW flags
141 #define FZT_SPAWNED 0x01 // just been spawned
142 #define FZT_SKIN 0x02 // has skin
143 #define FZT_LINKDRAW 0x04 // has linkdraw (combine with spawned only)
144 #define FZT_COLORIZED 0x08 // colorized (ditto)
145 #define FZT_SCALE 0x10 // different scale to object
146 // spare FZT slots 0x20 to 0x80
147 
148 static mobj_t oldmetal, oldghost;
149 
G_SaveMetal(UINT8 ** buffer)150 void G_SaveMetal(UINT8 **buffer)
151 {
152 	I_Assert(buffer != NULL && *buffer != NULL);
153 
154 	WRITEUINT32(*buffer, metal_p - metalbuffer);
155 }
156 
G_LoadMetal(UINT8 ** buffer)157 void G_LoadMetal(UINT8 **buffer)
158 {
159 	I_Assert(buffer != NULL && *buffer != NULL);
160 
161 	G_DoPlayMetal();
162 	metal_p = metalbuffer + READUINT32(*buffer);
163 }
164 
165 
G_ReadDemoTiccmd(ticcmd_t * cmd,INT32 playernum)166 void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
167 {
168 	UINT8 ziptic;
169 
170 	if (!demo_p || !demo_start)
171 		return;
172 	ziptic = READUINT8(demo_p);
173 
174 	if (ziptic & ZT_FWD)
175 		oldcmd.forwardmove = READSINT8(demo_p);
176 	if (ziptic & ZT_SIDE)
177 		oldcmd.sidemove = READSINT8(demo_p);
178 	if (ziptic & ZT_ANGLE)
179 		oldcmd.angleturn = READINT16(demo_p);
180 	if (ziptic & ZT_BUTTONS)
181 		oldcmd.buttons = (oldcmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) | (READUINT16(demo_p) & ~(BT_CAMLEFT|BT_CAMRIGHT));
182 	if (ziptic & ZT_AIMING)
183 		oldcmd.aiming = READINT16(demo_p);
184 
185 	G_CopyTiccmd(cmd, &oldcmd, 1);
186 	players[playernum].angleturn = cmd->angleturn;
187 
188 	if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER)
189 	{
190 		// end of demo data stream
191 		G_CheckDemoStatus();
192 		return;
193 	}
194 }
195 
G_WriteDemoTiccmd(ticcmd_t * cmd,INT32 playernum)196 void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
197 {
198 	char ziptic = 0;
199 	UINT8 *ziptic_p;
200 	(void)playernum;
201 
202 	if (!demo_p)
203 		return;
204 	ziptic_p = demo_p++; // the ziptic, written at the end of this function
205 
206 	if (cmd->forwardmove != oldcmd.forwardmove)
207 	{
208 		WRITEUINT8(demo_p,cmd->forwardmove);
209 		oldcmd.forwardmove = cmd->forwardmove;
210 		ziptic |= ZT_FWD;
211 	}
212 
213 	if (cmd->sidemove != oldcmd.sidemove)
214 	{
215 		WRITEUINT8(demo_p,cmd->sidemove);
216 		oldcmd.sidemove = cmd->sidemove;
217 		ziptic |= ZT_SIDE;
218 	}
219 
220 	if (cmd->angleturn != oldcmd.angleturn)
221 	{
222 		WRITEINT16(demo_p,cmd->angleturn);
223 		oldcmd.angleturn = cmd->angleturn;
224 		ziptic |= ZT_ANGLE;
225 	}
226 
227 	if (cmd->buttons != oldcmd.buttons)
228 	{
229 		WRITEUINT16(demo_p,cmd->buttons);
230 		oldcmd.buttons = cmd->buttons;
231 		ziptic |= ZT_BUTTONS;
232 	}
233 
234 	if (cmd->aiming != oldcmd.aiming)
235 	{
236 		WRITEINT16(demo_p,cmd->aiming);
237 		oldcmd.aiming = cmd->aiming;
238 		ziptic |= ZT_AIMING;
239 	}
240 
241 	*ziptic_p = ziptic;
242 
243 	// attention here for the ticcmd size!
244 	// latest demos with mouse aiming byte in ticcmd
245 	if (!(demoflags & DF_GHOST) && ziptic_p > demoend - 9)
246 	{
247 		G_CheckDemoStatus(); // no more space
248 		return;
249 	}
250 }
251 
G_GhostAddThok(void)252 void G_GhostAddThok(void)
253 {
254 	if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST)))
255 		return;
256 	ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_THOK;
257 }
258 
G_GhostAddSpin(void)259 void G_GhostAddSpin(void)
260 {
261 	if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST)))
262 		return;
263 	ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_SPIN;
264 }
265 
G_GhostAddRev(void)266 void G_GhostAddRev(void)
267 {
268 	if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST)))
269 		return;
270 	ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_REV;
271 }
272 
G_GhostAddFlip(void)273 void G_GhostAddFlip(void)
274 {
275 	if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST)))
276 		return;
277 	ghostext.flags |= EZT_FLIP;
278 }
279 
G_GhostAddColor(ghostcolor_t color)280 void G_GhostAddColor(ghostcolor_t color)
281 {
282 	if (!demorecording || !(demoflags & DF_GHOST))
283 		return;
284 	if (ghostext.lastcolor == (UINT16)color)
285 	{
286 		ghostext.flags &= ~EZT_COLOR;
287 		return;
288 	}
289 	ghostext.flags |= EZT_COLOR;
290 	ghostext.color = (UINT16)color;
291 }
292 
G_GhostAddScale(fixed_t scale)293 void G_GhostAddScale(fixed_t scale)
294 {
295 	if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST)))
296 		return;
297 	if (ghostext.lastscale == scale)
298 	{
299 		ghostext.flags &= ~EZT_SCALE;
300 		return;
301 	}
302 	ghostext.flags |= EZT_SCALE;
303 	ghostext.scale = scale;
304 }
305 
G_GhostAddHit(mobj_t * victim)306 void G_GhostAddHit(mobj_t *victim)
307 {
308 	if (!demorecording || !(demoflags & DF_GHOST))
309 		return;
310 	ghostext.flags |= EZT_HIT;
311 	ghostext.hits++;
312 	ghostext.hitlist = Z_Realloc(ghostext.hitlist, ghostext.hits * sizeof(mobj_t *), PU_LEVEL, NULL);
313 	ghostext.hitlist[ghostext.hits-1] = victim;
314 }
315 
G_WriteGhostTic(mobj_t * ghost)316 void G_WriteGhostTic(mobj_t *ghost)
317 {
318 	char ziptic = 0;
319 	UINT8 *ziptic_p;
320 	UINT32 i;
321 	fixed_t height;
322 
323 	if (!demo_p)
324 		return;
325 	if (!(demoflags & DF_GHOST))
326 		return; // No ghost data to write.
327 
328 	ziptic_p = demo_p++; // the ziptic, written at the end of this function
329 
330 	#define MAXMOM (0xFFFF<<8)
331 
332 	// GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic.
333 	if (abs(ghost->x-oldghost.x) > MAXMOM
334 	|| abs(ghost->y-oldghost.y) > MAXMOM
335 	|| abs(ghost->z-oldghost.z) > MAXMOM)
336 	{
337 		oldghost.x = ghost->x;
338 		oldghost.y = ghost->y;
339 		oldghost.z = ghost->z;
340 		ziptic |= GZT_XYZ;
341 		WRITEFIXED(demo_p,oldghost.x);
342 		WRITEFIXED(demo_p,oldghost.y);
343 		WRITEFIXED(demo_p,oldghost.z);
344 	}
345 	else
346 	{
347 		// For moving normally:
348 		fixed_t momx = ghost->x-oldghost.x;
349 		fixed_t momy = ghost->y-oldghost.y;
350 		if (momx != oldghost.momx
351 		|| momy != oldghost.momy)
352 		{
353 			oldghost.momx = momx;
354 			oldghost.momy = momy;
355 			ziptic |= GZT_MOMXY;
356 			WRITEFIXED(demo_p,momx);
357 			WRITEFIXED(demo_p,momy);
358 		}
359 		momx = ghost->z-oldghost.z;
360 		if (momx != oldghost.momz)
361 		{
362 			oldghost.momz = momx;
363 			ziptic |= GZT_MOMZ;
364 			WRITEFIXED(demo_p,momx);
365 		}
366 
367 		// This SHOULD set oldghost.x/y/z to match ghost->x/y/z
368 		oldghost.x += oldghost.momx;
369 		oldghost.y += oldghost.momy;
370 		oldghost.z += oldghost.momz;
371 	}
372 
373 	#undef MAXMOM
374 
375 	// Only store the 8 most relevant bits of angle
376 	// because exact values aren't too easy to discern to begin with when only 8 angles have different sprites
377 	// and it does not affect this mode of movement at all anyway.
378 	if (ghost->player && ghost->player->drawangle>>24 != oldghost.angle)
379 	{
380 		oldghost.angle = ghost->player->drawangle>>24;
381 		ziptic |= GZT_ANGLE;
382 		WRITEUINT8(demo_p,oldghost.angle);
383 	}
384 
385 	// Store the sprite frame.
386 	if ((ghost->frame & FF_FRAMEMASK) != oldghost.frame)
387 	{
388 		oldghost.frame = (ghost->frame & FF_FRAMEMASK);
389 		ziptic |= GZT_FRAME;
390 		WRITEUINT8(demo_p,oldghost.frame);
391 	}
392 
393 	if (ghost->sprite == SPR_PLAY
394 	&& ghost->sprite2 != oldghost.sprite2)
395 	{
396 		oldghost.sprite2 = ghost->sprite2;
397 		ziptic |= GZT_SPR2;
398 		WRITEUINT8(demo_p,oldghost.sprite2);
399 	}
400 
401 	// Check for sprite set changes
402 	if (ghost->sprite != oldghost.sprite)
403 	{
404 		oldghost.sprite = ghost->sprite;
405 		ghostext.flags |= EZT_SPRITE;
406 	}
407 
408 	if ((height = FixedDiv(ghost->height, ghost->scale)) != oldghost.height)
409 	{
410 		oldghost.height = height;
411 		ghostext.flags |= EZT_HEIGHT;
412 	}
413 
414 	if (ghostext.flags)
415 	{
416 		ziptic |= GZT_EXTRA;
417 
418 		if (ghostext.color == ghostext.lastcolor)
419 			ghostext.flags &= ~EZT_COLOR;
420 		if (ghostext.scale == ghostext.lastscale)
421 			ghostext.flags &= ~EZT_SCALE;
422 
423 		WRITEUINT8(demo_p,ghostext.flags);
424 		if (ghostext.flags & EZT_COLOR)
425 		{
426 			WRITEUINT16(demo_p,ghostext.color);
427 			ghostext.lastcolor = ghostext.color;
428 		}
429 		if (ghostext.flags & EZT_SCALE)
430 		{
431 			WRITEFIXED(demo_p,ghostext.scale);
432 			ghostext.lastscale = ghostext.scale;
433 		}
434 		if (ghostext.flags & EZT_HIT)
435 		{
436 			WRITEUINT16(demo_p,ghostext.hits);
437 			for (i = 0; i < ghostext.hits; i++)
438 			{
439 				mobj_t *mo = ghostext.hitlist[i];
440 				//WRITEUINT32(demo_p,UINT32_MAX); // reserved for some method of determining exactly which mobj this is. (mobjnum doesn't work here.)
441 				WRITEUINT32(demo_p,mo->type);
442 				WRITEUINT16(demo_p,(UINT16)mo->health);
443 				WRITEFIXED(demo_p,mo->x);
444 				WRITEFIXED(demo_p,mo->y);
445 				WRITEFIXED(demo_p,mo->z);
446 				WRITEANGLE(demo_p,mo->angle);
447 			}
448 			Z_Free(ghostext.hitlist);
449 			ghostext.hits = 0;
450 			ghostext.hitlist = NULL;
451 		}
452 		if (ghostext.flags & EZT_SPRITE)
453 			WRITEUINT16(demo_p,oldghost.sprite);
454 		if (ghostext.flags & EZT_HEIGHT)
455 		{
456 			WRITEFIXED(demo_p, height);
457 		}
458 		ghostext.flags = 0;
459 	}
460 
461 	if (ghost->player && ghost->player->followmobj && !(ghost->player->followmobj->sprite == SPR_NULL || (ghost->player->followmobj->flags2 & MF2_DONTDRAW))) // bloats tails runs but what can ya do
462 	{
463 		fixed_t temp;
464 		UINT8 *followtic_p = demo_p++;
465 		UINT8 followtic = 0;
466 
467 		ziptic |= GZT_FOLLOW;
468 
469 		if (ghost->player->followmobj->skin)
470 			followtic |= FZT_SKIN;
471 
472 		if (!(oldghost.flags2 & MF2_AMBUSH))
473 		{
474 			followtic |= FZT_SPAWNED;
475 			WRITEINT16(demo_p,ghost->player->followmobj->info->height>>FRACBITS);
476 			if (ghost->player->followmobj->flags2 & MF2_LINKDRAW)
477 				followtic |= FZT_LINKDRAW;
478 			if (ghost->player->followmobj->colorized)
479 				followtic |= FZT_COLORIZED;
480 			if (followtic & FZT_SKIN)
481 				WRITEUINT8(demo_p,(UINT8)(((skin_t *)(ghost->player->followmobj->skin))-skins));
482 			oldghost.flags2 |= MF2_AMBUSH;
483 		}
484 
485 		if (ghost->player->followmobj->scale != ghost->scale)
486 		{
487 			followtic |= FZT_SCALE;
488 			WRITEFIXED(demo_p,ghost->player->followmobj->scale);
489 		}
490 
491 		temp = ghost->player->followmobj->x-ghost->x;
492 		WRITEFIXED(demo_p,temp);
493 		temp = ghost->player->followmobj->y-ghost->y;
494 		WRITEFIXED(demo_p,temp);
495 		temp = ghost->player->followmobj->z-ghost->z;
496 		WRITEFIXED(demo_p,temp);
497 		if (followtic & FZT_SKIN)
498 			WRITEUINT8(demo_p,ghost->player->followmobj->sprite2);
499 		WRITEUINT16(demo_p,ghost->player->followmobj->sprite);
500 		WRITEUINT8(demo_p,(ghost->player->followmobj->frame & FF_FRAMEMASK));
501 		WRITEUINT16(demo_p,ghost->player->followmobj->color);
502 
503 		*followtic_p = followtic;
504 	}
505 	else
506 		oldghost.flags2 &= ~MF2_AMBUSH;
507 
508 	*ziptic_p = ziptic;
509 
510 	// attention here for the ticcmd size!
511 	// latest demos with mouse aiming byte in ticcmd
512 	if (demo_p >= demoend - (13 + 9 + 9))
513 	{
514 		G_CheckDemoStatus(); // no more space
515 		return;
516 	}
517 }
518 
519 // Uses ghost data to do consistency checks on your position.
520 // This fixes desynchronising demos when fighting eggman.
G_ConsGhostTic(void)521 void G_ConsGhostTic(void)
522 {
523 	UINT8 ziptic;
524 	UINT16 px,py,pz,gx,gy,gz;
525 	mobj_t *testmo;
526 
527 	if (!demo_p || !demo_start)
528 		return;
529 	if (!(demoflags & DF_GHOST))
530 		return; // No ghost data to use.
531 
532 	testmo = players[0].mo;
533 
534 	// Grab ghost data.
535 	ziptic = READUINT8(demo_p);
536 	if (ziptic & GZT_XYZ)
537 	{
538 		oldghost.x = READFIXED(demo_p);
539 		oldghost.y = READFIXED(demo_p);
540 		oldghost.z = READFIXED(demo_p);
541 	}
542 	else
543 	{
544 		if (ziptic & GZT_MOMXY)
545 		{
546 			oldghost.momx = (demoversion < 0x000e) ? READINT16(demo_p)<<8 : READFIXED(demo_p);
547 			oldghost.momy = (demoversion < 0x000e) ? READINT16(demo_p)<<8 : READFIXED(demo_p);
548 		}
549 		if (ziptic & GZT_MOMZ)
550 			oldghost.momz = (demoversion < 0x000e) ? READINT16(demo_p)<<8 : READFIXED(demo_p);
551 		oldghost.x += oldghost.momx;
552 		oldghost.y += oldghost.momy;
553 		oldghost.z += oldghost.momz;
554 	}
555 	if (ziptic & GZT_ANGLE)
556 		demo_p++;
557 	if (ziptic & GZT_FRAME)
558 		demo_p++;
559 	if (ziptic & GZT_SPR2)
560 		demo_p++;
561 
562 	if (ziptic & GZT_EXTRA)
563 	{ // But wait, there's more!
564 		UINT8 xziptic = READUINT8(demo_p);
565 		if (xziptic & EZT_COLOR)
566 			demo_p += (demoversion==0x000c) ? 1 : sizeof(UINT16);
567 		if (xziptic & EZT_SCALE)
568 			demo_p += sizeof(fixed_t);
569 		if (xziptic & EZT_HIT)
570 		{ // Resync mob damage.
571 			UINT16 i, count = READUINT16(demo_p);
572 			thinker_t *th;
573 			mobj_t *mobj;
574 
575 			UINT32 type;
576 			UINT16 health;
577 			fixed_t x;
578 			fixed_t y;
579 			fixed_t z;
580 
581 			for (i = 0; i < count; i++)
582 			{
583 				//demo_p += 4; // reserved.
584 				type = READUINT32(demo_p);
585 				health = READUINT16(demo_p);
586 				x = READFIXED(demo_p);
587 				y = READFIXED(demo_p);
588 				z = READFIXED(demo_p);
589 				demo_p += sizeof(angle_t); // angle, unnecessary for cons.
590 
591 				mobj = NULL;
592 				for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
593 				{
594 					if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
595 						continue;
596 					mobj = (mobj_t *)th;
597 					if (mobj->type == (mobjtype_t)type && mobj->x == x && mobj->y == y && mobj->z == z)
598 						break;
599 				}
600 				if (th != &thlist[THINK_MOBJ] && mobj->health != health) // Wasn't damaged?! This is desync! Fix it!
601 				{
602 					if (demosynced)
603 						CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n"));
604 					demosynced = false;
605 					P_DamageMobj(mobj, players[0].mo, players[0].mo, 1, 0);
606 				}
607 			}
608 		}
609 		if (xziptic & EZT_SPRITE)
610 			demo_p += sizeof(UINT16);
611 		if (xziptic & EZT_HEIGHT)
612 			demo_p += (demoversion < 0x000e) ? sizeof(INT16) : sizeof(fixed_t);
613 	}
614 
615 	if (ziptic & GZT_FOLLOW)
616 	{ // Even more...
617 		UINT8 followtic = READUINT8(demo_p);
618 		if (followtic & FZT_SPAWNED)
619 		{
620 			demo_p += sizeof(INT16);
621 			if (followtic & FZT_SKIN)
622 				demo_p++;
623 		}
624 		if (followtic & FZT_SCALE)
625 			demo_p += sizeof(fixed_t);
626 		// momx, momy and momz
627 		demo_p += (demoversion < 0x000e) ? sizeof(INT16) * 3 : sizeof(fixed_t) * 3;
628 		if (followtic & FZT_SKIN)
629 			demo_p++;
630 		demo_p += sizeof(UINT16);
631 		demo_p++;
632 		demo_p += (demoversion==0x000c) ? 1 : sizeof(UINT16);
633 	}
634 
635 	// Re-synchronise
636 	px = testmo->x>>FRACBITS;
637 	py = testmo->y>>FRACBITS;
638 	pz = testmo->z>>FRACBITS;
639 	gx = oldghost.x>>FRACBITS;
640 	gy = oldghost.y>>FRACBITS;
641 	gz = oldghost.z>>FRACBITS;
642 
643 	if (px != gx || py != gy || pz != gz)
644 	{
645 		if (demosynced)
646 			CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n"));
647 		demosynced = false;
648 
649 		P_UnsetThingPosition(testmo);
650 		testmo->x = oldghost.x;
651 		testmo->y = oldghost.y;
652 		P_SetThingPosition(testmo);
653 		testmo->z = oldghost.z;
654 	}
655 
656 	if (*demo_p == DEMOMARKER)
657 	{
658 		// end of demo data stream
659 		G_CheckDemoStatus();
660 		return;
661 	}
662 }
663 
G_GhostTicker(void)664 void G_GhostTicker(void)
665 {
666 	demoghost *g,*p;
667 	for(g = ghosts, p = NULL; g; g = g->next)
668 	{
669 		// Skip normal demo data.
670 		UINT8 ziptic = READUINT8(g->p);
671 		UINT8 xziptic = 0;
672 		if (ziptic & ZT_FWD)
673 			g->p++;
674 		if (ziptic & ZT_SIDE)
675 			g->p++;
676 		if (ziptic & ZT_ANGLE)
677 			g->p += 2;
678 		if (ziptic & ZT_BUTTONS)
679 			g->p += 2;
680 		if (ziptic & ZT_AIMING)
681 			g->p += 2;
682 
683 		// Grab ghost data.
684 		ziptic = READUINT8(g->p);
685 		if (ziptic & GZT_XYZ)
686 		{
687 			g->oldmo.x = READFIXED(g->p);
688 			g->oldmo.y = READFIXED(g->p);
689 			g->oldmo.z = READFIXED(g->p);
690 		}
691 		else
692 		{
693 			if (ziptic & GZT_MOMXY)
694 			{
695 				g->oldmo.momx = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p);
696 				g->oldmo.momy = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p);
697 			}
698 			if (ziptic & GZT_MOMZ)
699 				g->oldmo.momz = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p);
700 			g->oldmo.x += g->oldmo.momx;
701 			g->oldmo.y += g->oldmo.momy;
702 			g->oldmo.z += g->oldmo.momz;
703 		}
704 		if (ziptic & GZT_ANGLE)
705 			g->mo->angle = READUINT8(g->p)<<24;
706 		if (ziptic & GZT_FRAME)
707 			g->oldmo.frame = READUINT8(g->p);
708 		if (ziptic & GZT_SPR2)
709 			g->oldmo.sprite2 = READUINT8(g->p);
710 
711 		// Update ghost
712 		P_UnsetThingPosition(g->mo);
713 		g->mo->x = g->oldmo.x;
714 		g->mo->y = g->oldmo.y;
715 		g->mo->z = g->oldmo.z;
716 		P_SetThingPosition(g->mo);
717 		g->mo->frame = g->oldmo.frame | tr_trans30<<FF_TRANSSHIFT;
718 		if (g->fadein)
719 		{
720 			g->mo->frame += (((--g->fadein)/6)<<FF_TRANSSHIFT); // this calc never exceeds 9 unless g->fadein is bad, and it's only set once, so...
721 			g->mo->flags2 &= ~MF2_DONTDRAW;
722 		}
723 		g->mo->sprite2 = g->oldmo.sprite2;
724 
725 		if (ziptic & GZT_EXTRA)
726 		{ // But wait, there's more!
727 			xziptic = READUINT8(g->p);
728 			if (xziptic & EZT_COLOR)
729 			{
730 				g->color = (g->version==0x000c) ? READUINT8(g->p) : READUINT16(g->p);
731 				switch(g->color)
732 				{
733 				default:
734 				case GHC_RETURNSKIN:
735 					g->mo->skin = g->oldmo.skin;
736 					/* FALLTHRU */
737 				case GHC_NORMAL: // Go back to skin color
738 					g->mo->color = g->oldmo.color;
739 					break;
740 				// Handled below
741 				case GHC_SUPER:
742 				case GHC_INVINCIBLE:
743 					break;
744 				case GHC_FIREFLOWER: // Fireflower
745 					g->mo->color = SKINCOLOR_WHITE;
746 					break;
747 				case GHC_NIGHTSSKIN: // not actually a colour
748 					g->mo->skin = &skins[DEFAULTNIGHTSSKIN];
749 					break;
750 				}
751 			}
752 			if (xziptic & EZT_FLIP)
753 				g->mo->eflags ^= MFE_VERTICALFLIP;
754 			if (xziptic & EZT_SCALE)
755 			{
756 				g->mo->destscale = READFIXED(g->p);
757 				if (g->mo->destscale != g->mo->scale)
758 					P_SetScale(g->mo, g->mo->destscale);
759 			}
760 			if (xziptic & EZT_THOKMASK)
761 			{ // Let's only spawn ONE of these per frame, thanks.
762 				mobj_t *mobj;
763 				UINT32 type = MT_NULL;
764 				if (g->mo->skin)
765 				{
766 					skin_t *skin = (skin_t *)g->mo->skin;
767 					switch (xziptic & EZT_THOKMASK)
768 					{
769 					case EZT_THOK:
770 						type = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem;
771 						break;
772 					case EZT_SPIN:
773 						type = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem;
774 						break;
775 					case EZT_REV:
776 						type = skin->revitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem;
777 						break;
778 					}
779 				}
780 				if (type != MT_NULL)
781 				{
782 					if (type == MT_GHOST)
783 					{
784 						mobj = P_SpawnGhostMobj(g->mo); // does a large portion of the work for us
785 						mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<<FF_TRANSSHIFT; // P_SpawnGhostMobj sets trans50, we want trans60
786 					}
787 					else
788 					{
789 						mobj = P_SpawnMobjFromMobj(g->mo, 0, 0, -FixedDiv(FixedMul(g->mo->info->height, g->mo->scale) - g->mo->height,3*FRACUNIT), MT_THOK);
790 						mobj->sprite = states[mobjinfo[type].spawnstate].sprite;
791 						mobj->frame = (states[mobjinfo[type].spawnstate].frame & FF_FRAMEMASK) | tr_trans60<<FF_TRANSSHIFT;
792 						mobj->color = g->mo->color;
793 						mobj->skin = g->mo->skin;
794 						P_SetScale(mobj, (mobj->destscale = g->mo->scale));
795 
796 						if (type == MT_THOK) // spintrail-specific modification for MT_THOK
797 						{
798 							mobj->frame = FF_TRANS80;
799 							mobj->fuse = mobj->tics;
800 						}
801 						mobj->tics = -1; // nope.
802 					}
803 					mobj->floorz = mobj->z;
804 					mobj->ceilingz = mobj->z+mobj->height;
805 					P_UnsetThingPosition(mobj);
806 					mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up...
807 					P_SetThingPosition(mobj);
808 					if (!mobj->fuse)
809 						mobj->fuse = 8;
810 					P_SetTarget(&mobj->target, g->mo);
811 				}
812 			}
813 			if (xziptic & EZT_HIT)
814 			{ // Spawn hit poofs for killing things!
815 				UINT16 i, count = READUINT16(g->p), health;
816 				UINT32 type;
817 				fixed_t x,y,z;
818 				angle_t angle;
819 				mobj_t *poof;
820 				for (i = 0; i < count; i++)
821 				{
822 					//g->p += 4; // reserved
823 					type = READUINT32(g->p);
824 					health = READUINT16(g->p);
825 					x = READFIXED(g->p);
826 					y = READFIXED(g->p);
827 					z = READFIXED(g->p);
828 					angle = READANGLE(g->p);
829 					if (!(mobjinfo[type].flags & MF_SHOOTABLE)
830 					|| !(mobjinfo[type].flags & (MF_ENEMY|MF_MONITOR))
831 					|| health != 0 || i >= 4) // only spawn for the first 4 hits per frame, to prevent ghosts from splode-spamming too bad.
832 						continue;
833 					poof = P_SpawnMobj(x, y, z, MT_GHOST);
834 					poof->angle = angle;
835 					poof->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up...
836 					poof->health = 0;
837 					P_SetMobjStateNF(poof, S_XPLD1);
838 				}
839 			}
840 			if (xziptic & EZT_SPRITE)
841 				g->mo->sprite = READUINT16(g->p);
842 			if (xziptic & EZT_HEIGHT)
843 			{
844 				fixed_t temp = (g->version < 0x000e) ? READINT16(g->p)<<FRACBITS : READFIXED(g->p);
845 				g->mo->height = FixedMul(temp, g->mo->scale);
846 			}
847 		}
848 
849 		// Tick ghost colors (Super and Mario Invincibility flashing)
850 		switch(g->color)
851 		{
852 		case GHC_SUPER: // Super (P_DoSuperStuff)
853 			if (g->mo->skin)
854 			{
855 				skin_t *skin = (skin_t *)g->mo->skin;
856 				g->mo->color = skin->supercolor;
857 			}
858 			else
859 				g->mo->color = SKINCOLOR_SUPERGOLD1;
860 			g->mo->color += abs( ( (signed)( (unsigned)leveltime >> 1 ) % 9) - 4);
861 			break;
862 		case GHC_INVINCIBLE: // Mario invincibility (P_CheckInvincibilityTimer)
863 			g->mo->color = (UINT16)(SKINCOLOR_RUBY + (leveltime % (FIRSTSUPERCOLOR - SKINCOLOR_RUBY))); // Passes through all saturated colours
864 			break;
865 		default:
866 			break;
867 		}
868 
869 #define follow g->mo->tracer
870 		if (ziptic & GZT_FOLLOW)
871 		{ // Even more...
872 			UINT8 followtic = READUINT8(g->p);
873 			fixed_t temp;
874 			if (followtic & FZT_SPAWNED)
875 			{
876 				if (follow)
877 					P_RemoveMobj(follow);
878 				P_SetTarget(&follow, P_SpawnMobjFromMobj(g->mo, 0, 0, 0, MT_GHOST));
879 				P_SetTarget(&follow->tracer, g->mo);
880 				follow->tics = -1;
881 				temp = READINT16(g->p)<<FRACBITS;
882 				follow->height = FixedMul(follow->scale, temp);
883 
884 				if (followtic & FZT_LINKDRAW)
885 					follow->flags2 |= MF2_LINKDRAW;
886 
887 				if (followtic & FZT_COLORIZED)
888 					follow->colorized = true;
889 
890 				if (followtic & FZT_SKIN)
891 					follow->skin = &skins[READUINT8(g->p)];
892 			}
893 			if (follow)
894 			{
895 				if (followtic & FZT_SCALE)
896 					follow->destscale = READFIXED(g->p);
897 				else
898 					follow->destscale = g->mo->destscale;
899 				if (follow->destscale != follow->scale)
900 					P_SetScale(follow, follow->destscale);
901 
902 				P_UnsetThingPosition(follow);
903 				temp = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p);
904 				follow->x = g->mo->x + temp;
905 				temp = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p);
906 				follow->y = g->mo->y + temp;
907 				temp = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p);
908 				follow->z = g->mo->z + temp;
909 				P_SetThingPosition(follow);
910 				if (followtic & FZT_SKIN)
911 					follow->sprite2 = READUINT8(g->p);
912 				else
913 					follow->sprite2 = 0;
914 				follow->sprite = READUINT16(g->p);
915 				follow->frame = (READUINT8(g->p)) | (g->mo->frame & FF_TRANSMASK);
916 				follow->angle = g->mo->angle;
917 				follow->color = (g->version==0x000c) ? READUINT8(g->p) : READUINT16(g->p);
918 
919 				if (!(followtic & FZT_SPAWNED))
920 				{
921 					if (xziptic & EZT_FLIP)
922 					{
923 						follow->flags2 ^= MF2_OBJECTFLIP;
924 						follow->eflags ^= MFE_VERTICALFLIP;
925 					}
926 				}
927 			}
928 		}
929 		else if (follow)
930 		{
931 			P_RemoveMobj(follow);
932 			P_SetTarget(&follow, NULL);
933 		}
934 		// Demo ends after ghost data.
935 		if (*g->p == DEMOMARKER)
936 		{
937 			g->mo->momx = g->mo->momy = g->mo->momz = 0;
938 #if 1 // freeze frame (maybe more useful for time attackers)
939 			g->mo->colorized = true;
940 			if (follow)
941 				follow->colorized = true;
942 #else // dissapearing act
943 			g->mo->fuse = TICRATE;
944 			if (follow)
945 				follow->fuse = TICRATE;
946 #endif
947 			if (p)
948 				p->next = g->next;
949 			else
950 				ghosts = g->next;
951 			Z_Free(g);
952 			continue;
953 		}
954 		p = g;
955 #undef follow
956 	}
957 }
958 
G_ReadMetalTic(mobj_t * metal)959 void G_ReadMetalTic(mobj_t *metal)
960 {
961 	UINT8 ziptic;
962 	UINT8 xziptic = 0;
963 
964 	if (!metal_p)
965 		return;
966 
967 	if (!metal->health)
968 	{
969 		G_StopMetalDemo();
970 		return;
971 	}
972 
973 	switch (*metal_p)
974 	{
975 		case METALSNICE:
976 			break;
977 		case METALDEATH:
978 			if (metal->tracer)
979 				P_RemoveMobj(metal->tracer);
980 			P_KillMobj(metal, NULL, NULL, 0);
981 			/* FALLTHRU */
982 		case DEMOMARKER:
983 		default:
984 			// end of demo data stream
985 			G_StopMetalDemo();
986 			return;
987 	}
988 	metal_p++;
989 
990 	ziptic = READUINT8(metal_p);
991 
992 	// Read changes from the tic
993 	if (ziptic & GZT_XYZ)
994 	{
995 		// make sure the values are read in the right order
996 		oldmetal.x = READFIXED(metal_p);
997 		oldmetal.y = READFIXED(metal_p);
998 		oldmetal.z = READFIXED(metal_p);
999 		P_TeleportMove(metal, oldmetal.x, oldmetal.y, oldmetal.z);
1000 		oldmetal.x = metal->x;
1001 		oldmetal.y = metal->y;
1002 		oldmetal.z = metal->z;
1003 	}
1004 	else
1005 	{
1006 		if (ziptic & GZT_MOMXY)
1007 		{
1008 			oldmetal.momx = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p);
1009 			oldmetal.momy = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p);
1010 		}
1011 		if (ziptic & GZT_MOMZ)
1012 			oldmetal.momz = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p);
1013 		oldmetal.x += oldmetal.momx;
1014 		oldmetal.y += oldmetal.momy;
1015 		oldmetal.z += oldmetal.momz;
1016 	}
1017 	if (ziptic & GZT_ANGLE)
1018 		metal->angle = READUINT8(metal_p)<<24;
1019 	if (ziptic & GZT_FRAME)
1020 		oldmetal.frame = READUINT32(metal_p);
1021 	if (ziptic & GZT_SPR2)
1022 		oldmetal.sprite2 = READUINT8(metal_p);
1023 
1024 	// Set movement, position, and angle
1025 	// oldmetal contains where you're supposed to be.
1026 	metal->momx = oldmetal.momx;
1027 	metal->momy = oldmetal.momy;
1028 	metal->momz = oldmetal.momz;
1029 	P_UnsetThingPosition(metal);
1030 	metal->x = oldmetal.x;
1031 	metal->y = oldmetal.y;
1032 	metal->z = oldmetal.z;
1033 	P_SetThingPosition(metal);
1034 	metal->frame = oldmetal.frame;
1035 	metal->sprite2 = oldmetal.sprite2;
1036 
1037 	if (ziptic & GZT_EXTRA)
1038 	{ // But wait, there's more!
1039 		xziptic = READUINT8(metal_p);
1040 		if (xziptic & EZT_FLIP)
1041 		{
1042 			metal->eflags ^= MFE_VERTICALFLIP;
1043 			metal->flags2 ^= MF2_OBJECTFLIP;
1044 		}
1045 		if (xziptic & EZT_SCALE)
1046 		{
1047 			metal->destscale = READFIXED(metal_p);
1048 			if (metal->destscale != metal->scale)
1049 				P_SetScale(metal, metal->destscale);
1050 		}
1051 		if (xziptic & EZT_THOKMASK)
1052 		{ // Let's only spawn ONE of these per frame, thanks.
1053 			mobj_t *mobj;
1054 			UINT32 type = MT_NULL;
1055 			if (metal->skin)
1056 			{
1057 				skin_t *skin = (skin_t *)metal->skin;
1058 				switch (xziptic & EZT_THOKMASK)
1059 				{
1060 				case EZT_THOK:
1061 					type = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem;
1062 					break;
1063 				case EZT_SPIN:
1064 					type = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem;
1065 					break;
1066 				case EZT_REV:
1067 					type = skin->revitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem;
1068 					break;
1069 				}
1070 			}
1071 			if (type != MT_NULL)
1072 			{
1073 				if (type == MT_GHOST)
1074 				{
1075 					mobj = P_SpawnGhostMobj(metal); // does a large portion of the work for us
1076 				}
1077 				else
1078 				{
1079 					mobj = P_SpawnMobjFromMobj(metal, 0, 0, -FixedDiv(FixedMul(metal->info->height, metal->scale) - metal->height,3*FRACUNIT), MT_THOK);
1080 					mobj->sprite = states[mobjinfo[type].spawnstate].sprite;
1081 					mobj->frame = states[mobjinfo[type].spawnstate].frame;
1082 					mobj->angle = metal->angle;
1083 					mobj->color = metal->color;
1084 					mobj->skin = metal->skin;
1085 					P_SetScale(mobj, (mobj->destscale = metal->scale));
1086 
1087 					if (type == MT_THOK) // spintrail-specific modification for MT_THOK
1088 					{
1089 						mobj->frame = FF_TRANS70;
1090 						mobj->fuse = mobj->tics;
1091 					}
1092 					mobj->tics = -1; // nope.
1093 				}
1094 				mobj->floorz = mobj->z;
1095 				mobj->ceilingz = mobj->z+mobj->height;
1096 				P_UnsetThingPosition(mobj);
1097 				mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up...
1098 				P_SetThingPosition(mobj);
1099 				if (!mobj->fuse)
1100 					mobj->fuse = 8;
1101 				P_SetTarget(&mobj->target, metal);
1102 			}
1103 		}
1104 		if (xziptic & EZT_SPRITE)
1105 			metal->sprite = READUINT16(metal_p);
1106 		if (xziptic & EZT_HEIGHT)
1107 		{
1108 			fixed_t temp = (metalversion < 0x000e) ? READINT16(metal_p)<<FRACBITS : READFIXED(metal_p);
1109 			metal->height = FixedMul(temp, metal->scale);
1110 		}
1111 	}
1112 
1113 #define follow metal->tracer
1114 		if (ziptic & GZT_FOLLOW)
1115 		{ // Even more...
1116 			UINT8 followtic = READUINT8(metal_p);
1117 			fixed_t temp;
1118 			if (followtic & FZT_SPAWNED)
1119 			{
1120 				if (follow)
1121 					P_RemoveMobj(follow);
1122 				P_SetTarget(&follow, P_SpawnMobjFromMobj(metal, 0, 0, 0, MT_GHOST));
1123 				P_SetTarget(&follow->tracer, metal);
1124 				follow->tics = -1;
1125 				temp = READINT16(metal_p)<<FRACBITS;
1126 				follow->height = FixedMul(follow->scale, temp);
1127 
1128 				if (followtic & FZT_LINKDRAW)
1129 					follow->flags2 |= MF2_LINKDRAW;
1130 
1131 				if (followtic & FZT_COLORIZED)
1132 					follow->colorized = true;
1133 
1134 				if (followtic & FZT_SKIN)
1135 					follow->skin = &skins[READUINT8(metal_p)];
1136 			}
1137 			if (follow)
1138 			{
1139 				if (followtic & FZT_SCALE)
1140 					follow->destscale = READFIXED(metal_p);
1141 				else
1142 					follow->destscale = metal->destscale;
1143 				if (follow->destscale != follow->scale)
1144 					P_SetScale(follow, follow->destscale);
1145 
1146 				P_UnsetThingPosition(follow);
1147 				temp = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p);
1148 				follow->x = metal->x + temp;
1149 				temp = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p);
1150 				follow->y = metal->y + temp;
1151 				temp = (metalversion < 0x000e) ? READINT16(metal_p)<<8 : READFIXED(metal_p);
1152 				follow->z = metal->z + temp;
1153 				P_SetThingPosition(follow);
1154 				if (followtic & FZT_SKIN)
1155 					follow->sprite2 = READUINT8(metal_p);
1156 				else
1157 					follow->sprite2 = 0;
1158 				follow->sprite = READUINT16(metal_p);
1159 				follow->frame = READUINT32(metal_p); // NOT & FF_FRAMEMASK here, so 32 bits
1160 				follow->angle = metal->angle;
1161 				follow->color = (metalversion==0x000c) ? READUINT8(metal_p) : READUINT16(metal_p);
1162 
1163 				if (!(followtic & FZT_SPAWNED))
1164 				{
1165 					if (xziptic & EZT_FLIP)
1166 					{
1167 						follow->flags2 ^= MF2_OBJECTFLIP;
1168 						follow->eflags ^= MFE_VERTICALFLIP;
1169 					}
1170 				}
1171 			}
1172 		}
1173 		else if (follow)
1174 		{
1175 			P_RemoveMobj(follow);
1176 			P_SetTarget(&follow, NULL);
1177 		}
1178 #undef follow
1179 }
1180 
G_WriteMetalTic(mobj_t * metal)1181 void G_WriteMetalTic(mobj_t *metal)
1182 {
1183 	UINT8 ziptic = 0;
1184 	UINT8 *ziptic_p;
1185 	fixed_t height;
1186 
1187 	if (!demo_p) // demo_p will be NULL until the race start linedef executor is activated!
1188 		return;
1189 
1190 	WRITEUINT8(demo_p, METALSNICE);
1191 	ziptic_p = demo_p++; // the ziptic, written at the end of this function
1192 
1193 	#define MAXMOM (0xFFFF<<8)
1194 
1195 	// GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic.
1196 	if (abs(metal->x-oldmetal.x) > MAXMOM
1197 	|| abs(metal->y-oldmetal.y) > MAXMOM
1198 	|| abs(metal->z-oldmetal.z) > MAXMOM)
1199 	{
1200 		oldmetal.x = metal->x;
1201 		oldmetal.y = metal->y;
1202 		oldmetal.z = metal->z;
1203 		ziptic |= GZT_XYZ;
1204 		WRITEFIXED(demo_p,oldmetal.x);
1205 		WRITEFIXED(demo_p,oldmetal.y);
1206 		WRITEFIXED(demo_p,oldmetal.z);
1207 	}
1208 	else
1209 	{
1210 		// For moving normally:
1211 		// Store movement as a fixed value
1212 		fixed_t momx = metal->x-oldmetal.x;
1213 		fixed_t momy = metal->y-oldmetal.y;
1214 		if (momx != oldmetal.momx
1215 		|| momy != oldmetal.momy)
1216 		{
1217 			oldmetal.momx = momx;
1218 			oldmetal.momy = momy;
1219 			ziptic |= GZT_MOMXY;
1220 			WRITEFIXED(demo_p,momx);
1221 			WRITEFIXED(demo_p,momy);
1222 		}
1223 		momx = metal->z-oldmetal.z;
1224 		if (momx != oldmetal.momz)
1225 		{
1226 			oldmetal.momz = momx;
1227 			ziptic |= GZT_MOMZ;
1228 			WRITEFIXED(demo_p,momx);
1229 		}
1230 
1231 		// This SHOULD set oldmetal.x/y/z to match metal->x/y/z
1232 		oldmetal.x += oldmetal.momx;
1233 		oldmetal.y += oldmetal.momy;
1234 		oldmetal.z += oldmetal.momz;
1235 	}
1236 
1237 	#undef MAXMOM
1238 
1239 	// Only store the 8 most relevant bits of angle
1240 	// because exact values aren't too easy to discern to begin with when only 8 angles have different sprites
1241 	// and it does not affect movement at all anyway.
1242 	if (metal->player && metal->player->drawangle>>24 != oldmetal.angle)
1243 	{
1244 		oldmetal.angle = metal->player->drawangle>>24;
1245 		ziptic |= GZT_ANGLE;
1246 		WRITEUINT8(demo_p,oldmetal.angle);
1247 	}
1248 
1249 	// Store the sprite frame.
1250 	if ((metal->frame & FF_FRAMEMASK) != oldmetal.frame)
1251 	{
1252 		oldmetal.frame = metal->frame; // NOT & FF_FRAMEMASK here, so 32 bits
1253 		ziptic |= GZT_FRAME;
1254 		WRITEUINT32(demo_p,oldmetal.frame);
1255 	}
1256 
1257 	if (metal->sprite == SPR_PLAY
1258 	&& metal->sprite2 != oldmetal.sprite2)
1259 	{
1260 		oldmetal.sprite2 = metal->sprite2;
1261 		ziptic |= GZT_SPR2;
1262 		WRITEUINT8(demo_p,oldmetal.sprite2);
1263 	}
1264 
1265 	// Check for sprite set changes
1266 	if (metal->sprite != oldmetal.sprite)
1267 	{
1268 		oldmetal.sprite = metal->sprite;
1269 		ghostext.flags |= EZT_SPRITE;
1270 	}
1271 
1272 	if ((height = FixedDiv(metal->height, metal->scale)) != oldmetal.height)
1273 	{
1274 		oldmetal.height = height;
1275 		ghostext.flags |= EZT_HEIGHT;
1276 	}
1277 
1278 	if (ghostext.flags & ~(EZT_COLOR|EZT_HIT)) // these two aren't handled by metal ever
1279 	{
1280 		ziptic |= GZT_EXTRA;
1281 
1282 		if (ghostext.scale == ghostext.lastscale)
1283 			ghostext.flags &= ~EZT_SCALE;
1284 
1285 		WRITEUINT8(demo_p,ghostext.flags);
1286 		if (ghostext.flags & EZT_SCALE)
1287 		{
1288 			WRITEFIXED(demo_p,ghostext.scale);
1289 			ghostext.lastscale = ghostext.scale;
1290 		}
1291 		if (ghostext.flags & EZT_SPRITE)
1292 			WRITEUINT16(demo_p,oldmetal.sprite);
1293 		if (ghostext.flags & EZT_HEIGHT)
1294 		{
1295 			WRITEFIXED(demo_p, height);
1296 		}
1297 		ghostext.flags = 0;
1298 	}
1299 
1300 	if (metal->player && metal->player->followmobj && !(metal->player->followmobj->sprite == SPR_NULL || (metal->player->followmobj->flags2 & MF2_DONTDRAW)))
1301 	{
1302 		fixed_t temp;
1303 		UINT8 *followtic_p = demo_p++;
1304 		UINT8 followtic = 0;
1305 
1306 		ziptic |= GZT_FOLLOW;
1307 
1308 		if (metal->player->followmobj->skin)
1309 			followtic |= FZT_SKIN;
1310 
1311 		if (!(oldmetal.flags2 & MF2_AMBUSH))
1312 		{
1313 			followtic |= FZT_SPAWNED;
1314 			WRITEINT16(demo_p,metal->player->followmobj->info->height>>FRACBITS);
1315 			if (metal->player->followmobj->flags2 & MF2_LINKDRAW)
1316 				followtic |= FZT_LINKDRAW;
1317 			if (metal->player->followmobj->colorized)
1318 				followtic |= FZT_COLORIZED;
1319 			if (followtic & FZT_SKIN)
1320 				WRITEUINT8(demo_p,(UINT8)(((skin_t *)(metal->player->followmobj->skin))-skins));
1321 			oldmetal.flags2 |= MF2_AMBUSH;
1322 		}
1323 
1324 		if (metal->player->followmobj->scale != metal->scale)
1325 		{
1326 			followtic |= FZT_SCALE;
1327 			WRITEFIXED(demo_p,metal->player->followmobj->scale);
1328 		}
1329 
1330 		temp = metal->player->followmobj->x-metal->x;
1331 		WRITEFIXED(demo_p,temp);
1332 		temp = metal->player->followmobj->y-metal->y;
1333 		WRITEFIXED(demo_p,temp);
1334 		temp = metal->player->followmobj->z-metal->z;
1335 		WRITEFIXED(demo_p,temp);
1336 		if (followtic & FZT_SKIN)
1337 			WRITEUINT8(demo_p,metal->player->followmobj->sprite2);
1338 		WRITEUINT16(demo_p,metal->player->followmobj->sprite);
1339 		WRITEUINT32(demo_p,metal->player->followmobj->frame); // NOT & FF_FRAMEMASK here, so 32 bits
1340 		WRITEUINT16(demo_p,metal->player->followmobj->color);
1341 
1342 		*followtic_p = followtic;
1343 	}
1344 	else
1345 		oldmetal.flags2 &= ~MF2_AMBUSH;
1346 
1347 	*ziptic_p = ziptic;
1348 
1349 	// attention here for the ticcmd size!
1350 	// latest demos with mouse aiming byte in ticcmd
1351 	if (demo_p >= demoend - 32)
1352 	{
1353 		G_StopMetalRecording(false); // no more space
1354 		return;
1355 	}
1356 }
1357 
1358 //
1359 // G_RecordDemo
1360 //
G_RecordDemo(const char * name)1361 void G_RecordDemo(const char *name)
1362 {
1363 	INT32 maxsize;
1364 
1365 	strcpy(demoname, name);
1366 	strcat(demoname, ".lmp");
1367 	maxsize = 1024*1024;
1368 	if (M_CheckParm("-maxdemo") && M_IsNextParm())
1369 		maxsize = atoi(M_GetNextParm()) * 1024;
1370 //	if (demobuffer)
1371 //		free(demobuffer);
1372 	demo_p = NULL;
1373 	demobuffer = malloc(maxsize);
1374 	demoend = demobuffer + maxsize;
1375 
1376 	demorecording = true;
1377 }
1378 
G_RecordMetal(void)1379 void G_RecordMetal(void)
1380 {
1381 	INT32 maxsize;
1382 	maxsize = 1024*1024;
1383 	if (M_CheckParm("-maxdemo") && M_IsNextParm())
1384 		maxsize = atoi(M_GetNextParm()) * 1024;
1385 	demo_p = NULL;
1386 	demobuffer = malloc(maxsize);
1387 	demoend = demobuffer + maxsize;
1388 	metalrecording = true;
1389 }
1390 
G_BeginRecording(void)1391 void G_BeginRecording(void)
1392 {
1393 	UINT8 i;
1394 	char name[MAXCOLORNAME+1];
1395 	player_t *player = &players[consoleplayer];
1396 
1397 	if (demo_p)
1398 		return;
1399 	memset(name,0,sizeof(name));
1400 
1401 	demo_p = demobuffer;
1402 	demoflags = DF_GHOST|(modeattacking<<DF_ATTACKSHIFT);
1403 
1404 	// Setup header.
1405 	M_Memcpy(demo_p, DEMOHEADER, 12); demo_p += 12;
1406 	WRITEUINT8(demo_p,VERSION);
1407 	WRITEUINT8(demo_p,SUBVERSION);
1408 	WRITEUINT16(demo_p,DEMOVERSION);
1409 
1410 	// demo checksum
1411 	demo_p += 16;
1412 
1413 	// game data
1414 	M_Memcpy(demo_p, "PLAY", 4); demo_p += 4;
1415 	WRITEINT16(demo_p,gamemap);
1416 	M_Memcpy(demo_p, mapmd5, 16); demo_p += 16;
1417 
1418 	WRITEUINT8(demo_p,demoflags);
1419 	switch ((demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
1420 	{
1421 	case ATTACKING_NONE: // 0
1422 		break;
1423 	case ATTACKING_RECORD: // 1
1424 		demotime_p = demo_p;
1425 		WRITEUINT32(demo_p,UINT32_MAX); // time
1426 		WRITEUINT32(demo_p,0); // score
1427 		WRITEUINT16(demo_p,0); // rings
1428 		break;
1429 	case ATTACKING_NIGHTS: // 2
1430 		demotime_p = demo_p;
1431 		WRITEUINT32(demo_p,UINT32_MAX); // time
1432 		WRITEUINT32(demo_p,0); // score
1433 		break;
1434 	default: // 3
1435 		break;
1436 	}
1437 
1438 	WRITEUINT32(demo_p,P_GetInitSeed());
1439 
1440 	// Name
1441 	for (i = 0; i < 16 && cv_playername.string[i]; i++)
1442 		name[i] = cv_playername.string[i];
1443 	for (; i < 16; i++)
1444 		name[i] = '\0';
1445 	M_Memcpy(demo_p,name,16);
1446 	demo_p += 16;
1447 
1448 	// Skin
1449 	for (i = 0; i < 16 && cv_skin.string[i]; i++)
1450 		name[i] = cv_skin.string[i];
1451 	for (; i < 16; i++)
1452 		name[i] = '\0';
1453 	M_Memcpy(demo_p,name,16);
1454 	demo_p += 16;
1455 
1456 	// Color
1457 	for (i = 0; i < MAXCOLORNAME && cv_playercolor.string[i]; i++)
1458 		name[i] = cv_playercolor.string[i];
1459 	for (; i < MAXCOLORNAME; i++)
1460 		name[i] = '\0';
1461 	M_Memcpy(demo_p,name,MAXCOLORNAME);
1462 	demo_p += MAXCOLORNAME;
1463 
1464 	// Stats
1465 	WRITEUINT8(demo_p,player->charability);
1466 	WRITEUINT8(demo_p,player->charability2);
1467 	WRITEUINT8(demo_p,player->actionspd>>FRACBITS);
1468 	WRITEUINT8(demo_p,player->mindash>>FRACBITS);
1469 	WRITEUINT8(demo_p,player->maxdash>>FRACBITS);
1470 	WRITEUINT8(demo_p,player->normalspeed>>FRACBITS);
1471 	WRITEUINT8(demo_p,player->runspeed>>FRACBITS);
1472 	WRITEUINT8(demo_p,player->thrustfactor);
1473 	WRITEUINT8(demo_p,player->accelstart);
1474 	WRITEUINT8(demo_p,player->acceleration);
1475 	WRITEFIXED(demo_p,player->height);
1476 	WRITEFIXED(demo_p,player->spinheight);
1477 	WRITEUINT8(demo_p,player->camerascale>>FRACBITS);
1478 	WRITEUINT8(demo_p,player->shieldscale>>FRACBITS);
1479 
1480 	// Trying to convert it back to % causes demo desync due to precision loss.
1481 	// Don't do it.
1482 	WRITEFIXED(demo_p, player->jumpfactor);
1483 
1484 	// And mobjtype_t is best with UINT32 too...
1485 	WRITEUINT32(demo_p, player->followitem);
1486 
1487 	// Save pflag data - see SendWeaponPref()
1488 	{
1489 		UINT8 buf = 0;
1490 		pflags_t pflags = 0;
1491 		if (cv_flipcam.value)
1492 		{
1493 			buf |= 0x01;
1494 			pflags |= PF_FLIPCAM;
1495 		}
1496 		if (cv_analog[0].value)
1497 		{
1498 			buf |= 0x02;
1499 			pflags |= PF_ANALOGMODE;
1500 		}
1501 		if (cv_directionchar[0].value)
1502 		{
1503 			buf |= 0x04;
1504 			pflags |= PF_DIRECTIONCHAR;
1505 		}
1506 		if (cv_autobrake.value)
1507 		{
1508 			buf |= 0x08;
1509 			pflags |= PF_AUTOBRAKE;
1510 		}
1511 		if (cv_usejoystick.value)
1512 			buf |= 0x10;
1513 		CV_SetValue(&cv_showinputjoy, !!(cv_usejoystick.value));
1514 
1515 		WRITEUINT8(demo_p,buf);
1516 		player->pflags = pflags;
1517 	}
1518 
1519 	// Save netvar data
1520 	CV_SaveDemoVars(&demo_p);
1521 
1522 	memset(&oldcmd,0,sizeof(oldcmd));
1523 	memset(&oldghost,0,sizeof(oldghost));
1524 	memset(&ghostext,0,sizeof(ghostext));
1525 	ghostext.lastcolor = ghostext.color = GHC_NORMAL;
1526 	ghostext.lastscale = ghostext.scale = FRACUNIT;
1527 
1528 	if (player->mo)
1529 	{
1530 		oldghost.x = player->mo->x;
1531 		oldghost.y = player->mo->y;
1532 		oldghost.z = player->mo->z;
1533 		oldghost.angle = player->mo->angle>>24;
1534 
1535 		// preticker started us gravity flipped
1536 		if (player->mo->eflags & MFE_VERTICALFLIP)
1537 			ghostext.flags |= EZT_FLIP;
1538 	}
1539 }
1540 
G_BeginMetal(void)1541 void G_BeginMetal(void)
1542 {
1543 	mobj_t *mo = players[consoleplayer].mo;
1544 
1545 #if 0
1546 	if (demo_p)
1547 		return;
1548 #endif
1549 
1550 	demo_p = demobuffer;
1551 
1552 	// Write header.
1553 	M_Memcpy(demo_p, DEMOHEADER, 12); demo_p += 12;
1554 	WRITEUINT8(demo_p,VERSION);
1555 	WRITEUINT8(demo_p,SUBVERSION);
1556 	WRITEUINT16(demo_p,DEMOVERSION);
1557 
1558 	// demo checksum
1559 	demo_p += 16;
1560 
1561 	M_Memcpy(demo_p, "METL", 4); demo_p += 4;
1562 
1563 	memset(&ghostext,0,sizeof(ghostext));
1564 	ghostext.lastscale = ghostext.scale = FRACUNIT;
1565 
1566 	// Set up our memory.
1567 	memset(&oldmetal,0,sizeof(oldmetal));
1568 	oldmetal.x = mo->x;
1569 	oldmetal.y = mo->y;
1570 	oldmetal.z = mo->z;
1571 	oldmetal.angle = mo->angle>>24;
1572 }
1573 
G_SetDemoTime(UINT32 ptime,UINT32 pscore,UINT16 prings)1574 void G_SetDemoTime(UINT32 ptime, UINT32 pscore, UINT16 prings)
1575 {
1576 	if (!demorecording || !demotime_p)
1577 		return;
1578 	if (demoflags & DF_RECORDATTACK)
1579 	{
1580 		WRITEUINT32(demotime_p, ptime);
1581 		WRITEUINT32(demotime_p, pscore);
1582 		WRITEUINT16(demotime_p, prings);
1583 		demotime_p = NULL;
1584 	}
1585 	else if (demoflags & DF_NIGHTSATTACK)
1586 	{
1587 		WRITEUINT32(demotime_p, ptime);
1588 		WRITEUINT32(demotime_p, pscore);
1589 		demotime_p = NULL;
1590 	}
1591 }
1592 
1593 // Returns bitfield:
1594 // 1 == new demo has lower time
1595 // 2 == new demo has higher score
1596 // 4 == new demo has higher rings
G_CmpDemoTime(char * oldname,char * newname)1597 UINT8 G_CmpDemoTime(char *oldname, char *newname)
1598 {
1599 	UINT8 *buffer,*p;
1600 	UINT8 flags;
1601 	UINT32 oldtime, newtime, oldscore, newscore;
1602 	UINT16 oldrings, newrings, oldversion;
1603 	size_t bufsize ATTRUNUSED;
1604 	UINT8 c;
1605 	UINT16 s ATTRUNUSED;
1606 	UINT8 aflags = 0;
1607 
1608 	// load the new file
1609 	FIL_DefaultExtension(newname, ".lmp");
1610 	bufsize = FIL_ReadFile(newname, &buffer);
1611 	I_Assert(bufsize != 0);
1612 	p = buffer;
1613 
1614 	// read demo header
1615 	I_Assert(!memcmp(p, DEMOHEADER, 12));
1616 	p += 12; // DEMOHEADER
1617 	c = READUINT8(p); // VERSION
1618 	I_Assert(c == VERSION);
1619 	c = READUINT8(p); // SUBVERSION
1620 	I_Assert(c == SUBVERSION);
1621 	s = READUINT16(p);
1622 	I_Assert(s >= 0x000c);
1623 	p += 16; // demo checksum
1624 	I_Assert(!memcmp(p, "PLAY", 4));
1625 	p += 4; // PLAY
1626 	p += 2; // gamemap
1627 	p += 16; // map md5
1628 	flags = READUINT8(p); // demoflags
1629 
1630 	aflags = flags & (DF_RECORDATTACK|DF_NIGHTSATTACK);
1631 	I_Assert(aflags);
1632 	if (flags & DF_RECORDATTACK)
1633 	{
1634 		newtime = READUINT32(p);
1635 		newscore = READUINT32(p);
1636 		newrings = READUINT16(p);
1637 	}
1638 	else if (flags & DF_NIGHTSATTACK)
1639 	{
1640 		newtime = READUINT32(p);
1641 		newscore = READUINT32(p);
1642 		newrings = 0;
1643 	}
1644 	else // appease compiler
1645 		return 0;
1646 
1647 	Z_Free(buffer);
1648 
1649 	// load old file
1650 	FIL_DefaultExtension(oldname, ".lmp");
1651 	if (!FIL_ReadFile(oldname, &buffer))
1652 	{
1653 		CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), oldname);
1654 		return UINT8_MAX;
1655 	}
1656 	p = buffer;
1657 
1658 	// read demo header
1659 	if (memcmp(p, DEMOHEADER, 12))
1660 	{
1661 		CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname);
1662 		Z_Free(buffer);
1663 		return UINT8_MAX;
1664 	} p += 12; // DEMOHEADER
1665 	p++; // VERSION
1666 	p++; // SUBVERSION
1667 	oldversion = READUINT16(p);
1668 	switch(oldversion) // demoversion
1669 	{
1670 	case DEMOVERSION: // latest always supported
1671 	case 0x000c: // all that changed between then and now was longer color name
1672 		break;
1673 	// too old, cannot support.
1674 	default:
1675 		CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname);
1676 		Z_Free(buffer);
1677 		return UINT8_MAX;
1678 	}
1679 	p += 16; // demo checksum
1680 	if (memcmp(p, "PLAY", 4))
1681 	{
1682 		CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname);
1683 		Z_Free(buffer);
1684 		return UINT8_MAX;
1685 	} p += 4; // "PLAY"
1686 	if (oldversion <= 0x0008)
1687 		p++; // gamemap
1688 	else
1689 		p += 2; // gamemap
1690 	p += 16; // mapmd5
1691 	flags = READUINT8(p);
1692 	if (!(flags & aflags))
1693 	{
1694 		CONS_Alert(CONS_NOTICE, M_GetText("File '%s' not from same game mode. It will be overwritten.\n"), oldname);
1695 		Z_Free(buffer);
1696 		return UINT8_MAX;
1697 	}
1698 	if (flags & DF_RECORDATTACK)
1699 	{
1700 		oldtime = READUINT32(p);
1701 		oldscore = READUINT32(p);
1702 		oldrings = READUINT16(p);
1703 	}
1704 	else if (flags & DF_NIGHTSATTACK)
1705 	{
1706 		oldtime = READUINT32(p);
1707 		oldscore = READUINT32(p);
1708 		oldrings = 0;
1709 	}
1710 	else // appease compiler
1711 		return UINT8_MAX;
1712 
1713 	Z_Free(buffer);
1714 
1715 	c = 0;
1716 	if (newtime < oldtime
1717 	|| (newtime == oldtime && (newscore > oldscore || newrings > oldrings)))
1718 		c |= 1; // Better time
1719 	if (newscore > oldscore
1720 	|| (newscore == oldscore && newtime < oldtime))
1721 		c |= 1<<1; // Better score
1722 	if (newrings > oldrings
1723 	|| (newrings == oldrings && newtime < oldtime))
1724 		c |= 1<<2; // Better rings
1725 	return c;
1726 }
1727 
1728 //
1729 // G_PlayDemo
1730 //
G_DeferedPlayDemo(const char * name)1731 void G_DeferedPlayDemo(const char *name)
1732 {
1733 	COM_BufAddText("playdemo \"");
1734 	COM_BufAddText(name);
1735 	COM_BufAddText("\"\n");
1736 }
1737 
1738 //
1739 // Start a demo from a .LMP file or from a wad resource
1740 //
G_DoPlayDemo(char * defdemoname)1741 void G_DoPlayDemo(char *defdemoname)
1742 {
1743 	UINT8 i;
1744 	lumpnum_t l;
1745 	char skin[17],color[MAXCOLORNAME+1],*n,*pdemoname;
1746 	UINT8 version,subversion,charability,charability2,thrustfactor,accelstart,acceleration,cnamelen;
1747 	pflags_t pflags;
1748 	UINT32 randseed, followitem;
1749 	fixed_t camerascale,shieldscale,actionspd,mindash,maxdash,normalspeed,runspeed,jumpfactor,height,spinheight;
1750 	char msg[1024];
1751 #ifdef OLD22DEMOCOMPAT
1752 	boolean use_old_demo_vars = false;
1753 #endif
1754 
1755 	skin[16] = '\0';
1756 	color[MAXCOLORNAME] = '\0';
1757 
1758 	n = defdemoname+strlen(defdemoname);
1759 	while (*n != '/' && *n != '\\' && n != defdemoname)
1760 		n--;
1761 	if (n != defdemoname)
1762 		n++;
1763 	pdemoname = ZZ_Alloc(strlen(n)+1);
1764 	strcpy(pdemoname,n);
1765 
1766 	// Internal if no extension, external if one exists
1767 	if (FIL_CheckExtension(defdemoname))
1768 	{
1769 		//FIL_DefaultExtension(defdemoname, ".lmp");
1770 		if (!FIL_ReadFile(defdemoname, &demobuffer))
1771 		{
1772 			snprintf(msg, 1024, M_GetText("Failed to read file '%s'.\n"), defdemoname);
1773 			CONS_Alert(CONS_ERROR, "%s", msg);
1774 			gameaction = ga_nothing;
1775 			M_StartMessage(msg, NULL, MM_NOTHING);
1776 			return;
1777 		}
1778 		demo_p = demobuffer;
1779 	}
1780 	// load demo resource from WAD
1781 	else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR)
1782 	{
1783 		snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname);
1784 		CONS_Alert(CONS_ERROR, "%s", msg);
1785 		gameaction = ga_nothing;
1786 		M_StartMessage(msg, NULL, MM_NOTHING);
1787 		return;
1788 	}
1789 	else // it's an internal demo
1790 		demobuffer = demo_p = W_CacheLumpNum(l, PU_STATIC);
1791 
1792 	// read demo header
1793 	gameaction = ga_nothing;
1794 	demoplayback = true;
1795 	if (memcmp(demo_p, DEMOHEADER, 12))
1796 	{
1797 		snprintf(msg, 1024, M_GetText("%s is not a SRB2 replay file.\n"), pdemoname);
1798 		CONS_Alert(CONS_ERROR, "%s", msg);
1799 		M_StartMessage(msg, NULL, MM_NOTHING);
1800 		Z_Free(pdemoname);
1801 		Z_Free(demobuffer);
1802 		demoplayback = false;
1803 		titledemo = false;
1804 		return;
1805 	}
1806 	demo_p += 12; // DEMOHEADER
1807 
1808 	version = READUINT8(demo_p);
1809 	subversion = READUINT8(demo_p);
1810 	demoversion = READUINT16(demo_p);
1811 	switch(demoversion)
1812 	{
1813 	case 0x000d:
1814 	case DEMOVERSION: // latest always supported
1815 		cnamelen = MAXCOLORNAME;
1816 		break;
1817 #ifdef OLD22DEMOCOMPAT
1818 	// all that changed between then and now was longer color name
1819 	case 0x000c:
1820 		cnamelen = 16;
1821 		use_old_demo_vars = true;
1822 		break;
1823 #endif
1824 	// too old, cannot support.
1825 	default:
1826 		snprintf(msg, 1024, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemoname);
1827 		CONS_Alert(CONS_ERROR, "%s", msg);
1828 		M_StartMessage(msg, NULL, MM_NOTHING);
1829 		Z_Free(pdemoname);
1830 		Z_Free(demobuffer);
1831 		demoplayback = false;
1832 		titledemo = false;
1833 		return;
1834 	}
1835 	demo_p += 16; // demo checksum
1836 	if (memcmp(demo_p, "PLAY", 4))
1837 	{
1838 		snprintf(msg, 1024, M_GetText("%s is the wrong type of recording and cannot be played.\n"), pdemoname);
1839 		CONS_Alert(CONS_ERROR, "%s", msg);
1840 		M_StartMessage(msg, NULL, MM_NOTHING);
1841 		Z_Free(pdemoname);
1842 		Z_Free(demobuffer);
1843 		demoplayback = false;
1844 		titledemo = false;
1845 		return;
1846 	}
1847 	demo_p += 4; // "PLAY"
1848 	gamemap = READINT16(demo_p);
1849 	demo_p += 16; // mapmd5
1850 
1851 	demoflags = READUINT8(demo_p);
1852 	modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT;
1853 	CON_ToggleOff();
1854 
1855 	hu_demoscore = 0;
1856 	hu_demotime = UINT32_MAX;
1857 	hu_demorings = 0;
1858 
1859 	switch (modeattacking)
1860 	{
1861 	case ATTACKING_NONE: // 0
1862 		break;
1863 	case ATTACKING_RECORD: // 1
1864 		hu_demotime  = READUINT32(demo_p);
1865 		hu_demoscore = READUINT32(demo_p);
1866 		hu_demorings = READUINT16(demo_p);
1867 		break;
1868 	case ATTACKING_NIGHTS: // 2
1869 		hu_demotime  = READUINT32(demo_p);
1870 		hu_demoscore = READUINT32(demo_p);
1871 		break;
1872 	default: // 3
1873 		modeattacking = ATTACKING_NONE;
1874 		break;
1875 	}
1876 
1877 	// Random seed
1878 	randseed = READUINT32(demo_p);
1879 
1880 	// Player name
1881 	M_Memcpy(player_names[0],demo_p,16);
1882 	demo_p += 16;
1883 
1884 	// Skin
1885 	M_Memcpy(skin,demo_p,16);
1886 	demo_p += 16;
1887 
1888 	// Color
1889 	M_Memcpy(color,demo_p,cnamelen);
1890 	demo_p += cnamelen;
1891 
1892 	charability = READUINT8(demo_p);
1893 	charability2 = READUINT8(demo_p);
1894 	actionspd = (fixed_t)READUINT8(demo_p)<<FRACBITS;
1895 	mindash = (fixed_t)READUINT8(demo_p)<<FRACBITS;
1896 	maxdash = (fixed_t)READUINT8(demo_p)<<FRACBITS;
1897 	normalspeed = (fixed_t)READUINT8(demo_p)<<FRACBITS;
1898 	runspeed = (fixed_t)READUINT8(demo_p)<<FRACBITS;
1899 	thrustfactor = READUINT8(demo_p);
1900 	accelstart = READUINT8(demo_p);
1901 	acceleration = READUINT8(demo_p);
1902 	height = (demoversion < 0x000e) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
1903 	spinheight = (demoversion < 0x000e) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
1904 	camerascale = (fixed_t)READUINT8(demo_p)<<FRACBITS;
1905 	shieldscale = (fixed_t)READUINT8(demo_p)<<FRACBITS;
1906 	jumpfactor = READFIXED(demo_p);
1907 	followitem = READUINT32(demo_p);
1908 
1909 	// pflag data
1910 	{
1911 		UINT8 buf = READUINT8(demo_p);
1912 		pflags = 0;
1913 		if (buf & 0x01)
1914 			pflags |= PF_FLIPCAM;
1915 		if (buf & 0x02)
1916 			pflags |= PF_ANALOGMODE;
1917 		if (buf & 0x04)
1918 			pflags |= PF_DIRECTIONCHAR;
1919 		if (buf & 0x08)
1920 			pflags |= PF_AUTOBRAKE;
1921 		CV_SetValue(&cv_showinputjoy, !!(buf & 0x10));
1922 	}
1923 
1924 	// net var data
1925 #ifdef OLD22DEMOCOMPAT
1926 	if (use_old_demo_vars)
1927 		CV_LoadOldDemoVars(&demo_p);
1928 	else
1929 #endif
1930 		CV_LoadDemoVars(&demo_p);
1931 
1932 	// Sigh ... it's an empty demo.
1933 	if (*demo_p == DEMOMARKER)
1934 	{
1935 		snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname);
1936 		CONS_Alert(CONS_ERROR, "%s", msg);
1937 		M_StartMessage(msg, NULL, MM_NOTHING);
1938 		Z_Free(pdemoname);
1939 		Z_Free(demobuffer);
1940 		demoplayback = false;
1941 		titledemo = false;
1942 		return;
1943 	}
1944 
1945 	Z_Free(pdemoname);
1946 
1947 	memset(&oldcmd,0,sizeof(oldcmd));
1948 	memset(&oldghost,0,sizeof(oldghost));
1949 
1950 	if (VERSION != version || SUBVERSION != subversion)
1951 		CONS_Alert(CONS_WARNING, M_GetText("Demo version does not match game version. Desyncs may occur.\n"));
1952 
1953 	// didn't start recording right away.
1954 	demo_start = false;
1955 
1956 	// Set skin
1957 	SetPlayerSkin(0, skin);
1958 
1959 	LUAh_MapChange(gamemap);
1960 	displayplayer = consoleplayer = 0;
1961 	memset(playeringame,0,sizeof(playeringame));
1962 	playeringame[0] = true;
1963 	P_SetRandSeed(randseed);
1964 	G_InitNew(false, G_BuildMapName(gamemap), true, true, false);
1965 
1966 	// Set color
1967 	players[0].skincolor = skins[players[0].skin].prefcolor;
1968 	for (i = 0; i < numskincolors; i++)
1969 		if (!stricmp(skincolors[i].name,color))
1970 		{
1971 			players[0].skincolor = i;
1972 			break;
1973 		}
1974 	CV_StealthSetValue(&cv_playercolor, players[0].skincolor);
1975 	if (players[0].mo)
1976 	{
1977 		players[0].mo->color = players[0].skincolor;
1978 		oldghost.x = players[0].mo->x;
1979 		oldghost.y = players[0].mo->y;
1980 		oldghost.z = players[0].mo->z;
1981 	}
1982 
1983 	// Set saved attribute values
1984 	// No cheat checking here, because even if they ARE wrong...
1985 	// it would only break the replay if we clipped them.
1986 	players[0].camerascale = camerascale;
1987 	players[0].shieldscale = shieldscale;
1988 	players[0].charability = charability;
1989 	players[0].charability2 = charability2;
1990 	players[0].actionspd = actionspd;
1991 	players[0].mindash = mindash;
1992 	players[0].maxdash = maxdash;
1993 	players[0].normalspeed = normalspeed;
1994 	players[0].runspeed = runspeed;
1995 	players[0].thrustfactor = thrustfactor;
1996 	players[0].accelstart = accelstart;
1997 	players[0].acceleration = acceleration;
1998 	players[0].height = height;
1999 	players[0].spinheight = spinheight;
2000 	players[0].jumpfactor = jumpfactor;
2001 	players[0].followitem = followitem;
2002 	players[0].pflags = pflags;
2003 
2004 	demo_start = true;
2005 }
2006 
G_AddGhost(char * defdemoname)2007 void G_AddGhost(char *defdemoname)
2008 {
2009 	INT32 i;
2010 	lumpnum_t l;
2011 	char name[17],skin[17],color[MAXCOLORNAME+1],*n,*pdemoname,md5[16];
2012 	UINT8 cnamelen;
2013 	demoghost *gh;
2014 	UINT8 flags;
2015 	UINT8 *buffer,*p;
2016 	mapthing_t *mthing;
2017 	UINT16 count, ghostversion;
2018 
2019 	name[16] = '\0';
2020 	skin[16] = '\0';
2021 	color[16] = '\0';
2022 
2023 	n = defdemoname+strlen(defdemoname);
2024 	while (*n != '/' && *n != '\\' && n != defdemoname)
2025 		n--;
2026 	if (n != defdemoname)
2027 		n++;
2028 	pdemoname = ZZ_Alloc(strlen(n)+1);
2029 	strcpy(pdemoname,n);
2030 
2031 	// Internal if no extension, external if one exists
2032 	if (FIL_CheckExtension(defdemoname))
2033 	{
2034 		//FIL_DefaultExtension(defdemoname, ".lmp");
2035 		if (!FIL_ReadFileTag(defdemoname, &buffer, PU_LEVEL))
2036 		{
2037 			CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), defdemoname);
2038 			Z_Free(pdemoname);
2039 			return;
2040 		}
2041 		p = buffer;
2042 	}
2043 	// load demo resource from WAD
2044 	else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR)
2045 	{
2046 		CONS_Alert(CONS_ERROR, M_GetText("Failed to read lump '%s'.\n"), defdemoname);
2047 		Z_Free(pdemoname);
2048 		return;
2049 	}
2050 	else // it's an internal demo
2051 		buffer = p = W_CacheLumpNum(l, PU_LEVEL);
2052 
2053 	// read demo header
2054 	if (memcmp(p, DEMOHEADER, 12))
2055 	{
2056 		CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Not a SRB2 replay.\n"), pdemoname);
2057 		Z_Free(pdemoname);
2058 		Z_Free(buffer);
2059 		return;
2060 	} p += 12; // DEMOHEADER
2061 	p++; // VERSION
2062 	p++; // SUBVERSION
2063 	ghostversion = READUINT16(p);
2064 	switch(ghostversion)
2065 	{
2066 	case 0x000d:
2067 	case DEMOVERSION: // latest always supported
2068 		cnamelen = MAXCOLORNAME;
2069 		break;
2070 	// all that changed between then and now was longer color name
2071 	case 0x000c:
2072 		cnamelen = 16;
2073 		break;
2074 	// too old, cannot support.
2075 	default:
2076 		CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo version incompatible.\n"), pdemoname);
2077 		Z_Free(pdemoname);
2078 		Z_Free(buffer);
2079 		return;
2080 	}
2081 	M_Memcpy(md5, p, 16); p += 16; // demo checksum
2082 	for (gh = ghosts; gh; gh = gh->next)
2083 		if (!memcmp(md5, gh->checksum, 16)) // another ghost in the game already has this checksum?
2084 		{ // Don't add another one, then!
2085 			CONS_Debug(DBG_SETUP, "Rejecting duplicate ghost %s (MD5 was matched)\n", pdemoname);
2086 			Z_Free(pdemoname);
2087 			Z_Free(buffer);
2088 			return;
2089 		}
2090 	if (memcmp(p, "PLAY", 4))
2091 	{
2092 		CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo format unacceptable.\n"), pdemoname);
2093 		Z_Free(pdemoname);
2094 		Z_Free(buffer);
2095 		return;
2096 	} p += 4; // "PLAY"
2097 	if (ghostversion <= 0x0008)
2098 		p++; // gamemap
2099 	else
2100 		p += 2; // gamemap
2101 	p += 16; // mapmd5 (possibly check for consistency?)
2102 	flags = READUINT8(p);
2103 	if (!(flags & DF_GHOST))
2104 	{
2105 		CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: No ghost data in this demo.\n"), pdemoname);
2106 		Z_Free(pdemoname);
2107 		Z_Free(buffer);
2108 		return;
2109 	}
2110 	switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
2111 	{
2112 	case ATTACKING_NONE: // 0
2113 		break;
2114 	case ATTACKING_RECORD: // 1
2115 		p += 10; // demo time, score, and rings
2116 		break;
2117 	case ATTACKING_NIGHTS: // 2
2118 		p += 8; // demo time left, score
2119 		break;
2120 	default: // 3
2121 		break;
2122 	}
2123 
2124 	p += 4; // random seed
2125 
2126 	// Player name (TODO: Display this somehow if it doesn't match cv_playername!)
2127 	M_Memcpy(name, p,16);
2128 	p += 16;
2129 
2130 	// Skin
2131 	M_Memcpy(skin, p,16);
2132 	p += 16;
2133 
2134 	// Color
2135 	M_Memcpy(color, p,cnamelen);
2136 	p += cnamelen;
2137 
2138 	// Ghosts do not have a player structure to put this in.
2139 	p++; // charability
2140 	p++; // charability2
2141 	p++; // actionspd
2142 	p++; // mindash
2143 	p++; // maxdash
2144 	p++; // normalspeed
2145 	p++; // runspeed
2146 	p++; // thrustfactor
2147 	p++; // accelstart
2148 	p++; // acceleration
2149 	p += (ghostversion < 0x000e) ? 2 : 2 * sizeof(fixed_t); // height and spinheight
2150 	p++; // camerascale
2151 	p++; // shieldscale
2152 	p += 4; // jumpfactor
2153 	p += 4; // followitem
2154 
2155 	p++; // pflag data
2156 
2157 	// net var data
2158 	count = READUINT16(p);
2159 	while (count--)
2160 	{
2161 		SKIPSTRING(p);
2162 		SKIPSTRING(p);
2163 		p++;
2164 	}
2165 
2166 	if (*p == DEMOMARKER)
2167 	{
2168 		CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), pdemoname);
2169 		Z_Free(pdemoname);
2170 		Z_Free(buffer);
2171 		return;
2172 	}
2173 
2174 	gh = Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL);
2175 	gh->next = ghosts;
2176 	gh->buffer = buffer;
2177 	M_Memcpy(gh->checksum, md5, 16);
2178 	gh->p = p;
2179 
2180 	ghosts = gh;
2181 
2182 	gh->version = ghostversion;
2183 	mthing = playerstarts[0];
2184 	I_Assert(mthing);
2185 	{ // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling.
2186 		fixed_t z,f,c;
2187 		fixed_t offset = mthing->z << FRACBITS;
2188 		gh->mo = P_SpawnMobj(mthing->x << FRACBITS, mthing->y << FRACBITS, 0, MT_GHOST);
2189 		gh->mo->angle = FixedAngle(mthing->angle << FRACBITS);
2190 		f = gh->mo->floorz;
2191 		c = gh->mo->ceilingz - mobjinfo[MT_PLAYER].height;
2192 		if (!!(mthing->options & MTF_AMBUSH) ^ !!(mthing->options & MTF_OBJECTFLIP))
2193 		{
2194 			z = c - offset;
2195 			if (z < f)
2196 				z = f;
2197 		}
2198 		else
2199 		{
2200 			z = f + offset;
2201 			if (z > c)
2202 				z = c;
2203 		}
2204 		gh->mo->z = z;
2205 	}
2206 
2207 	gh->oldmo.x = gh->mo->x;
2208 	gh->oldmo.y = gh->mo->y;
2209 	gh->oldmo.z = gh->mo->z;
2210 
2211 	// Set skin
2212 	gh->mo->skin = &skins[0];
2213 	for (i = 0; i < numskins; i++)
2214 		if (!stricmp(skins[i].name,skin))
2215 		{
2216 			gh->mo->skin = &skins[i];
2217 			break;
2218 		}
2219 	gh->oldmo.skin = gh->mo->skin;
2220 
2221 	// Set color
2222 	gh->mo->color = ((skin_t*)gh->mo->skin)->prefcolor;
2223 	for (i = 0; i < numskincolors; i++)
2224 		if (!stricmp(skincolors[i].name,color))
2225 		{
2226 			gh->mo->color = (UINT16)i;
2227 			break;
2228 		}
2229 	gh->oldmo.color = gh->mo->color;
2230 
2231 	gh->mo->state = states+S_PLAY_STND;
2232 	gh->mo->sprite = gh->mo->state->sprite;
2233 	gh->mo->sprite2 = (gh->mo->state->frame & FF_FRAMEMASK);
2234 	//gh->mo->frame = tr_trans30<<FF_TRANSSHIFT;
2235 	gh->mo->flags2 |= MF2_DONTDRAW;
2236 	gh->fadein = (9-3)*6; // fade from invisible to trans30 over as close to 35 tics as possible
2237 	gh->mo->tics = -1;
2238 
2239 	CONS_Printf(M_GetText("Added ghost %s from %s\n"), name, pdemoname);
2240 	Z_Free(pdemoname);
2241 }
2242 
2243 // Clean up all ghosts
G_FreeGhosts(void)2244 void G_FreeGhosts(void)
2245 {
2246 	while (ghosts)
2247 	{
2248 		demoghost *next = ghosts->next;
2249 		Z_Free(ghosts);
2250 		ghosts = next;
2251 	}
2252 	ghosts = NULL;
2253 }
2254 
2255 //
2256 // G_TimeDemo
2257 // NOTE: name is a full filename for external demos
2258 //
2259 static INT32 restorecv_vidwait;
2260 
G_TimeDemo(const char * name)2261 void G_TimeDemo(const char *name)
2262 {
2263 	nodrawers = M_CheckParm("-nodraw");
2264 	noblit = M_CheckParm("-noblit");
2265 	restorecv_vidwait = cv_vidwait.value;
2266 	if (cv_vidwait.value)
2267 		CV_Set(&cv_vidwait, "0");
2268 	timingdemo = true;
2269 	singletics = true;
2270 	framecount = 0;
2271 	demostarttime = I_GetTime();
2272 	G_DeferedPlayDemo(name);
2273 }
2274 
G_DoPlayMetal(void)2275 void G_DoPlayMetal(void)
2276 {
2277 	lumpnum_t l;
2278 	mobj_t *mo = NULL;
2279 	thinker_t *th;
2280 
2281 	// it's an internal demo
2282 	if ((l = W_CheckNumForName(va("%sMS",G_BuildMapName(gamemap)))) == LUMPERROR)
2283 	{
2284 		CONS_Alert(CONS_WARNING, M_GetText("No bot recording for this map.\n"));
2285 		return;
2286 	}
2287 	else
2288 		metalbuffer = metal_p = W_CacheLumpNum(l, PU_STATIC);
2289 
2290 	// find metal sonic
2291 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
2292 	{
2293 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
2294 			continue;
2295 
2296 		mo = (mobj_t *)th;
2297 		if (mo->type != MT_METALSONIC_RACE)
2298 			continue;
2299 
2300 		break;
2301 	}
2302 	if (th == &thlist[THINK_MOBJ])
2303 	{
2304 		CONS_Alert(CONS_ERROR, M_GetText("Failed to find bot entity.\n"));
2305 		Z_Free(metalbuffer);
2306 		return;
2307 	}
2308 
2309 	// read demo header
2310     metal_p += 12; // DEMOHEADER
2311 	metal_p++; // VERSION
2312 	metal_p++; // SUBVERSION
2313 	metalversion = READUINT16(metal_p);
2314 	switch(metalversion)
2315 	{
2316 	case DEMOVERSION: // latest always supported
2317 	case 0x000d: // There are checks wheter the momentum is from older demo versions or not
2318 	case 0x000c: // all that changed between then and now was longer color name
2319 		break;
2320 	// too old, cannot support.
2321 	default:
2322 		CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, format version incompatible.\n"));
2323 		Z_Free(metalbuffer);
2324 		return;
2325 	}
2326 	metal_p += 16; // demo checksum
2327 	if (memcmp(metal_p, "METL", 4))
2328 	{
2329 		CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, wasn't recorded in Metal format.\n"));
2330 		Z_Free(metalbuffer);
2331 		return;
2332 	} metal_p += 4; // "METL"
2333 
2334 	// read initial tic
2335 	memset(&oldmetal,0,sizeof(oldmetal));
2336 	oldmetal.x = mo->x;
2337 	oldmetal.y = mo->y;
2338 	oldmetal.z = mo->z;
2339 	metalplayback = mo;
2340 }
2341 
G_DoneLevelLoad(void)2342 void G_DoneLevelLoad(void)
2343 {
2344 	CONS_Printf(M_GetText("Loaded level in %f sec\n"), (double)(I_GetTime() - demostarttime) / TICRATE);
2345 	framecount = 0;
2346 	demostarttime = I_GetTime();
2347 }
2348 
2349 /*
2350 ===================
2351 =
2352 = G_CheckDemoStatus
2353 =
2354 = Called after a death or level completion to allow demos to be cleaned up
2355 = Returns true if a new demo loop action will take place
2356 ===================
2357 */
2358 
2359 // Writes the demo's checksum, or just random garbage if you can't do that for some reason.
WriteDemoChecksum(void)2360 static void WriteDemoChecksum(void)
2361 {
2362 	UINT8 *p = demobuffer+16; // checksum position
2363 #ifdef NOMD5
2364 	UINT8 i;
2365 	for (i = 0; i < 16; i++, p++)
2366 		*p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct.
2367 #else
2368 	md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file.
2369 #endif
2370 }
2371 
2372 // Stops recording a demo.
G_StopDemoRecording(void)2373 static void G_StopDemoRecording(void)
2374 {
2375 	boolean saved = false;
2376 	if (demo_p)
2377 	{
2378 		WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
2379 		WriteDemoChecksum();
2380 		saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file.
2381 	}
2382 	free(demobuffer);
2383 	demorecording = false;
2384 
2385 	if (modeattacking != ATTACKING_RECORD)
2386 	{
2387 		if (saved)
2388 			CONS_Printf(M_GetText("Demo %s recorded\n"), demoname);
2389 		else
2390 			CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname);
2391 	}
2392 }
2393 
2394 // Stops metal sonic's demo. Separate from other functions because metal + replays can coexist
G_StopMetalDemo(void)2395 void G_StopMetalDemo(void)
2396 {
2397 
2398 	// Metal Sonic finishing doesn't end the game, dammit.
2399 	Z_Free(metalbuffer);
2400 	metalbuffer = NULL;
2401 	metalplayback = NULL;
2402 	metal_p = NULL;
2403 }
2404 
2405 // Stops metal sonic recording.
G_StopMetalRecording(boolean kill)2406 ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill)
2407 {
2408 	boolean saved = false;
2409 	if (demo_p)
2410 	{
2411 		WRITEUINT8(demo_p, (kill) ? METALDEATH : DEMOMARKER); // add the demo end (or metal death) marker
2412 		WriteDemoChecksum();
2413 		saved = FIL_WriteFile(va("%sMS.LMP", G_BuildMapName(gamemap)), demobuffer, demo_p - demobuffer); // finally output the file.
2414 	}
2415 	free(demobuffer);
2416 	metalrecording = false;
2417 	if (saved)
2418 		I_Error("Saved to %sMS.LMP", G_BuildMapName(gamemap));
2419 	I_Error("Failed to save demo!");
2420 }
2421 
2422 // Stops timing a demo.
G_StopTimingDemo(void)2423 static void G_StopTimingDemo(void)
2424 {
2425 	INT32 demotime;
2426 	double f1, f2;
2427 	demotime = I_GetTime() - demostarttime;
2428 	if (!demotime)
2429 		return;
2430 	G_StopDemo();
2431 	timingdemo = false;
2432 	f1 = (double)demotime;
2433 	f2 = (double)framecount*TICRATE;
2434 
2435 	CONS_Printf(M_GetText("timed %u gametics in %d realtics - %u frames\n%f seconds, %f avg fps\n"),
2436 		leveltime,demotime,(UINT32)framecount,f1/TICRATE,f2/f1);
2437 
2438 	// CSV-readable timedemo results, for external parsing
2439 	if (timedemo_csv)
2440 	{
2441 		FILE *f;
2442 		const char *csvpath = va("%s"PATHSEP"%s", srb2home, "timedemo.csv");
2443 		const char *header = "id,demoname,seconds,avgfps,leveltime,demotime,framecount,ticrate,rendermode,vidmode,vidwidth,vidheight,procbits\n";
2444 		const char *rowformat = "\"%s\",\"%s\",%f,%f,%u,%d,%u,%u,%u,%u,%u,%u,%u\n";
2445 		boolean headerrow = !FIL_FileExists(csvpath);
2446 		UINT8 procbits = 0;
2447 
2448 		// Bitness
2449 		if (sizeof(void*) == 4)
2450 			procbits = 32;
2451 		else if (sizeof(void*) == 8)
2452 			procbits = 64;
2453 
2454 		f = fopen(csvpath, "a+");
2455 
2456 		if (f)
2457 		{
2458 			if (headerrow)
2459 				fputs(header, f);
2460 			fprintf(f, rowformat,
2461 				timedemo_csv_id,timedemo_name,f1/TICRATE,f2/f1,leveltime,demotime,(UINT32)framecount,TICRATE,rendermode,vid.modenum,vid.width,vid.height,procbits);
2462 			fclose(f);
2463 			CONS_Printf("Timedemo results saved to '%s'\n", csvpath);
2464 		}
2465 		else
2466 		{
2467 			// Just print the CSV output to console
2468 			CON_LogMessage(header);
2469 			CONS_Printf(rowformat,
2470 				timedemo_csv_id,timedemo_name,f1/TICRATE,f2/f1,leveltime,demotime,(UINT32)framecount,TICRATE,rendermode,vid.modenum,vid.width,vid.height,procbits);
2471 		}
2472 	}
2473 
2474 	if (restorecv_vidwait != cv_vidwait.value)
2475 		CV_SetValue(&cv_vidwait, restorecv_vidwait);
2476 	D_AdvanceDemo();
2477 }
2478 
2479 // reset engine variable set for the demos
2480 // called from stopdemo command, map command, and g_checkdemoStatus.
G_StopDemo(void)2481 void G_StopDemo(void)
2482 {
2483 	Z_Free(demobuffer);
2484 	demobuffer = NULL;
2485 	demoplayback = false;
2486 	titledemo = false;
2487 	timingdemo = false;
2488 	singletics = false;
2489 
2490 	if (gamestate == GS_INTERMISSION)
2491 		Y_EndIntermission(); // cleanup
2492 
2493 	G_SetGamestate(GS_NULL);
2494 	wipegamestate = GS_NULL;
2495 	SV_StopServer();
2496 	SV_ResetServer();
2497 }
2498 
G_CheckDemoStatus(void)2499 boolean G_CheckDemoStatus(void)
2500 {
2501 	G_FreeGhosts();
2502 
2503 	// DO NOT end metal sonic demos here
2504 
2505 	if (timingdemo)
2506 	{
2507 		G_StopTimingDemo();
2508 		return true;
2509 	}
2510 
2511 	if (demoplayback)
2512 	{
2513 		if (singledemo)
2514 			I_Quit();
2515 		G_StopDemo();
2516 
2517 		if (modeattacking)
2518 			M_EndModeAttackRun();
2519 		else
2520 			D_AdvanceDemo();
2521 
2522 		return true;
2523 	}
2524 
2525 	if (demorecording)
2526 	{
2527 		G_StopDemoRecording();
2528 		return true;
2529 	}
2530 
2531 	return false;
2532 }
2533