Skip to content

Commit 5e45b3f

Browse files
committed
Add detailed initData parsing.
We can't do a full parse on older versions because pre 1.5.4 there seem to be changes in the structure.
1 parent 2f2ac91 commit 5e45b3f

File tree

1 file changed

+188
-54
lines changed

1 file changed

+188
-54
lines changed

sc2reader/readers.py

Lines changed: 188 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -21,71 +21,205 @@ def __call__(self, data, replay):
2121

2222
class InitDataReader_Base(Reader):
2323

24-
def get_player_names(self, data, replay):
25-
# The first block of the replay.initData file represents a list of
26-
# human player names; computers are no recorded. This list appears to
27-
# always be 16 long, with "" names filling in the balance. Each name
28-
# is followed by a 5 byte string that appears to be always all zeros.
29-
player_names = list()
30-
for player in range(data.read_uint8()):
31-
name = data.read_aligned_bytes(data.read_uint8())
32-
data.read_bytes(5)
33-
if name:
34-
player_names.append(name)
35-
return player_names
36-
3724
def __call__(self, data, replay):
3825
data = BitPackedDecoder(data)
3926

40-
player_names = self.get_player_names(data, replay)
41-
42-
# The next block contains information about the structure of the MPQ
43-
# archive. We don't read this information because we've got mpyq for
44-
# that. Its split into 3 sections because of the variable length segment
45-
# in the middle that prevents bulk skipping. The last section also
46-
# appears to be variable length, hack it to do a find for the section
47-
# we are looking for.
48-
data.read_bytes(24)
49-
#sc_account_id = data.read_string()
50-
distance = data.read_range(data.tell(), data.length).find('s2ma')
51-
data.read_bytes(distance)
52-
53-
# The final block of this file that we concern ourselves with is a list
54-
# of what appears to be map data with the s2ma header on each element.
55-
# Each element consists of two unknown bytes, a realm id (e.g EU or US)
56-
# and a map hash which probably ties back to the sc2map files.
57-
#
58-
# Some replays don't seem to have a maps section at all, now we can't
59-
# know what gateway its from? Very strange...
60-
#
61-
# TODO: Figure out how we could be missing a maps section.
62-
map_data = list()
63-
while data.peek(4) == 's2ma':
64-
map_data.append(DepotFile(data.read_bytes(40)))
27+
init_data = dict( #58
28+
player_init_data = [dict( #38
29+
name = data.read_aligned_bytes(data.read_uint8()),
30+
random_seed = data.read_uint32(),
31+
race_preference = data.read_uint8() if data.read_bool() else None, #38
32+
team_preference = data.read_uint8() if data.read_bool() else None, #39
33+
test_map = data.read_bool(),
34+
test_auto = data.read_bool(),
35+
examine = data.read_bool(),
36+
observe = data.read_bits(2),
37+
) for i in range(data.read_bits(5))
38+
],
39+
40+
game_description = dict( # 48
41+
random_value = data.read_uint32(), # 4
42+
game_cache_name = data.read_aligned_bytes(data.read_bits(10)), # 24
43+
game_options = dict( #40
44+
lock_teams = data.read_bool(), #27
45+
teams_together = data.read_bool(),
46+
advanced_shared_control = data.read_bool(),
47+
random_races = data.read_bool(),
48+
battle_net = data.read_bool(),
49+
amm = data.read_bool(),
50+
competitive = data.read_bool(),
51+
no_victory_or_defeat = data.read_bool(),
52+
fog = data.read_bits(2), #19
53+
observers = data.read_bits(2), #19
54+
user_difficulty = data.read_bits(2), #19
55+
client_debug_flags = data.read_uint64(), #15
56+
),
57+
game_speed = data.read_bits(3),
58+
game_type = data.read_bits(3),
59+
max_users = data.read_bits(5),
60+
max_observers = data.read_bits(5),
61+
max_players = data.read_bits(5),
62+
max_teams = data.read_bits(4)+1,
63+
max_colors = data.read_bits(6),
64+
max_races = data.read_uint8()+1,
65+
max_controls = data.read_uint8()+1,
66+
map_size_x = data.read_uint8(),
67+
map_size_y = data.read_uint8(),
68+
map_file_sync_checksum = data.read_uint32(),
69+
map_file_name = data.read_aligned_bytes(data.read_bits(11)),
70+
map_author_name = data.read_aligned_bytes(data.read_uint8()),
71+
mod_file_sync_checksum = data.read_uint32(),
72+
slot_descriptions = [dict( #47
73+
allowed_colors = data.read_bits(data.read_bits(6)),
74+
allowed_races = data.read_bits(data.read_uint8()),
75+
allowedDifficulty = data.read_bits(data.read_bits(6)),
76+
allowedControls = data.read_bits(data.read_uint8()),
77+
allowed_observe_types = data.read_bits(data.read_bits(2)),
78+
) for i in range(data.read_bits(5))],
79+
default_difficulty = data.read_bits(6),
80+
cache_handles = [
81+
DepotFile(data.read_aligned_bytes(40)) for i in range(data.read_bits(6))
82+
],
83+
is_blizzardMap = data.read_bool(),
84+
is_premade_ffa = data.read_bool(),
85+
),
86+
)
87+
""" # For builds <= 1.5.4 this won't work.
88+
lobby_state = dict( #56
89+
phase = data.read_bits(3),
90+
max_users = data.read_bits(5),
91+
max_observers = data.read_bits(5),
92+
slots = [dict( #54
93+
control = data.read_uint8(),
94+
user_id = data.read_bits(4) if data.read_bool() else None,
95+
team_id = data.read_bits(4),
96+
colorPref = data.read_bits(5) if data.read_bool() else None,
97+
race_pref = data.read_uint8() if data.read_bool() else None,
98+
difficulty = data.read_bits(6),
99+
handicap = data.read_bits(7),
100+
observe = data.read_bits(2),
101+
rewards = [data.read_uint32() for i in range(data.read_bits(6))],
102+
toon_handle = data.read_aligned_bytes(data.read_bits(7)), # 14
103+
licenses = [data.read_uint32() for i in range(data.read_bits(9))], # 56
104+
) for i in range(data.read_bits(5))], # 58
105+
random_seed = data.read_uint32(),
106+
host_user_id = data.read_bits(4) if data.read_bool() else None, # 52
107+
is_single_player = data.read_bool(), # 27
108+
game_duration = data.read_uint32(), # 4
109+
default_difficulty = data.read_bits(6), # 1
110+
),
111+
)"""
65112

