case studyUpdated Aug 15, 20267 min read$2E

Storm and Magneto Can Fight in MvC. Watch Where the Proof Stops.

Lightning Storm lands ten hits. Magneto runs seven gameplay segments. The footage is real; diagnostic setup and unfinished parity keep it from being a release claim.

Jump to evidence
cps2reverse-engineeringxmvsfmarvel-vs-capcomstorm
CPS-2 Reverse EngineeringPart 35 of 43
Browse all writing
On this page

The last post ended on two portraits. Storm occupied Jin's cell, Magneto occupied Captain America's, and the ROM drew both without a runtime patch. That was a real result, but it answered an identity question. It did not answer the question a player asks: can they fight?

Now they can, in one composed build, far enough to record two useful gameplay demonstrations. The important part is not that the footage looks coherent. It is that each half has a bounded proof contract and an intervention ledger—and neither contract is large enough to mean "the port is finished."

clip · loops · audio available
ONE COMPOSED ROM // Storm's replay-derived semantic Lightning Storm route, then Magneto's seven-part scoped gameplay reel. Final edit: 83.47 seconds, SHA-256 14a51b1bdd4d8482777dd17721ca30661e20661cab2a6c64acd3a0745666ea0e. Storm's HUD still says JIN and both capture harnesses disclose their diagnostic interventions; the footage is proof of these named paths, not complete XMvSF parity.

One ROM, two proof contracts

Both halves run from the same archive: md5 4279ea69bb585ad92dce98319082490b. The build combines Storm's current direct lanes, the MAG-16 Magneto fixes, the permanent select-grid patch and the audio layers into one artifact. The captures ask different questions:

StormMagneto
input authorityten replay-derived semantic masksdeterministic ordinary-control route
P1 fighter writes0 across action, animation, position, velocity, life and meter0 across action, animation and position
demonstrated surfacemovement, six standing normals, Lightning Storm contactmovement, sampled normals, Disruptor, Force Field, Flight, Tempest, Shockwave
destination identityStorm id $18 vs Gambit id $0AMagneto id $04
honest boundaryone diagnostic direct-lane routeseven named presentation segments

Treating either half as a complete-character test would erase the most useful information in the receipts.

Storm: replay-derived, deliberately not called exact

Storm's command input comes from ten consecutive rows of the verified xmvsfu replay 1783532505719-8213, source frames 5090..5099. Seven masks cross unchanged. Frames 5095..5097 contain the simultaneous direction mask D+L+R ($5F8), which has no useful single-stick meaning in the destination. Those three rows become D+R ($5E8): Left is removed, the newly added Right is retained, and no other row changes.

That makes the lineage semantic and explicit, not byte-exact. The receipt even carries exact_wire_claim: false. It would be wrong to describe it as an unchanged source window playing in MvC.

What the destination does prove is substantial and narrower. Storm moves, performs standing LP, MP, HP, LK, MK and HK on the expected $10:$00/$02/$04 and $12:$00/$02/$04 routes, then enters Lightning Storm action $2A. Its animation orders $2C:$08 before $2C:$36; Gambit's life drops ten times from 144 to 32, for 112 damage; the native counter reaches 10 monotonically; and Storm owns as many as 35 class-$28 objects at once. The pointer gate reports zero invalid fighter or pool-B events. Her one natural meter bar is spent without a meter write.

Storm's diagnostic interventions, in full

The harness writes the direct-core enable once, positions the select cursor, and stabilizes P2 for 1,015 frames with action, velocity and geometry writes. It performs zero writes to Storm's fighter fields, including action, animation, position, velocity, life and meter. Her HUD still reads JIN. Those facts are part of the result, not production debris to crop out of the claim.

Magneto: seven named segments, still scoped

The Magneto half selects id $04 with ordinary P1 inputs and zero cursor-RAM writes. In the match, the harness never writes Magneto's action, animation or position. Movement, sampled normals, EM Disruptor, Magnetic Force Field, Flight, Magnetic Tempest and Magnetic Shockwave all complete: 7/7 required segments, no missing states and zero invalid animation pointers.

