demo-reader 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/dm68/msg.h ADDED
@@ -0,0 +1,114 @@
1
+ /*
2
+ Quake III *.dm_6? Demo Specifications
3
+
4
+ Copyright (C) 2003 Andrey '[SkulleR]' Nazarov
5
+ Based on Argus and Quake II source code
6
+ Also contains some stuff from Q3A SDK
7
+
8
+ Argus is Copyright (C) 2000 Martin Otten
9
+ Quake II and Quake III are Copyright (C) 1997-2001 ID Software, Inc
10
+
11
+ This program is free software; you can redistribute it and/or
12
+ modify it under the terms of the GNU General Public License
13
+ as published by the Free Software Foundation; either version 2
14
+ of the License, or (at your option) any later version.
15
+
16
+ This program is distributed in the hope that it will be useful,
17
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
+
20
+ See the GNU General Public License for more details.
21
+
22
+ You should have received a copy of the GNU General Public License
23
+ along with this program; if not, write to the Free Software
24
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
+ */
26
+
27
+ //
28
+ // msg.h - message i/o subsystem API
29
+ //
30
+
31
+ #define MAX_MSGLEN 0x4000 // max length of demo message
32
+
33
+ typedef struct sizebuf_s {
34
+ qboolean allowoverflow; // if false, do a Com_Error
35
+ qboolean overflowed; // set to true if the buffer size failed
36
+ qboolean uncompressed; // don't do Huffman encoding, write raw bytes
37
+ byte *data; // pointer to message buffer, set by MSG_Init
38
+ int maxsize; // size in bytes of message buffer, set by MSG_Init
39
+ int cursize; // number of bytes written to the buffer, set by MSG_WriteBits
40
+ int readcount; // number of bytes read from the buffer, set by MSG_ReadBits
41
+ int bit; // number of bits written to or read from the buffer
42
+ } sizebuf_t;
43
+
44
+
45
+ void MSG_Init( sizebuf_t *msg, byte *buffer, int size );
46
+ void MSG_InitRaw( sizebuf_t *msg, byte *buffer, int size );
47
+ void MSG_Clear( sizebuf_t *msg );
48
+ void MSG_SetBitstream( sizebuf_t *msg );
49
+ void MSG_WriteRawData( sizebuf_t *msg, const void *data, int length );
50
+
51
+ void MSG_BeginWriting( sizebuf_t *msg );
52
+ void MSG_WriteBits( sizebuf_t *msg, int value, int bits );
53
+ void MSG_WriteData( sizebuf_t *msg, const void *data, int length );
54
+ void MSG_WriteString( sizebuf_t *msg, const char *string );
55
+ void MSG_WriteBigString( sizebuf_t *msg, const char *string );
56
+ void MSG_WriteDeltaEntity( sizebuf_t *msg, const entityState_t *from, const entityState_t *to, qboolean force );
57
+ void MSG_WriteDeltaPlayerstate( sizebuf_t *msg, const playerState_t *from, const playerState_t *to );
58
+
59
+ void MSG_BeginReading( sizebuf_t *msg );
60
+ int MSG_ReadBits( sizebuf_t *msg, int bits );
61
+ void MSG_ReadData( sizebuf_t *msg, void *data, int len );
62
+ char * MSG_ReadString( sizebuf_t *msg );
63
+ char * MSG_ReadStringLine( sizebuf_t *msg );
64
+ char * MSG_ReadBigString( sizebuf_t *msg );
65
+ void MSG_ReadDeltaEntity( sizebuf_t *msg, const entityState_t *from, entityState_t *to, int number );
66
+ void MSG_ReadDeltaPlayerstate( sizebuf_t *msg, const playerState_t *from, playerState_t *to );
67
+
68
+ #define MSG_WriteByte(msg,c) MSG_WriteBits(msg,c,8)
69
+ #define MSG_WriteShort(msg,c) MSG_WriteBits(msg,c,16)
70
+ #define MSG_WriteSignedShort(msg,c) MSG_WriteBits(msg,c,-16)
71
+ #define MSG_WriteLong(msg,c) MSG_WriteBits(msg,c,32)
72
+
73
+ static ID_INLINE int MSG_ReadByte( sizebuf_t *msg ) {
74
+ int c = MSG_ReadBits( msg, 8 ) & 0xFF;
75
+
76
+ if( msg->readcount > msg->cursize ) {
77
+ return -1;
78
+ }
79
+
80
+ return c;
81
+ }
82
+
83
+ static ID_INLINE int MSG_ReadShort( sizebuf_t *msg ) {
84
+ int c = MSG_ReadBits( msg, 16 );
85
+
86
+ if( msg->readcount > msg->cursize ) {
87
+ return -1;
88
+ }
89
+
90
+ return c;
91
+ }
92
+
93
+ static ID_INLINE int MSG_ReadSignedShort( sizebuf_t *msg ) {
94
+ int c = MSG_ReadBits( msg, -16 );
95
+
96
+ if( msg->readcount > msg->cursize ) {
97
+ return -1;
98
+ }
99
+
100
+ return c;
101
+ }
102
+
103
+ static ID_INLINE int MSG_ReadLong( sizebuf_t *msg ) {
104
+ int c = MSG_ReadBits( msg, 32 );
105
+
106
+ if( msg->readcount > msg->cursize ) {
107
+ return -1;
108
+ }
109
+
110
+ return c;
111
+ }
112
+
113
+
114
+
data/ext/dm68/parse.c ADDED
@@ -0,0 +1,393 @@
1
+ /*
2
+ Quake III *.dm_6? Demo Specifications
3
+
4
+ Copyright (C) 2003 Andrey '[SkulleR]' Nazarov
5
+ Based on Argus and Quake II source code
6
+ Also contains some stuff from Q3A SDK
7
+
8
+ Argus is Copyright (C) 2000 Martin Otten
9
+ Quake II and Quake III are Copyright (C) 1997-2001 ID Software, Inc
10
+
11
+ This program is free software; you can redistribute it and/or
12
+ modify it under the terms of the GNU General Public License
13
+ as published by the Free Software Foundation; either version 2
14
+ of the License, or (at your option) any later version.
15
+
16
+ This program is distributed in the hope that it will be useful,
17
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
+
20
+ See the GNU General Public License for more details.
21
+
22
+ You should have received a copy of the GNU General Public License
23
+ along with this program; if not, write to the Free Software
24
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
+ */
26
+
27
+ //
28
+ // parse.c - illustrates how to parse the demo file
29
+ //
30
+
31
+ #include "main.h"
32
+
33
+ /*
34
+ ==================
35
+ Parse_GameState
36
+ ==================
37
+ */
38
+ static void Parse_GameState( sizebuf_t *msg ) {
39
+ int c;
40
+ int index;
41
+ char *configString;
42
+
43
+ memset( &ds, 0, sizeof( ds ) );
44
+
45
+ ds.lastServerCommandNum = MSG_ReadLong( msg );
46
+ ds.currentServerCommandNum = ds.lastServerCommandNum;
47
+
48
+ while( 1 ) {
49
+ c = MSG_ReadByte( msg );
50
+
51
+ if( c == -1 ) {
52
+ Com_Error( ERR_DROP, "Parse_GameState: read past end of demo message" );
53
+ }
54
+
55
+ if( c == SVC_EOM ) {
56
+ break;
57
+ }
58
+
59
+
60
+ switch( c ) {
61
+ default:
62
+ Com_Error( ERR_DROP, "Parse_GameState: bad command byte" );
63
+ break;
64
+
65
+ case SVC_CONFIGSTRING:
66
+ index = MSG_ReadShort( msg );
67
+ if( index < 0 || index >= MAX_CONFIGSTRINGS ) {
68
+ Com_Error( ERR_DROP, "Parse_GameState: configString index %i out of range", index );
69
+ }
70
+ configString = MSG_ReadBigString( msg );
71
+ Com_InsertIntoGameState( &ds.gameState, index, configString );
72
+ break;
73
+
74
+ case SVC_BASELINE:
75
+ index = MSG_ReadBits( msg, GENTITYNUM_BITS );
76
+ if( index < 0 || index >= MAX_GENTITIES ) {
77
+ Com_Error( ERR_DROP, "Parse_GameState: baseline index %i out of range", index );
78
+ }
79
+ MSG_ReadDeltaEntity( msg, NULL, &ds.baselines[index], index );
80
+ break;
81
+ }
82
+ }
83
+
84
+ // FIXME: unknown stuff, not used in demos?
85
+ MSG_ReadLong( msg );
86
+ MSG_ReadLong( msg );
87
+
88
+ demo.gameStatesParsed++;
89
+
90
+ }
91
+
92
+ /*
93
+ =====================================================================
94
+
95
+ ACTION MESSAGES
96
+
97
+ =====================================================================
98
+ */
99
+
100
+ /*
101
+ =====================
102
+ Parse_ServerCommand
103
+ =====================
104
+ */
105
+ static void Parse_ServerCommand( sizebuf_t *msg ) {
106
+ int number;
107
+ char *string;
108
+
109
+ number = MSG_ReadLong( msg );
110
+ string = MSG_ReadString( msg );
111
+
112
+ if( number < ds.lastServerCommandNum ) {
113
+ return; // we have already received this command
114
+ }
115
+ ds.lastServerCommandNum = number;
116
+
117
+ // archive the command to be processed later
118
+ Q_strncpyz( ds.serverCommands[number & SERVERCMD_MASK], string, sizeof( ds.serverCommands[0] ) );
119
+ }
120
+
121
+ /*
122
+ ==================
123
+ Parse_DeltaEntity
124
+
125
+ Parses deltas from the given base and adds the resulting entity
126
+ to the current frame
127
+ ==================
128
+ */
129
+ static void Parse_DeltaEntity( sizebuf_t *msg, snapshot_t *frame, int newnum, entityState_t *old, qboolean unchanged ) {
130
+ entityState_t *state;
131
+
132
+ state = &ds.parseEntities[ds.firstParseEntity & PARSE_ENTITIES_MASK];
133
+
134
+ if( unchanged ) {
135
+ memcpy( state, old, sizeof( *state ) ); // don't read any bits
136
+ } else {
137
+ MSG_ReadDeltaEntity( msg, old, state, newnum );
138
+ if( state->number == ENTITYNUM_NONE ) {
139
+ // the entity present in oldframe is not in the current frame
140
+ return;
141
+ }
142
+ }
143
+
144
+ ds.firstParseEntity++;
145
+ frame->numEntities++;
146
+ }
147
+
148
+ /*
149
+ ==================
150
+ Parse_PacketEntities
151
+
152
+ An svc_packetentities has just been parsed, deal with the
153
+ rest of the data stream.
154
+ ==================
155
+ */
156
+ static void Parse_PacketEntities( sizebuf_t *msg, snapshot_t *oldframe, snapshot_t *newframe ) {
157
+ int newnum;
158
+ entityState_t *oldstate;
159
+ int oldindex, oldnum;
160
+
161
+ newframe->firstEntity = ds.firstParseEntity;
162
+ newframe->numEntities = 0;
163
+
164
+ // delta from the entities present in oldframe
165
+ oldindex = 0;
166
+ if( !oldframe ) {
167
+ oldnum = 99999;
168
+ } else {
169
+ if( oldindex >= oldframe->numEntities ) {
170
+ oldnum = 99999;
171
+ } else {
172
+ oldstate = &ds.parseEntities[(oldframe->firstEntity + oldindex) & PARSE_ENTITIES_MASK];
173
+ oldnum = oldstate->number;
174
+ }
175
+ }
176
+
177
+ while( 1 ) {
178
+ newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
179
+ if( newnum < 0 || newnum >= MAX_GENTITIES ) {
180
+ Com_Error( ERR_DROP, "Parse_PacketEntities: bad number %i", newnum );
181
+ }
182
+
183
+ if( msg->readcount > msg->cursize ) {
184
+ Com_Error( ERR_DROP, "Parse_PacketEntities: end of message" );
185
+ }
186
+
187
+ if( newnum == ENTITYNUM_NONE ) {
188
+ break; // end of packetentities
189
+ }
190
+
191
+ while( oldnum < newnum ) {
192
+ // one or more entities from the old packet are unchanged
193
+ Parse_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
194
+
195
+ oldindex++;
196
+
197
+ if( oldindex >= oldframe->numEntities ) {
198
+ oldnum = 99999;
199
+ } else {
200
+ oldstate = &ds.parseEntities[(oldframe->firstEntity + oldindex) & PARSE_ENTITIES_MASK];
201
+ oldnum = oldstate->number;
202
+ }
203
+ }
204
+
205
+ if( oldnum == newnum ) {
206
+ // delta from previous state
207
+ Parse_DeltaEntity( msg, newframe, newnum, oldstate, qfalse );
208
+
209
+ oldindex++;
210
+
211
+ if( oldindex >= oldframe->numEntities ) {
212
+ oldnum = 99999;
213
+ } else {
214
+ oldstate = &ds.parseEntities[(oldframe->firstEntity + oldindex) & PARSE_ENTITIES_MASK];
215
+ oldnum = oldstate->number;
216
+ }
217
+ continue;
218
+ }
219
+
220
+ if( oldnum > newnum ) {
221
+ // delta from baseline
222
+ Parse_DeltaEntity( msg, newframe, newnum, &ds.baselines[newnum], qfalse );
223
+ }
224
+ }
225
+
226
+ // any remaining entities in the old frame are copied over
227
+ while( oldnum != 99999 ) {
228
+ // one or more entities from the old packet are unchanged
229
+ Parse_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
230
+
231
+ oldindex++;
232
+
233
+ if( oldindex >= oldframe->numEntities ) {
234
+ oldnum = 99999;
235
+ } else {
236
+ oldstate = &ds.parseEntities[(oldframe->firstEntity + oldindex) & PARSE_ENTITIES_MASK];
237
+ oldnum = oldstate->number;
238
+ }
239
+ }
240
+ }
241
+
242
+ /*
243
+ =====================
244
+ ParseSnapshot
245
+ =====================
246
+ */
247
+ static void Parse_Snapshot( sizebuf_t *msg ) {
248
+ snapshot_t *oldsnap;
249
+ int delta;
250
+ int len;
251
+
252
+ // save the frame off in the backup array for later delta comparisons
253
+ ds.snap = &ds.snapshots[demo.demoMessageSequence & SNAPSHOT_MASK];
254
+ memset( ds.snap, 0, sizeof( *ds.snap ) );
255
+
256
+ ds.snap->seq = demo.demoMessageSequence;
257
+ ds.snap->serverTime = MSG_ReadLong( msg );
258
+
259
+ // If the frame is delta compressed from data that we
260
+ // no longer have available, we must suck up the rest of
261
+ // the frame, but not use it, then ask for a non-compressed
262
+ // message
263
+ delta = MSG_ReadByte( msg );
264
+ if( delta ) {
265
+ ds.snap->deltaSeq = demo.demoMessageSequence - delta;
266
+ oldsnap = &ds.snapshots[ds.snap->deltaSeq & SNAPSHOT_MASK];
267
+
268
+ if( !oldsnap->valid ) {
269
+ // should never happen
270
+ Com_Printf( "Delta from invalid frame (not supposed to happen!).\n" );
271
+ } else if( oldsnap->seq != ds.snap->deltaSeq ) {
272
+ // The frame that the server did the delta from
273
+ // is too old, so we can't reconstruct it properly.
274
+ Com_Printf( "Delta frame too old.\n" );
275
+ } else if( ds.firstParseEntity - oldsnap->firstEntity >
276
+ MAX_PARSE_ENTITIES - MAX_ENTITIES_IN_SNAPSHOT )
277
+ {
278
+ Com_Printf( "Delta parse_entities too old.\n" );
279
+ } else {
280
+ ds.snap->valid = qtrue; // valid delta parse
281
+ }
282
+ } else {
283
+ oldsnap = NULL;
284
+ ds.snap->deltaSeq = -1;
285
+ ds.snap->valid = qtrue; // uncompressed frame
286
+ }
287
+
288
+ // read snapFlags
289
+ ds.snap->snapFlags = MSG_ReadByte( msg );
290
+
291
+ // read areabits
292
+ len = MSG_ReadByte( msg );
293
+ MSG_ReadData( msg, ds.snap->areamask, len );
294
+
295
+ // read playerinfo
296
+ MSG_ReadDeltaPlayerstate( msg, oldsnap ? &oldsnap->ps : NULL, &ds.snap->ps );
297
+
298
+ // read packet entities
299
+ Parse_PacketEntities( msg, oldsnap, ds.snap );
300
+ }
301
+
302
+ /*
303
+ =====================
304
+ Parse_DemoMessage
305
+ =====================
306
+ */
307
+ static void Parse_DemoMessage( sizebuf_t *msg ) {
308
+ int cmd;
309
+
310
+ // remaining data is Huffman compressed
311
+ MSG_SetBitstream( msg );
312
+
313
+ MSG_ReadLong( msg ); // FIXME: not used in demos
314
+
315
+
316
+ //
317
+ // parse the message
318
+ //
319
+ while( 1 ) {
320
+ if( msg->readcount > msg->cursize ) {
321
+ Com_Error( ERR_DROP, "Parse_DemoMessage: read past end of demo message" );
322
+ break;
323
+ }
324
+
325
+ cmd = MSG_ReadByte( msg );
326
+
327
+ if( cmd == SVC_EOM ) {
328
+ break;
329
+ }
330
+
331
+ // other commands
332
+ switch( cmd ) {
333
+ default:
334
+ case SVC_BASELINE:
335
+ case SVC_CONFIGSTRING:
336
+ case SVC_DOWNLOAD:
337
+ Com_Error( ERR_DROP, "Parse_DemoMessage: illegible demo message" );
338
+ break;
339
+ case SVC_NOP: /* do nothing */ break;
340
+ case SVC_GAMESTATE: Parse_GameState( msg ); break;
341
+ case SVC_SERVERCOMMAND: Parse_ServerCommand( msg ); break;
342
+ case SVC_SNAPSHOT: Parse_Snapshot( msg ); break;
343
+ }
344
+ }
345
+
346
+ }
347
+
348
+ /*
349
+ =====================
350
+ Parse_NextDemoMessage
351
+
352
+ Read next message from demo file and parse it
353
+ Return qfalse if demo EOF reached or error occured
354
+ =====================
355
+ */
356
+ qboolean Parse_NextDemoMessage( void ) {
357
+ sizebuf_t msg;
358
+ byte buffer[MAX_MSGLEN];
359
+ int len;
360
+ int seq;
361
+
362
+ if( fread( &seq, 1, 4, demo.demofile ) != 4 ) {
363
+ Com_Printf( "Demo file was truncated\n" );
364
+ return qfalse;
365
+ }
366
+
367
+ if( fread( &len, 1, 4, demo.demofile ) != 4 ) {
368
+ Com_Printf( "Demo file was truncated\n" );
369
+ return qfalse;
370
+ }
371
+
372
+ if( seq == -1 || len == -1 ) {
373
+ return qfalse; // demo EOF reached
374
+ }
375
+
376
+ MSG_Init( &msg, buffer, sizeof( buffer ) );
377
+
378
+ demo.demoMessageSequence = LittleLong( seq );
379
+ msg.cursize = LittleLong( len );
380
+
381
+ if( msg.cursize <= 0 || msg.cursize >= msg.maxsize ) {
382
+ Com_Error( ERR_DROP, "Illegal demo message length" );
383
+ }
384
+
385
+ if( fread( msg.data, 1, msg.cursize, demo.demofile ) != msg.cursize ) {
386
+ Com_Printf( "Demo file was truncated\n" );
387
+ return qfalse;
388
+ }
389
+
390
+ Parse_DemoMessage( &msg ); // parse the message
391
+
392
+ return qtrue;
393
+ }