Weltkarten-Daten fest eingebettet (SEGMENTS/LEVEL_NAMES), parse_worldmap raus aus dem Spiel -- Extraktor als tools/worldmap_extract.py, Laufzeit braucht nur noch PCKELL.DAT
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
#!/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('<H', d[p:p + 2])[0]
|
||||
p += 2
|
||||
if v == 0xffff:
|
||||
return vals, p
|
||||
vals.append(v)
|
||||
if len(vals) > 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()
|
||||
Reference in New Issue
Block a user