case study6 min read$38

Capcom Shipped Norimaro in the American Arcade ROM. One Byte Turns Him On.

The US version of Marvel Super Heroes vs. Street Fighter hides its Japan-only character behind a single zero in a defaults table. His portrait, name, stance, move data and sounds are all still in the ROM. Change one byte and he is selectable and playable. I found him while looking for somewhere to put Strider.

cps2mshvsfmarvel-super-heroes-vs-street-fighternorimarostrider
CPS-2 Reverse EngineeringPart 44 of 45
Browse all writing
On this page

The American arcade version of Marvel Super Heroes vs. Street Fighter has a hole in its character select. Bottom row, middle cell, where every other box holds a fighter's face, there is the game's own logo instead.

In Japan that cell is Norimaro — a gangly office worker in a purple suit, a comedian's creation, the least likely fighter in a game about Hulk and Akuma. He was cut everywhere else.

He is still in the American ROM. All of him. And the difference between the logo and a fully playable Norimaro is one byte.

Two select screens side by side. Left: the cursor sits on Wolverine and the bottom-centre cell shows the game logo. Right: the cursor is on Norimaro, whose portrait, name and stance sprite all render.
Same ROM. Left is stock. Right has one data byte changed from $00 to $01. Nothing else is modified.

The screen has no grid

The obvious model for a character select is a two-dimensional array and a cursor with a row and a column. That is not what this game does. There is no row and no column stored anywhere.

Movement is a link table: four words per character, giving the id you land on for up, down, left and right. The geometry is emergent. An entry that points at itself is a wall.

a0 = flag ? TABLE_A : TABLE_B
d5 = stored_value * 4                  ; the stored value is the id doubled
a0 += d5
btst #3 -> d5 = (a0)      ; UP
btst #2 -> d5 = 2(a0)     ; DOWN
btst #1 -> d5 = 4(a0)     ; LEFT
btst #0 -> d5 = 6(a0)     ; RIGHT

Walking that table from Ryu reconstructs a six-by-three grid with zero coordinate conflicts, and it matches the portraits on screen cell for cell. That agreement is the evidence the grid view is legitimate at all — a link table is perfectly capable of describing something non-planar, and if it had, no row-and-column picture would have been honest.

The two tables differ in exactly three entries, and all three are edges that touch the missing cell. Wolverine's DOWN, Omega Red's LEFT, Blackheart's RIGHT. In the American table they route around the hole. In the other they route into it.

Following the switch backwards

The table choice is a single byte in work RAM, and the same byte also picks which artwork the bottom-centre cell draws. That is why the first attempt produced a half-result worth keeping: the character became selectable while the box still showed the logo. The cell artwork is chosen once, when the screen is built, and the byte had been set after that moment.

Close-up of the bottom-centre cell, before and after. Before: the game logo. After: a portrait of a bespectacled man in a purple suit.
Set the flag before the screen is constructed and the same cell draws its real occupant.

Tracing that byte backwards through the code that writes it leads out of RAM and into a defaults table in ROM — one that carries readable region tags, MSHVSJPN and MSHVSUSA, alongside build dates. The byte the running American ROM reads is $00. Making it $01 lets the game's own configuration path do everything else: the flag gets set, the navigation table switches, the cell artwork switches, and the tile data the artwork points at gets loaded.

No code patch. Nothing inside the encrypted region. One data byte.

He is playable, not just clickable

A selectable character that falls over the moment you pick him would be a curiosity. So the last check was to play him.

Left: the select screen with Norimaro chosen. Right: a live match with Norimaro fighting Shuma-Gorath, his name and face on the health bar.
Selected, teamed, stage loaded, fighting. Correct sprites, correct animation, correct name and face icon on the health bar.

Select, confirm, pick a partner, mode, speed, stage load, match. Correct sprites and animation, a working health bar with his name and face, the round timer counting down, damage going both ways. Capcom shipped the entire character and declined to route the cursor to him.

clip · loops
86 seconds, one changed byte // cursor route into the cell, portrait and name, stance sprite, then a live match. Same ROM otherwise.

Two wrong answers on the way

Both are worth more than the result.

The first was an instrument that never fired. A memory watch on a single odd byte reports zero writes in this emulator — silently, with no error. That produced a completely confident and completely false "nothing ever writes this flag". The fix is to watch the containing word and read which half changed. Every watch after that carried a positive control: a deliberate write at a known moment that must appear in the log, or the log is not evidence.

The second was a hypothesis that fit. A global byte is compared against five, and "press something at least once every five frames" is an elegant reading of that. It is also wrong: that address is referenced from 158 separate places in the binary. It is a shared base, not a press timer. What killed the idea was counting the references, not reasoning about the code.

There is a pattern in both. The instrument agreed with the story before the story was checked.

What this is not

It is not a Strider port. Strider Hiryu is not in this game and nothing here puts him there. What this establishes is the seat: the cell exists, it is fully wired, its data slots are located, and a character record sitting in it is proven to work end to end. Swapping the occupant is a different and much larger job — artwork, code translation, every reference repointed — and most of it is still open.

It is also not a download. There is no ROM to distribute here and there will not be one.

And the playtest was one match, partway, player one. Round two, a knockout, the win screen, the continue, the ending and the character as a CPU opponent are all untested. His specials and supers were never thrown, so his move data is unverified.

The interesting thing was never the byte. It is that a character everyone believes was removed from a game was, the whole time, sitting in the ROM behind a single zero.

Written by Daniel Plas Rivera · 1,386 words · $38

ShareXLinkedIn