Pokemon Black 2 and White 2 Decompilation: 43,046 Functions Match and the Goal Is a PC Port

By Indie Kings | October 21, 2026

Updated October 21, 2026: The Pokémon Black 2 and White 2 decompilation now has 43,046 of 43,418 functions matching, which is 97.45% of the code by byte, and 1,672 of 1,792 source files complete. Both the USA and European ROMs rebuild byte for byte from the same source tree, and the long-term goal is a native PC port.

GitHub repository card for the Pokemon Black 2 and White 2 decompilation

Image: GitHub repository card for fuddlesworth/pokebw2.

What the project is rebuilding

Per the repository README, this is a work-in-progress decompilation of Pokémon Black 2 and White 2 with a long-term goal of a native PC port. The key property it claims is that both the USA and European ROMs rebuild byte for byte from the same source tree, which means one body of C produces two retail games.

The project tracks progress automatically from CI, and the report breaks the work down by version, by the ARM9 main program and the overlays, and by the game's own code and each library. That last part is what makes the project unusual: a Pokémon decompilation has to reconstruct not just the game but the Nintendo SDK libraries underneath it, because the library functions the game calls have to be rebuilt too.

GitHub reports 45 stars, 18 forks and 23 open issues for the repository, with 16 contributors. The lead contributor accounts for 1,703 commits, with the next at 498, 487, 231, 85 and 76, and the sixteen contributors together account for 3,293. The GitHub language breakdown puts C at 32,433,982 bytes, which is 60.5% of the 53,651,491 bytes in the project, with assembly at 20,193,281 bytes and Python at 1,007,175 bytes. The licence is the GNU GPL v3.0, and the README notes that this covers the symbol names imported from the swan project.

The match numbers from the CI report

The project's progress report is generated by CI on every push, and the figures below are the state of the build as of October 21, 2026.

MeasureValue
Code matched3,768,768 bytes of 3,867,576, which is 97.45%
Functions matched43,046 of 43,418, which is 99.14%
Files complete1,672 of 1,792, with complete code at 88.16%
Data matched1,735,536 bytes, which is 95.12%
Fuzzy match99.72%
Versions builtb2_us and w2_us, the USA and Europe NDSi Enhanced releases

As with every decompilation, the function count flatters the position and the file count does not. 99.14% of functions match, but 1,672 of 1,792 files are complete, and complete code stands at 88.16%. A single stubborn function inside a file keeps that whole file out of the complete count, and with 120 files outstanding that is where the remaining work actually sits.

Where the unmatched code sits

The report breaks the game into the ARM9 main program, the overlays, the game's own code and each library, and the shape of the remaining work is visible in the libraries rather than in the game.

ComponentCode matchedFunctions matched
Game code3,088,134 of 3,108,92036,741 of 36,800
Overlays3,162,024 of 3,251,88234,654 of 34,921
ARM9 main606,744 of 615,6948,392 of 8,497
Library total680,634 of 758,6566,305 of 6,618
lib/dwc249,486 of 280,4682,079 of 2,134
lib/twl53,994 of 60,676381 of 477
lib/nitrowifi37,654 of 52,382608 of 685
lib/dsprot8,076 of 18,13247 of 95
lib/msl, lib/spl, lib/irc, lib/dpwall matched165, 54, 26 and 31 of the same

That split is the interesting part. The game code is effectively finished, with 99.84% of its functions matching, while the SDK libraries are where the gap is. The DS Protect library is the worst of them at 47 of 95 functions, and its unmatched rows in the project are flashcart detection and checksum routines rather than anything the player sees. The twl library is next at 381 of 477, ahead of the wireless stack at 608 of 685.

Four libraries are already complete, on every measure including their unit counts: msl at 165 of 165 functions, spl at 54 of 54, irc at 26 of 26 and dpw at 31 of 31. Those are the ones a native port can lean on hardest.

The one-byte problems that hold up a whole file

The project keeps a dedicated document listing every function that is written in C but does not compile to the original, and the entries read like a debugging diary. The first one, in the card ROM wrapper, is a single byte.

Per that document, the original stores the secure ROM reader through cardi_rom_base at offset 4, while the reconstruction stores it directly through the shared sRomReader symbol. The remaining control flow and register allocation match. The document records what was tried: file IPA, arena-alignment order, exposing the ROM reader as its own shared global, and defining both symbols in the file under file-scope IPA with three spellings of the section directive and the globals in either order, which still leaves one byte different because the globals each get a section. The original's literal turns out to be the base of the card ROM's pooled bss, from which the ROM reader function also reaches the same three symbols.

That is the shape of the remaining work in this project. It is not missing understanding of the game; it is one byte here, a different register allocation there, and a different ordering of a section directive. The project's own tooling shows how to chase it: a compiler probe script compiles a candidate file with each selected compiler and reports which one reproduces the retail bytes, and it can show the instruction difference for a single function.

What the recovered code says about a PC port

The project publishes porting notes that collect the target assumptions and open questions found in the recovered code, and they are unusually blunt about what a native port still needs to solve. They cover the wireless stack's SSL, the socket layer and the connection management layer.

