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/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
+