The final secret of this executable: With a loaded MDRV2, enter
fuuin t1
on the DOS prompt to view the 魔界/Makai Good Ending, or
fuuin t
for the 地獄/Jigoku Good Ending.
(Bad Endings can only be accessed by pre-allocating the TH01 resident
structure and setting one of the `continues_per_scene` array members to
a nonzero value.)
Part of P0213, funded by Ember2528 and GhostRiderCog.
The .BOS freeing funtions, others that are close to them in the typical
#include order, and the ending picture functions.
Part of P0198, funded by Lmocinemod and Ember2528.
You can definitely make an argument that these if/else if branches are
easier to read than their implied formula, especially with all
variables being signed here.
Completes P0197, funded by Yanga and Ember2528.
Turns out that the "new idea" for handling these data slices won't work
out in reality. At least the amount of necessary manual `extern`
declarations isn't soul-crushingly large, so this way isn't bad either,
after all!
Part of P0197, funded by Yanga and Ember2528.
These could have been nicely separated into per-device translations
(one for GDC, one for EGC, one for regular VRAM accesses)… if their
original order followed that classification.
OK, so let's make it one full `graph_ex.cpp` translation unit… nope,
FUUIN.EXE needs graph_2xscale_byterect_1_to_0_slow() with different
compilation flags. 🤦
Whatever, fuck it, let's go for individual translation units, with
individual headers, so that we at least get predictably organized
source code out of this.
Part of P0196, funded by Yanga.
TH01 doesn't use "back" and "front" pages in the way the later games
do, after all. Indicating that the source page of this parameter-less
function is the one that was previously set via graph_accesspage_func()
is going to make pretty much all its call sites way clearer, and stops
implying any further state.
Part of P0167, funded by Ember2528.
Boom! Clever segment renaming allows us to link the same .OBJ into 12
binaries.
(Well, 10 for now, due to alignment issues in TH04's OP.EXE and
MAINE.EXE.)
Part of P0138, funded by [Anonymous] and Blue Bolt.
Whoops, turns out that the build has been broken on TASM32 version 5.3
(the one in the DevKit) ever since 7897bf1. In contrast to version 5.0
(which I use for my development), 5.3 actually defines 32-bit segments
if you specify a .386 CPU before using .MODEL.
That might have been the reason for the .286 workaround all along?
Turns out there's the USE16 modifier, which makes this much more
explicit than switching CPUs.
Since I'd still prefer to not use include guards *and* to have that
standalone .GRZ viewer, these would have actually caused somewhat of
an issue with the upcoming final stretch of the master.hpp transition.
Part of P0133, funded by [Anonymous].
Might seem inconsistent, given that the function initializes pointers
that are declared in planar.h… but it's always called from other game
initialization functions that don't require pc98.h or planar.h.
Part of P0133, funded by [Anonymous].
Which gets rid of 13 redundant translation units. Definitely a good
start, before I figure out how to best handle the more complicated
cases.
(Maintenance mode commit)
By now, it's become obvious where it has to be. Apparently, the order
of translation units inside ZUN's modified master.lib did somehow
change with TH02?
(Maintenance mode commit)
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.
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.
Lol, the internal alphabet cursor position is actually stored as the
on-screen top-left position of the selected character, which this
function then maps back to a character index. At least that gets rid of
quite a bunch of PI false positives now.
Part of P0092, funded by Yanga.
With ternary operator expressions straight out of Jigoku.
(TL note: Jigoku means hell.)
Nice to see that Turbo C++ apparently has no nesting limit on function
call inlining in general, though!
Part of P0092, funded by Yanga.