BenKrueger

freeski_flag_extract.py

Nov 30th, 2025
20
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.63 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. import random
  3. import binascii
  4.  
  5. mountain_width = 1000  # same as in the game
  6.  
  7.  
  8. class Mountain:
  9.     def __init__(self, name, height, treeline, yetiline, encoded_flag):
  10.         self.name = name
  11.         self.height = height
  12.         self.treeline = treeline
  13.         self.yetiline = yetiline
  14.         self.encoded_flag = encoded_flag
  15.         self.treasures = self.GetTreasureLocations()
  16.  
  17.     def GetTreasureLocations(self):
  18.         """
  19.        Reimplementation of the game's treasure placement:
  20.        - Deterministic per mountain name via crc32(name)
  21.        - 5 treasures at decreasing elevations (down the mountain)
  22.        - Each treasure has a horizontal position (can be out of bounds, later modded)
  23.        """
  24.         locations = {}
  25.         random.seed(binascii.crc32(self.name.encode("utf-8")))
  26.         prev_height = self.height
  27.         prev_horiz = 0
  28.  
  29.         for _ in range(5):
  30.             e_delta = random.randint(200, 800)
  31.             h_delta = random.randint(int(0 - e_delta / 4), int(e_delta / 4))
  32.  
  33.             elevation = prev_height - e_delta
  34.             horiz = prev_horiz + h_delta
  35.  
  36.             locations[elevation] = horiz
  37.  
  38.             prev_height = elevation
  39.             prev_horiz = horiz
  40.  
  41.         return locations
  42.  
  43.  
  44. def SetFlag(mountain, treasure_list):
  45.     """
  46.    Exact copy of the game's flag-decoding logic:
  47.  
  48.    product = (product << 8) ^ treasure_val
  49.    random.seed(product)
  50.    decoded_byte = encoded_flag[i] ^ random.randint(0, 255)
  51.    """
  52.     product = 0
  53.     for treasure_val in treasure_list:
  54.         product = (product << 8) ^ treasure_val
  55.  
  56.     random.seed(product)
  57.  
  58.     decoded_chars = []
  59.     for b in mountain.encoded_flag:
  60.         r = random.randint(0, 255)
  61.         decoded_chars.append(chr(b ^ r))
  62.  
  63.     flag_text = "Flag: %s" % "".join(decoded_chars)
  64.     return flag_text
  65.  
  66.  
  67. # Recreate Mountains as in the decompiled code
  68. Mountains = [
  69.     Mountain(
  70.         "Mount Snow",
  71.         3586,
  72.         3400,
  73.         2400,
  74.         b"\x90\x00\x1d\xbc\x17b\xed6S\"\xb0<Y\xd6\xce\x169\xae\xe9|\xe2Gs\xb7\xfdy\xcf5\x98",
  75.     ),
  76.     Mountain(
  77.         "Aspen",
  78.         11211,
  79.         11000,
  80.         10000,
  81.         b"U\xd7%x\xbfvj!\xfe\x9d\xb9\xc2\xd1k\x02y\x17\x9dK\x98\xf1\x92\x0f!\xf1\\\xa0\x1b\x0f",
  82.     ),
  83.     Mountain(
  84.         "Whistler",
  85.         7156,
  86.         6000,
  87.         6500,
  88.         b"\x1cN\x13\x1a\x97\xd4\xb2!\xf9\xf6\xd4#\xee\xebh\xecs.\x08M!hr9?\xde\x0c\x86\x02",
  89.     ),
  90.     Mountain(
  91.         "Mount Baker",
  92.         10781,
  93.         9000,
  94.         6000,
  95.         b"\xac\xf9#\xf4T\xf1%h\xbe3FI+h\r\x01V\xee\xc2C\x13\xf3\x97ef\xac\xe3z\x96",
  96.     ),
  97.     Mountain(
  98.         "Mount Norquay",
  99.         6998,
  100.         6300,
  101.         3000,
  102.         b"\x0c\x1c\xad!\xc6,\xec0\x0b+\"\x9f@.\xc8\x13\xadb\x86\xea{\xfeS\xe0S\x85\x90\x03q",
  103.     ),
  104.     Mountain(
  105.         "Mount Erciyes",
  106.         12848,
  107.         10000,
  108.         12000,
  109.         b"n\xad\xb4l^I\xdb\xe1\xd0\x7f\x92\x92\x96\x1bq\xca`PvWg\x85\xb21^\x93F\x1a\xee",
  110.     ),
  111.     Mountain(
  112.         "Dragonmount",
  113.         16282,
  114.         15500,
  115.         16000,
  116.         b"Z\xf9\xdf\x7f_\x02\xd8\x89\x12\xd2\x11p\xb6\x96\x19\x05x))v\xc3\xecv\xf4\xe2\\\x9a\xbe\xb5",
  117.     ),
  118. ]
  119.  
  120.  
  121. def compute_treasure_list(mountain: Mountain):
  122.     """
  123.    In-game, when you collide with a treasure at:
  124.      - elevation = collided_row[0]
  125.      - column    = collided_row_offset (0..mountain_width-1)
  126.    it records: elevation * mountain_width + collided_row_offset
  127.  
  128.    We reconstruct the SAME values from the deterministic treasure map:
  129.      - elevation is the key in mountain.treasures
  130.      - horizontal index is (horiz % mountain_width)
  131.    """
  132.     treasure_values = []
  133.  
  134.     # Treasures are generated from top to bottom (highest to lowest elevation),
  135.     # and the player will naturally encounter them in that order.
  136.     # We emulate that ordering explicitly.
  137.     for elevation, horiz in sorted(mountain.treasures.items(), key=lambda kv: kv[0], reverse=True):
  138.         col = horiz % mountain_width
  139.         treasure_val = elevation * mountain_width + col
  140.         treasure_values.append(treasure_val)
  141.  
  142.     return treasure_values
  143.  
  144.  
  145. def main():
  146.     for mnt in Mountains:
  147.         treasure_list = compute_treasure_list(mnt)
  148.         flag = SetFlag(mnt, treasure_list)
  149.         print(f"Mountain: {mnt.name}")
  150.         print(f"  Treasures (elev, horiz_mod):")
  151.         for elevation, horiz in sorted(mnt.treasures.items(), key=lambda kv: kv[0], reverse=True):
  152.             print(f"    elevation={elevation}, horiz_mod={horiz % mountain_width}")
  153.         print(f"  {flag}")
  154.         print("-" * 60)
  155.  
  156.  
  157. if __name__ == "__main__":
  158.     main()
  159.  
Advertisement
Add Comment
Please, Sign In to add comment