#!/usr/bin/env python3 """Extrahiert die Weltkarten-Daten (Pfadsegmente, Levelpunkte, Levelnamen) aus dem Original-START.EXE und gibt sie als Python-Literal aus -- damit wurde die eingebettete SEGMENTS/LEVEL_NAMES-Tabelle in game/worldmap.py erzeugt (26.07.2026). Nur noetig, falls die Tabelle je neu generiert werden muss; das Spiel selbst liest START.EXE nicht mehr. Aufruf: python3 tools/worldmap_extract.py [pfad/zu/START.EXE] Binary-Struktur (NE-Datensegment, Offsets ~145114..146760): Header, dann pro Level ein Segment aus 0xffff-terminierten u16-Records: [x y 0 25] Standpunkt (Wiederholung des vorigen Endpunkts) [x y 1 25] ... Wegpunkte (die Kartenfigur laeuft sie ab) [x y 0 25] Endpunkt = Standpunkt an diesem Level [label_x label_y] Position des Levelpunkt-Markers [0] (Flag/Reserve) danach die 22 Levelnamen als Pascal-Strings (\x04W1L0...). y-Achse ist geflippt (Screen-Basis = (x, 218 - y), siehe game/worldmap.py). """ import os import struct import sys def parse_worldmap(exe_path): '''Rueckgabe: (segments, names); segments = Liste von dicts {waypoints: [(x,y)...], stand: (x,y), label: (x,y)}.''' d = open(exe_path, 'rb').read() anchor = struct.pack('<4H', 58, 40, 0, 25) start = d.find(anchor) if start < 0: raise ValueError('Kartenpfad-Anker nicht gefunden') names_at = d.find(b'\x04W1L0\x04W1L1') if names_at < 0: raise ValueError('Levelnamen nicht gefunden') # Namen lesen (22 Pascal-Strings) names = [] p = names_at while len(names) < 22 and p < len(d) - 1: ln = d[p] if not (1 <= ln <= 8): break names.append(d[p + 1:p + 1 + ln].decode('ascii')) p += 1 + ln def read_list(p): vals = [] while p <= names_at - 2: v = struct.unpack(' 8: return None, p return vals, p segments = [] p = start cur = {'waypoints': [], 'stand': None, 'label': None} while p < names_at - 2: vals, p = read_list(p) if vals is None: continue if len(vals) == 4 and vals[3] == 25: x, y, flag = vals[0], vals[1], vals[2] if x > 320 or y > 200: continue # Sonder-/Sprungrecords (Weltwechsel) ueberspringen cur['waypoints'].append((x, y)) if flag == 0 and len(cur['waypoints']) > 1: cur['stand'] = (x, y) elif len(vals) == 2: cur['label'] = (vals[0], vals[1]) if cur['stand'] is None and cur['waypoints']: cur['stand'] = cur['waypoints'][-1] segments.append(cur) cur = {'waypoints': [], 'stand': None, 'label': None} # len==1 ([0]-Flag) und leere Listen: ignorieren return segments, names def main(): default = os.path.join(os.path.dirname(__file__), '..', 'game', 'original', 'START.EXE') exe = sys.argv[1] if len(sys.argv) > 1 else default segments, names = parse_worldmap(exe) print('LEVEL_NAMES = %r' % (names,)) print() print('SEGMENTS = [') for s in segments: print(' dict(waypoints=%r,' % (s['waypoints'],)) print(' stand=%r, label=%r),' % (s['stand'], s['label'])) print(']') print('# %d Segmente, %d Namen' % (len(segments), len(names)), file=sys.stderr) if __name__ == '__main__': main()