"""#35 fix-landed comment + the particles.png discovery issue.""" import base64 import json import subprocess import urllib.request REPO = r"C:\git\bt411" BASE = "https://gitea.mysticmachines.com/api/v1/repos/VWE/BT411" out = subprocess.run(["git", "credential", "fill"], input="protocol=https\nhost=gitea.mysticmachines.com\n\n", capture_output=True, text=True, cwd=REPO) cred = dict(l.split("=", 1) for l in out.stdout.strip().splitlines() if "=" in l) AUTH = "Basic " + base64.b64encode( (cred["username"] + ":" + cred["password"]).encode()).decode() def call(method, path, payload=None): data = json.dumps(payload).encode() if payload is not None else None r = urllib.request.Request(BASE + path, data=data, method=method) r.add_header("Authorization", AUTH) r.add_header("Content-Type", "application/json") return json.load(urllib.request.urlopen(r)) def comment(n, body): marker = body.strip().splitlines()[0][:60] for c in call("GET", "/issues/%d/comments" % n): if marker in c.get("body", ""): print(" #%d already has this comment - skipped" % n) return call("POST", "/issues/%d/comments" % n, {"body": body}) print(" commented on #%d" % n) C35 = """**FIX LANDED 2026-07-29** (`dca2586`, build 4.11.622) -- staying OPEN until Conn Man confirms in the field, per the house rule. The device-loss handling was wrong three ways at once, in two inline copies (scene Present + wait-screen Present): 1. On `D3DERR_DEVICELOST` it called `Reset()` **immediately** -- Reset on a still-lost device always fails, and `V()` only logged. No `TestCooperativeLevel` gate existed. 2. It then ran `ParticleEngine::Initialize` against the lost device; the creates fail there and NULL their out-params (proven on the bench: the repro faults at `target=0x0`, not at a dangling address). 3. The next lost frame's `ParticleEngine::Destroy` Release()d those NULLs blind -> read of vtable at 0x0. Two frames, deterministic -- which is why all 8 field stacks are byte-identical. **Reproduced before fixing.** New bench hook `BT_DEVICELOST_TEST=,crashrepro` runs the field sequence; on the unfixed build it died at `Destroy +0x11`, `access=0 target=0x0`, symbolizing to the same four frames as the field logs. **The fix** -- one shared `DPLRenderer::BTResetLostDevice()` replacing both inline copies: idempotent null-safe `Destroy`; `Reset()` gated on `TestCooperativeLevel()`; Reset HRESULT checked (retry next frame on failure); on success re-create via the new `CreateDeviceObjects()` -- NOT `Initialize()`, whose effects-table memset silently killed every particle effect for the rest of the mission on each reset that DID succeed (a second bug, same commit). Draw paths guard the dormant state. **Verified on the bench:** the crash shape now logs `SURVIVED`; three forced full loss/reset cycles each log `[render] device reset OK`; plain run assert-free. **Field verification wanted from Conn Man on 4.11.622+:** fly the exact crash loadout (two triggers, missiles + lasers, in the Owens). Expected: instead of a dead process, at worst a brief hitch and `[render] device reset OK` in `steam_YYYYMMDD.log`. If the crash still occurs the log will now carry the stack for the new build -- send it either way. Side note for the record: the trigger correlation (two triggers + missiles, but a one-trigger alpha never crashed) is about what provokes the GPU timeout on the Iris Xe, not about the crash itself -- the alpha-vs-two-trigger asymmetry likely reflects fire-event timing within a frame. With the recovery fixed, the provocation becomes harmless; if his GPU still times out we may see brief hitches, which is the driver recovering, not the bug.""" PNGT = "VIDEO\\particles.png has never existed -- every billboard particle draws untextured" PNG = """Discovered 2026-07-29 while verifying the #35 fix: `ParticleEngine::CreateDeviceObjects` (formerly `Initialize`) loads `VIDEO\\particles.png` for the billboard particle texture, and that file does not exist -- not in the content tree, not in `BTL4.RES`, and never anywhere in git history. `D3DXCreateTextureFromFile` has therefore failed on **every machine since the engine was written**, leaving `mParticleTexture` NULL, and `RenderParticles` draws with `SetTexture(0, NULL)` -- **untextured solid quads**. That is the shipped look; nobody has ever seen these particles textured. The #35 fix deliberately does NOT gate rendering on the texture (that would disable all billboard particles everywhere); it logs the state at each device-objects creation: ``` [particles] device objects created (max=8192, texture=MISSING (untextured quads -- the shipped look)) ``` Possibly related to VGL Lynx's night-6 observation that the missile launch artifact does not look like the pod video (https://youtu.be/eWb4bZSRkWY -- "you can clearly see it in the launch cycle"). Fix is a CONTENT task, not code: author or recover a particle sheet. Worth checking the 1995 pod asset tree (`CONTENT/BT/` in 410srczipped) for whatever texture the DPL particle system originally used, before authoring a new one.""" def main(): print("=== #35 comment ===") comment(35, C35) print("=== particles.png issue ===") titles = {} for state in ("open", "closed"): page = 1 while True: b = call("GET", "/issues?state=%s&limit=50&page=%d" % (state, page)) if not b: break for i in b: titles[i["title"].strip()] = i["number"] page += 1 if PNGT.strip() in titles: print(" SKIP (exists as #%d)" % titles[PNGT.strip()]) else: j = call("POST", "/issues", {"title": PNGT, "body": PNG}) print(" created #%d %s" % (j["number"], PNGT[:60])) main()