66-
# Return the extracted information inside an AttributeDict.
67113
return AttributeDict(
68-
map_data=map_data,
69-
player_names=player_names,
114+
map_data=init_data['game_description']['cache_handles'],
115+
player_names=[d['name'] for d in init_data['player_init_data'] if d['name']],
70116
sc_account_id=None,#sc_account_id,
71117
)
72118

73119
class InitDataReader_24764(InitDataReader_Base):
74-
def get_player_names(self, data, replay):
75-
player_names = list()
76-
for player in range(data.read_uint8()):
77-
name = data.read_aligned_bytes(data.read_uint8())
78-
79-
# Flag is 1 for multiplayer, 0 for single player
80-
if data.read_bits(1):
81-
clan_name = data.read_aligned_bytes(data.read_uint8())
82-
unknown = data.read_bits(42)
83120

84-
data.read_bytes(5)
85-
if name:
86-
player_names.append(name)
87-
return player_names
121+
def __call__(self, data, replay):
122+
data = BitPackedDecoder(data)
88123

124+
init_data = dict(
125+
player_init_data = [dict(
126+
name = data.read_aligned_bytes(data.read_uint8()),
127+
clan_tag = data.read_aligned_bytes(data.read_uint8()) if data.read_bool() else "", # 36
128+
highest_league = data.read_uint8() if data.read_bool() else None, #20
129+
combined_race_levels = data.read_uint32() if data.read_bool() else None, #37
130+
random_seed = data.read_uint32(),
131+
race_preference = data.read_uint8() if data.read_bool() else None, #38
132+
team_preference = data.read_uint8() if data.read_bool() else None, #39
133+
test_map = data.read_bool(),
134+
test_auto = data.read_bool(),
135+
examine = data.read_bool(),
136+
custom_interface = data.read_bool(),
137+
observe = data.read_bits(2),
138+
) for i in range(data.read_bits(5))
139+
],
140+
141+
game_description = dict(
142+
random_value = data.read_uint32(), # 4
143+
game_cache_name = data.read_aligned_bytes(data.read_bits(10)), # 24
144+
game_options = dict(
145+
lock_teams = data.read_bool(), #27
146+
teams_together = data.read_bool(),
147+
advanced_shared_control = data.read_bool(),
148+
random_races = data.read_bool(),
149+
battle_net = data.read_bool(),
150+
amm = data.read_bool(),
151+
competitive = data.read_bool(),
152+
no_victory_or_defeat = data.read_bool(),
153+
fog = data.read_bits(2), #19
154+
observers = data.read_bits(2), #19
155+
user_difficulty = data.read_bits(2), #19
156+
client_debug_flags = data.read_uint64(), #15
157+
),
158+
game_speed = data.read_bits(3),
159+
game_type = data.read_bits(3),
160+
max_users = data.read_bits(5),
161+
max_observers = data.read_bits(5),
162+
max_players = data.read_bits(5),
163+
max_teams = data.read_bits(4)+1,
164+
max_colors = data.read_bits(6),
165+
max_races = data.read_uint8()+1,
166+
max_controls = data.read_uint8()+1,
167+
map_size_x = data.read_uint8(),
168+
map_size_y = data.read_uint8(),
169+
map_file_sync_checksum = data.read_uint32(),
170+
map_file_name = data.read_aligned_bytes(data.read_bits(11)),
171+
map_author_name = data.read_aligned_bytes(data.read_uint8()),
172+
mod_file_sync_checksum = data.read_uint32(),
173+
slot_descriptions = [dict( #50
174+
allowed_colors = data.read_bits(data.read_bits(6)),
175+
allowed_races = data.read_bits(data.read_uint8()),
176+
allowedDifficulty = data.read_bits(data.read_bits(6)),
177+
allowedControls = data.read_bits(data.read_uint8()),
178+
allowed_observe_types = data.read_bits(data.read_bits(2)),
179+
allowed_ai_builds = data.read_bits(data.read_bits(7)),
180+
) for i in range(data.read_bits(5))],
181+
default_difficulty = data.read_bits(6),
182+
default_AI_build = data.read_bits(7),
183+
cache_handles = [
184+
DepotFile(data.read_aligned_bytes(40)) for i in range(data.read_bits(6))
185+
],
186+
is_blizzardMap = data.read_bool(),
187+
is_premade_ffa = data.read_bool(),
188+
is_coop_mode = data.read_bool(),
189+
),
190+
191+
lobby_state = dict(
192+
phase = data.read_bits(3),
193+
max_users = data.read_bits(5),
194+
max_observers = data.read_bits(5),
195+
slots = [dict(
196+
control = data.read_uint8(),
197+
user_id = data.read_bits(4) if data.read_bool() else None,
198+
team_id = data.read_bits(4),
199+
colorPref = data.read_bits(5) if data.read_bool() else None,
200+
race_pref = data.read_uint8() if data.read_bool() else None,
201+
difficulty = data.read_bits(6),
202+
ai_build = data.read_bits(7),
203+
handicap = data.read_bits(7),
204+
observe = data.read_bits(2),
205+
working_set_slot_id = data.read_uint8() if data.read_bool() else None,
206+
rewards = [data.read_uint32() for i in range(data.read_bits(6))],
207+
toon_handle = data.read_aligned_bytes(data.read_bits(7)), # 14
208+
licenses = [data.read_uint32() for i in range(data.read_bits(9))], # 56
209+
) for i in range(data.read_bits(5))], # 58
210+
random_seed = data.read_uint32(),
211+
host_user_id = data.read_bits(4) if data.read_bool() else None, # 52
212+
is_single_player = data.read_bool(), # 27
213+
game_duration = data.read_uint32(), # 4
214+
default_difficulty = data.read_bits(6), # 1
215+
default_ai_build = data.read_bits(7), # 0
216+
),
217+
)
218+
return AttributeDict(
219+
map_data=init_data['game_description']['cache_handles'],
220+
player_names=[d['name'] for d in init_data['player_init_data'] if d['name']],
221+
sc_account_id=None,#sc_account_id,
222+
)
89223

90224

91225
class AttributesEventsReader_Base(Reader):

0 commit comments

Comments
 (0)