@@ -270,9 +270,16 @@ def __init__(self, replay_file, filename=None, load_level=4, engine=sc2reader.en
270
270
# Load basic details if requested
271
271
if load_level >= 1 :
272
272
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 :
274
281
self ._read_data (data_file , self ._get_reader (data_file ))
275
- self .load_details ()
282
+ self .load_all_details ()
276
283
self .datapack = self ._get_datapack ()
277
284
278
285
# 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
311
318
312
319
engine .run (self )
313
320
314
- def load_details (self ):
321
+ def load_init_data (self ):
315
322
if 'replay.initData' in self .raw_data :
316
323
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
325
328
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 ):
326
339
if 'replay.attributes.events' in self .raw_data :
327
340
# Organize the attribute data to be useful
328
341
self .attributes = defaultdict (dict )
@@ -337,57 +350,74 @@ def load_details(self):
337
350
self .is_ladder = (self .category == "Ladder" )
338
351
self .is_private = (self .category == "Private" )
339
352
353
+ def load_details (self ):
340
354
if 'replay.details' in self .raw_data :
341
355
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'
342
369
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 = ''
344
379
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 )
348
383
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 )
352
392
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
375
397
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 ()
380
402
381
403
def load_map (self ):
382
404
self .map = self .factory .load_map (self .map_file , ** self .opt )
383
405
384
406
def load_players (self ):
385
407
# 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 :
387
413
return
388
414
if 'replay.attributes.events' not in self .raw_data :
389
415
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 :
391
421
return
392
422
393
423
self .clients = list ()
@@ -397,8 +427,6 @@ def load_players(self):
397
427
# information. detail_id marks the current index into this data.
398
428
detail_id = 0
399
429
player_id = 1
400
- details = self .raw_data ['replay.details' ]
401
- initData = self .raw_data ['replay.initData' ]
402
430
403
431
# Assume that the first X map slots starting at 1 are player slots
404
432
# so that we can assign player ids without the map
@@ -568,6 +596,8 @@ def register_default_readers(self):
568
596
"""Registers factory default readers."""
569
597
self .register_reader ('replay.details' , readers .DetailsReader (), lambda r : True )
570
598
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 )
571
601
self .register_reader ('replay.tracker.events' , readers .TrackerEventsReader (), lambda r : True )
572
602
self .register_reader ('replay.message.events' , readers .MessageEventsReader (), lambda r : True )
573
603
self .register_reader ('replay.attributes.events' , readers .AttributesEventsReader (), lambda r : True )
0 commit comments