Start with the choice the game throws away
Stock MvC has six attack buttons, but its palette selector collapses them into two values: punches select 0 and kicks select 1. The restriction is in the software. More buttons do not create more palette choices while that stored value remains a Boolean.
N41's notes identify the native palette directory and point out that portraits
and intro palettes have separate consumers. I used that distinction while
following the native selector into the select object's color byte at +0x61,
through the confirmed-slot latches at 0xFF40C4..0xFF40C7, and into the fighter's
color byte at +0x277. The patch keeps a full index instead: 0–5 for six choices.
DHC v9.1 adds three paired-button choices, 6–8.
Every reader must agree about that index. The large select figure, selected
animation and live body were three separate paths in the standalone patch.
A correct match body did not prove the other two. The animation at 0x07AF6C
still chose bank 1 for any nonzero index until its reader was widened too.
Measure the room before using it
On the clean Euro 980123 set, mvc.06a contains an existing FF padding run from
CPU 0x183E7A to 0x188000, exclusive. Aligning the start to 0x184000 leaves
exactly 16,384 bytes. It is file offset 0x4000 in that 512 KiB member.
The patch uses that existing room; it does not remove sprites, sound or old
palettes to create it.
A complete body palette block occupies 0x60 bytes. The bank sizes explain
both what fits and why the nine-color version moved:
| Layout | Groups × choices × bytes | Body bank, end exclusive |
|---|---|---|
| Standalone v1 | 23 × 6 × 96 = 13,248 | 0x184000..0x1873C0 |
| Combined six-color v2 | 25 × 6 × 96 = 14,400 | 0x184000..0x187840 |
| DHC v9.1 nine-color release | 25 × 9 × 96 = 21,600 | 0x239E00..0x23F260 |
There are 21 stock-selectable identities. The original table also reserved body rows for Dark Sakura and Shuma-Gorath; data rows alone do not make them playable. Combined v2 adds separate Ryu Ken/Akuma form groups, bringing the body directory to 25. Its six-color bank leaves 1,984 bytes in the old slot. Nine choices exceed that slot by 5,216 bytes, so the new bank uses a separately qualified range. It does not spill into the following stock data. Repointing a directory changes where a reader looks; it does not erase the original palettes. ARCADE keeps its native sources, and the older six-color arena remains retained in this nine-color diagnostic.
0x3F4000 was the standalone code location: the 744-byte six-color payload.
The body bank was at 0x184000. In combined DHC, the core moved to 0x3F8000
and the v2 helper uses 0x3F8300; large select-art palettes use 0x3F5000.
The older code slot serves the combined magnetic extension. A code address,
body bank and presentation bank have different jobs even when all occupy
existing program-ROM space.
Check the padding in your own clean input
This read-only check verifies the exact member before counting the available space. It produces no patched ROM and does not need a decryption key.
import hashlib
import zipfile
with zipfile.ZipFile("mvsc.zip") as archive:
chip = archive.read("mvc.06a")
expected = "132316e99d02d197cd8b4b9227dccd9215f5f741bcd21d5abf5c3f07fb64354b"
assert len(chip) == 0x80000
assert hashlib.sha256(chip).hexdigest() == expected
slot = chip[0x4000:0x8000]
assert slot == bytes([0xFF]) * 0x4000
print("16,384 existing bytes at CPU 0x184000..0x188000")For another game, an FF run is a candidate, not permission to overwrite it.
Check the actual address map, code and data references, computed pointers,
and every component already using that range. Reserve an aligned interval,
assert the complete clean preimage, and reject overlaps before writing.
Keep every original member's length exact. Retest the paths that could touch
that region, including alternate modes and forms.
Build a directory, then change its readers
A color bank needs a pointer per fighter/form, a fixed stride, and an index that
stays within the number of installed blocks. The body directory is at
0x1C7268, with longword pointers indexed by characterId / 2. The native
reader families revealed where the stored choice became a Boolean offset.
Nine body-reader families were widened; the similar-looking assist/effect
reader at 0x00A920 retained its native source behavior.
For the combined six-color layout:
fighter base = 0x184000 + compact_group × 0x240
color source = fighter base + color_index × 0x60The nine-color layout changes the group stride to 0x360 and base to
0x239E00. Changing the table alone is insufficient: a consumer that tests
“zero or nonzero” will still collapse choices, and a transformed fighter that
keeps a two-block table can read unrelated data for index 2.
Keep the code and data views separate. 0x184000 is a CPU address;
mvc.06a + 0x4000 is its raw member location. CPS-2 instruction bytes also have
an encrypted/decrypted distinction, while palette data must retain the loader's
word layout. Use the correct view for each operation and compare exact
preimages instead of copying an address from another revision.
MvC2's donor records are little-endian ARGB4444. The original extractor checks alpha, keeps the lower 12 RGB bits, and maps visible donor pens into the native row layout; it does not turn RGB565 into RGB444. Keep that exact nibble-copy process separate from the RGB888-to-RGB444 quantization used for later indexed PNG imports. Mapping colors into a native row requires proving the pen layout, not just making the swatches look right.
Make every surface prove its own result
The original standalone release physically selected all 21 vanilla identities with each of the six buttons: 126 selections. Each accepted run compared 32 words for the selected animation, 48 for the large figure and 48 for the body. That scope does not include every portrait, helper or attack effect.
The latest DHC palette revision has different checks: complete source-bound palette reconstruction, 1,800 retained-code artwork upload cases, and 45 secret choices compared against 216 regular/form references. Those fixture checks do not replace natural rendered WIN scenes, fresh all-secret selection captures, normal-speed audible sound or Windows/online acceptance.
When extending another game, test the full path rather than just the new lookup: native buttons, duplicate fighters, both sides, tag/ownership changes, transformations, interrupted actions and return to the selected color. Compare all words the reader uploads. Keep effect and portrait ownership explicit.
N41's contribution
Thanks to N41 for documenting the native palette directory and separate consumers, and for asking for decoded instructions, breakpoint/register checks and boundary tests. His questions pushed the write-up to explain what a changed hex pattern actually does, rather than treating a range of modified bytes as an explanation. His selection-transition report also identified a specific follow-up: whether a palette write can happen one frame before the sprite's character identity changes. That requires recording both writes and the intervening rendered frame; it is not established as fixed by the six-color selection census.
The ROM-space question is another useful review prompt: identify what already occupied a range, show the preimage, and explain where the code and color data went. These checks make the technique usable by someone building their own patch. N41 is credited for that technical review, the documented native structure and the reported timing issue.
Continue from the measured sources
The original selector assembly and standalone hashes preserve the v1 implementation. The full historical notes contain the Ghidra setup, encrypted preimages, donor mapping and native traces. Their historical build status remains labeled separately from today's release.
The DHC page covers the combined project's current v9.2.1 release and remaining checks. The workshop models are explanations you can manipulate; the original screenshots and receipts remain the evidence. Capcom's artwork and native palettes retain their credit; the standalone body's donor banks came from MvC2. This article is about carrying a color choice through the engine.
The current v9.2.1 update corrects Orange Hulk’s seven added large SELECT face palettes. His two original faces, all nine body colors and the other secret artwork stay unchanged. Focused notes separate machine palette checks from native rendering still awaiting playtesting.
