From 24ddbd4970c503082af0bce8eee87be32890f61e Mon Sep 17 00:00:00 2001 From: Cyd Date: Thu, 16 Jul 2026 22:19:21 -0500 Subject: [PATCH] Second scene recovered: the Klingon vessel (klngvid) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit recover_scene.py generalizes the recovery (dual-window dump -> chain walk -> position/segment parse -> render) for any capture. Applied to klngvid: 139 content payload programs, 96 positions + 62 edge segments = a sparse starfield with a dense edge tangle right-of-center -- a vessel in space. Readout §06 now shows both recovered scenes (warp field + Klingon) side by side. Co-Authored-By: Claude Opus 4.8 --- emulator/firmware-decomp/recover_scene.py | 72 ++++++++++++++++++++ emulator/firmware-decomp/render-readout.html | 30 ++++++++ 2 files changed, 102 insertions(+) create mode 100644 emulator/firmware-decomp/recover_scene.py diff --git a/emulator/firmware-decomp/recover_scene.py b/emulator/firmware-decomp/recover_scene.py new file mode 100644 index 0000000..8dfb808 --- /dev/null +++ b/emulator/firmware-decomp/recover_scene.py @@ -0,0 +1,72 @@ +"""Generalized scene recovery: given a dual-window dump pkl (bin pages + content +region), walk the DMA chains, parse every content payload's screen-space floats +(+0x14/+0x18, +0x24/+0x28), and render points+streaks to PNG. +Usage: recover_scene.py """ +import sys, pickle, struct, collections +S = r'C:\Users\cyd\AppData\Local\Temp\claude\c--VWE-TeslaRel410\4e848c76-6e89-4034-8047-d8d491cb32d8\scratchpad' +dump = sys.argv[1] if len(sys.argv) > 1 else 'klng_wide30.pkl' +outb = sys.argv[2] if len(sys.argv) > 2 else 'klng_scene' +mem = pickle.load(open(S + '\\' + dump, 'rb'))['mem'] + +def asf(w): + return struct.unpack('> 23) & 0xff + return 1 < e < 254 and 1e-6 < abs(asf(w)) < 1e6 + +# chain walk: every SEND target from bin pages +sends = set() +for a in sorted(k for k in mem if 0x0801e000 <= k < 0x08030000): + w0 = mem.get(a); w1 = mem.get(a + 4) + if w0 is None or w1 is None: continue + if ((w1 >> 28) & 0xf) in (1, 9) and w0 >= 0x08030000: + sends.add((w0, w1 & 0x7f)) +print("content sends:", len(sends)) + +pts = []; segs = [] +for addr, size in sorted(sends): + def f(off): + w = mem.get(addr + off) + return asf(w) if w is not None and isf(w) else None + x1, y1, x2, y2 = f(0x14), f(0x18), f(0x24), f(0x28) + if x1 is not None and y1 is not None and -100 < x1 < 1000 and -100 < y1 < 600: + pts.append((x1, y1)) + if x2 is not None and y2 is not None and -100 < x2 < 1000 and -100 < y2 < 600: + segs.append((x1, y1, x2, y2)) +print("positions: %d segments: %d" % (len(pts), len(segs))) +if not pts: + print("no positions parsed -- content window may differ; check send addrs:") + for a, s in sorted(sends)[:10]: + print(" %08x size=%d present=%s" % (a, s, (a in mem))) + sys.exit() + +W, H = 832, 512 +img = [[(5, 8, 13) for _ in range(W)] for _ in range(H)] +def put(x, y, c, blend=1.0): + xi, yi = int(x), int(y) + if 0 <= xi < W and 0 <= yi < H: + r0, g0, b0 = img[yi][xi] + img[yi][xi] = (min(255, int(r0 + c[0] * blend)), + min(255, int(g0 + c[1] * blend)), + min(255, int(b0 + c[2] * blend))) +def line(x1, y1, x2, y2, c): + steps = max(1, int(max(abs(x2 - x1), abs(y2 - y1)))) + for i in range(steps + 1): + t = i / steps + put(x1 + (x2 - x1) * t, y1 + (y2 - y1) * t, c, 0.5) +for x1, y1, x2, y2 in segs: + line(x1, y1, x2, y2, (200, 80, 70)) +for x, y in pts: + put(x, y, (255, 240, 230)); put(x + 1, y, (170, 120, 110)); put(x, y + 1, (170, 120, 110)) +buf = bytearray() +for row in img: + for r, g, b in row: + buf += bytes((r, g, b)) +open(S + '\\' + outb + '.ppm', 'wb').write(b'P6\n%d %d\n255\n' % (W, H) + bytes(buf)) +try: + from PIL import Image + Image.open(S + '\\' + outb + '.ppm').save(S + '\\' + outb + '.png') + print("wrote %s.png" % outb) +except Exception as e: + print("ppm only:", e) +pickle.dump({'pts': pts, 'segs': segs}, open(S + '\\' + outb + '_pos.pkl', 'wb')) diff --git a/emulator/firmware-decomp/render-readout.html b/emulator/firmware-decomp/render-readout.html index 85f2751..2ba6bbb 100644 --- a/emulator/firmware-decomp/render-readout.html +++ b/emulator/firmware-decomp/render-readout.html @@ -379,6 +379,13 @@ position read from its compiled coefficient block (fields +0x14/+0x18 and +0x24/+0x28, plain IEEE floats in screen space). +
+ same recovery · the Klingon capture + +
the Klingon scene — 96 positions and 62 edge segments: a sparse + starfield with a dense tangle of intersecting edges right-of-center — a vessel, + recovered by the identical chain-walk from the klngvid capture.
+
@@ -745,6 +752,29 @@ var WARP_SEGS=[[196.5,174.7,485.3,268.5],[73.5,277.0,469.5,268.5],[113.9,474.8,5 }); })(); + /* ---- the Klingon scene: same recovery, klngvid capture ---- */ + var KLNG_PTS=[[634.1,336.3],[655.5,143.7],[633.2,120.7],[742.5,406.4],[731.1,409.1],[685.4,430.7],[763.6,87.0],[713.4,428.6],[754.3,516.8],[559.1,507.4],[632.1,378.5],[729.6,493.3],[744.2,139.9],[708.8,461.7],[716.8,225.7],[625.4,209.7],[690.5,514.9],[665.5,560.0],[755.0,303.2],[696.3,417.4],[686.0,505.1],[694.7,384.9],[740.5,47.5],[733.5,295.4],[750.3,15.0],[709.3,521.6],[306.5,131.8],[523.3,30.9],[608.5,400.1],[710.5,212.0],[737.3,70.4],[680.3,381.3],[759.0,494.0],[735.2,429.2],[539.6,391.9],[159.3,478.9],[551.3,181.5],[221.2,246.9],[728.5,516.6],[674.6,420.1],[458.0,383.2],[630.6,395.1],[447.9,388.9],[339.3,427.6],[601.5,64.8],[547.8,404.2],[421.2,424.0],[602.9,69.6],[682.6,303.3],[501.6,579.4],[658.2,475.4],[20.5,582.0],[643.9,513.8],[620.1,297.1],[546.2,301.7],[495.6,300.0],[207.3,326.4],[465.0,536.9],[625.6,39.4],[635.8,389.8],[382.2,478.9],[378.2,14.5],[578.0,231.3],[635.6,379.8],[578.0,565.2],[340.2,582.1],[685.5,536.7],[481.1,484.7],[560.5,61.5],[293.6,119.6],[211.3,448.9],[576.1,220.1],[25.2,309.4],[658.6,482.1],[664.0,399.7],[458.2,541.4],[673.8,154.1],[590.9,348.1],[681.4,90.3],[610.4,268.0],[317.0,549.9],[407.3,486.8],[442.2,80.8],[500.7,597.4],[338.5,142.9],[402.8,46.7],[544.0,82.7],[482.8,5.5],[539.8,103.7],[767.5,322.4],[534.7,547.2],[300.9,441.2],[586.1,238.1],[575.0,548.3],[545.6,239.1],[525.0,9.8]]; +var KLNG_SEGS=[[634.1,336.3,452.4,268.5],[655.5,143.7,459.2,268.5],[633.2,120.7,487.3,268.5],[742.5,406.4,429.0,268.5],[731.1,409.1,420.4,268.5],[685.4,430.7,439.1,268.5],[763.6,87.0,472.1,268.5],[713.4,428.6,416.3,268.5],[754.3,516.8,409.6,268.5],[559.1,507.4,479.0,268.5],[632.1,378.5,458.0,268.5],[729.6,493.3,403.5,268.5],[744.2,139.9,566.1,268.5],[708.8,461.7,407.5,268.5],[716.8,225.7,420.0,268.5],[625.4,209.7,439.4,268.5],[690.5,514.9,411.7,268.5],[665.5,560.0,536.9,268.5],[755.0,303.2,526.9,268.5],[696.3,417.4,588.4,268.5],[686.0,505.1,550.5,268.5],[694.7,384.9,567.3,268.5],[740.5,47.5,512.5,268.5],[733.5,295.4,740.1,268.5],[750.3,15.0,537.7,268.5],[709.3,521.6,682.6,268.5],[306.5,131.8,725.5,268.5],[523.3,30.9,530.3,268.5],[608.5,400.1,810.0,268.5],[710.5,212.0,629.1,268.5],[737.3,70.4,731.9,268.5],[680.3,381.3,693.8,268.5],[759.0,494.0,429.4,268.5],[735.2,429.2,579.1,268.5],[539.6,391.9,844.9,268.5],[159.3,478.9,843.5,268.5],[551.3,181.5,928.0,268.5],[221.2,246.9,931.0,268.5],[728.5,516.6,984.5,268.5],[674.6,420.1,801.6,268.5],[458.0,383.2,843.1,268.5],[630.6,395.1,727.7,268.5],[447.9,388.9,812.6,268.5],[339.3,427.6,880.1,268.5],[601.5,64.8,700.0,268.5],[547.8,404.2,860.3,268.5],[421.2,424.0,798.5,268.5],[602.9,69.6,994.4,268.5],[682.6,303.3,633.9,268.5],[501.6,579.4,775.9,268.5],[643.9,513.8,787.7,268.5],[620.1,297.1,761.4,268.5],[495.6,300.0,929.0,268.5],[207.3,326.4,953.2,268.5],[465.0,536.9,755.8,268.5],[625.6,39.4,757.9,268.5],[635.8,389.8,926.6,268.5],[382.2,478.9,745.5,268.5],[378.2,14.5,790.5,268.5],[578.0,565.2,889.0,268.5],[340.2,582.1,760.3,268.5],[458.2,541.4,972.6,268.5]]; + (function(){ + var kc=document.getElementById('klngframe'); if(!kc) return; + var kx=kc.getContext('2d'), W=kc.width, H=kc.height; + kx.fillStyle='#0a0608'; kx.fillRect(0,0,W,H); + kx.lineWidth=0.8; + KLNG_SEGS.forEach(function(s){ + var g=kx.createLinearGradient(s[0],s[1],s[2],s[3]); + g.addColorStop(0,'rgba(255,140,110,0.8)'); + g.addColorStop(1,'rgba(160,50,50,0.1)'); + kx.strokeStyle=g; + kx.beginPath(); kx.moveTo(s[0],s[1]); kx.lineTo(s[2],s[3]); kx.stroke(); + }); + KLNG_PTS.forEach(function(p){ + kx.fillStyle='rgba(255,245,235,0.95)'; + kx.fillRect(p[0]-0.5,p[1]-0.5,1.6,1.6); + kx.fillStyle='rgba(220,140,110,0.35)'; + kx.fillRect(p[0]-1.5,p[1]-1.5,3.5,3.5); + }); + })(); + /* ---- IGC tile footprint: which 64x128 tiles the array lit for the object ---- */ var TILES={ntx:10,nty:5,touched:[[0,2],[1,1],[1,2],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3],[4,1],[4,2],[4,3],[5,1],[5,2],[6,1],[6,2],[7,1],[8,1]]}; (function(){