Add demo format auto-detection.

nq now rejects anything that doesn't look like a .dem, and qw not only
rejects anything that looks like neither a .qwd nor a .mvd, but even
detects which one is being played by the contents rather than the file name
(foo.mvd.gz would be mis-detected as a qwd!!).
This commit is contained in:
Bill Currie 2012-06-03 19:51:43 +09:00
parent dfc8f8bb20
commit 93e35d7ec0
2 changed files with 143 additions and 3 deletions

View file

@ -35,6 +35,7 @@
# include <strings.h> # include <strings.h>
#endif #endif
#include <time.h> #include <time.h>
#include <ctype.h>
#include "QF/cmd.h" #include "QF/cmd.h"
#include "QF/cvar.h" #include "QF/cvar.h"
@ -374,6 +375,58 @@ CL_Record (const char *argv1, int track)
demo_start_recording (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 static void
CL_StartDemo (void) CL_StartDemo (void)
{ {
@ -396,6 +449,14 @@ CL_StartDemo (void)
dstring_delete (name); dstring_delete (name);
return; 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; cls.demoplayback = true;
CL_SetState (ca_connected); CL_SetState (ca_connected);

View file

@ -42,6 +42,7 @@
#include <sys/time.h> #include <sys/time.h>
#endif #endif
#include <time.h> #include <time.h>
#include <math.h>
#include "QF/cbuf.h" #include "QF/cbuf.h"
#include "QF/cmd.h" #include "QF/cmd.h"
@ -880,10 +881,80 @@ CL_Record (const char *argv1, int track)
demo_start_recording (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 static void
CL_StartDemo (void) CL_StartDemo (void)
{ {
dstring_t *name; dstring_t *name;
int type;
// open the demo file // open the demo file
name = dstring_strdup (demoname); name = dstring_strdup (demoname);
@ -897,16 +968,24 @@ CL_StartDemo (void)
dstring_delete (name); dstring_delete (name);
return; 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; cls.demoplayback = true;
key_game_target = IMT_DEMO; key_game_target = IMT_DEMO;
Key_SetKeyDest (key_game); Key_SetKeyDest (key_game);
net_blocksend = 1; net_blocksend = 1;
if (strequal (QFS_FileExtension (name->str), ".mvd")) { if (type == 2) {
cls.demoplayback2 = true; cls.demoplayback2 = true;
Sys_Printf ("mvd\n"); Sys_MaskPrintf (SYS_DEV, "mvd\n");
} else { } else {
Sys_Printf ("qwd\n"); Sys_MaskPrintf (SYS_DEV, "qwd\n");
} }
CL_SetState (ca_demostart); CL_SetState (ca_demostart);
Netchan_Setup (&cls.netchan, net_from, 0, NC_QPORT_SEND); Netchan_Setup (&cls.netchan, net_from, 0, NC_QPORT_SEND);