demo-reader 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }