-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcsgo.py
356 lines (324 loc) · 11.5 KB
/
csgo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
import struct
import netmessages_public_pb2 as protonet
#-------------------------------------------
# Demo variables
DEMO_HEADER_ID = b'HL2DEMO\x00'
DEMO_PROTOCOL = 4
# Flags
FDEMO_NORMAL = 0
FDEMO_USE_ORIGIN2 = 1
FDEMO_USE_ANGLES2 = 2
FDEMO_NOINTERP = 4
# CSGO consts
MAX_SPLITSCREEN_CLIENTS = 2
NET_MAX_PAYLOAD = 252140
# Sizes depends on C++
INT_SIZE = 4
FLOAT_SIZE = 4
DEMOCMDINFO_SIZE = 152
DEMO_HEADER_SIZE = 1072
# ENUMS
dem_signon = b'\x01'
dem_packet = b'\x02'
dem_synctick = b'\x03'
dem_consolecmd = b'\x04'
dem_usercmd = b'\x05'
dem_datatables = b'\x06'
dem_stop = b'\x07'
dem_customdata = b'\x08'
dem_stringtables = b'\x09'
dem_lastcmd = b'\x09'
up_enterPVS = 0
up_leavePVS = 1
up_deltaEnt = 2
up_preserveEnt = 3
up_finished = 4
up_failed = 5
fhdr_zero = 0
fhdr_leavePVS = 1
fhdr_delete = 2
fhdr_enterPVS = 4
#-------------------------------------------
def b2int(x):
return int.from_bytes(x, byteorder="little")
def b2float(x):
return struct.unpack('f', x)[0]
def b2str(x):
return x.decode("utf-8")
# Remove \x00 bytes in string
def readString_raw(a_str):
return a_str.split(b'\0',1)[0]
#-------------------------------------------
class Vector:
x, y, z = 0, 0, 0
def __init__(self, x=0, y=0, z=0):
self.x, self.y, self.z = x, y, z
class QAngle:
x, y, z = 0, 0, 0
def __init__(self, x=0, y=0, z=0):
self.x, self.y, self.z = x, y, z
#-------------------------------------------
# Demo header structure
class struct_demoheader:
demofilestamp = "" # Should be HL2DEMO
demoprotocol = 0 # Should be DEMO_PROTOCOL
networkprotocol = 0 # Should be PROTOCOL_VERSION
servername = "" # Name of server
clientname = "" # Name of client who recorded the game
mapname = "" # Name of map
gamedirectory = "" # Name of game directory (com_gamedir)
playback_time = 0.0 # Time of track
playback_ticks = 0 # Number of ticks in track
playback_frames = 0 # Number of frames in track
signonlength = 0 # Length of sigondata in bytes
# Split structure
class struct_split:
flags = FDEMO_NORMAL
viewOrigin = Vector()
viewAngles = QAngle()
localViewAngles = QAngle()
viewOrigin2 = Vector()
viewAngles2 = QAngle()
localViewAngles2 = QAngle()
#---------------------------------------
def GetViewOrigin(self):
if(self.flags & FDEMO_USE_ORIGIN2):
return self.viewOrigin2
return self.viewOrigin
def GetViewAngles(self):
if(self.flags & FDEMO_USE_ANGLES2):
return self.viewAngles2
return self.viewAngles
def GetLocalViewAngles(self):
if(self.flags & FDEMO_USE_ANGLES2):
return self.localViewAngles2
return self.localViewAngles
# ---------------------------------------
def reset(self):
self.flags = 0
self.viewOrigin2 = self.viewOrigin
self.viewAngles2 = self.viewAngles
self.localViewAngles2 = self.localViewAngles
# Demo command information structure
class struct_democmdinfo:
u = [struct_split()]*MAX_SPLITSCREEN_CLIENTS
def ReadCmdInfo(self, text):
textPos = 0
for i in self.u:
# flags
i.flags = b2int(text[textPos:textPos+INT_SIZE])
textPos += INT_SIZE
# viewOrigin
i.viewOrigin.x = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.viewOrigin.y = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.viewOrigin.z = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
# viewAngles
i.viewAngles.x = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.viewAngles.y = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.viewAngles.z = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
# localViewAngles
i.localViewAngles.x = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.localViewAngles.y = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.localViewAngles.z = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
# viewOrigin2
i.viewOrigin2.x = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.viewOrigin2.y = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.viewOrigin2.z = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
# viewAngles2
i.viewAngles2.x = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.viewAngles2.y = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.viewAngles2.z = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
# localViewAngles2
i.localViewAngles2.x = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.localViewAngles2.y = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
i.localViewAngles2.z = b2float(text[textPos:textPos + FLOAT_SIZE])
textPos += FLOAT_SIZE
def reset(self):
for i in self.u:
i.reset()
# Flattened prop entry
class flattenedPropEntry:
prop = protonet.CSVCMsg_SendTable().sendprop_t()
arrayElementProp = protonet.CSVCMsg_SendTable().sendprop_t()
def __init__(self, a_prop, a_arrayProp):
self.prop = protonet.CSVCMsg_SendTable().sendprop_t().ParseFromString(a_prop)
self.arrayElementProp = protonet.CSVCMsg_SendTable().sendprop_t().ParseFromString(a_arrayProp)
# Server class information
class serverClass_t:
classID = 0
name = ""
DTname = ""
dataTable = 0
flattenedProps = []
# Player information
class player_info_t:
version = 0
xuid = 0
name = ""
userID = 0
guid = ""
friendsID = 0
friendsName = ""
fakeplayer = False
ishltv = False
customFiles = []
filesDownloaded = 0
entityID = 0
def parse(self, a_data, a_entry):
parsed = struct.unpack("2Q128si33sI128s2?3LBi", a_data)
self.version = parsed[0]
self.xuid = parsed[1]
self.name = b2str(readString_raw(parsed[2]))
self.userID = parsed[3]
self.guid = b2str(readString_raw(parsed[4]))
self.friendsID = parsed[5]
self.friendsName = b2str(readString_raw(parsed[6]))
self.fakeplayer = parsed[7]
self.ishltv = parsed[8]
self.customFiles = parsed[9]
self.filesDownloaded = [parsed[10], parsed[11], parsed[12], parsed[13]]
self.entityID = a_entry
# Buffer reader
class CBitRead:
data = ""
dataBytes = 0
dataPart = ""
posByte = 0
bitsFree = 0
overflow = False
# ---------------------------------------------------------
# Grab another part of data to buffer
def grabNext4Bytes(self):
if(self.posByte >= len(self.data)):
self.bitsFree = 1
self.dataPart = 0
self.overflow = True
else:
self.dataPart = self.data[self.posByte] + (self.data[self.posByte + 1] << 8) + (self.data[self.posByte + 2] << 16) + (self.data[self.posByte + 3] << 24)
self.posByte += 4
# Add 32 bits free to use and grab new data to buffer
def fetchNext(self):
self.bitsFree = 32
self.grabNext4Bytes()
# ---------------------------------------------------------
# Read VAR
def readUBitVar(self):
ret = self.readUBitLong(6)
if(ret & 48 == 16):
ret = (ret & 15) | (self.readUBitLong(4) << 4)
elif(ret & 48 == 32):
ret = (ret & 15) | (self.readUBitLong(8) << 4)
elif (ret & 48 == 48):
ret = (ret & 15) | (self.readUBitLong(28) << 4)
return ret
# Read unsigned n-bits
def readUBitLong(self, a_bits):
if(self.bitsFree >= a_bits):
# By using mask take data needed from buffer
res = self.dataPart & ((2**a_bits)-1)
self.bitsFree -= a_bits
# Check if we need to grab new data to buffer
if(self.bitsFree == 0):
self.fetchNext()
else:
# Move buffer to the right
self.dataPart >>= a_bits
return res
else:
# Take whats left
res = self.dataPart
a_bits -= self.bitsFree
# Save how many free bits we used
t_bitsFree = self.bitsFree
# Grab new data to buffer
self.fetchNext()
# Append new data to result
if(self.overflow):
return 0
res |= ((self.dataPart & ((2**a_bits)-1)) << t_bitsFree)
self.bitsFree -= a_bits
# Move buffer to the right
self.dataPart >>= a_bits
return res
# Read signed n-bits
def readSBitLong(self, a_bits):
return (self.readUBitLong(a_bits) << (32 - a_bits)) >> (32 - a_bits)
# Read string
def readString(self):
res = ""
while True:
char = self.readSBitLong(8)
if(char == 0):
break
res += chr(char)
return res
# Read n-bits
def readBits(self, a_bits):
res = b''
bitsleft = a_bits
while(bitsleft >= 32):
res += bytes([self.readUBitLong(8), self.readUBitLong(8), self.readUBitLong(8), self.readUBitLong(8)])
bitsleft -= 32
while(bitsleft >= 8):
res += bytes([self.readUBitLong(8)])
bitsleft -= 8
if(bitsleft):
res += bytes([self.readUBitLong(bitsleft)])
return res
# Read n-bytes
def readBytes(self, a_bytes):
return self.readBits(a_bytes << 3)
# Read 1 bit
def readBit(self):
aBit = self.dataPart & 1
self.bitsFree -= 1
if(self.bitsFree == 0):
self.fetchNext()
else:
self.dataPart >>= 1
return aBit
# ---------------------------------------------------------
def __init__(self, a_data):
# Save data to vars
self.data = a_data
self.dataBytes = len(a_data)
# Calculate head
head = self.dataBytes%4
# If there is less bytes than potencial head OR head exists
if( self.dataBytes < 4 or head > 0 ):
if(head > 2):
self.dataPart = self.data[0] + (self.data[1] << 8) + (self.data[2] << 16)
self.posByte = 3
elif(head > 1):
self.dataPart = self.data[0] + (self.data[1] << 8)
self.posByte = 2
else:
self.dataPart = self.data[0]
self.posByte = 1
self.bitsFree = head << 3
else:
self.posByte = head
self.dataPart = self.data[self.posByte] + (self.data[self.posByte + 1] << 8) + (self.data[self.posByte + 2] << 16) + (self.data[self.posByte + 3] << 24)
if(self.data):
self.fetchNext()
else:
self.dataPart = 0
self.bitsFree = 1
self.bitsFree = min(self.bitsFree, 32)