@@ -110,7 +110,10 @@ def parse_mod(header, num_players, version):
110110
111111def parse_player (header , player_number , num_players , save ):
112112 """Parse a player (and objects)."""
113- type_ , * diplomacy , name_length = unpack (f'<bx{ num_players } x9i5xh' , header )
113+ rep = 9
114+ if save >= 61.5 :
115+ rep = num_players
116+ type_ , * diplomacy , name_length = unpack (f'<bx{ num_players } x{ rep } i5xh' , header )
114117 name , resources = unpack (f'<{ name_length - 1 } s2xIx' , header )
115118 header .read (resources * 4 )
116119 start_x , start_y , civilization_id , color_id = unpack ('<xff9xb3xbx' , header )
@@ -133,12 +136,16 @@ def parse_player(header, player_number, num_players, save):
133136 if save >= 37 :
134137 offset = header .tell ()
135138 data = header .read (100 )
139+ device = data [8 ]
136140 # Jump to the end of player data
137141 player_end = re .search (b'\xff \xff \xff \xff \xff \xff \xff \xff .\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x0b ' , data , re .DOTALL )
138142 if not player_end :
139- raise RuntimeError ("could not find player end" )
140- device = data [8 ]
141- header .seek (offset + player_end .end ())
143+ # only a failure if this is not the last player, since we seek to the next block anyway
144+ # this issue happens on restored games
145+ if player_number < num_players - 1 :
146+ raise RuntimeError ("could not find player end" )
147+ else :
148+ header .seek (offset + player_end .end ())
142149
143150 return dict (
144151 number = player_number ,
@@ -194,12 +201,13 @@ def parse_lobby(data, version, save):
194201 )
195202
196203
197- def parse_map (data , version ):
204+ def parse_map (data , version , save ):
198205 """Parse map."""
199- data .read (60 )
200206 tile_format = '<xbbx'
201207 if version is Version .DE :
202208 tile_format = '<bxb6x'
209+ if save >= 62.0 :
210+ tile_format = '<bxxb6x'
203211 data .read (8 )
204212 size_x , size_y , zone_num = unpack ('<III' , data )
205213 tile_num = size_x * size_y
@@ -220,6 +228,8 @@ def parse_map(data, version):
220228 data .read (num_obs * 8 )
221229 x2 , y2 = unpack ('<II' , data )
222230 data .read (x2 * y2 * 4 )
231+ if save >= 61.5 :
232+ data .read (x2 * y2 * 4 )
223233 restore_time = unpack ('<I' , data )
224234 #if restore_time > 0:
225235 # raise RuntimeError("restored matches can't be parsed yet")
@@ -233,7 +243,10 @@ def parse_map(data, version):
233243
234244def parse_scenario (data , num_players , version , save ):
235245 """Parse scenario section."""
236- data .read (4455 )
246+ next_uid , scenario_version = unpack ('<II' , data )
247+ if save >= 61.5 :
248+ data .read (72 )
249+ data .read (4447 )
237250 scenario_filename = None
238251 if version is Version .DE :
239252 data .read (102 )
@@ -264,7 +277,9 @@ def parse_scenario(data, num_players, version, save):
264277 map_id , difficulty_id = unpack ('<II' , data )
265278 remainder = data .read ()
266279 if version is Version .DE :
267- if save >= 37 :
280+ if save >= 61.5 :
281+ settings_version = 3.6
282+ elif save >= 37 :
268283 settings_version = 3.5
269284 elif save >= 26.21 :
270285 settings_version = 3.2
@@ -346,20 +361,26 @@ def parse_de(data, version, save, skip=False):
346361 for i in range (0 , unpack ('<I' , data )):
347362 dlc_ids .append (unpack ('<I' , data ))
348363 data .read (4 )
349- difficulty_id = unpack ('<I' , data )
364+ if save >= 61.5 :
365+ map_dimension = unpack ('<I' , data )
366+ else :
367+ difficulty_id = unpack ('<I' , data )
350368 data .read (4 )
351369 rms_map_id = unpack ('<I' , data )
352370 data .read (4 )
353371 victory_type_id = unpack ('<I' , data )
354372 starting_resources_id = unpack ('<I' , data )
355373 starting_age_id = unpack ('<I' , data )
356374 ending_age_id = unpack ('<I' , data )
357- data .read (8 )
358- speed = unpack ('<d ' , data )
375+ data .read (12 )
376+ speed = unpack ('<f ' , data )
359377 treaty_length = unpack ('<I' , data )
360378 population_limit = unpack ('<I' , data )
361379 num_players = unpack ('<I' , data )
362380 data .read (14 )
381+ if save >= 61.5 :
382+ # not sure if this is difficulty under 61.5 or not
383+ difficulty_id = unpack ('<B' , data )
363384 random_positions , all_technologies = unpack ('<bb' , data )
364385 data .read (1 )
365386 lock_teams = unpack ('<b' , data )
@@ -385,6 +406,8 @@ def parse_de(data, version, save, skip=False):
385406 team_id = unpack ('<b' , data )
386407 data .read (9 )
387408 civilization_id = unpack ('<I' , data )
409+ if save >= 61.5 :
410+ data .read (4 )
388411 de_string (data )
389412 data .read (1 )
390413 ai_name = de_string (data )
@@ -412,6 +435,8 @@ def parse_de(data, version, save, skip=False):
412435 data .read (12 )
413436 if save >= 37 :
414437 for _ in range (8 - num_players ):
438+ if save >= 61.5 :
439+ data .read (4 )
415440 data .read (12 )
416441 de_string (data )
417442 data .read (1 )
@@ -463,6 +488,8 @@ def parse_de(data, version, save, skip=False):
463488 data .read (3 )
464489 if save > 50 :
465490 data .read (8 )
491+ if save >= 61.5 :
492+ data .read (1 )
466493 if not skip :
467494 de_string (data )
468495 data .read (8 )
@@ -595,11 +622,17 @@ def parse_players(header, num_players, version, save):
595622 cur = header .tell ()
596623 gaia = b'Gaia' if version in (Version .DE , Version .HD ) else b'GAIA'
597624 anchor = header .read ().find (b'\x05 \x00 ' + gaia + b'\x00 ' )
598- header .seek (cur + anchor - num_players - 43 )
625+ rev = 43
626+ if save >= 61.5 :
627+ rev = 7 + (num_players * 4 )
628+ header .seek (cur + anchor - num_players - rev )
599629 mod = parse_mod (header , num_players , version )
600630 players = [parse_player (header , number , num_players , save ) for number in range (num_players )]
601631 cur = header .tell ()
602- points_version = header .read ().find (b'\x00 \x00 \x00 @' )
632+ pv = b'\x00 \x00 \x00 @'
633+ if save >= 61.5 :
634+ pv = b'\x66 \x66 \x06 \x40 '
635+ points_version = header .read ().find (pv )
603636 header .seek (cur )
604637 header .read (points_version )
605638 for _ in range (num_players ):
@@ -611,7 +644,7 @@ def parse_players(header, num_players, version, save):
611644 return [p [0 ] for p in players ], mod , players [0 ][1 ]
612645
613646
614- def parse_metadata (header , skip_ai = True ):
647+ def parse_metadata (header , save , skip_ai = True ):
615648 """Parse recorded game metadata."""
616649 ai = unpack ('<I' , header )
617650
@@ -630,6 +663,12 @@ def parse_metadata(header, skip_ai=True):
630663 header .seek (offset + ai_end .end ())
631664
632665 game_speed , owner_id , num_players , cheats = unpack ('<24xf17xhbxb' , header )
666+
667+ if save < 61.5 :
668+ header .read (60 )
669+ else :
670+ header .read (24 + (num_players * 4 ))
671+
633672 return dict (
634673 speed = game_speed ,
635674 owner_id = owner_id ,
@@ -646,8 +685,8 @@ def parse(data):
646685 raise RuntimeError (f"{ version } not supported" )
647686 de = parse_de (header , version , save )
648687 hd = parse_hd (header , version , save )
649- metadata , num_players = parse_metadata (header )
650- map_ = parse_map (header , version )
688+ metadata , num_players = parse_metadata (header , save )
689+ map_ = parse_map (header , version , save )
651690 players , mod , device = parse_players (header , num_players , version , save )
652691 scenario = parse_scenario (header , num_players , version , save )
653692 lobby = parse_lobby (header , version , save )
0 commit comments