"""Recreate the map display's own overhead track drawing. NavDisplay::DrawStatic (RP_L4/RPL4GAUG.cpp) walks the static entities, looks up each one's L4GaugeImage by resource id, and draws it through localToWorld x worldToView. An entity with no gauge image is skipped. This does the same thing offline: same outlines, same placements, straight down. GaugeImage stream (MUNGA_L4/L4GAUIMA.cpp): int vertexCount Point3D vertices[vertexCount] int LODCount Scalar LODScales[LODCount] per LOD: int primitiveCount per primitive: int type, int colour, int attributes, int indexCount, int indices[indexCount] """ import re, struct def resource_table(res, listing): rows = [] for line in open(listing, encoding='latin1'): m = re.match(r'\s*(\d+)\s+(\d+)?\s*(.*)$', line.rstrip('\n')) if m and m.group(3).strip(): rows.append((int(m.group(1)), m.group(3).strip())) walk, o, n = [], res.find(b'StaticAudioStream\x00') - 8, len(res) while o + 0x38 <= n: name = res[o + 8:o + 0x28].split(b'\x00')[0].decode('latin1', 'replace') addr, size = struct.unpack_from(' n: break walk.append((name, addr, size)) o = addr + size table, i = {}, 0 for rid, desc in rows: if desc == 'Not Used': continue if i < len(walk): nm, addr, size = walk[i] table[rid] = {'name': nm, 'addr': addr, 'size': size, 'desc': desc} i += 1 return table def read_gauge_image(res, addr, size): """-> (vertices, [polyline of (x,y,z) ...]) using the finest LOD.""" o = addr def i32(): nonlocal o v = struct.unpack_from(' addr + size: return None return verts, lines def instances(res, addr, size, count, gauge_ids): """Map instance records: 76 bytes, model gauge-image id at +44, position at +48, unit quaternion at +60. The quaternion validates the record; a record whose +44 is not a gauge image is simply not drawn, exactly as DrawStatic skips it.""" out, o, end = [], addr + 4, addr + size while len(out) < count and o + 76 <= end: pos = struct.unpack_from('<3f', res, o + 48) q = struct.unpack_from('<4f', res, o + 60) if abs(sum(v * v for v in q) - 1.0) < 0.02 and all(abs(v) < 1e5 for v in pos): gid = struct.unpack_from('= 5 and seg[0] == seg[-1] else None if not p or len(p) != 4: return [seg] import math edge = [math.hypot(p[(i + 1) % 4][0] - p[i][0], p[(i + 1) % 4][2] - p[i][2]) for i in range(4)] if max(edge) == 0 or min(edge) / max(edge) > 0.5: return [seg] # not a bar - leave it alone lo = min(range(4), key=lambda i: edge[i]) a, b = p[lo], p[(lo + 1) % 4] c, d = p[(lo + 2) % 4], p[(lo + 3) % 4] mid = lambda u, v: tuple((u[k] + v[k]) / 2 for k in range(3)) return [[mid(a, b), mid(c, d)]] def rotate(q, p): """Quaternion (x,y,z,w) applied to a point.""" qx, qy, qz, qw = q x, y, z = p tx = 2.0 * (qy * z - qz * y) ty = 2.0 * (qz * x - qx * z) tz = 2.0 * (qx * y - qy * x) return (x + qw * tx + qy * tz - qz * ty, y + qw * ty + qz * tx - qx * tz, z + qw * tz + qx * ty - qy * tx) def track_lines(res, table, map_addr, map_size, map_count): """Every drawn outline in the track, in world space, flattened to XZ.""" gauge = {} for rid, r in table.items(): if r['desc'].endswith(': GaugeImage'): gauge[rid] = r segs = [] drawn = skipped = 0 placed = instances(res, map_addr, map_size, map_count, set(gauge)) # Fourteen of the eighteen tracks carry a single bar parked at exactly # (1200, 0, 0), well off the course; the four that don't are the four # that always framed correctly. One stray placement drags the bounding # box out to twelve times the width of the course and squeezes the # track into a sliver, so drop placements that stand alone. A real # branch - Paingod's second canyon is sixty bars out at x=-400 - has # neighbours and stays. def isolated(i): x, _, z = placed[i][1] for j, (g, p, _) in enumerate(placed): if j != i and g is not None and (p[0] - x) ** 2 + (p[2] - z) ** 2 < 200 ** 2: return False return True for i, (gid, pos, q) in enumerate(placed): if gid is None: skipped += 1 continue if isolated(i): skipped += 1 continue r = gauge[gid] img = read_gauge_image(res, r['addr'], r['size']) if img is None: skipped += 1 continue verts, prims = img drawn += 1 for idx in prims: pts = [] for k in idx: if 0 <= k < len(verts): wx, wy, wz = rotate(q, verts[k]) pts.append((wx + pos[0], wy + pos[1], wz + pos[2])) if len(pts) > 1: segs.extend(spine(pts)) # Walls are stacked to build height. Seen from above those copies land # on each other exactly, so draw each distinct wall once. seen, out = set(), [] for s in segs: key = tuple(round(v, 1) for p in s for v in (p[0], p[2])) if key not in seen: seen.add(key) out.append(s) return out, drawn, skipped