@@ -270,9 +270,16 @@ def __init__(self, replay_file, filename=None, load_level=4, engine=sc2reader.en
270270 # Load basic details if requested
271271 if load_level >= 1 :
272272 self .load_level = 1
273- for data_file in ['replay.initData' , 'replay.details' , 'replay.attributes.events' ]:
273+ files = [
274+ 'replay.initData.backup' ,
275+ 'replay.details.backup' ,
276+ 'replay.attributes.events' ,
277+ 'replay.initData' ,
278+ 'replay.details'
279+ ]
280+ for data_file in files :
274281 self ._read_data (data_file , self ._get_reader (data_file ))
275- self .load_details ()
282+ self .load_all_details ()
276283 self .datapack = self ._get_datapack ()
277284
278285 # Can only be effective if map data has been loaded
@@ -311,18 +318,24 @@ def __init__(self, replay_file, filename=None, load_level=4, engine=sc2reader.en
311318
312319 engine .run (self )
313320
314- def load_details (self ):
321+ def load_init_data (self ):
315322 if 'replay.initData' in self .raw_data :
316323 initData = self .raw_data ['replay.initData' ]
317- options = initData ['game_description' ]['game_options' ]
318- self .amm = options ['amm' ]
319- self .ranked = options ['ranked' ]
320- self .competitive = options ['competitive' ]
321- self .practice = options ['practice' ]
322- self .cooperative = options ['cooperative' ]
323- self .battle_net = options ['battle_net' ]
324- self .hero_duplicates_allowed = options ['hero_duplicates_allowed' ]
324+ elif 'replay.initData.backup' in self .raw_data :
325+ initData = self .raw_data ['replay.initData.backup' ]
326+ else :
327+ return
325328
329+ options = initData ['game_description' ]['game_options' ]
330+ self .amm = options ['amm' ]
331+ self .ranked = options ['ranked' ]
332+ self .competitive = options ['competitive' ]
333+ self .practice = options ['practice' ]
334+ self .cooperative = options ['cooperative' ]
335+ self .battle_net = options ['battle_net' ]
336+ self .hero_duplicates_allowed = options ['hero_duplicates_allowed' ]
337+
338+ def load_attribute_events (self ):
326339 if 'replay.attributes.events' in self .raw_data :
327340 # Organize the attribute data to be useful
328341 self .attributes = defaultdict (dict )
@@ -337,57 +350,74 @@ def load_details(self):
337350 self .is_ladder = (self .category == "Ladder" )
338351 self .is_private = (self .category == "Private" )
339352
353+ def load_details (self ):
340354 if 'replay.details' in self .raw_data :
341355 details = self .raw_data ['replay.details' ]
356+ elif 'replay.details.backup' in self .raw_data :
357+ details = self .raw_data ['replay.details.backup' ]
358+ else :
359+ return
360+
361+ self .map_name = details ['map_name' ]
362+ self .region = details ['cache_handles' ][0 ].server .lower ()
363+ self .map_hash = details ['cache_handles' ][- 1 ].hash
364+ self .map_file = details ['cache_handles' ][- 1 ]
365+
366+ # Expand this special case mapping
367+ if self .region == 'sg' :
368+ self .region = 'sea'
342369
343- self .map_name = details ['map_name' ]
370+ dependency_hashes = [d .hash for d in details ['cache_handles' ]]
371+ if hashlib .sha256 ('Standard Data: Void.SC2Mod' .encode ('utf8' )).hexdigest () in dependency_hashes :
372+ self .expansion = 'LotV'
373+ elif hashlib .sha256 ('Standard Data: Swarm.SC2Mod' .encode ('utf8' )).hexdigest () in dependency_hashes :
374+ self .expansion = 'HotS'
375+ elif hashlib .sha256 ('Standard Data: Liberty.SC2Mod' .encode ('utf8' )).hexdigest () in dependency_hashes :
376+ self .expansion = 'WoL'
377+ else :
378+ self .expansion = ''
344379
345- self .region = details ['cache_handles' ][ 0 ]. server . lower ()
346- self .map_hash = details [ 'cache_handles' ][ - 1 ]. hash
347- self .map_file = details [ 'cache_handles' ][ - 1 ]
380+ self .windows_timestamp = details ['file_time' ]
381+ self .unix_timestamp = utils . windows_to_unix ( self . windows_timestamp )
382+ self .end_time = datetime . utcfromtimestamp ( self . unix_timestamp )
348383
349- # Expand this special case mapping
350- if self .region == 'sg' :
351- self .region = 'sea'
384+ # The utc_adjustment is either the adjusted windows timestamp OR
385+ # the value required to get the adjusted timestamp. We know the upper
386+ # limit for any adjustment number so use that to distinguish between
387+ # the two cases.
388+ if details ['utc_adjustment' ] < 10 ** 7 * 60 * 60 * 24 :
389+ self .time_zone = details ['utc_adjustment' ]/ (10 ** 7 * 60 * 60 )
390+ else :
391+ self .time_zone = (details ['utc_adjustment' ]- details ['file_time' ])/ (10 ** 7 * 60 * 60 )
352392
353- dependency_hashes = [d .hash for d in details ['cache_handles' ]]
354- if hashlib .sha256 ('Standard Data: Void.SC2Mod' .encode ('utf8' )).hexdigest () in dependency_hashes :
355- self .expansion = 'LotV'
356- elif hashlib .sha256 ('Standard Data: Swarm.SC2Mod' .encode ('utf8' )).hexdigest () in dependency_hashes :
357- self .expansion = 'HotS'
358- elif hashlib .sha256 ('Standard Data: Liberty.SC2Mod' .encode ('utf8' )).hexdigest () in dependency_hashes :
359- self .expansion = 'WoL'
360- else :
361- self .expansion = ''
362-
363- self .windows_timestamp = details ['file_time' ]
364- self .unix_timestamp = utils .windows_to_unix (self .windows_timestamp )
365- self .end_time = datetime .utcfromtimestamp (self .unix_timestamp )
366-
367- # The utc_adjustment is either the adjusted windows timestamp OR
368- # the value required to get the adjusted timestamp. We know the upper
369- # limit for any adjustment number so use that to distinguish between
370- # the two cases.
371- if details ['utc_adjustment' ] < 10 ** 7 * 60 * 60 * 24 :
372- self .time_zone = details ['utc_adjustment' ]/ (10 ** 7 * 60 * 60 )
373- else :
374- self .time_zone = (details ['utc_adjustment' ]- details ['file_time' ])/ (10 ** 7 * 60 * 60 )
393+ self .game_length = self .length
394+ self .real_length = utils .Length (seconds = int (self .length .seconds / GAME_SPEED_FACTOR [self .expansion ][self .speed ]))
395+ self .start_time = datetime .utcfromtimestamp (self .unix_timestamp - self .real_length .seconds )
396+ self .date = self .end_time # backwards compatibility
375397
376- self . game_length = self . length
377- self .real_length = utils . Length ( seconds = int ( self . length . seconds / GAME_SPEED_FACTOR [ self . expansion ][ self . speed ]) )
378- self .start_time = datetime . utcfromtimestamp ( self . unix_timestamp - self . real_length . seconds )
379- self .date = self . end_time # backwards compatibility
398+ def load_all_details ( self ):
399+ self .load_init_data ( )
400+ self .load_attribute_events ( )
401+ self .load_details ()
380402
381403 def load_map (self ):
382404 self .map = self .factory .load_map (self .map_file , ** self .opt )
383405
384406 def load_players (self ):
385407 # If we don't at least have details and attributes_events we can go no further
386- if 'replay.details' not in self .raw_data :
408+ if 'replay.details' in self .raw_data :
409+ details = self .raw_data ['replay.details' ]
410+ elif 'replay.details.backup' in self .raw_data :
411+ details = self .raw_data ['replay.details.backup' ]
412+ else :
387413 return
388414 if 'replay.attributes.events' not in self .raw_data :
389415 return
390- if 'replay.initData' not in self .raw_data :
416+ if 'replay.initData' in self .raw_data :
417+ initData = self .raw_data ['replay.initData' ]
418+ elif 'replay.initData.backup' in self .raw_data :
419+ initData = self .raw_data ['replay.initData.backup' ]
420+ else :
391421 return
392422
393423 self .clients = list ()
@@ -397,8 +427,6 @@ def load_players(self):
397427 # information. detail_id marks the current index into this data.
398428 detail_id = 0
399429 player_id = 1
400- details = self .raw_data ['replay.details' ]
401- initData = self .raw_data ['replay.initData' ]
402430
403431 # Assume that the first X map slots starting at 1 are player slots
404432 # so that we can assign player ids without the map
@@ -568,6 +596,8 @@ def register_default_readers(self):
568596 """Registers factory default readers."""
569597 self .register_reader ('replay.details' , readers .DetailsReader (), lambda r : True )
570598 self .register_reader ('replay.initData' , readers .InitDataReader (), lambda r : True )
599+ self .register_reader ('replay.details.backup' , readers .DetailsReader (), lambda r : True )
600+ self .register_reader ('replay.initData.backup' , readers .InitDataReader (), lambda r : True )
571601 self .register_reader ('replay.tracker.events' , readers .TrackerEventsReader (), lambda r : True )
572602 self .register_reader ('replay.message.events' , readers .MessageEventsReader (), lambda r : True )
573603 self .register_reader ('replay.attributes.events' , readers .AttributesEventsReader (), lambda r : True )
0 commit comments