Guest User

Untitled

a guest
May 11th, 2020
85
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import difflib
  2. import json
  3. import lzma
  4. import re
  5. import struct
  6. import sys
  7.  
  8. from savegame import TTDSavegameDecoder
  9.  
  10. TTD_TYPES = {
  11.     'SLE_BOOL': '?',
  12.     'SLE_UINT8': 'b',
  13.     'SLE_INT8': 'B',
  14.     'SLE_FILE_U8': 'b',
  15.     'SLE_UINT16': 'h',
  16.     'SLE_INT16': 'H',
  17.     'SLE_FILE_U16': 'h',
  18.     'SLE_UINT32': 'i',
  19.     'SLE_INT32': 'I',
  20.     'SLE_FILE_U32': 'i',
  21.     'SLE_UINT64': 'q',
  22.     'SLE_INT64': 'Q',
  23.     'REF_ORDER': 'i',
  24.     'REF_TOWN': 'i',
  25.     'REF_STATION': 'i',
  26.     'REF_STORAGE': 'i',
  27. }
  28.  
  29. CONSTANTS = {
  30.     'NUM_TE': 6,
  31. }
  32.  
  33. RX_SLE_VAR = re.compile(r'SLE_(?:VAR|REF)\([^,]*,\s+([^,]*),\s+([\w\s|]+)\)')
  34. RX_SLE_CONDVAR = re.compile(r'SLE_COND(?:VAR|REF)\([^,]*,\s+([^,]*),\s+([\w\s|]+),\s+(\w+),\s+(\w+)\)')
  35. RX_SLE_CONDARR = re.compile(r'SLE_CONDARR\([^,]*,\s+([^,]*),\s+([\w\s|]+),\s+(\d+),\s+(\w+),\s+(\w+)\)')
  36. RX_SLE_CONDNULL = re.compile(r'SLE_CONDNULL\((\d+),\s+(\w+),\s+(\w+)\)')
  37.  
  38.  
  39. def hex_str(s):
  40.     if isinstance(s, (bytes, memoryview)):
  41.         return ':'.join('{:02x}'.format(b) for b in s)
  42.     return ':'.join('{:02x}'.format(ord(c)) for c in s)
  43.  
  44.  
  45. def parse_saveload_desc(str):
  46.     fields = []
  47.     for l in str.split('\n'):
  48.         m = RX_SLE_VAR.search(l)
  49.         if m:
  50.             name, vtype = m.groups()
  51.             fields.append((name, vtype))
  52.             continue
  53.  
  54.         m = RX_SLE_CONDVAR.search(l)
  55.         if m:
  56.             name, vtype, _min_ver, max_ver = m.groups()
  57.             if max_ver != 'SL_MAX_VERSION':
  58.                 continue
  59.             fields.append((name, vtype))
  60.             continue
  61.  
  62.         m = RX_SLE_CONDARR.search(l)
  63.         if m:
  64.             name, vtype, vlen, _min_ver, max_ver = m.groups()
  65.             if max_ver != 'SL_MAX_VERSION':
  66.                 continue
  67.             fields.append((name, (vtype, int(vlen))))
  68.             continue
  69.  
  70.         m = RX_SLE_CONDNULL.search(l)
  71.         if m:
  72.             length, _min_ver, max_ver = m.groups()
  73.             if max_ver != 'SL_MAX_VERSION':
  74.                 continue
  75.             fields.append((None, ('SLE_UINT8', int(length))))
  76.             continue
  77.     # print (fields)
  78.     # return {
  79.     #     'fiel'
  80.     # }
  81.     fmt = '>'
  82.     fn = []
  83.     for name, p in fields:
  84.         if isinstance(p, tuple):
  85.             t, n = p
  86.         else:
  87.             t, n = p, 1
  88.  
  89.         if '|' in t: t = t.split('|')[0].strip()
  90.         f = TTD_TYPES[t]
  91.  
  92.         fmt += f * n
  93.         fn += [name] * n
  94.  
  95.     return (fmt, fn)
  96.  
  97. def load_desc(desc, data):
  98.     fmt, names = desc
  99.     res = {}
  100.     for n, v in zip(names, struct.unpack(fmt, data)):
  101.         if n in res:
  102.             if isinstance(res[n], list):
  103.                 res[n].append(v)
  104.             else:
  105.                 res[n] = [res[n], v]
  106.         else:
  107.             res[n] = v
  108.     return json.dumps(res, sort_keys=True)
  109.  
  110. parse_saveload_desc("""
  111.    SLE_CONDVAR(Town, xy,                    SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
  112.    SLE_CONDVAR(Town, xy,                    SLE_UINT32,                 SLV_6, SL_MAX_VERSION),
  113.  
  114.    SLE_CONDNULL(2, SL_MIN_VERSION, SLV_3),                   ///< population, no longer in use
  115.    SLE_CONDNULL(4, SLV_3, SLV_85),                  ///< population, no longer in use
  116.    SLE_CONDNULL(2, SL_MIN_VERSION, SLV_92),                  ///< num_houses, no longer in use
  117.  
  118.    SLE_CONDVAR(Town, townnamegrfid,         SLE_UINT32, SLV_66, SL_MAX_VERSION),
  119.        SLE_VAR(Town, townnametype,          SLE_UINT16),
  120.        SLE_VAR(Town, townnameparts,         SLE_UINT32),
  121.    SLE_CONDSTR(Town, name,                  SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION),
  122.  
  123.        SLE_VAR(Town, flags,                 SLE_UINT8),
  124.    SLE_CONDVAR(Town, statues,               SLE_FILE_U8  | SLE_VAR_U16, SL_MIN_VERSION, SLV_104),
  125.    SLE_CONDVAR(Town, statues,               SLE_UINT16,               SLV_104, SL_MAX_VERSION),
  126.  
  127.    SLE_CONDNULL(1, SL_MIN_VERSION, SLV_2),                   ///< sort_index, no longer in use
  128.  
  129.    SLE_CONDVAR(Town, have_ratings,          SLE_FILE_U8  | SLE_VAR_U16, SL_MIN_VERSION, SLV_104),
  130.    SLE_CONDVAR(Town, have_ratings,          SLE_UINT16,               SLV_104, SL_MAX_VERSION),
  131.    SLE_CONDARR(Town, ratings,               SLE_INT16, 8,               SL_MIN_VERSION, SLV_104),
  132.    SLE_CONDARR(Town, ratings,               SLE_INT16, MAX_COMPANIES, SLV_104, SL_MAX_VERSION),
  133.    /* failed bribe attempts are stored since savegame format 4 */
  134.    SLE_CONDARR(Town, unwanted,              SLE_INT8,  8,               SLV_4, SLV_104),
  135.    SLE_CONDARR(Town, unwanted,              SLE_INT8,  MAX_COMPANIES, SLV_104, SL_MAX_VERSION),
  136.  
  137.    SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  138.    SLE_CONDVAR(Town, supplied[CT_MAIL].old_max,       SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  139.    SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  140.    SLE_CONDVAR(Town, supplied[CT_MAIL].new_max,       SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  141.    SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  142.    SLE_CONDVAR(Town, supplied[CT_MAIL].old_act,       SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  143.    SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  144.    SLE_CONDVAR(Town, supplied[CT_MAIL].new_act,       SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  145.  
  146.    SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_UINT32,                 SLV_9, SLV_165),
  147.    SLE_CONDVAR(Town, supplied[CT_MAIL].old_max,       SLE_UINT32,                 SLV_9, SLV_165),
  148.    SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_UINT32,                 SLV_9, SLV_165),
  149.    SLE_CONDVAR(Town, supplied[CT_MAIL].new_max,       SLE_UINT32,                 SLV_9, SLV_165),
  150.    SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_UINT32,                 SLV_9, SLV_165),
  151.    SLE_CONDVAR(Town, supplied[CT_MAIL].old_act,       SLE_UINT32,                 SLV_9, SLV_165),
  152.    SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_UINT32,                 SLV_9, SLV_165),
  153.    SLE_CONDVAR(Town, supplied[CT_MAIL].new_act,       SLE_UINT32,                 SLV_9, SLV_165),
  154.  
  155.    SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164),                 ///< pct_pass_transported / pct_mail_transported, now computed on the fly
  156.  
  157.    SLE_CONDVAR(Town, received[TE_FOOD].old_act,       SLE_UINT16,                 SL_MIN_VERSION, SLV_165),
  158.    SLE_CONDVAR(Town, received[TE_WATER].old_act,      SLE_UINT16,                 SL_MIN_VERSION, SLV_165),
  159.    SLE_CONDVAR(Town, received[TE_FOOD].new_act,       SLE_UINT16,                 SL_MIN_VERSION, SLV_165),
  160.    SLE_CONDVAR(Town, received[TE_WATER].new_act,      SLE_UINT16,                 SL_MIN_VERSION, SLV_165),
  161.  
  162.    SLE_CONDARR(Town, goal, SLE_UINT32, NUM_TE, SLV_165, SL_MAX_VERSION),
  163.  
  164.    SLE_CONDSTR(Town, text,                  SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_168, SL_MAX_VERSION),
  165.  
  166.    SLE_CONDVAR(Town, time_until_rebuild,    SLE_FILE_U8 | SLE_VAR_U16,  SL_MIN_VERSION, SLV_54),
  167.    SLE_CONDVAR(Town, grow_counter,          SLE_FILE_U8 | SLE_VAR_U16,  SL_MIN_VERSION, SLV_54),
  168.    SLE_CONDVAR(Town, growth_rate,           SLE_FILE_U8 | SLE_VAR_I16,  SL_MIN_VERSION, SLV_54),
  169.  
  170.    SLE_CONDVAR(Town, time_until_rebuild,    SLE_UINT16,                SLV_54, SL_MAX_VERSION),
  171.    SLE_CONDVAR(Town, grow_counter,          SLE_UINT16,                SLV_54, SL_MAX_VERSION),
  172.  
  173.    SLE_CONDVAR(Town, growth_rate,           SLE_FILE_I16 | SLE_VAR_U16, SLV_54, SLV_165),
  174.    SLE_CONDVAR(Town, growth_rate,           SLE_UINT16,                 SLV_165, SL_MAX_VERSION),
  175.  
  176.        SLE_VAR(Town, fund_buildings_months, SLE_UINT8),
  177.        SLE_VAR(Town, road_build_months,     SLE_UINT8),
  178.  
  179.    SLE_CONDVAR(Town, exclusivity,           SLE_UINT8,                  SLV_2, SL_MAX_VERSION),
  180.    SLE_CONDVAR(Town, exclusive_counter,     SLE_UINT8,                  SLV_2, SL_MAX_VERSION),
  181.  
  182.    SLE_CONDVAR(Town, larger_town,           SLE_BOOL,                  SLV_56, SL_MAX_VERSION),
  183.    SLE_CONDVAR(Town, layout,                SLE_UINT8,                SLV_113, SL_MAX_VERSION),
  184.  
  185.    SLE_CONDLST(Town, psa_list,            REF_STORAGE,                SLV_161, SL_MAX_VERSION),
  186.  
  187.    SLE_CONDVAR(Town, cargo_produced,        SLE_FILE_U32 | SLE_VAR_U64, SLV_166, SLV_EXTEND_CARGOTYPES),
  188.    SLE_CONDVAR(Town, cargo_produced,        SLE_UINT64,                 SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION),
  189.  
  190.    /* reserve extra space in savegame here. (currently 30 bytes) */
  191.    SLE_CONDNULL(30, SLV_2, SL_MAX_VERSION),
  192.                    """)
  193.  
  194. indy_desc = parse_saveload_desc("""
  195.    SLE_CONDVAR(Industry, location.tile,              SLE_FILE_U16 | SLE_VAR_U32,  SL_MIN_VERSION, SLV_6),
  196.    SLE_CONDVAR(Industry, location.tile,              SLE_UINT32,                  SLV_6, SL_MAX_VERSION),
  197.        SLE_VAR(Industry, location.w,                 SLE_FILE_U8 | SLE_VAR_U16),
  198.        SLE_VAR(Industry, location.h,                 SLE_FILE_U8 | SLE_VAR_U16),
  199.        SLE_REF(Industry, town,                       REF_TOWN),
  200.    SLE_CONDREF(Industry, neutral_station,            REF_STATION,                SLV_SERVE_NEUTRAL_INDUSTRIES, SL_MAX_VERSION),
  201.    SLE_CONDNULL( 2, SL_MIN_VERSION, SLV_61),       ///< used to be industry's produced_cargo
  202.    SLE_CONDARR(Industry, produced_cargo,             SLE_UINT8,   2,              SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  203.    SLE_CONDARR(Industry, produced_cargo,             SLE_UINT8,  16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  204.    SLE_CONDARR(Industry, incoming_cargo_waiting,     SLE_UINT16,  3,              SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  205.    SLE_CONDARR(Industry, incoming_cargo_waiting,     SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  206.    SLE_CONDARR(Industry, produced_cargo_waiting,     SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  207.    SLE_CONDARR(Industry, produced_cargo_waiting,     SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  208.    SLE_CONDARR(Industry, production_rate,            SLE_UINT8,   2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  209.    SLE_CONDARR(Industry, production_rate,            SLE_UINT8,  16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  210.    SLE_CONDNULL( 3, SL_MIN_VERSION, SLV_61),       ///< used to be industry's accepts_cargo
  211.    SLE_CONDARR(Industry, accepts_cargo,              SLE_UINT8,   3,              SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  212.    SLE_CONDARR(Industry, accepts_cargo,              SLE_UINT8,  16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  213.        SLE_VAR(Industry, prod_level,                 SLE_UINT8),
  214.    SLE_CONDARR(Industry, this_month_production,      SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  215.    SLE_CONDARR(Industry, this_month_production,      SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  216.    SLE_CONDARR(Industry, this_month_transported,     SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  217.    SLE_CONDARR(Industry, this_month_transported,     SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  218.    SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8,   2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  219.    SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8,  16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  220.    SLE_CONDARR(Industry, last_month_production,      SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  221.    SLE_CONDARR(Industry, last_month_production,      SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  222.    SLE_CONDARR(Industry, last_month_transported,     SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  223.    SLE_CONDARR(Industry, last_month_transported,     SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  224.  
  225.        SLE_VAR(Industry, counter,                    SLE_UINT16),
  226.  
  227.        SLE_VAR(Industry, type,                       SLE_UINT8),
  228.        SLE_VAR(Industry, owner,                      SLE_UINT8),
  229.        SLE_VAR(Industry, random_colour,              SLE_UINT8),
  230.    SLE_CONDVAR(Industry, last_prod_year,             SLE_FILE_U8 | SLE_VAR_I32,  SL_MIN_VERSION, SLV_31),
  231.    SLE_CONDVAR(Industry, last_prod_year,             SLE_INT32,                 SLV_31, SL_MAX_VERSION),
  232.        SLE_VAR(Industry, was_cargo_delivered,        SLE_UINT8),
  233.  
  234.    SLE_CONDVAR(Industry, founder,                    SLE_UINT8,                 SLV_70, SL_MAX_VERSION),
  235.    SLE_CONDVAR(Industry, construction_date,          SLE_INT32,                 SLV_70, SL_MAX_VERSION),
  236.    SLE_CONDVAR(Industry, construction_type,          SLE_UINT8,                 SLV_70, SL_MAX_VERSION),
  237.    SLE_CONDVAR(Industry, last_cargo_accepted_at[0],  SLE_INT32,                 SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
  238.    SLE_CONDARR(Industry, last_cargo_accepted_at,     SLE_INT32, 16,            SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
  239.    SLE_CONDVAR(Industry, selected_layout,            SLE_UINT8,                 SLV_73, SL_MAX_VERSION),
  240.  
  241.    SLEG_CONDARR(_old_ind_persistent_storage.storage, SLE_UINT32, 16,            SLV_76, SLV_161),
  242.    SLE_CONDREF(Industry, psa,                        REF_STORAGE,              SLV_161, SL_MAX_VERSION),
  243.  
  244.    SLE_CONDNULL(1, SLV_82, SLV_197), // random_triggers
  245.    SLE_CONDVAR(Industry, random,                     SLE_UINT16,                SLV_82, SL_MAX_VERSION),
  246.  
  247.    SLE_CONDNULL(32, SLV_2, SLV_144), // old reserved space""")
  248.  
  249.  
  250. class TTDSavegameDecoder:
  251.     def __init__(self):
  252.         pass
  253.  
  254.     @staticmethod
  255.     def _parse_label(data):
  256.         return ''.join(map(chr, data[:4]))
  257.  
  258.     @staticmethod
  259.     def _read_gamma(data):
  260.         if (data[0] & 0xF0) == 0xE0:
  261.             return (data[0] & 0x0F) << 24 | data[1] << 16 | data[2] << 8 | data[3], 4
  262.         elif (data[0] & 0xE0) == 0xC0:
  263.             return (data[0] & 0x1F) << 16 | data[1] << 8 | data[2], 3
  264.         elif (data[0] & 0xC0) == 0x80:
  265.             return (data[0] & 0x3F) << 8 | data[1], 2
  266.         elif (data[0] & 0x80) == 0:
  267.             return data[0] & 0x7F, 1
  268.         raise ValueError('Invalid gamma')
  269.  
  270.     def _read_int(self, data, size, is_signed):
  271.         fmt = {8: '>b', 16: '>h', 32: '>i'}[size]
  272.         if not is_signed:
  273.             fmt = fmt.upper()
  274.         return struct.unpack_from(fmt, data)[0], size // 8
  275.  
  276.     def _load_pats(self, data, version):
  277.         res = {}
  278.         for k, v in settings_info.data.items():
  279.             if 'SLF_NOT_IN_SAVE' in v['flags']:
  280.                 continue
  281.             if not (v['from'] <= version <= v['to']):
  282.                 continue
  283.             ctype = v['ctype']
  284.             size = 0
  285.             if ctype in ('SLE_UINT8', 'SLE_UINT16', 'SLE_UINT32'):
  286.                 value, size = self._read_int(data, int(ctype[8:]), False)
  287.             elif ctype in ('SLE_INT8', 'SLE_INT16', 'SLE_INT32'):
  288.                 value, size = self._read_int(data, int(ctype[7:]), True)
  289.             elif ctype == 'BOOL':
  290.                 value, size = bool(data[0]), 1
  291.             elif ctype == 'SLE_STRQ':
  292.                 l, size = self._read_gamma(data)
  293.                 value = struct.unpack_from(f'{l}s', data, offset=size)[0].decode()
  294.                 size += l
  295.             else:
  296.                 raise ValueError(f'Unsupported setting type {ctype}')
  297.             data = data[size:]
  298.             res[k] = value
  299.             # print (f'{k} {value}   ({ctype}: {size})  {len(data)}')
  300.         return res
  301.  
  302.     def _load_order(self, data):
  303.         return struct.unpack('>bbhibhhh', data)
  304.         # (otype, flags, dest, onext, refit_cargo, wait_time, travel_time, max_speed)
  305.         # SLE_VAR(Order, type,           SLE_UINT8),
  306.         # SLE_VAR(Order, flags,          SLE_UINT8),
  307.         # SLE_VAR(Order, dest,           SLE_UINT16),
  308.         # SLE_REF(Order, next,           REF_ORDER),
  309.         # SLE_CONDVAR(Order, refit_cargo,    SLE_UINT8,   SLV_36, SL_MAX_VERSION),
  310.         # SLE_CONDVAR(Order, wait_time,      SLE_UINT16,  SLV_67, SL_MAX_VERSION),
  311.         # SLE_CONDVAR(Order, travel_time,    SLE_UINT16,  SLV_67, SL_MAX_VERSION),
  312.         # SLE_CONDVAR(Order, max_speed,      SLE_UINT16, SLV_172, SL_MAX_VERSION),
  313.  
  314.     def _load_cargo_packet(self, data):
  315.         # print(len(data))
  316.         return struct.unpack('>hiihbqbh', data)
  317.         #  SLE_VAR(CargoPacket, source,          SLE_UINT16),
  318.         #      SLE_VAR(CargoPacket, source_xy,       SLE_UINT32),
  319.         #      SLE_VAR(CargoPacket, loaded_at_xy,    SLE_UINT32),
  320.         #      SLE_VAR(CargoPacket, count,           SLE_UINT16),
  321.         #      SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8),
  322.         #      SLE_VAR(CargoPacket, feeder_share,    SLE_INT64),
  323.         #  SLE_CONDVAR(CargoPacket, source_type,     SLE_UINT8,  SLV_125, SL_MAX_VERSION),
  324.         #  SLE_CONDVAR(CargoPacket, source_id,       SLE_UINT16, SLV_125, SL_MAX_VERSION),
  325.         # SLE_CONDNULL(1, SL_MIN_VERSION, SLV_121),
  326.  
  327.     def _load_date(self, data):
  328.         return struct.unpack('>Ihhihiibibb', data)
  329. #     SLEG_CONDVAR(_date,                   SLE_INT32,                  SLV_31, SL_MAX_VERSION),
  330. #         SLEG_VAR(_date_fract,             SLE_UINT16),
  331. #         SLEG_VAR(_tick_counter,           SLE_UINT16),
  332. #     SLEG_CONDVAR(_cur_tileloop_tile,      SLE_UINT32,                  SLV_6, SL_MAX_VERSION),
  333. #         SLEG_VAR(_disaster_delay,         SLE_UINT16),
  334. #         SLEG_VAR(_random.state[0],        SLE_UINT32),
  335. #         SLEG_VAR(_random.state[1],        SLE_UINT32),
  336. #         SLEG_VAR(_cur_company_tick_index, SLE_FILE_U8  | SLE_VAR_U32),
  337. #     SLEG_CONDVAR(_next_competitor_start,  SLE_UINT32,                SLV_109, SL_MAX_VERSION),
  338. #         SLEG_VAR(_trees_tick_ctr,         SLE_UINT8),
  339. #     SLEG_CONDVAR(_pause_mode,             SLE_UINT8,                   SLV_4, SL_MAX_VERSION),
  340.  
  341.     def _load_town_desc(self, data):
  342.         pass
  343.     # SLE_CONDVAR(Town, xy,                    SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
  344.     # SLE_CONDVAR(Town, xy,                    SLE_UINT32,                 SLV_6, SL_MAX_VERSION),
  345.  
  346.     # SLE_CONDNULL(2, SL_MIN_VERSION, SLV_3),                   ///< population, no longer in use
  347.     # SLE_CONDNULL(4, SLV_3, SLV_85),                  ///< population, no longer in use
  348.     # SLE_CONDNULL(2, SL_MIN_VERSION, SLV_92),                  ///< num_houses, no longer in use
  349.  
  350.     # SLE_CONDVAR(Town, townnamegrfid,         SLE_UINT32, SLV_66, SL_MAX_VERSION),
  351.     #     SLE_VAR(Town, townnametype,          SLE_UINT16),
  352.     #     SLE_VAR(Town, townnameparts,         SLE_UINT32),
  353.     # SLE_CONDSTR(Town, name,                  SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION),
  354.  
  355.     #     SLE_VAR(Town, flags,                 SLE_UINT8),
  356.     # SLE_CONDVAR(Town, statues,               SLE_FILE_U8  | SLE_VAR_U16, SL_MIN_VERSION, SLV_104),
  357.     # SLE_CONDVAR(Town, statues,               SLE_UINT16,               SLV_104, SL_MAX_VERSION),
  358.  
  359.     # SLE_CONDNULL(1, SL_MIN_VERSION, SLV_2),                   ///< sort_index, no longer in use
  360.  
  361.     # SLE_CONDVAR(Town, have_ratings,          SLE_FILE_U8  | SLE_VAR_U16, SL_MIN_VERSION, SLV_104),
  362.     # SLE_CONDVAR(Town, have_ratings,          SLE_UINT16,               SLV_104, SL_MAX_VERSION),
  363.     # SLE_CONDARR(Town, ratings,               SLE_INT16, 8,               SL_MIN_VERSION, SLV_104),
  364.     # SLE_CONDARR(Town, ratings,               SLE_INT16, MAX_COMPANIES, SLV_104, SL_MAX_VERSION),
  365.     # /* failed bribe attempts are stored since savegame format 4 */
  366.     # SLE_CONDARR(Town, unwanted,              SLE_INT8,  8,               SLV_4, SLV_104),
  367.     # SLE_CONDARR(Town, unwanted,              SLE_INT8,  MAX_COMPANIES, SLV_104, SL_MAX_VERSION),
  368.  
  369.     # SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  370.     # SLE_CONDVAR(Town, supplied[CT_MAIL].old_max,       SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  371.     # SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  372.     # SLE_CONDVAR(Town, supplied[CT_MAIL].new_max,       SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  373.     # SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  374.     # SLE_CONDVAR(Town, supplied[CT_MAIL].old_act,       SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  375.     # SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  376.     # SLE_CONDVAR(Town, supplied[CT_MAIL].new_act,       SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
  377.  
  378.     # SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_UINT32,                 SLV_9, SLV_165),
  379.     # SLE_CONDVAR(Town, supplied[CT_MAIL].old_max,       SLE_UINT32,                 SLV_9, SLV_165),
  380.     # SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_UINT32,                 SLV_9, SLV_165),
  381.     # SLE_CONDVAR(Town, supplied[CT_MAIL].new_max,       SLE_UINT32,                 SLV_9, SLV_165),
  382.     # SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_UINT32,                 SLV_9, SLV_165),
  383.     # SLE_CONDVAR(Town, supplied[CT_MAIL].old_act,       SLE_UINT32,                 SLV_9, SLV_165),
  384.     # SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_UINT32,                 SLV_9, SLV_165),
  385.     # SLE_CONDVAR(Town, supplied[CT_MAIL].new_act,       SLE_UINT32,                 SLV_9, SLV_165),
  386.  
  387.     # SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164),                 ///< pct_pass_transported / pct_mail_transported, now computed on the fly
  388.  
  389.     # SLE_CONDVAR(Town, received[TE_FOOD].old_act,       SLE_UINT16,                 SL_MIN_VERSION, SLV_165),
  390.     # SLE_CONDVAR(Town, received[TE_WATER].old_act,      SLE_UINT16,                 SL_MIN_VERSION, SLV_165),
  391.     # SLE_CONDVAR(Town, received[TE_FOOD].new_act,       SLE_UINT16,                 SL_MIN_VERSION, SLV_165),
  392.     # SLE_CONDVAR(Town, received[TE_WATER].new_act,      SLE_UINT16,                 SL_MIN_VERSION, SLV_165),
  393.  
  394.     # SLE_CONDARR(Town, goal, SLE_UINT32, NUM_TE, SLV_165, SL_MAX_VERSION),
  395.  
  396.     # SLE_CONDSTR(Town, text,                  SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_168, SL_MAX_VERSION),
  397.  
  398.     # SLE_CONDVAR(Town, time_until_rebuild,    SLE_FILE_U8 | SLE_VAR_U16,  SL_MIN_VERSION, SLV_54),
  399.     # SLE_CONDVAR(Town, grow_counter,          SLE_FILE_U8 | SLE_VAR_U16,  SL_MIN_VERSION, SLV_54),
  400.     # SLE_CONDVAR(Town, growth_rate,           SLE_FILE_U8 | SLE_VAR_I16,  SL_MIN_VERSION, SLV_54),
  401.  
  402.     # SLE_CONDVAR(Town, time_until_rebuild,    SLE_UINT16,                SLV_54, SL_MAX_VERSION),
  403.     # SLE_CONDVAR(Town, grow_counter,          SLE_UINT16,                SLV_54, SL_MAX_VERSION),
  404.  
  405.     # SLE_CONDVAR(Town, growth_rate,           SLE_FILE_I16 | SLE_VAR_U16, SLV_54, SLV_165),
  406.     # SLE_CONDVAR(Town, growth_rate,           SLE_UINT16,                 SLV_165, SL_MAX_VERSION),
  407.  
  408.     #     SLE_VAR(Town, fund_buildings_months, SLE_UINT8),
  409.     #     SLE_VAR(Town, road_build_months,     SLE_UINT8),
  410.  
  411.     # SLE_CONDVAR(Town, exclusivity,           SLE_UINT8,                  SLV_2, SL_MAX_VERSION),
  412.     # SLE_CONDVAR(Town, exclusive_counter,     SLE_UINT8,                  SLV_2, SL_MAX_VERSION),
  413.  
  414.     # SLE_CONDVAR(Town, larger_town,           SLE_BOOL,                  SLV_56, SL_MAX_VERSION),
  415.     # SLE_CONDVAR(Town, layout,                SLE_UINT8,                SLV_113, SL_MAX_VERSION),
  416.  
  417.     # SLE_CONDLST(Town, psa_list,            REF_STORAGE,                SLV_161, SL_MAX_VERSION),
  418.  
  419.     # SLE_CONDVAR(Town, cargo_produced,        SLE_FILE_U32 | SLE_VAR_U64, SLV_166, SLV_EXTEND_CARGOTYPES),
  420.     # SLE_CONDVAR(Town, cargo_produced,        SLE_UINT64,                 SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION),
  421.  
  422.     # /* reserve extra space in savegame here. (currently 30 bytes) */
  423.     # SLE_CONDNULL(30, SLV_2, SL_MAX_VERSION),
  424.  
  425.     def _decode_ottx(self, data, version):
  426.         decompressor = lzma.LZMADecompressor()
  427.         d = memoryview(decompressor.decompress(data))
  428.         # print('Savegame version', version, 'uncompressed size', len(d))
  429.         res = {}
  430.         while d:
  431.             chunk_id = self._parse_label(d[:4])
  432.             if chunk_id == '\0\0\0\0':
  433.                 break
  434.             assert chunk_id not in res, f"Duplicate chunk {chunk_id}"
  435.             # print('Chunk', chunk_id, end=' ')
  436.             block_mode = d[4]
  437.             d = d[5:]
  438.             # CH_RIFF         =  0,
  439.             # CH_ARRAY        =  1,
  440.             # CH_SPARSE_ARRAY =  2,
  441.             # CH_TYPE_MASK    =  3,
  442.             # CH_LAST         =  8, ///< Last chunk in this array.
  443.             # CH_AUTO_LENGTH  = 16,
  444.             if block_mode & 0xF == 0:  # RIFF
  445.                 size = d[0] << 16 | d[1] << 8 | d[2] | (block_mode >> 4) << 24;
  446.                 # print('RIFF', size)
  447.                 # if chunk_id == 'PATS':
  448.                 #     res['settings'] = self._load_pats(d[3:3 + size], version)
  449.                 parser = {
  450.                     'DATE': self._load_date,
  451.                 }.get(chunk_id)
  452.                 if parser:
  453.                     res[chunk_id] = parser(d[3:3 + size])
  454.                 else:
  455.                     res[chunk_id] = d[3:3 + size]
  456.                 d = d[3 + size:]
  457.             elif block_mode == 1 or block_mode == 2:
  458.                 items = []
  459.                 while True:
  460.                     item_size, l = self._read_gamma(d)
  461.                     # print('    ARRAY', block_mode, l, item_size)
  462.                     d = d[l:]
  463.                     if not item_size:
  464.                         break
  465.                     # if block_mode == 2: index = ReadGamma(stdin);
  466.                     if item_size > 1:
  467.                         items.append(d[:item_size - 1])
  468.                     d = d[item_size - 1:]
  469.                 parser = {
  470.                     'ORDR': self._load_order,
  471.                     'CAPA': self._load_cargo_packet,
  472.                     'INDY': lambda d: load_desc(indy_desc, d),
  473.                 }.get(chunk_id)
  474.                 if parser:
  475.                     items = list(map(parser, items))
  476.                 res[chunk_id] = items
  477.             else:
  478.                 raise ValueError(f'Unknown chunk mode {block_mode}')
  479.         return res
  480.  
  481.     def decode(self, data):
  482.         tag = self._parse_label(data)
  483.         if tag != 'OTTX':
  484.             raise ValueError(f'Unknown savegame format {tag}')
  485.         major_version = data[4] * 8 + data[5]
  486.         minor_version = data[6] * 8 + data[7]
  487.  
  488.         res = {}
  489.         res['chunks'] = self._decode_ottx(data[8:], major_version)
  490.         res['version'] = major_version
  491.         return res
  492.  
  493.  
  494. d = TTDSavegameDecoder()
  495. s1 = d.decode(open(sys.argv[1], 'rb').read())
  496. s2 = d.decode(open(sys.argv[2], 'rb').read())
  497.  
  498. c1, c2 = s1['chunks'], s2['chunks']
  499. chunks = set(c1.keys()) | set(c2.keys())
  500.  
  501. print(f'Versions: {s1["version"]} {s2["version"]}')
  502. for c in chunks:
  503.     print(f'Chunk {c}:', end='')
  504.     if c not in c1:
  505.         print('Missing in save 1')
  506.         continue
  507.     if c not in c2:
  508.         print('Missing in save 2')
  509.         continue
  510.     d1 = c1[c]
  511.     d2 = c2[c]
  512.     if d1 == d2:
  513.         print('OK')
  514.         continue
  515.  
  516.     def fmt(data):
  517.         if isinstance(data, memoryview):
  518.             return hex_str(data)
  519.         return data
  520.  
  521.     if isinstance(d1, list) and isinstance(d2, list):
  522.         l1 = len(d1)
  523.         l2 = len(d2)
  524.         m = difflib.SequenceMatcher(a=d1, b=d2)
  525.         print(f'LIST l1={l1} l2={l2} ratio={m.ratio()}')
  526.         # print(next(m.get_grouped_opcodes()))
  527.         for tag, i1, i2, j1, j2 in m.get_opcodes():
  528.             if tag == 'equal':
  529.                 continue
  530.             if tag == 'replace':
  531.                 for i, j in zip(range(i1, i2), range(j1, j2)):
  532.                     print(f'  {i}: - {fmt(d1[i])}')
  533.                     print(f'  {j}: + {fmt(d2[j])}')
  534.             if tag == 'delete':
  535.                 for i in range(i1, i2):
  536.                     print(f'  {i}: - {fmt(d1[i])}')
  537.             if tag == 'insert':
  538.                 for j in range(j1, j2):
  539.                     print(f'  {j}: - {fmt(d2[j])}')
  540.     elif c.startswith('MAP') or c in ('M3LO', 'M3HI'):
  541.         print()
  542.         if c in ('MAP2', 'MAP8'):
  543.             dd1, dd2 = d1.cast('h'), d2.cast('h')
  544.         else:
  545.             dd1, dd2 = d1, d2
  546.         for t in range(len(dd1)):
  547.             if (dd1[t] != dd2[t]):
  548.                 x, y = t & 2047, t >> 11
  549.                 print(f'  Tile {t} ({x}x{y}): -{dd1[t]} +{dd2[t]}')
  550.     else:
  551.         print()
  552.         print(f'  - {fmt(d1)}')
  553.         print(f'  + {fmt(d2)}')
  554.  
  555.     print()
RAW Paste Data