diff --git a/nq/source/cl_demo.c b/nq/source/cl_demo.c index 4f967bff1..32699a4ea 100644 --- a/nq/source/cl_demo.c +++ b/nq/source/cl_demo.c @@ -35,6 +35,7 @@ # include #endif #include +#include #include "QF/cmd.h" #include "QF/cvar.h" @@ -374,6 +375,58 @@ CL_Record (const char *argv1, int track) demo_start_recording (track); } +static int +demo_check_dem (void) +{ + int c, ret = 0; + uint32_t len, bytes; + + // .dem demo files begin with an ascii integer (possibly negative) + // representing the forced bgm track, followed by a newline + c = Qgetc (cls.demofile); + if (c == '-') + c = Qgetc (cls.demofile); + while (isdigit (c)) + c = Qgetc (cls.demofile); + if (c != '\n') + goto done; + + // After the bgm track comes the packet length plus 3 floats for view + // angles (not included in the packet length) + Qread (cls.demofile, &len, sizeof (len)); + len = LittleLong (len); + if (len > MAX_DEMMSG) + goto done; + Qseek (cls.demofile, 3 * 4, SEEK_CUR); // 3 * float (angles) + + // Normally, svc_serverinfo is the first command in the packet, but some + // servers (eg, fitzquake) add in an svc_print command first. Allow + // multiple svc_print commands (but nothing else) before the svc_serverinfo + net_message->message->cursize = len; + bytes = Qread (cls.demofile, net_message->message->data, len); + if (bytes != len) + goto done; + MSG_BeginReading (net_message); + while (!ret) { + if (net_message->badread) + goto done; + c = MSG_ReadByte (net_message); + switch (c) { + case svc_print: + MSG_ReadString (net_message); + break; + case svc_serverinfo: + ret = 1; + break; + default: + goto done; + } + } +done: + Qseek (cls.demofile, 0, SEEK_SET); + return ret; +} + static void CL_StartDemo (void) { @@ -396,6 +449,14 @@ CL_StartDemo (void) dstring_delete (name); return; } + if (!demo_check_dem ()) { + Sys_Printf ("%s is not a valid .dem file.\n", name->str); + cls.demonum = -1; // stop demo loop + dstring_delete (name); + Qclose (cls.demofile); + cls.demofile = 0; + return; + } cls.demoplayback = true; CL_SetState (ca_connected); diff --git a/qw/source/cl_demo.c b/qw/source/cl_demo.c index 8086fb9de..2e890b9af 100644 --- a/qw/source/cl_demo.c +++ b/qw/source/cl_demo.c @@ -42,6 +42,7 @@ #include #endif #include +#include #include "QF/cbuf.h" #include "QF/cmd.h" @@ -880,10 +881,80 @@ CL_Record (const char *argv1, int track) demo_start_recording (track); } +static inline uint32_t +get_ulong (const byte *buf) +{ + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); +} + +static inline float +get_float (const byte *buf) +{ + union { + uint32_t u; + float f; + } uf; + uf.u = get_ulong (buf); + return uf.f; +} + +static int +demo_check_qwd_mvd (void) +{ + byte buf[22]; + size_t bytes; + int c, ret = 0; + float f; + uint32_t u; + + bytes = Qread (cls.demofile, buf, sizeof (buf)); + if (bytes != sizeof (buf)) + goto done; + + if ((f = get_float (buf + 0)) >= 0 && !isinf (f) && !isnan (f) + && buf[4] == dem_read && get_ulong (buf + 5) <= MAX_DEMMSG + && get_ulong (buf + 9) == 1 && get_ulong (buf + 13) == 1 + && buf[17] == svc_serverdata + && (u = get_ulong (buf + 18)) >= 26 && u <= PROTOCOL_VERSION) { + ret = 1; + goto done; + } + if (buf[0] != 0 || (buf[1] != dem_read && buf[1] != dem_all) + || (u = get_ulong (buf + 2)) > MAX_DEMMSG) + goto done; + Qseek (cls.demofile, 6, SEEK_SET); + net_message->message->cursize = u; + bytes = Qread (cls.demofile, net_message->message->data, u); + if (bytes != u) + goto done; + MSG_BeginReading (net_message); + while (!ret) { + if (net_message->badread) + goto done; + c = MSG_ReadByte (net_message); + switch (c) { + case svc_print: + MSG_ReadString (net_message); + break; + case svc_serverdata: + if (MSG_ReadLong (net_message) != PROTOCOL_VERSION) + goto done; + ret = 2; + break; + default: + goto done; + } + } +done: + Qseek (cls.demofile, 0, SEEK_SET); + return ret; +} + static void CL_StartDemo (void) { dstring_t *name; + int type; // open the demo file name = dstring_strdup (demoname); @@ -897,16 +968,24 @@ CL_StartDemo (void) dstring_delete (name); return; } + if (!(type = demo_check_qwd_mvd ())) { + Sys_Printf ("%s is not a valid .qwd or .mvd file.\n", name->str); + cls.demonum = -1; // stop demo loop + dstring_delete (name); + Qclose (cls.demofile); + cls.demofile = 0; + return; + } cls.demoplayback = true; key_game_target = IMT_DEMO; Key_SetKeyDest (key_game); net_blocksend = 1; - if (strequal (QFS_FileExtension (name->str), ".mvd")) { + if (type == 2) { cls.demoplayback2 = true; - Sys_Printf ("mvd\n"); + Sys_MaskPrintf (SYS_DEV, "mvd\n"); } else { - Sys_Printf ("qwd\n"); + Sys_MaskPrintf (SYS_DEV, "qwd\n"); } CL_SetState (ca_demostart); Netchan_Setup (&cls.netchan, net_from, 0, NC_QPORT_SEND);