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/huff.h ADDED
@@ -0,0 +1,34 @@
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
+ // huff.h - Huffman encoder API
29
+ //
30
+
31
+ void Huff_Init( void );
32
+
33
+ void Huff_EmitByte( int ch, byte *buffer, int *count );
34
+ int Huff_GetByte( byte *buffer, int *count );
data/ext/dm68/main.c ADDED
@@ -0,0 +1,175 @@
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
+ // main.c - example main program
29
+ //
30
+
31
+ #include "main.h"
32
+
33
+ demoFileState_t demo;
34
+ demoState_t ds;
35
+
36
+
37
+ char result[MAXRESULT];
38
+ /*
39
+ * appends a string to the final result string
40
+ *
41
+ */
42
+ void append_result(const char *to_append, ...)
43
+ {
44
+ va_list argptr;
45
+ char buffer[MAXRESULT];
46
+
47
+ va_start(argptr, to_append);
48
+ vsprintf(buffer, to_append, argptr);
49
+ va_end(argptr);
50
+
51
+ Q_strcat(result, MAXRESULT, buffer);
52
+ }
53
+
54
+
55
+ /*
56
+ ============
57
+ Com_AppendToGameState
58
+ ============
59
+ */
60
+ static void Com_AppendToGameState( gameState_t *gameState, int index, const char *configString ) {
61
+ int len;
62
+
63
+ if( !configString || !(len=strlen( configString )) ) {
64
+ return;
65
+ }
66
+
67
+ if( gameState->dataCount + len + 2 >= MAX_GAMESTATE_CHARS ) {
68
+ Com_Error( ERR_DROP, "Com_AppendToGameState: MAX_GAMESTATE_CHARS" );
69
+ }
70
+
71
+ gameState->stringOffsets[index] = gameState->dataCount + 1;
72
+ strcpy( &gameState->stringData[gameState->dataCount + 1], configString );
73
+ gameState->dataCount += len + 1;
74
+ }
75
+
76
+ /*
77
+ ============
78
+ Com_InsertIntoGameState
79
+ ============
80
+ */
81
+ void Com_InsertIntoGameState( gameState_t *gameState, int index, const char *configString ) {
82
+ char *strings[MAX_CONFIGSTRINGS];
83
+ int ofs;
84
+ int i;
85
+
86
+ if( index < 0 || index >= MAX_CONFIGSTRINGS ) {
87
+ Com_Error( ERR_DROP, "Com_InsertIntoGameState: bad index %i", index );
88
+ }
89
+
90
+ if( !gameState->stringOffsets[index] ) {
91
+ // just append to the end of gameState
92
+ Com_AppendToGameState( gameState, index, configString );
93
+ return;
94
+ }
95
+
96
+ //
97
+ // resort gameState
98
+ //
99
+ for( i=0 ; i<MAX_CONFIGSTRINGS ; i++ ) {
100
+ ofs = gameState->stringOffsets[i];
101
+ if( !ofs ) {
102
+ strings[i] = NULL;
103
+ } else if( i == index ) {
104
+ strings[i] = CopyString( configString );
105
+ } else {
106
+ strings[i] = CopyString( &gameState->stringData[ofs] );
107
+ }
108
+ }
109
+
110
+ memset( gameState, 0, sizeof( *gameState ) );
111
+
112
+ for( i=0 ; i<MAX_CONFIGSTRINGS ; i++ ) {
113
+ if( strings[i] ) {
114
+ Com_AppendToGameState( gameState, i, strings[i] );
115
+ free( strings[i] );
116
+ }
117
+ }
118
+
119
+ }
120
+
121
+ /*
122
+ ============
123
+ Com_GetStringFromGameState
124
+ ============
125
+ */
126
+ char *Com_GetStringFromGameState( gameState_t *gameState, int index ) {
127
+ int ofs;
128
+
129
+ if( index < 0 || index >= MAX_CONFIGSTRINGS ) {
130
+ Com_Error( ERR_DROP, "Com_GetStringFromGameState: bad index %i", index );
131
+ }
132
+
133
+ ofs = gameState->stringOffsets[index ];
134
+
135
+ if( !ofs ) {
136
+ return "";
137
+ }
138
+
139
+ return &gameState->stringData[ofs];
140
+ }
141
+
142
+ int main( int argc, char **argv ) {
143
+ if( argc < 2 ) {
144
+ Com_Error( ERR_FATAL, "Usage: dm68 [demofile]\n" );
145
+ }
146
+ if( !(demo.demofile=fopen( argv[1], "rb" )) ) {
147
+ Com_Error( ERR_FATAL, "Couldn't open demofile" );
148
+ }
149
+
150
+ Huff_Init();
151
+
152
+ Com_Printf("---\n");
153
+
154
+ while( !demo.gameStatesParsed ) {
155
+ if( !Parse_NextDemoMessage() ) {
156
+ break;
157
+ }
158
+ }
159
+
160
+ GameStateParsed();
161
+
162
+ Com_Printf("prints:\n");
163
+
164
+ while( 1 ) {
165
+ if( !Parse_NextDemoMessage() ) {
166
+ break;
167
+ }
168
+ NewFrameParsed();
169
+ }
170
+
171
+ fclose( demo.demofile );
172
+
173
+ return EXIT_SUCCESS;
174
+ }
175
+
data/ext/dm68/main.h ADDED
@@ -0,0 +1,126 @@
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
+ #include "common.h"
28
+ #include "huff.h"
29
+ #include "msg.h"
30
+
31
+ // result
32
+ //
33
+
34
+ #define MAXRESULT 32000
35
+ extern char result[MAXRESULT];
36
+ void append_result(const char *to_append, ...);
37
+
38
+
39
+ //
40
+ // sizes of misc circular buffers in client and server system
41
+ //
42
+
43
+ // for delta compression of snapshots
44
+ #define MAX_SNAPSHOTS 32
45
+ #define SNAPSHOT_MASK (MAX_SNAPSHOTS-1)
46
+
47
+ // for keeping reliable text commands not acknowledged by receiver yet
48
+ #define MAX_SERVERCMDS 64
49
+ #define SERVERCMD_MASK (MAX_SERVERCMDS-1)
50
+
51
+ // for keeping all entityStates for delta encoding
52
+ #define MAX_PARSE_ENTITIES (MAX_GENTITIES*2)
53
+ #define PARSE_ENTITIES_MASK (MAX_PARSE_ENTITIES-1)
54
+
55
+ //
56
+ // max number of entityState_t present in a single update
57
+ //
58
+ #define MAX_ENTITIES_IN_SNAPSHOT 256
59
+
60
+ //
61
+ // possible server to client commands
62
+ //
63
+ typedef enum svc_ops_e {
64
+ SVC_BAD,
65
+ SVC_NOP,
66
+ SVC_GAMESTATE,
67
+ SVC_CONFIGSTRING, // only inside game
68
+ SVC_BASELINE,
69
+ SVC_SERVERCOMMAND,
70
+ SVC_DOWNLOAD, // not used in demos
71
+ SVC_SNAPSHOT,
72
+ SVC_EOM
73
+ } svc_ops_t;
74
+
75
+ typedef struct {
76
+ qboolean valid;
77
+
78
+ int seq; // die seqeunc number des snapshots
79
+ int deltaSeq;
80
+ int snapFlags; // SNAPFLAG_RATE_DELAYED, etc
81
+
82
+
83
+ int serverTime; // server time the message is valid for (in msec)
84
+
85
+ byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
86
+
87
+ playerState_t ps; // complete information about the current player at this time
88
+
89
+ int numEntities; // all of the entities that need to be presented
90
+ int firstEntity; // ab dieser Position sind im Ringbuffer die numEntities viele Entities des Snapshots
91
+ // ersetzt entities[Max_entities_in snapshot]
92
+ } snapshot_t;
93
+
94
+ typedef struct {
95
+ int lastServerCommandNum;
96
+ int currentServerCommandNum;
97
+ char serverCommands[MAX_SERVERCMDS][MAX_STRING_CHARS];
98
+
99
+ gameState_t gameState;
100
+ entityState_t baselines[MAX_GENTITIES];
101
+
102
+ entityState_t parseEntities[MAX_PARSE_ENTITIES];
103
+ int firstParseEntity;
104
+
105
+ snapshot_t snapshots[MAX_SNAPSHOTS];
106
+ snapshot_t *snap;
107
+ } demoState_t;
108
+
109
+ typedef struct {
110
+ FILE *demofile;
111
+
112
+ int demoMessageSequence;
113
+ int gameStatesParsed;
114
+ } demoFileState_t;
115
+
116
+ extern demoFileState_t demo;
117
+ extern demoState_t ds;
118
+
119
+ qboolean Parse_NextDemoMessage( void );
120
+
121
+ void GameStateParsed( void );
122
+ void NewFrameParsed( void );
123
+
124
+ void Com_InsertIntoGameState( gameState_t *gameState, int index, const char *configString );
125
+ char * Com_GetStringFromGameState( gameState_t *gameState, int index );
126
+
data/ext/dm68/msg.c ADDED
@@ -0,0 +1,1082 @@
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.c - message i/o subsystem
29
+ //
30
+
31
+ #include "common.h"
32
+ #include "huff.h"
33
+ #include "msg.h"
34
+
35
+ // snapped vectors are packed in 13 bits instead of 32
36
+ #define SNAPPED_BITS 13
37
+ #define MAX_SNAPPED (1<<SNAPPED_BITS)
38
+
39
+ /*
40
+ =======================================================================================
41
+
42
+ OFFSET TABLES FOR MAIN GAME STRUCTURES
43
+
44
+ If you want something from playerState_t or entityState structures to be
45
+ transmitted on the network, just insert a field into one of the following tables.
46
+
47
+ For network bandwidth saving, all fields are sorted in order from highest
48
+ modification frequency (during active gameplay) to lowest.
49
+
50
+ =======================================================================================
51
+ */
52
+
53
+ typedef struct {
54
+ int offset;
55
+ int bits; // bits > 0 --> unsigned integer
56
+ // bits = 0 --> float value
57
+ // bits < 0 --> signed integer
58
+ } field_t;
59
+
60
+ // field declarations
61
+ #define PS_FIELD(n,b) { ((int)&(((playerState_t *)0)->n)), b }
62
+ #define ES_FIELD(n,b) { ((int)&(((entityState_t *)0)->n)), b }
63
+
64
+
65
+ // field data accessing
66
+ #define FIELD_INTEGER(s) (*(int *)((byte *)(s)+field->offset))
67
+ #define FIELD_FLOAT(s) (*(float *)((byte *)(s)+field->offset))
68
+
69
+ //
70
+ // playerState_t
71
+ //
72
+ static field_t psTable[] = {
73
+ PS_FIELD( commandTime, 32 ),
74
+ PS_FIELD( origin[0], 0 ),
75
+ PS_FIELD( origin[1], 0 ),
76
+ PS_FIELD( bobCycle, 8 ),
77
+ PS_FIELD( velocity[0], 0 ),
78
+ PS_FIELD( velocity[1], 0 ),
79
+ PS_FIELD( viewangles[1], 0 ),
80
+ PS_FIELD( viewangles[0], 0 ),
81
+ PS_FIELD( weaponTime, -16 ),
82
+ PS_FIELD( origin[2], 0 ),
83
+ PS_FIELD( velocity[2], 0 ),
84
+ PS_FIELD( legsTimer, 8 ),
85
+ PS_FIELD( pm_time, -16 ),
86
+ PS_FIELD( eventSequence, 16 ),
87
+ PS_FIELD( torsoAnim, 8 ),
88
+ PS_FIELD( movementDir, 4 ),
89
+ PS_FIELD( events[0], 8 ),
90
+ PS_FIELD( legsAnim, 8 ),
91
+ PS_FIELD( events[1], 8 ),
92
+ PS_FIELD( pm_flags, 16 ),
93
+ PS_FIELD( groundEntityNum, 10 ),
94
+ PS_FIELD( weaponstate, 4 ),
95
+ PS_FIELD( eFlags, 16 ),
96
+ PS_FIELD( externalEvent, 10 ),
97
+ PS_FIELD( gravity, 16 ),
98
+ PS_FIELD( speed, 16 ),
99
+ PS_FIELD( delta_angles[1], 16 ),
100
+ PS_FIELD( externalEventParm, 8 ),
101
+ PS_FIELD( viewheight, -8 ),
102
+ PS_FIELD( damageEvent, 8 ),
103
+ PS_FIELD( damageYaw, 8 ),
104
+ PS_FIELD( damagePitch, 8 ),
105
+ PS_FIELD( damageCount, 8 ),
106
+ PS_FIELD( generic1, 8 ),
107
+ PS_FIELD( pm_type, 8 ),
108
+ PS_FIELD( delta_angles[0], 16 ),
109
+ PS_FIELD( delta_angles[2], 16 ),
110
+ PS_FIELD( torsoTimer, 12 ),
111
+ PS_FIELD( eventParms[0], 8 ),
112
+ PS_FIELD( eventParms[1], 8 ),
113
+ PS_FIELD( clientNum, 8 ),
114
+ PS_FIELD( weapon, 5 ),
115
+ PS_FIELD( viewangles[2], 0 ),
116
+ PS_FIELD( grapplePoint[0], 0 ),
117
+ PS_FIELD( grapplePoint[1], 0 ),
118
+ PS_FIELD( grapplePoint[2], 0 ),
119
+ PS_FIELD( jumppad_ent, 10 ),
120
+ PS_FIELD( loopSound, 16 )
121
+ };
122
+
123
+ //
124
+ // entityState_t
125
+ //
126
+ static field_t esTable[] = {
127
+ ES_FIELD( pos.trTime, 32 ),
128
+ ES_FIELD( pos.trBase[0], 0 ),
129
+ ES_FIELD( pos.trBase[1], 0 ),
130
+ ES_FIELD( pos.trDelta[0], 0 ),
131
+ ES_FIELD( pos.trDelta[1], 0 ),
132
+ ES_FIELD( pos.trBase[2], 0 ),
133
+ ES_FIELD( apos.trBase[1], 0 ),
134
+ ES_FIELD( pos.trDelta[2], 0 ),
135
+ ES_FIELD( apos.trBase[0], 0 ),
136
+ ES_FIELD( event, 10 ),
137
+ ES_FIELD( angles2[1], 0 ),
138
+ ES_FIELD( eType, 8 ),
139
+ ES_FIELD( torsoAnim, 8 ),
140
+ ES_FIELD( eventParm, 8 ),
141
+ ES_FIELD( legsAnim, 8 ),
142
+ ES_FIELD( groundEntityNum, 10 ),
143
+ ES_FIELD( pos.trType, 8 ),
144
+ ES_FIELD( eFlags, 19 ),
145
+ ES_FIELD( otherEntityNum, 10 ),
146
+ ES_FIELD( weapon, 8 ),
147
+ ES_FIELD( clientNum, 8 ),
148
+ ES_FIELD( angles[1], 0 ),
149
+ ES_FIELD( pos.trDuration, 32 ),
150
+ ES_FIELD( apos.trType, 8 ),
151
+ ES_FIELD( origin[0], 0 ),
152
+ ES_FIELD( origin[1], 0 ),
153
+ ES_FIELD( origin[2], 0 ),
154
+ ES_FIELD( solid, 24 ),
155
+ ES_FIELD( powerups, 16 ),
156
+ ES_FIELD( modelindex, 8 ),
157
+ ES_FIELD( otherEntityNum2, 10 ),
158
+ ES_FIELD( loopSound, 8 ),
159
+ ES_FIELD( generic1, 8 ),
160
+ ES_FIELD( origin2[2], 0 ),
161
+ ES_FIELD( origin2[0], 0 ),
162
+ ES_FIELD( origin2[1], 0 ),
163
+ ES_FIELD( modelindex2, 8 ),
164
+ ES_FIELD( angles[0], 0 ),
165
+ ES_FIELD( time, 32 ),
166
+ ES_FIELD( apos.trTime, 32 ),
167
+ ES_FIELD( apos.trDuration, 32 ),
168
+ ES_FIELD( apos.trBase[2], 0 ),
169
+ ES_FIELD( apos.trDelta[0], 0 ),
170
+ ES_FIELD( apos.trDelta[1], 0 ),
171
+ ES_FIELD( apos.trDelta[2], 0 ),
172
+ ES_FIELD( time2, 32 ),
173
+ ES_FIELD( angles[2], 0 ),
174
+ ES_FIELD( angles2[0], 0 ),
175
+ ES_FIELD( angles2[2], 0 ),
176
+ ES_FIELD( constantLight, 32 ),
177
+ ES_FIELD( frame, 16 )
178
+ };
179
+
180
+ static const int psTableSize = sizeof( psTable ) / sizeof( psTable[0] );
181
+ static const int esTableSize = sizeof( esTable ) / sizeof( esTable[0] );
182
+
183
+ static const entityState_t nullEntityState;
184
+ static const playerState_t nullPlayerState;
185
+
186
+ /*
187
+ =======================================================================================
188
+
189
+ MISC UTILITY FUNCTIONS
190
+
191
+ =======================================================================================
192
+ */
193
+
194
+ /*
195
+ ============
196
+ MSG_GetSpace
197
+ ============
198
+ */
199
+ static void *MSG_GetSpace( sizebuf_t *msg, int length ) {
200
+ void *data;
201
+
202
+ if( msg->cursize + length > msg->maxsize ) {
203
+ if( !msg->allowoverflow ) {
204
+ Com_Error( ERR_FATAL, "MSG_GetSpace: overflow without allowoverflow set" );
205
+ }
206
+
207
+ if( length > msg->maxsize ) {
208
+ Com_Error( ERR_FATAL, "MSG_GetSpace: %i is > full buffer size", length );
209
+ }
210
+
211
+ MSG_Clear( msg );
212
+ msg->overflowed = qtrue;
213
+ }
214
+
215
+ data = msg->data + msg->cursize;
216
+ msg->cursize += length;
217
+ msg->bit += length << 3;
218
+
219
+ return data;
220
+ }
221
+
222
+ /*
223
+ ============
224
+ MSG_Init
225
+ ============
226
+ */
227
+ void MSG_Init( sizebuf_t *msg, byte *buffer, int size ) {
228
+ memset( msg, 0, sizeof( *msg ) );
229
+ msg->data = buffer;
230
+ msg->maxsize = size;
231
+ }
232
+
233
+ /*
234
+ ============
235
+ MSG_InitRaw
236
+ ============
237
+ */
238
+ void MSG_InitRaw( sizebuf_t *msg, byte *buffer, int size ) {
239
+ memset( msg, 0, sizeof( *msg ) );
240
+ msg->data = buffer;
241
+ msg->maxsize = size;
242
+ msg->uncompressed = qtrue;
243
+ }
244
+
245
+ /*
246
+ ============
247
+ MSG_Clear
248
+ ============
249
+ */
250
+ void MSG_Clear( sizebuf_t *msg ) {
251
+ msg->cursize = 0;
252
+ msg->overflowed = qfalse;
253
+ msg->uncompressed = qfalse;
254
+ msg->bit = 0;
255
+ }
256
+
257
+ /*
258
+ ============
259
+ MSG_SetBitstream
260
+ ============
261
+ */
262
+ void MSG_SetBitstream( sizebuf_t *msg ) {
263
+ msg->uncompressed = qfalse;
264
+ }
265
+
266
+ /*
267
+ ============
268
+ MSG_WriteRawData
269
+ ============
270
+ */
271
+ void MSG_WriteRawData( sizebuf_t *msg, const void *data, int length ) {
272
+ if( length > 0 ) {
273
+ memcpy( MSG_GetSpace( msg, length ), data, length );
274
+ }
275
+ }
276
+
277
+ /*
278
+ =======================================================================================
279
+
280
+ WRITING FUNCTIONS
281
+
282
+ =======================================================================================
283
+ */
284
+
285
+ /*
286
+ ============
287
+ MSG_BeginWriting
288
+ ============
289
+ */
290
+ void MSG_BeginWriting( sizebuf_t *msg ) {
291
+ msg->uncompressed = qtrue;
292
+ msg->overflowed = 0;
293
+ msg->cursize = 0;
294
+ msg->bit = 0;
295
+ }
296
+
297
+
298
+ /*
299
+ ============
300
+ MSG_WriteBits
301
+ ============
302
+ */
303
+ void MSG_WriteBits( sizebuf_t *msg, int value, int bits ) {
304
+ int remaining;
305
+ int i;
306
+ byte *buf;
307
+
308
+ if( msg->maxsize - msg->cursize < 4 ) {
309
+ msg->overflowed = qtrue;
310
+ return;
311
+ }
312
+
313
+ if( !bits || bits < -31 || bits > 32 ) {
314
+ Com_Error( ERR_DROP, "MSG_WriteBits: bad bits %i", bits );
315
+ }
316
+
317
+ if( bits < 0 ) {
318
+ bits = -bits;
319
+ }
320
+
321
+ if( msg->uncompressed ) {
322
+ if( bits <= 8 ) {
323
+ buf = MSG_GetSpace( msg, 1 );
324
+ buf[0] = value;
325
+ } else if( bits <= 16 ) {
326
+ buf = MSG_GetSpace( msg, 2 );
327
+ buf[0] = value & 0xFF;
328
+ buf[1] = value >> 8;
329
+ } else if( bits <= 32 ) {
330
+ buf = MSG_GetSpace( msg, 4 );
331
+ buf[0] = value & 0xFF;
332
+ buf[1] = (value >> 8) & 0xFF;
333
+ buf[2] = (value >> 16) & 0xFF;
334
+ buf[3] = value >> 24;
335
+ }
336
+ return;
337
+ }
338
+
339
+ value &= 0xFFFFFFFFU >> (32 - bits);
340
+ remaining = bits & 7;
341
+
342
+ for( i=0; i<remaining ; i++ ) {
343
+ if( !(msg->bit & 7) ) {
344
+ msg->data[msg->bit >> 3] = 0;
345
+ }
346
+ msg->data[msg->bit >> 3] |= (value & 1) << (msg->bit & 7);
347
+ msg->bit++;
348
+ value >>= 1;
349
+ }
350
+ bits -= remaining;
351
+
352
+ if( bits > 0 ) {
353
+ for( i=0 ; i<(bits+7)>>3 ; i++ ) {
354
+ Huff_EmitByte( value & 255, msg->data, &msg->bit );
355
+ value >>= 8;
356
+ }
357
+ }
358
+
359
+ msg->cursize = (msg->bit >> 3) + 1;
360
+ }
361
+
362
+
363
+ /*
364
+ ============
365
+ MSG_WriteData
366
+ ============
367
+ */
368
+ void MSG_WriteData( sizebuf_t *msg, const void *data, int length ) {
369
+ int i;
370
+
371
+ for( i=0 ; i<length ; i++ ) {
372
+ MSG_WriteByte( msg, ((byte *)data)[i] );
373
+ }
374
+ }
375
+
376
+ /*
377
+ ============
378
+ MSG_WriteString
379
+ ============
380
+ */
381
+ void MSG_WriteString( sizebuf_t *msg, const char *string ) {
382
+ char buffer[MAX_STRING_CHARS];
383
+ int i;
384
+ int len;
385
+
386
+ if( !string ) {
387
+ MSG_WriteByte( msg, 0 );
388
+ return;
389
+ }
390
+
391
+ len = strlen( string );
392
+
393
+ if( len >= sizeof( buffer ) ) {
394
+ Com_Printf( "MSG_WriteString: MAX_STRING_CHARS\n" );
395
+ MSG_WriteByte( msg, 0 );
396
+ return;
397
+ }
398
+
399
+ Q_strncpyz( buffer, string, sizeof( buffer ) );
400
+
401
+ for( i=0 ; i<len ; i++ ) {
402
+ if( buffer[i] > 127 ) {
403
+ buffer[i] = '.';
404
+ }
405
+ }
406
+
407
+ for( i=0 ; i<=len ; i++ ) {
408
+ MSG_WriteByte( msg, buffer[i] );
409
+ }
410
+ }
411
+
412
+ /*
413
+ ============
414
+ MSG_WriteString
415
+ ============
416
+ */
417
+ void MSG_WriteBigString( sizebuf_t *msg, const char *string ) {
418
+ char buffer[BIG_INFO_STRING];
419
+ int i;
420
+ int len;
421
+
422
+ if( !string ) {
423
+ MSG_WriteByte( msg, 0 );
424
+ return;
425
+ }
426
+
427
+ len = strlen( string );
428
+
429
+ if( len >= sizeof( buffer ) ) {
430
+ Com_Printf( "MSG_WriteString: BIG_INFO_STRING\n" );
431
+ MSG_WriteByte( msg, 0 );
432
+ return;
433
+ }
434
+
435
+ Q_strncpyz( buffer, string, sizeof( buffer ) );
436
+
437
+ for( i=0 ; i<len ; i++ ) {
438
+ if( buffer[i] > 127 ) {
439
+ buffer[i] = '.';
440
+ }
441
+ }
442
+
443
+ for( i=0 ; i<=len ; i++ ) {
444
+ MSG_WriteByte( msg, buffer[i] );
445
+ }
446
+ }
447
+
448
+ /*
449
+ ============
450
+ MSG_WriteDeltaEntity
451
+
452
+ If 'force' parm is false, this won't result any bits
453
+ emitted if entity didn't changed at all
454
+
455
+ 'from' == NULL --> nodelta update
456
+ 'to' == NULL --> entity removed
457
+ ============
458
+ */
459
+ void MSG_WriteDeltaEntity( sizebuf_t *msg, const entityState_t *from, const entityState_t *to, qboolean force ) {
460
+ field_t *field;
461
+ int to_value;
462
+ int to_integer;
463
+ float to_float;
464
+ int maxFieldNum;
465
+ int i;
466
+
467
+ if( !to ) {
468
+ if( from ) {
469
+ MSG_WriteBits( msg, from->number, GENTITYNUM_BITS );
470
+ MSG_WriteBits( msg, 1, 1 );
471
+ }
472
+ return; // removed
473
+ }
474
+
475
+ if( to->number < 0 || to->number > MAX_GENTITIES ) {
476
+ Com_Error( ERR_DROP, "MSG_WriteDeltaEntity: Bad entity number: %i", to->number );
477
+ }
478
+
479
+ if( !from ) {
480
+ from = &nullEntityState; // nodelta update
481
+ }
482
+
483
+ //
484
+ // find last modified field in table
485
+ //
486
+ maxFieldNum = 0;
487
+ for( i=0, field=esTable ; i<esTableSize ; i++, field++ ) {
488
+ if( FIELD_INTEGER( from ) != FIELD_INTEGER( to ) ) {
489
+ maxFieldNum = i + 1;
490
+ }
491
+ }
492
+
493
+ if( !maxFieldNum ) {
494
+ if( !force ) {
495
+ return; // don't emit any bits at all
496
+ }
497
+
498
+ MSG_WriteBits( msg, to->number, GENTITYNUM_BITS );
499
+ MSG_WriteBits( msg, 0, 1 );
500
+ MSG_WriteBits( msg, 0, 1 );
501
+ return; // unchanged
502
+ }
503
+
504
+ MSG_WriteBits( msg, to->number, GENTITYNUM_BITS );
505
+ MSG_WriteBits( msg, 0, 1 );
506
+ MSG_WriteBits( msg, 1, 1 );
507
+ MSG_WriteByte( msg, maxFieldNum );
508
+
509
+ //
510
+ // write all modified fields
511
+ //
512
+ for( i=0, field=esTable ; i<maxFieldNum ; i++, field++ ) {
513
+ to_value = FIELD_INTEGER( to );
514
+
515
+ if( FIELD_INTEGER( from ) == to_value ) {
516
+ MSG_WriteBits( msg, 0, 1 );
517
+ continue; // field unchanged
518
+ }
519
+ MSG_WriteBits( msg, 1, 1 );
520
+
521
+ if( !to_value ) {
522
+ MSG_WriteBits( msg, 0, 1 );
523
+ continue; // field set to zero
524
+ }
525
+ MSG_WriteBits( msg, 1, 1 );
526
+
527
+ if( field->bits ) {
528
+ MSG_WriteBits( msg, to_value, field->bits );
529
+ continue; // integer value
530
+ }
531
+
532
+ //
533
+ // figure out how to pack float value
534
+ //
535
+ to_float = FIELD_FLOAT( to );
536
+ to_integer = (int)to_float;
537
+
538
+ if( (float)to_integer == to_float
539
+ && to_integer + MAX_SNAPPED/2 >= 0
540
+ && to_integer + MAX_SNAPPED/2 < MAX_SNAPPED )
541
+ {
542
+ MSG_WriteBits( msg, 0, 1 ); // pack in 13 bits
543
+ MSG_WriteBits( msg, to_integer + MAX_SNAPPED/2, SNAPPED_BITS );
544
+ } else {
545
+ MSG_WriteBits( msg, 1, 1 ); // pack in 32 bits
546
+ MSG_WriteLong( msg, to_value );
547
+ }
548
+ }
549
+
550
+ }
551
+
552
+ /*
553
+ ============
554
+ MSG_WriteDeltaPlayerstate
555
+
556
+ 'from' == NULL --> nodelta update
557
+ 'to' == NULL --> do nothing
558
+ ============
559
+ */
560
+ void MSG_WriteDeltaPlayerstate( sizebuf_t *msg, const playerState_t *from, const playerState_t *to ) {
561
+ field_t *field;
562
+ int to_value;
563
+ float to_float;
564
+ int to_integer;
565
+ int maxFieldNum;
566
+ int statsMask;
567
+ int persistantMask;
568
+ int ammoMask;
569
+ int powerupsMask;
570
+ int i;
571
+
572
+ if( !to ) {
573
+ return;
574
+ }
575
+
576
+ if( !from ) {
577
+ from = &nullPlayerState; // nodelta update
578
+ }
579
+
580
+ //
581
+ // find last modified field in table
582
+ //
583
+ maxFieldNum = 0;
584
+ for( i=0, field=psTable ; i<psTableSize ; i++, field++ ) {
585
+ if( FIELD_INTEGER( from ) != FIELD_INTEGER( to ) ) {
586
+ maxFieldNum = i + 1;
587
+ }
588
+ }
589
+
590
+ MSG_WriteByte( msg, maxFieldNum );
591
+
592
+ //
593
+ // write all modified fields
594
+ //
595
+ for( i=0, field=psTable ; i<maxFieldNum ; i++, field++ ) {
596
+ to_value = FIELD_INTEGER( to );
597
+
598
+ if( FIELD_INTEGER( from ) == to_value ) {
599
+ MSG_WriteBits( msg, 0, 1 );
600
+ continue; // field unchanged
601
+ }
602
+ MSG_WriteBits( msg, 1, 1 );
603
+
604
+ if( field->bits ) {
605
+ MSG_WriteBits( msg, to_value, field->bits );
606
+ continue; // integer value
607
+ }
608
+
609
+ //
610
+ // figure out how to pack float value
611
+ //
612
+ to_float = FIELD_FLOAT( to );
613
+ to_integer = (int)to_float;
614
+
615
+ if( (float)to_integer == to_float
616
+ && to_integer + MAX_SNAPPED/2 >= 0
617
+ && to_integer + MAX_SNAPPED/2 < MAX_SNAPPED )
618
+ {
619
+ MSG_WriteBits( msg, 0, 1 ); // pack in 13 bits
620
+ MSG_WriteBits( msg, to_integer + MAX_SNAPPED/2, SNAPPED_BITS );
621
+ } else {
622
+ MSG_WriteBits( msg, 1, 1 ); // pack in 32 bits
623
+ MSG_WriteLong( msg, to_value );
624
+ }
625
+ }
626
+
627
+ //
628
+ // find modified arrays
629
+ //
630
+ statsMask = 0;
631
+ for( i=0 ; i<MAX_STATS ; i++ ) {
632
+ if( from->stats[i] != to->stats[i] ) {
633
+ statsMask |= (1 << i);
634
+ }
635
+ }
636
+
637
+ persistantMask = 0;
638
+ for( i=0 ; i<MAX_PERSISTANT ; i++ ) {
639
+ if( from->persistant[i] != to->persistant[i] ) {
640
+ persistantMask |= (1 << i);
641
+ }
642
+ }
643
+
644
+ ammoMask = 0;
645
+ for( i=0 ; i<MAX_WEAPONS ; i++ ) {
646
+ if( from->ammo[i] != to->ammo[i] ) {
647
+ ammoMask |= (1 << i);
648
+ }
649
+ }
650
+
651
+ powerupsMask = 0;
652
+ for( i=0 ; i<MAX_POWERUPS ; i++ ) {
653
+ if( from->powerups[i] != to->powerups[i] ) {
654
+ powerupsMask |= (1 << i);
655
+ }
656
+ }
657
+
658
+ if( !statsMask && !persistantMask && !ammoMask && !powerupsMask ) {
659
+ MSG_WriteBits( msg, 0, 1 );
660
+ return; // no arrays modified
661
+ }
662
+
663
+ //
664
+ // write all modified arrays
665
+ //
666
+ MSG_WriteBits( msg, 1, 1 );
667
+
668
+ // PS_STATS
669
+ if( statsMask ) {
670
+ MSG_WriteBits( msg, 1, 1 );
671
+ MSG_WriteShort( msg, statsMask );
672
+ for( i=0 ; i<MAX_STATS ; i++ ) {
673
+ if( statsMask & (1 << i) ) {
674
+ MSG_WriteSignedShort( msg, to->stats[i] );
675
+ }
676
+ }
677
+ } else {
678
+ MSG_WriteBits( msg, 0, 1 ); // unchanged
679
+ }
680
+
681
+ // PS_PERSISTANT
682
+ if( persistantMask ) {
683
+ MSG_WriteBits( msg, 1, 1 );
684
+ MSG_WriteShort( msg, persistantMask );
685
+ for( i=0 ; i<MAX_PERSISTANT ; i++ ) {
686
+ if( persistantMask & (1 << i) ) {
687
+ MSG_WriteSignedShort( msg, to->persistant[i] );
688
+ }
689
+ }
690
+ } else {
691
+ MSG_WriteBits( msg, 0, 1 ); // unchanged
692
+ }
693
+
694
+
695
+ // PS_AMMO
696
+ if( ammoMask ) {
697
+ MSG_WriteBits( msg, 1, 1 );
698
+ MSG_WriteShort( msg, ammoMask );
699
+ for( i=0 ; i<MAX_WEAPONS ; i++ ) {
700
+ if( ammoMask & (1 << i) ) {
701
+ MSG_WriteShort( msg, to->ammo[i] );
702
+ }
703
+ }
704
+ } else {
705
+ MSG_WriteBits( msg, 0, 1 ); // unchanged
706
+ }
707
+
708
+ // PS_POWERUPS
709
+ if( powerupsMask ) {
710
+ MSG_WriteBits( msg, 1, 1 );
711
+ MSG_WriteShort( msg, powerupsMask );
712
+ for( i=0 ; i<MAX_POWERUPS ; i++ ) {
713
+ if( powerupsMask & (1 << i) ) {
714
+ MSG_WriteLong( msg, to->powerups[i] ); // WARNING: powerups use 32 bits, not 16
715
+ }
716
+ }
717
+ } else {
718
+ MSG_WriteBits( msg, 0, 1 ); // unchanged
719
+ }
720
+
721
+ }
722
+
723
+
724
+ /*
725
+ =======================================================================================
726
+
727
+ READING FUNCTIONS
728
+
729
+ =======================================================================================
730
+ */
731
+
732
+ /*
733
+ ============
734
+ MSG_BeginReading
735
+ ============
736
+ */
737
+ void MSG_BeginReading( sizebuf_t *msg ) {
738
+ msg->readcount = 0;
739
+ msg->bit = 0;
740
+ msg->uncompressed = qtrue;
741
+ }
742
+
743
+
744
+
745
+ /*
746
+ ============
747
+ MSG_ReadBits
748
+ ============
749
+ */
750
+ int MSG_ReadBits( sizebuf_t *msg, int bits ) {
751
+ int i;
752
+ int val;
753
+ int bitmask = 0;
754
+ int remaining;
755
+ qboolean extend = qfalse;
756
+
757
+ if( !bits || bits < -31 || bits > 32 ) {
758
+ Com_Error( ERR_DROP, "MSG_ReadBits: bad bits %i", bits );
759
+ }
760
+
761
+ if( bits < 0 ) {
762
+ bits = -bits;
763
+ extend = qtrue;
764
+ }
765
+
766
+ if( msg->uncompressed ) {
767
+ if( bits <= 8 ) {
768
+ bitmask = (unsigned char)msg->data[msg->readcount];
769
+ msg->readcount++;
770
+ msg->bit += 8;
771
+ } else if( bits <= 16 ) {
772
+ bitmask = (unsigned short)(msg->data[msg->readcount]
773
+ + (msg->data[msg->readcount+1] << 8));
774
+ msg->readcount += 2;
775
+ msg->bit += 16;
776
+ } else if( bits <= 32 ) {
777
+ bitmask = msg->data[msg->readcount]
778
+ + (msg->data[msg->readcount+1] << 8)
779
+ + (msg->data[msg->readcount+2] << 16)
780
+ + (msg->data[msg->readcount+3] << 24);
781
+ msg->readcount += 4;
782
+ msg->bit += 32;
783
+ }
784
+ } else {
785
+ remaining = bits & 7;
786
+
787
+ for( i=0 ; i<remaining ; i++ ) {
788
+ val = msg->data[msg->bit >> 3] >> (msg->bit & 7);
789
+ msg->bit++;
790
+ bitmask |= (val & 1) << i;
791
+ }
792
+
793
+ for( i=0 ; i<bits-remaining ; i+=8 ) {
794
+ val = Huff_GetByte( msg->data, &msg->bit );
795
+ bitmask |= val << (i + remaining);
796
+ }
797
+
798
+ msg->readcount = (msg->bit >> 3) + 1;
799
+ }
800
+
801
+ if( extend ) {
802
+ if( bitmask & (1 << (bits - 1)) ) {
803
+ bitmask |= ~((1 << bits) - 1);
804
+ }
805
+ }
806
+
807
+ return bitmask;
808
+ }
809
+
810
+ /*
811
+ ============
812
+ MSG_ReadData
813
+ ============
814
+ */
815
+ void MSG_ReadData( sizebuf_t *msg, void *data, int len ) {
816
+ int i;
817
+ int c;
818
+
819
+ for( i=0 ; i<len ; i++ ) {
820
+ c = MSG_ReadByte( msg );
821
+ if( c == -1 ) {
822
+ break;
823
+ }
824
+ if( data ) {
825
+ ((byte *)data)[i] = c;
826
+ }
827
+ }
828
+ }
829
+
830
+ /*
831
+ ============
832
+ MSG_ReadString
833
+ ============
834
+ */
835
+ char *MSG_ReadString( sizebuf_t *msg ) {
836
+ static char string[MAX_STRING_CHARS];
837
+ int i;
838
+ int c;
839
+
840
+ for( i=0 ; i<sizeof( string )-1; i++ ) {
841
+ c = MSG_ReadByte( msg );
842
+ if( c == -1 || c == 0 ) {
843
+ break;
844
+ }
845
+ if( c == '%' || c > 127 ) {
846
+ c = '.';
847
+ }
848
+ string[i] = c;
849
+ }
850
+
851
+ string[i] = 0;
852
+
853
+ return string;
854
+ }
855
+
856
+ /*
857
+ ============
858
+ MSG_ReadStringLine
859
+ ============
860
+ */
861
+ char *MSG_ReadStringLine( sizebuf_t *msg ) {
862
+ static char string[MAX_STRING_CHARS];
863
+ int i;
864
+ int c;
865
+
866
+ for( i=0 ; i<sizeof( string )-1 ; i++ ) {
867
+ c = MSG_ReadByte( msg );
868
+ if( c == -1 || c == 0 || c == '\n' ) {
869
+ break;
870
+ }
871
+ if( c == '%' || c > 127 ) {
872
+ c = '.';
873
+ }
874
+ string[i] = c;
875
+ }
876
+
877
+ string[i] = 0;
878
+
879
+ return string;
880
+ }
881
+
882
+ /*
883
+ ============
884
+ MSG_ReadBigString
885
+ ============
886
+ */
887
+ char *MSG_ReadBigString( sizebuf_t *msg ) {
888
+ static char string[BIG_INFO_STRING];
889
+ int i;
890
+ int c;
891
+
892
+ for( i=0 ; i<sizeof( string )-1 ; i++ ) {
893
+ c = MSG_ReadByte( msg );
894
+ if( c == -1 || c == 0 ) {
895
+ break;
896
+ }
897
+ if( c == '%' || c > 127 ) {
898
+ c = '.';
899
+ }
900
+ string[i] = c;
901
+ }
902
+
903
+ string[i] = 0;
904
+
905
+ return string;
906
+ }
907
+
908
+ /*
909
+ ============
910
+ MSG_ReadDeltaEntity
911
+
912
+ 'from' == NULL --> nodelta update
913
+ 'to' == NULL --> do nothing
914
+ ============
915
+ */
916
+ void MSG_ReadDeltaEntity( sizebuf_t *msg, const entityState_t *from, entityState_t *to, int number ) {
917
+ field_t *field;
918
+ int maxFieldNum;
919
+ int i;
920
+
921
+ if( number < 0 || number >= MAX_GENTITIES ) {
922
+ Com_Error( ERR_DROP, "MSG_ReadDeltaEntity: Bad delta entity number: %i\n", number );
923
+ }
924
+
925
+ if( !to ) {
926
+ return;
927
+ }
928
+
929
+ if( MSG_ReadBits( msg, 1 ) ) {
930
+ memset( to, 0, sizeof( *to ) );
931
+ to->number = ENTITYNUM_NONE;
932
+ return; // removed
933
+ }
934
+
935
+ if( !from ) {
936
+ memset( to, 0, sizeof( *to ) ); // nodelta update
937
+ } else {
938
+ memcpy( to, from, sizeof( *to ) );
939
+ }
940
+ to->number = number;
941
+
942
+ if( !MSG_ReadBits( msg, 1 ) ) {
943
+ return; // unchanged
944
+ }
945
+
946
+ maxFieldNum = MSG_ReadByte( msg );
947
+ if( maxFieldNum > esTableSize ) {
948
+ Com_Error( ERR_DROP, "MSG_ReadDeltaEntity: maxFieldNum > esTableSize" );
949
+ }
950
+
951
+ //
952
+ // read all modified fields
953
+ //
954
+ for( i=0, field=esTable ; i<maxFieldNum ; i++, field++ ) {
955
+ if( !MSG_ReadBits( msg, 1 ) ) {
956
+ continue; // field unchanged
957
+ }
958
+ if( !MSG_ReadBits( msg, 1 ) ) {
959
+ FIELD_INTEGER( to ) = 0;
960
+ continue; // field set to zero
961
+ }
962
+
963
+ if( field->bits ) {
964
+ FIELD_INTEGER( to ) = MSG_ReadBits( msg, field->bits );
965
+ continue; // integer value
966
+ }
967
+
968
+ //
969
+ // read packed float value
970
+ //
971
+ if( !MSG_ReadBits( msg, 1 ) ) {
972
+ FIELD_FLOAT( to ) = (float)(MSG_ReadBits( msg, SNAPPED_BITS ) - MAX_SNAPPED/2);
973
+ } else {
974
+ FIELD_INTEGER( to ) = MSG_ReadLong( msg );
975
+ }
976
+ }
977
+ }
978
+
979
+
980
+ /*
981
+ ============
982
+ MSG_ReadDeltaPlayerstate
983
+
984
+ 'from' == NULL --> nodelta update
985
+ 'to' == NULL --> do nothing
986
+ ============
987
+ */
988
+ void MSG_ReadDeltaPlayerstate( sizebuf_t *msg, const playerState_t *from, playerState_t *to ) {
989
+ field_t *field;
990
+ int maxFieldNum;
991
+ int bitmask;
992
+ int i;
993
+
994
+ if( !to ) {
995
+ return;
996
+ }
997
+
998
+ if( !from ) {
999
+ memset( to, 0, sizeof( *to ) ); // nodelta update
1000
+ } else {
1001
+ memcpy( to, from, sizeof( *to ) );
1002
+ }
1003
+
1004
+ maxFieldNum = MSG_ReadByte( msg );
1005
+ if( maxFieldNum > psTableSize ) {
1006
+ Com_Error( ERR_DROP, "MSG_ReadDeltaPlayerstate: maxFieldNum > psTableSize" );
1007
+ }
1008
+
1009
+ //
1010
+ // read all modified fields
1011
+ //
1012
+ for( i=0, field=psTable ; i<maxFieldNum ; i++, field++ ) {
1013
+ if( !MSG_ReadBits( msg, 1 ) ) {
1014
+ continue; // field unchanged
1015
+ }
1016
+
1017
+ if( field->bits ) {
1018
+ FIELD_INTEGER( to ) = MSG_ReadBits( msg, field->bits );
1019
+ continue; // integer value
1020
+ }
1021
+
1022
+ //
1023
+ // read packed float value
1024
+ //
1025
+ if( !MSG_ReadBits( msg, 1 ) ) {
1026
+ FIELD_FLOAT( to ) = (float)(MSG_ReadBits( msg, SNAPPED_BITS ) - MAX_SNAPPED/2);
1027
+ } else {
1028
+ FIELD_INTEGER( to ) = MSG_ReadLong( msg );
1029
+ }
1030
+ }
1031
+
1032
+ //
1033
+ // read all modified arrays
1034
+ //
1035
+ if( !MSG_ReadBits( msg, 1 ) ) {
1036
+ return; // no arrays modified
1037
+ }
1038
+
1039
+ // PS_STATS
1040
+ if( MSG_ReadBits( msg, 1 ) ) {
1041
+ bitmask = MSG_ReadShort( msg );
1042
+ for( i=0 ; i<MAX_STATS ; i++ ) {
1043
+ if( bitmask & (1 << i) ) {
1044
+ to->stats[i] = MSG_ReadSignedShort( msg ); // PS_STATS can be negative
1045
+ }
1046
+ }
1047
+ }
1048
+
1049
+ // PS_PERSISTANT
1050
+ if( MSG_ReadBits( msg, 1 ) ) {
1051
+ bitmask = MSG_ReadShort( msg );
1052
+ for( i=0 ; i<MAX_PERSISTANT ; i++ ) {
1053
+ if( bitmask & (1 << i) ) {
1054
+ to->persistant[i] = MSG_ReadSignedShort( msg ); // PS_PERSISTANT can be negative
1055
+ }
1056
+ }
1057
+ }
1058
+
1059
+ // PS_AMMO
1060
+ if( MSG_ReadBits( msg, 1 ) ) {
1061
+ bitmask = MSG_ReadShort( msg );
1062
+ for( i=0 ; i<MAX_WEAPONS ; i++ ) {
1063
+ if( bitmask & (1 << i) ) {
1064
+ to->ammo[i] = MSG_ReadShort( msg );
1065
+ }
1066
+ }
1067
+ }
1068
+
1069
+ // PS_POWERUPS
1070
+ if( MSG_ReadBits( msg, 1 ) ) {
1071
+ bitmask = MSG_ReadShort( msg );
1072
+ for( i=0 ; i<MAX_POWERUPS ; i++ ) {
1073
+ if( bitmask & (1 << i) ) {
1074
+ to->powerups[i] = MSG_ReadLong( msg ); // WARNING: powerups use 32 bits, not 16
1075
+ }
1076
+ }
1077
+ }
1078
+ }
1079
+
1080
+
1081
+
1082
+