Its intervention ledger matters too. Meter is filled twice so both supers fit in a one-minute presentation, and the passive defender is stabilized. Force Field therefore proves that its activation states execute, not that it counters a live attack. The MAG-16 audio layer has an 8/8 static source sequence/PCM transport pass, but this montage is not a move-by-move listening oracle.

Standing HK's second hit remains non-source-exact; Shockwave lands [10,8] in the fresh destination check where the source lands [10,9]; and shared action $0122 remains outside the scoped audio gate. Hyper Grav, Magnetic Blast, throws, tags, Variable Cross, story/AI, full-roster damage and complete presentation parity are not closed by this clip.

What changed, and what did not

The August 13 result was identity: ROM-resident grid art. The August 14 result is gameplay: input reaches imported state, effect and object paths long enough to produce coherent, measurable fight footage. Full parity would be a third and much larger claim.

That final claim still requires every normal and special on both sides, hit and block, damage and stun, throws, tags, Variable Cross, child cleanup, audio attribution, round and match transitions, story/AI, presentation and stock-character regressions. Until those rows pass, the accurate sentence is simple: Storm and Magneto are fighting in MvC; neither port is finished.

The embedded video is a local evidence artifact. Its receipt records that no upload was performed; no YouTube upload is implied by this post.

Editor's update — August 15, 2026: the identity rig's emulator now has a capability receipt

The milestone after gameplay is fail-closed identity acceptance — owner-verified select-screen evidence, scored from object/work/cel contracts rather than eyeballed screenshots. The first live attempt died at frame 637, and the sealed receipt blames the tooling, not the ROM: the headless trace build fbneo-trace-schema11 services memory.registerexec hooks but never composes video, so gui.gdscreenshot walked a null XBuf (ld4.16b {v16,v17,v18,v19}, [x7] with x7 = 0) and took SIGSEGV. Classification: emulator capability mismatch, not ROM rejection. The V5 candidate (0c6b9bd65001…) was never judged.

A combined-capability build, fbneo-runtime-schema12 (b86cb4b864e4… — real CPS draw, QSound, SDL frame pump and exec hooks; no trace-only path), then had to earn its seat with a one-boot capability probe before any identity case: immutable staged inputs, ordinary P1 coin/start only, zero memory-write APIs. It passed exactly as narrowly as designed — a functional $00BD28 callback at select phase 4 (first hit at frame 549) plus a genuine 344,075-byte GD framebuffer with 247 distinct pixel words, written, reopened and byte-compared on disk (probe receipt SHA-256 6ec96b8270e2…; frame fa6c196b10ea…). That frame is below — the first rendered pixel evidence this program has of the V5 select screen, captured fully headless.

MvC character select screen rendered by the schema12 build booting the V5 candidate, with Storm and Magneto portraits present in the grid and the P1 cursor on Ryu
rendered_frame.gd from the capability probe, converted 1:1 from the GD truecolor buffer. Capability evidence only — identity acceptance is a separate, stricter gate.

The probe's first revision also paid for a CPS-2 lesson worth pinning. FBNeo's Lua memory.readbyte goes through the cheat core's data read path, and on a keyed set a data read of program space returns the raw stored bytes: $00BD28 reads back 42521a07b5fdc500, while only opcode fetches see the decrypted 302c000c4a2c0002. Probe V1 compared a load-time readback against the opcode view and died deterministically for it — sealed as a harness contract error (0880eeadc8dc…), not an emulator or ROM finding. The V2 fix pins the loaded view instead, which quietly upgrades the check into something stronger: proof the emulator loaded exactly the pinned candidate's bytes.

Where the ladder stands now: the V9 identity harness (every screenshot call hardened to fail terminally on any capture defect) is frozen, independently reviewed, and double-preflighted to byte-identical pins (ba866803a9d6…). Its two-case natural-selector smoke — MAGNETO then STORM, P1/LP, zero cursor writes, stop on first fail — is the next runtime gate. After that the order is fixed: effect palettes, the 256-capture wind/damage matrix, the team/audio lifecycle, and one semantic replay proxy, each fail-closed, before any release decision exists. Nothing in this update moves either port past identity-pending.

Written by Daniel Plas Rivera · 1,542 words · $2E

ShareXLinkedIn