Skip to content

Commit 86b85de

Browse files
committed
Add parse level support for HotS beta replays.
This doesn't represent full support because we still need to load in the new unit/ability id mappings and information. One step at a time.
1 parent 2d96dbc commit 86b85de

File tree

2 files changed

+44
-2
lines changed

2 files changed

+44
-2
lines changed

sc2reader/readers.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ def __call__(self, data, replay):
161161
class DetailsReader_22612(DetailsReader_Base):
162162
Details = namedtuple('Details',['players','map','unknown1','unknown2','os','file_time','utc_adjustment','unknown4','unknown5','unknown6','unknown7','unknown8','unknown9','unknown10', 'unknown11'])
163163

164+
class DetailsReader_Beta(DetailsReader_Base):
165+
Details = namedtuple('Details',['players','map','unknown1','unknown2','os','file_time','utc_adjustment','unknown4','unknown5','unknown6','unknown7','unknown8','unknown9','unknown10', 'unknown11', 'unknown12'])
166+
164167
class MessageEventsReader_Base(Reader):
165168
def __call__(self, data, replay):
166169
# The replay.message.events file is a single long list containing three
@@ -484,3 +487,37 @@ class GameEventsReader_22612(GameEventsReader_19595):
484487
PLAYER_JOIN_FLAGS = 5 # or 6
485488
PLAYER_ABILITY_FLAGS = 20
486489
UNIT_INDEX_BITS = 9 # Now can select up to 512 units
490+
491+
class GameEventsReader_Beta(GameEventsReader_22612):
492+
def camera_event(self, data, fstamp, pid, event_type):
493+
x = y= distance = pitch = yaw = height = 0
494+
if data.read_bits(1):
495+
block = data.read_int(BIG_ENDIAN)
496+
x = (block >> 16)/256.0
497+
y = (block & 0xFFFF)/256.0
498+
if data.read_bits(1):
499+
distance = data.read_short(BIG_ENDIAN)/256.0
500+
if data.read_bits(1):
501+
#Note: this angle is relative to the horizontal plane, but the editor shows the angle relative to the vertical plane. Subtract from 90 degrees to convert.
502+
pitch = data.read_short(BIG_ENDIAN) #?
503+
pitch = 45 * (((((pitch * 0x10 - 0x2000) << 17) - 1) >> 17) + 1) / 4096.0
504+
if data.read_bits(1):
505+
#Note: this angle is the vector from the camera head to the camera target projected on to the x-y plane in positive coordinates. So, default is 90 degrees, while insert and delete produce 45 and 135 degrees by default.
506+
yaw = data.read_short(BIG_ENDIAN) #?
507+
yaw = 45 * (((((yaw * 0x10 - 0x2000) << 17) - 1) >> 17) + 1) / 4096.0
508+
return CameraEvent(fstamp, pid, event_type, x, y, distance, pitch, yaw, height)
509+
510+
def player_selection_event(self, data, fstamp, pid, event_type):
511+
bank = data.read_bits(4)
512+
subgroup = data.read_bits(self.UNIT_INDEX_BITS) #??
513+
overlay = self._parse_selection_update(data)
514+
515+
type_count = data.read_bits(self.UNIT_INDEX_BITS)
516+
unit_types = [(data.read_short(BIG_ENDIAN) << 16 | data.read_short(BIG_ENDIAN),data.read_bits(self.UNIT_INDEX_BITS)) for index in range(type_count)]
517+
518+
unit_count = data.read_bits(self.UNIT_INDEX_BITS)
519+
unit_ids = [data.read_int(BIG_ENDIAN) for index in range(unit_count)]
520+
521+
unit_types = chain(*[[utype]*count for (utype, count) in unit_types])
522+
units = list(zip(unit_ids, unit_types))
523+
return SelectionEvent(fstamp, pid, event_type, bank, units, overlay)

sc2reader/resources.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ class Replay(Resource):
185185
#: of the game game.
186186
people_hash = str()
187187

188+
#: SC2 Expansion. One of 'WoL', 'HotS'
189+
expasion = str()
188190

189191
def __init__(self, replay_file, filename=None, load_level=4, **options):
190192
super(Replay, self).__init__(replay_file, filename, **options)
@@ -231,6 +233,7 @@ def __init__(self, replay_file, filename=None, load_level=4, **options):
231233
if load_level >= 0:
232234
# Set ('versions', 'frames', 'build', 'release_string', 'length')
233235
self.__dict__.update(utils.read_header(replay_file))
236+
self.expansion = ['','WoL','HotS'][self.versions[1]]
234237
self.archive = utils.open_archive(replay_file)
235238

236239
# Load basic details if requested
@@ -516,7 +519,8 @@ def register_datapack(self, datapack, filterfunc=lambda r: True):
516519
def register_default_readers(self):
517520
"""Registers factory default readers."""
518521
self.register_reader('replay.details', readers.DetailsReader_Base(), lambda r: r.build < 22612)
519-
self.register_reader('replay.details', readers.DetailsReader_22612(), lambda r: r.build >= 22612)
522+
self.register_reader('replay.details', readers.DetailsReader_22612(), lambda r: r.build >= 22612 and r.expansion=='WoL')
523+
self.register_reader('replay.details', readers.DetailsReader_Beta(), lambda r: r.expansion=='HotS')
520524
self.register_reader('replay.initData', readers.InitDataReader_Base())
521525
self.register_reader('replay.message.events', readers.MessageEventsReader_Base())
522526
self.register_reader('replay.attributes.events', readers.AttributesEventsReader_Base(), lambda r: r.build < 17326)
@@ -525,7 +529,8 @@ def register_default_readers(self):
525529
self.register_reader('replay.game.events', readers.GameEventsReader_16561(), lambda r: 16561 <= r.build < 18574)
526530
self.register_reader('replay.game.events', readers.GameEventsReader_18574(), lambda r: 18574 <= r.build < 19595)
527531
self.register_reader('replay.game.events', readers.GameEventsReader_19595(), lambda r: 19595 <= r.build < 22612)
528-
self.register_reader('replay.game.events', readers.GameEventsReader_22612(), lambda r: 22612 <= r.build)
532+
self.register_reader('replay.game.events', readers.GameEventsReader_22612(), lambda r: 22612 <= r.build and r.expansion=='WoL')
533+
self.register_reader('replay.game.events', readers.GameEventsReader_Beta(), lambda r: r.expansion=='HotS')
529534

530535
def register_default_datapacks(self):
531536
"""Registers factory default datapacks."""

0 commit comments

Comments
 (0)