⮜ Blog

📝 Posted:
🚚 Summary of:
P0092, P0093, P0094
⌨ Commits:
29c5a73...4403308, 4403308...0e73029, 0e73029...57a8487
💰 Funded by:
Yanga, Ember2528
🏷 Tags:
rec98 th01 file-format menu score jank waste

Three pushes to decompile the TH01 high score menu… because it's completely terrible, and needlessly complicated in pretty much every aspect:

  • Another, final set of differences between the REIIDEN.EXE and FUUIN.EXE versions of the code. Which are so insignificant that it must mean that ZUN kept this code in two separate, manually and imperfectly synced files. The REIIDEN.EXE version, only shown when game-overing, automatically jumps to the enter/終 button after the 8th character was entered, and also has a completely invisible timeout that force-enters a high score name after 1000… key presses? Not frames? Why. Like, how do you even realistically such a number. (Best guess: It's a hidden easter egg to amuse players who place drinking glasses on cursor keys. Or beer bottles.)
    That's all the differences that are maybe visible if you squint hard enough. On top of that though, we got a bunch of further, minor code organization differences that serve no purpose other than to waste decompilation time, and certainly did their part in stretching this out to 3 pushes instead of 2.
  • Entered names are restricted to a set of 16-bit, full-width Shift-JIS codepoints, yet are still accessed as 8-bit byte arrays everywhere. This bloats both the C++ and generated ASM code with needless byte splits, swaps, and bit shifts. Same for the route kanji. You have this 16-, heck, even 32-bit CPU, why not use it?! (Fun fact: FUUIN.EXE is explicitly compiled for a 80186, for the most part – unlike REIIDEN.EXE, which does use Turbo C++'s 80386 mode.)
  • The sensible way of storing the current position of the alphabet cursor would simply be two variables, indicating the logical row and column inside the character map. When rendering, you'd then transform these into screen space. This can keep the on-screen position constants in a single place of code.
    TH01 does the opposite: The selected character is stored directly in terms of its on-screen position, which is then mapped back to a character index for every processed input and the subsequent screen update. There's no notion of a logical row or column anywhere, and consequently, the position constants are vomited all over the code.
  • Which might not be as bad if the character map had a uniform grid structure, with no gaps. But the one in TH01 looks like this: And with no sense of abstraction anywhere, both input handling and rendering end up with a separate if branch for at least 4 of the 6 rows.

In the end, I just gave up with my usual redundancy reduction efforts for this one. Anyone wanting to change TH01's high score name entering code would be better off just rewriting the entire thing properly.

And that's all of the shared code in TH01! Both OP.EXE and FUUIN.EXE are now only missing the actual main menu and ending code, respectively. Next up, though: The long awaited TH01 PI push. Which will not only deliver 100% PI for OP.EXE and FUUIN.EXE, but also probably quite some gains in REIIDEN.EXE. With now over 30% of the game decompiled, it's about time we get to look at some gameplay code!