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/.gitignore +4 -0
- data/README.rdoc +1 -1
- data/Rakefile +3 -2
- data/VERSION +1 -1
- data/demo-reader.gemspec +30 -7
- data/ext/dm68/README.txt +14 -0
- data/ext/dm68/common.c +797 -0
- data/ext/dm68/common.h +568 -0
- data/ext/dm68/dm68.c +57 -0
- data/ext/dm68/dump.c +196 -0
- data/ext/dm68/extconf.rb +16 -0
- data/ext/dm68/huff.c +542 -0
- data/ext/dm68/huff.h +34 -0
- data/ext/dm68/main.c +175 -0
- data/ext/dm68/main.h +126 -0
- data/ext/dm68/msg.c +1082 -0
- data/ext/dm68/msg.h +114 -0
- data/ext/dm68/parse.c +393 -0
- data/lib/demo-reader.rb +11 -3
- data/lib/demo_reader_defrag.rb +106 -0
- data/lib/demo_reader_warsow.rb +3 -5
- data/test/demo_reader_defrag_dm_68_cpm_test.rb +29 -0
- data/test/demo_reader_defrag_dm_68_vq3_test.rb +29 -0
- data/test/fixtures/defrag/dm_68/cpm/pornchronostar_mdf.cpm_00.49.216_tyaz.germany.dm_68 +0 -0
- data/test/fixtures/defrag/dm_68/cpm/puremotion_df.cpm_00.10.600_eS-Rody.russia.dm_68 +0 -0
- data/test/fixtures/defrag/dm_68/vq3/runkull2_df.vq3_01.05.904_XunderBIRD.Germany.dm_68 +0 -0
- data/test/fixtures/defrag/dm_68/vq3/un-dead029_df.vq3_00.16.912_uN-DeaD!WiNTeR.ru.dm_68 +0 -0
- metadata +43 -9
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
|
+
|