On the SSL context, the notes say it mixes pointers with fields recovered at original 32-bit ARM offsets. On the connection manager's work, global and access point list structures, the recovered sizes and offsets do not establish a native 64-bit layout. On the socket layer, the public socket handles are integer values carrying pointers, and their representation and conversion to host socket handles need a separate audit before a native port. On the connection manager's notices, the structure forwards mixed pointer and integer payloads through 32-bit words, and the notes point out that a wider pointer representation alone would not extend the temporary notice's lifetime.

On synchronisation, one function masks interrupts around state and alarm transitions, and another recognises the address of the original interrupt handler as an ownership marker rather than an ordinary thread object, with the mapping to host synchronisation and timers described as unresolved. The notes are careful to say that the recovered calls do not identify a host wireless implementation, which is a useful restraint for a project whose goal is a port that would need one.

What the notes amount to is a list of assumptions a port would have to replace rather than inherit, and they are the kind of list that only a byte-perfect reconstruction can produce. This is the practical argument for the decompilation being worth the effort even for a project that has not started its port yet.

Building it and the rules for contributors

The build needs Linux, WSL or macOS, Python 3.11 or newer, ninja, and LLVM with clang and llvm-objcopy on the path. You place your own ROM dumps in the project directory under specific filenames, run a configure script and then ninja, and the first run downloads the remaining tools. Every available version is built and checked against its SHA-1, and the ROMs are not included or provided.

ROMInput fileSHA-1
Black 2orig/baserom_b2_us.ndse51e6dfb8678a3d19dcd2a10691b96a569ca0abb
White 2orig/baserom_w2_us.ndsb5d7490be7b415b8f1e672a53e978a9cc667e56a

The output is a rebuilt Black 2 or White 2 file. The README points to a separate build document for macOS setup, toolchain options and modified builds.

Contributions follow a claimed-range model. The README says to read the contribution guide for the workflow and rules, and to claim an overlay's issue before starting work. CI compiles every C file for both versions, and fork pull requests have to verify ROM matching locally before they can land. The documentation index covers building, decompiling, matching behaviour, code organization, the porting notes, source files and boundary evidence, the nonmatching functions, game data formats, scripts, the configuration and ROM structure, and the tool reference.

FAQ

Does this decompilation include the Pokémon Black 2 and White 2 ROMs?
No. The README states the ROMs are not included or provided. You place your own dumps in the project directory and the build checks them against the SHA-1 values given, and the output is a rebuilt file generated on your machine.

How do both games rebuild from one source tree?
Per the README, the USA and European ROMs rebuild byte for byte from the same source tree, and the progress report tracks each version separately, with the report naming b2_us and w2_us as the two it builds.

What is the biggest remaining obstacle?
The SDK libraries rather than the game. The game code is at 99.84% of functions matched, while the dsprot library sits at 47 of 95 and the wireless stack at 608 of 685. Nintendo's DSi secure-protocol library is the least documented of the set.

How is a native PC port meant to work?
The README names a native PC port as the long-term goal, and the porting notes collect the assumptions that have to be replaced. They point out that the recovered layouts are 32-bit ARM and do not establish a native 64-bit layout, that socket handles are integers carrying pointers and need a separate audit, and that the synchronisation primitives do not map cleanly onto host timers.

Is the decompilation using AI coding tools?
The repository carries directories for AI tooling, with 40 entries under .claude, 21 under .codex and 19 under .agents, and the commit history records merges from branches named for those lanes, such as codex/complete-os-thread and codex/ov036-talk-frame-matches. The verifiable point is that every change still has to pass the same CI check that compiles each C file for both versions and verifies the ROM matching.

Which regions of the two games are being targeted?
The USA and European releases, described in the README as the NDSi Enhanced ROMs, with the SHA-1 values given for the Black 2 and White 2 input files. The report tracks each version separately, and the CI builds and checks both.

Bottom Line

Black 2 and White 2 are effectively reconstructed as source, with 43,046 of 43,418 functions matching and 97.45% of the code by byte, and both regional ROMs rebuild from the same C. The number that describes the work left is the file count, 1,672 of 1,792, because a single mismatched byte in a function keeps an entire file out of the complete column. The obstacle is not the game but the SDK under it, where three libraries are done and the DSi secure-protocol library is barely half matched. The payoff for pushing on is the porting notes, which spell out the 32-bit ARM assumptions, the pointer-carrying socket handles and the unresolved synchronisation primitives that any native PC port would have to replace. The project also shows what matching decompilation actually costs: a single byte in how the original stores the secure ROM reader through an offset, chased across three section-directive spellings before the answer turned out to be a pooled bss literal.

Sources

  • fuddlesworth/pokebw2 repository README, docs/nonmatching-functions.md and docs/porting-notes.md, accessed October 21, 2026
  • Repository metadata, language breakdown, contributor list and commit history via the GitHub API, accessed October 21, 2026
  • ByteMatch report for fuddlesworth/pokebw2, generated by CI and accessed October 21, 2026

Related: PlatinumSharp Is Not an Emulator: Pokémon Platinum's Own Code Runs as C# | ssrecomp: Pokémon SoulSilver in Native C++, and Claude Wrote Nearly All of It | 24,515 Functions Later: The 3.3.5a Client Is 90 Percent Decompiled

Share