Goal: Import MSHvSF's two-tier New York street -- the one where the ground gives way and the fight drops into the subway -- into MvC1 as a playable stage, on the same custom-ROM lineage that carries the native secret characters. Secondary goal: understand the Sega Saturn port of MSHvSF well enough to say, with numbers, whether MvC content could go to the Saturn.
Constraints: No source. Base ROMs untouched; everything is a build-script artifact. MvC's scroll-GFX space is nearly full -- the stage's art appetite versus MvC's free space is the central conflict of this post.
Approach: Live RAM archaeology in MSHvSF to prove what the tier flip actually is (it is not what any of us guessed); a donor-stage + 64MB-expansion strategy for the art budget; differential and poison-tile testing before seating any graphics; for the Saturn, a byte-level audit of the disc's data files against the arcade ROM.
Result: The tier-flip mechanism is fully RE'd and was live-verified in MSHvSF on 2026-07-02 (counter $FFFF30, gate $06A70A, a +$200 scroll shift, 51/64 palette pages). The police van -- the stage's signature prop -- renders and animates live in MvC, camera-tracked, bounce frames cycling. The Saturn audit found byte-identical character data tables under a mechanical pixel transform, documented in two gists. The stage as a whole is not yet playable in MvC -- forcing it hangs at load. That open defect is described honestly below.
Proof / Validation: Live-verified tier flip in the source game; pixel-level differential runs against pristine stock for every merge; a GFX poison test that caught art being seated on cells the attract mode secretly used; byte-diff receipts for the Saturn claims (a 1,604-byte frame-record table matching 100%, a decode test with 0 mismatched pixels out of 7,680).
Primary docs: SATURN_VS_CPS2 -- what the Saturn port actually is and MVC_TO_SATURN_PLAN -- the 4-phase import plan with honest effort ranges.
Everyone Wants This Stage
If you ask MvC1 players what the game is missing, the answers cluster fast: more characters (we're working on it), and that stage. Marvel Super Heroes vs. Street Fighter's New York street is the most theatrical backdrop in the whole Capcom Versus line: you fight on the pavement, someone gets bounced off a parked police van a few too many times, and the street collapses -- a scripted camera plunge into a subway tunnel where the rest of the match plays out underground, emergency lightbars strobing above.
MvC1 shipped one year later on the same CPS-2 hardware, same engine family, and has nothing like it. So the port question looks simple: same hardware, adjacent engine, move the data. That framing survived about one afternoon of actual reverse engineering.
This post is the story of what the stage turned out to be, why the real wall was never the code, the first piece of it now rendering live in the wrong game, and a detour through the Sega Saturn that started as curiosity and ended as a costed engineering plan.
What the Underground Actually Is
Before touching MvC, I needed ground truth from the source game. There were three plausible theories for how the two-tier gimmick works, and the community holds all three:
- Two stages -- the collapse triggers a stage load, underground is a separate stage id.
- Object scenery -- the underground is drawn by the OBJ (sprite) system over a static backdrop, like Onslaught's transformation set-pieces.
- One tall tilemap -- both tiers live in a single oversized scroll map, and the "collapse" is a camera trick.
Theory 3 is the truth, and it's the most elegant of the three. The underground is the same stage id $06, and the flip is nothing more than a +512-pixel (+$200) shift of the scroll base offset into the same tall tilemap. There is no second stage. There is no load. The subway was under the street the whole time, in the most literal sense a tile engine allows: the tilemap is simply twice as tall as what the camera shows, and the trigger slides the window down.
The trigger chain is small enough to state exactly, and every piece of it was live-verified in MSHvSF on 2026-07-02:
- A bounce counter at
$FFFF30increments each time the opponent is bounced off the police van. - The gate check at
$06A70Afires once the counter exceeds 5. - A scripted camera pan runs the plunge.
- The scroll base takes the +$200 shift -- same tilemap, lower window.
- 51 of 64 palette pages swap, which is what sells "underground": same tile art budget-wise where shared, completely different light.
This finding matters for the port in a very practical way. If the underground had been a second stage, the port doubles. If it had been OBJ scenery, it lands in a completely different subsystem with its own budget. Because it's one tall tilemap plus a scroll delta and a palette swap, the mechanism ports almost for free -- the flip logic is a counter, a compare, and an add. The mechanism was never going to be the problem.
- $06
- MSHvSF stage id -- both tiers
- +$200
- scroll base shift on collapse
- $FFFF30
- van-bounce counter address
- $06A70A
- the trigger gate
- 51/64
- palette pages swapped
- 2026-07-02
- live-verified in MSHvSF
The Real Wall Is the Art Budget
Here's the number that reframed the whole project. The NYC stage's scroll art -- both tiers of that tall tilemap, the props, the animation cells -- needs roughly 400KB of scroll-GFX space. I mapped MvC's scroll graphics region for free space, and the largest contiguous free run is about 51KB.
Not 400 versus 300. Not "compress harder and squeak in." An 8-to-1 shortfall against the largest hole in the map. No amount of cleverness inside the stock address space closes that; the tile data for MvC's own stages is packed nearly wall to wall.
Two moves together make the budget real:
- Donate a stage. One of MvC's existing stages gives up its scroll allocation; NYC takes over the donor's space and stage slot. (In the current build lineage the target is stage slot
$0D.) - The custom 64MB GFX expansion. The same expanded FBNeo core that carries the native characters' tiles -- extra graphics chips plus a mapper above the stock 32MB ceiling -- absorbs what the donor slot alone can't. The standing caveat applies here too: the 64MB core is an offline build and breaks Fightcade netplay compatibility.
This is the same shape of solution as the character work, and that's not a coincidence: on CPS-2, code is almost never the scarce resource. Space is.
A Police Van, Rendering in the Wrong Game
The stage's soul is the police van -- it's the trigger object, the thing players remember, and mechanically it's an OBJ-system prop with its own bounce animation frames rather than part of the scroll tilemap. So it made the right first milestone: small enough to seat honestly, load-bearing enough to prove the pipeline.
That milestone is shipped: the van renders and animates live in MvC. Bounce frames cycle, and it tracks the camera correctly -- which sounds cosmetic but is the actual test, because camera-relative OBJ placement is where a half-ported prop falls apart first.
Two bugs from this phase are worth writing down, because both are the kind that silently eat weeks.
The silent canvas trim. For several builds, the underground tier came out of the pipeline with garbled yellow blocks across the top of the tunnel. The art source was provably right; the seated bytes were provably wrong; everything in between claimed success. The culprit was an image-processing step that silently trimmed the working canvas to 1024 pixels -- and since the underground lives in the bottom half of a taller-than-1024 canvas, the trim quietly dropped the tunnel's top rows and shifted everything above them into garbage. v26 of the stage build fixed it. The lesson generalizes: any pipeline stage that can resize without erroring is a pipeline stage that will eventually lie to you.
The attract-mode font. An early merge of the stage's first scroll layer corrupted MvC's attract mode -- four of the GFX cells I'd seated art into were doing double duty as glyphs in an attract-screen font. Those cells read as free by every static test I'd run: nothing in the stage tables referenced them, and they were full of $FF. The fix is deployed and the attract screen is now pixel-identical to stock, but the cautionary tale is the point: all-$FF is not sufficient evidence that a tile is unowned. The rule that came out of it is the GFX poison test, now standard for every seating operation: fill the candidate cells with a loud sentinel pattern, sweep attract mode, the select screen, and full matches, and prove the sentinel renders nowhere before a single byte of real art goes in. Prove cells unreferenced by observation, not by absence of evidence.
Where It Stands: The Load Hang
Honesty section, and it's a real one. The stage work has been merged onto the current native-character build lineage, and the character regression suite passes -- Dark Sakura and Shuma-Gorath are unharmed by the merge. But forcing a match onto stage $0D hangs at load. The diagnosis so far points at a relocated-pointer integration bug -- some pointer in the stage-load path still aims at where data lived before the donor-slot relocation -- but I have not root-caused it, and I won't pretend otherwise. The NYC stage is not playable in the currently deployed ROM. The van milestone is real; the stage milestone is not yet.
Also still open on the art side: eight more OBJ props are extracted but not seated (cops, medics, the dog, civilians), and the flashing emergency lightbars turn out to be the most interesting remaining piece -- they are not OBJ sprites at all but scroll-tilemap animation driven by a small VM in the stage script, a mechanism MvC's stages use differently. That's a genuine porting sub-project, not a copy job.
The Detour: What the Saturn Port Turned Out to Be
While the load hang sat on the bench, a side question got interesting: MSHvSF had a Sega Saturn port in 1998, famously good, running on the 4MB RAM cartridge. If that port's data formats were close to arcade, the Saturn version could be a second source of truth -- or even a second target. So I audited the disc against the arcade ROM, byte by byte. The full writeup is the SATURN_VS_CPS2 gist; here is what it turned out to be.
The Saturn port is a source-level SH-2 recompile with mechanically-converted assets. Not an emulator, not a rewrite-from-scratch: Capcom compiled the game's logic for the Saturn's CPUs and ran the arcade's data through deterministic converters. The evidence is unusually clean:
- Character data tables are byte-identical to arcade. Shuma-Gorath's file (
MC05) carries his 1,604-byte frame-record table and his 1,216-byte sound map byte-perfect against the CPS-2 ROM. The same tables I spent weeks locating in the arcade ROM for the native-character work exist on a consumer CD, unencrypted, in files named per character. - Sprites are a deterministic pen-map away from arcade tiles:
saturn_pixel = (arcade_pixel + 1) & 15, with arcade's transparent pen 15 mapping to Saturn's 0, wrapped in a bit-packed codec keyed by 2-bit codes. Proof standard: decoding record #694 produces 30 tiles with 0 of 7,680 pixels differing from the arcade originals under the pen-map. PALET.TBLis a 128KB lookup table mapping every CPS-2 12-bit color to Saturn RGB555 -- the whole colorspace conversion is one precomputed LUT on the disc.- The roster index is
arcade_id >> 1-- the Saturn character list is literally the arcade's id space with the always-even ids compacted. - The 4MB RAM cart is mandatory and organized as five streaming slots of 668,448 bytes each, refilled from CD as characters and stages swap.
- The one place byte-identity fully breaks: audio, re-encoded to 4-bit ADPCM with zero byte-level correspondence to the QSound data.
For the arcade project this was an immediate, practical win: the Saturn disc is a plaintext mirror of data I otherwise dig out of an encrypted ROM. Byte-identical frame tables on a CD mean a second, independent witness for every table the native-character ports depend on.
- 1,604B
- Shuma frame-record table
- 1,216B
- his sound map
- (p+1)&15
- the whole sprite pixel transform
- 0/7,680
- pixel mismatches, decode test
- 128KB
- PALET.TBL colorspace LUT
- id >> 1
- roster index formula
- 5 x 668,448B
- RAM-cart streaming slots
- 0%
- audio byte-identity
Going the Other Direction: MvC on a Saturn?
Once you know the asset pipeline is deterministic and the data tables are arcade-identical, the tempting question writes itself: could MvC1 content -- a character, eventually the game -- go to the Saturn port's engine? I costed it properly rather than vibing it; the full plan is the MVC_TO_SATURN_PLAN gist. It's a phased plan with honest effort ranges, and the ranges are wide because they should be:
The verdict I stand behind: Phases 0-2 are demo-feasible and low-risk -- the pen-map and codec are proven reversible, so building the toolchain, swapping an asset, and getting one MvC sprite rendering in Saturn MSHvSF is a bounded project with a great screenshot at the end. Phases 3-4 are research projects, and I label them that way deliberately. Three walls stand between a rendered sprite and a playable character:
- The animation stream format is undecoded. Frame tables are byte-identical; the runtime stream that sequences them on Saturn is not yet understood.
- Character logic means hand-authored SH-2. The recompile is Capcom's source through Capcom's compiler; we don't have the source. New character behavior means writing it, per character, in SH-2 assembly against a partially-understood engine.
- The 4MB cart is saturated. Five slots of 668,448 bytes, already streaming-tight for the shipped roster. MvC's bigger cast and effects don't fit the existing memory choreography without redesigning it.
So the recommendation in the plan is the one this whole series already lives by: CPS-2-native porting is the right road for gameplay work, and the Saturn is a superb data source and a fun bounded demo target -- not the venue for new characters. Knowing precisely why, with hour ranges attached, is worth more than the fantasy.
Open Items
Stated plainly, because the project rule is no false success claims:
- The load hang. Stage
$0Dforced-load hangs in the merged build; suspected relocated-pointer bug; not root-caused. The NYC stage is not playable in the deployed ROM. - Eight OBJ props (cops, medics, dog, civilians) extracted but not seated.
- The lightbar VM. The flashing emergency lights are scroll-tilemap animation driven by a small VM, not sprites -- not yet ported, and genuinely nontrivial.
- Saturn Phases 0-2 are a plan, not an artifact. Nothing MvC-related runs on a Saturn today.
- The 64MB expansion the art budget depends on remains an offline-only core -- it breaks Fightcade netplay compatibility.
Proven, with receipts: the tier-flip mechanism (live-verified in MSHvSF), the van rendering and animating in MvC, the attract-mode fix (pixel-identical to stock), and every Saturn byte-identity claim above. Promised, and only promised: the stage playable in MvC, and anything at all on Saturn hardware. When the load hang falls, that's a follow-up post -- and if it turns out my donor-slot relocation design was itself the mistake, that will be in the post too.