1 wrong byte of ASM that slipped through due to a off-by-one error in
an experimental mzdiff branch. Having MOV rather than LES here was
ultimately a harmless mistake though. In context, it's even covered by
the "identical instruction encoding" exception 🙂
A future sprite converter (documented in #8) could then convert these
to C or ASM arrays.
(Except for the piano sprites for TH05's Music Room, which are stored
and used in such a compressed way that it defeats the purpose of
storing them as bitmaps.
Which works in both Borland C++, Open Watcom, and Visual C++.
Not that we're about to port any of the games to these compilers, just
something I noticed while evaluating 32-bit compilers for ReC98's own
32-bit pipeline tools. Modders might want to look into that though,
since 100% position independence also makes it easier to change
compilers.
And that's exactly where TH01's sprite flickering comes from. You
aren't supposed to unblit all entities of one type immediately before
rendering them, since sprites of different entity types are free to
overlap each other. And you might have already blitted one of those
before!
Not to mention that ZUN *still* uses the sloppy unblitting method here.
🙄
Part of P0098, funded by Yanga.
Why the sloppyness of unblitting a whole 16×16 rectangle *if you have
a dedicated function to precisely unblit a .PTN sprite using its alpha
mask*???
Oh well, it's not the regular function called in the main loop, so who
cares…?
Part of P0098, funded by Yanga.
"Physics". Not only did ZUN restrict the X velocity to the 5 discrete
states of -8, -4, 0, 4, and 8 (because hey, unaligned blitting is slow
anyway?), but gravity is also only applied every 5 frames.
We're still missing quite a bit of usage code, but these are the core
functions. One of which turned out to be undecompilable, due to… a
rigorously defined instruction order when performing arithmetic between
`double`s and `float`s?! Still, spelling out all this stuff in ASM
seems much better than somehow splitting the data segment, just so that
we can immediately use literals there.
Part of P0097, funded by Ember2528.
… Wow, the orb is *actually* only ever displayed at byte-aligned X
coordinates, divisible by 8. It's only thanks to the constant spinning
that its movement appears at least *somewhat* smooth.
(And yeah, this is purely a rendering issue; internally, its position
*is* tracked at pixel precision.)
Part of P0097, funded by Ember2528.
So even TH01 wasn't 100% C++ after all. Turns out that this function
was the only instance in all of REIIDEN.EXE where ReC98 previously had
different encodings for identical x86 instructions.
Part of P0096, funded by Ember2528.
Surprise, it's the (terribly suboptimal) setgsta() example function
from the PC-9801 Programmers' Bible! 100% identical, so ZUN must have
read that book as well.
Used for screen shaking effects, as well as the scrolling backgrounds
at the start of the Final Boss stages.
Completes P0095, funded by Yanga.
It's actually not that easy to find memory references in REIIDEN.EXE
that can both be quickly be RE'd at the end of a push, and which also
aren't more sensibly handled by immediate decompilation. So, have more
false positive busywork…
Part of P0095, funded by Yanga.
No idea why ZUN split the 6 slots into 4 with a regular alpha plane,
and 2 with a negated alpha plane. The latter are only used for Sariel's
animations.
Part of P0095, funded by Yanga.
100% PI for both! Now, where are all the modders who wanted to replace
MDRV2 with PMD… because you can now prototype that, without worring
about x86 instruction lengths, at least inside the main menu.
Part of P0095, funded by Yanga.
> "Sure, this is a PI push, but this second-to-last set of PI false
positives so small, let's immediately decompile it ^.^"
> Time taken: 22 minutes
Nice when this works out!
Part of P0095, funded by Yanga.
Too bad we still have to bother with renaming the function via #define
in the few cases where an executable contains two or more copies of
this function. We can't just make it `static`, since TH02 MAINE.EXE
actually does far-call it from a different segment (and thus,
translation unit).
Part of P0094, funded by Yanga.
The REIIDEN.EXE version (which is only shown when game-overing) has a
completely invisible timeout that force-enters a high score name after
1000... *keyboard inputs*? Not frames? Why. Like, how do even you
realistically to such a number.
(Best guess: It's a hidden easter egg to amuse players who place
drinking glasses on cursor keys. Or beer bottles.)
And hey, that initialization of the name variable with ASCII spaces.
The only actually meaningful byte-wise access… except that it's not,
because ZUN could have just used the Shift-JIS ideographic space for
the exact same effect.
Completes P0093, funded by Ember2528.
Now with POSIX file I/O in both executables. And the confirmation that
the name array is indeed exclusively accessed per-byte.
Part of P0093, funded by Ember2528.
It's exactly as terrible as you would have expected after hearing
"alphabet cursor actually stored as on-screen position". Nothing gained
in reducing redundancy any further here, any meaningful change would
pretty much have to rewrite the entire thing.
Part of P0093, funded by Ember2528.
In which inline functions are apparently the only way to trick Turbo
C++ into not actually optimizing away the useless `AND AL, 0FFh`.
But come on. Just accessing the high score name characters in chunks of
16-bit Shift-JIS codepoints (which is what they are!) instead of
breaking them down to bytes *everywhere* would have been both more
readable (no swapping of Shift-JIS literals, no bitshifts), more
efficient (this isn't running on an 8-bit CPU after all), and less of a
waste of my time…
Completes P0092, funded by Yanga.