How was this game even *built*, originally, if it uses *both* a common
shared set of library functions *and* obviously copy-pasted and
separately compiled versions of some of these functions?
Part of P0132, funded by [Anonymous].
Functions with 12 parameters are hard to describe, y'know. Looking
forward to decompiling these giant expressions for the actual
boss↔orb collision parameter passed to this function…
Oh well, at least we're now totally ready for some boss code next
year. 😌
Completes P0131, funded by Yanga.
Yes, no "generator", just a single number. Used to add some very
minimal randomness to certain things, by taking it modulo a small
number.
Part of P0131, funded by Yanga.
And once again, SinGyoku is the only boss to get its own variable.
Maybe this means that it was the first boss to be implemented after
all?
Part of P0131, funded by Yanga.
And we're right back to things not being nice. Because yeah, why
shouldn't these three distinct rendering functions be part of a single
function, selected by magic numbers?
Or why shouldn't the 16×16 wrapper around a 32×32 set of graphics
functions be used to handle backgrounds for 16×8 sprites, resulting in
needlessly complex parameter calculations that lead to sloppy code?
Part of P0131, funded by Yanga.
Nice GRCG use! The 8 dots of its tile register, which are commonly just
set to the same color value, can of course hold an arbitrary bit
pattern for every bitplane. This allows you to get different colors for
every pixel, with still just a single VRAM write of the alpha mask to
one bitplane.
And I thought TH01 only suffered the drawbacks of PC-98 hardware, and
made so little use of its actual features that it's perhaps not even
fair to call it "a PC-98 game"…
Completes P0130, funded by Yanga.
Made truly generic by its use in both the upcoming boss collision
handling, as well as some SinGyoku pellet spawning function.
Part of P0130, funded by Yanga.
And with that, TH01 is pushed over the 50% completion mark! 🎉
This time, it's only YuugenMagan who gets no own copy. Giant RE% gains
from all these calls, but let's hope I don't regret already decompiling
this one for all bosses. It's not quite at the beginning of SinGyoku's,
Mima's, and Elis' code segment, after all…
Part of P0130, funded by Yanga.
The placement at the beginning of Kikuri's code segment makes you think
this is only used for the barely noticeable white-in effect during
Kikuri's entrance animation. It's also used to periodically reset boss
sprite colors during the flashing effect after getting hit by the Orb,
though.
Part of P0130, funded by Yanga.
Lol @ Konngara decompilation being blocked by this giant card-flipping
function that took 2 pushes to understand, only for it to start with
if((stage % 5) == 4) {
return;
}
…
Completes P0129, funded by Yanga.
This is where the code generation actually confirms the SoA layout of
the global obstacle structure, rather than it being distinct pointers.
Part of P0129, funded by Yanga.
Continuing the good error handling from the .PTN functions they're
based on… if only its sole caller actually cared.
Also: A sort-of limit of 102 objects per stage, just because someone
didn't use huge pointers where they would have been necessary…
:tannedcirno:
Part of P0128, funded by Yanga.
Case in point: This structure member, which is located after all the
obstacle members. Sure, it'll look weird to see this one initialized by
a class method, but it'll be much weirder to somehow group both cards
and obstacles into one class.
Part of P0128, funded by Yanga.
And another one for all the obstacle types we saw earlier. The original
game probably combined all SoA pointers for both cards and obstacles
into a single "stage object". But that'll get way too unwieldy in the
functions later, given that those aren't even methods, and simply
reference one global variable. `stageobjs.obstacles.member`… yeah, no.
Part of P0128, funded by Yanga.
Sure, a SoA layout might actually be genius and what you should be
doing most of the time on modern systems, but you still wouldn't
allocate every member separately.
Part of P0128, funded by Yanga.
An enum to distinguish between bumpers, turrets, portals, bumper bars…
and cards that have to be flipped more than once?!
Part of P0128, funded by Yanga.
Finishing this push with another highly questionable one… Let's hope
that the port developers will certainly appreciate that they just have
to remove the weirdness here, and not mess with defining entirely new
functions in C land.
Completes P0127, funded by [Anonymous].
Undecompilable again. The loading functions have these *_noalpha()
variants that simply set a global variable and fall through to the
regular functions, while cdg_free() has its first `PUSH DI` instruction
after the first expression we'd be decompiling. cdg_free_all() *could*
be decompiled… but would also require _FLAGS trickery, and it's simply
not worth starting a translation unit for one such small function.
Part of P0127, funded by [Anonymous].
Nooooo, gotta throw away that decompilation for the stupidest of
reasons :( Turns out that a function may also be "undecompilable" if
the original code layout places it at a word-aligned address, but the
last byte of the previous function in just one of the original binaries
(TH03's MAIN.EXE, in this case) also lies at a word-aligned address.
There's simply no way to enforce per-function word alignment in Turbo
C++ alone. You *could* fake it with `#pragma codestring`, but of course
that won't work for functions that are part of the SHARED segment, and
where the alignment previously would have been correct. Conditionally
emitting that codestring would work, but then we'd also have to compile
that translation unit at least twice.
Now, I could have created a dummy .ASM file that just contains a single
zero-length but word-aligned SHARED segment, which could be placed
anywhere on the link command line where word alignment is needed… but
the decompilation of this function was a mess anyway, and probably
helped nobody.
Part of P0127, funded by [Anonymous].
And since inlining even removes longer if-else chains if they branch
depending on a literal constant, we can use a regular parameter to
select either MOV or OR in our _FS and _GS poke() template functions,
without needing to duplicate them!
Part of P0127, funded by [Anonymous].
Containing not one, but two decompilation innovations, one of which
works around a compiler bug using C++ template functions…
Completes P0126, funded by [Anonymous] and Blue Bolt.
Another function consisting almost entirely of inline ASM. Still worth
it though, if only to save us from duplicating any declarations in ASM
land.
Part of P0126, funded by [Anonymous] and Blue Bolt.
Actually fairly average, as far as unreasonable decompilations are
concerned. No `goto`, at least! Another place that would benefit from
EGC raster op documentation, though.
Also, got one more padding byte in TH05's MAINE.EXE correct. 🙂
Part of P0126, funded by [Anonymous] and Blue Bolt.
With a type-safe wrapper template that removes the need for ID length
and structure-size-in-paragraphs macros. *And* <dos.h>!
Part of P0126, funded by [Anonymous] and Blue Bolt.
Rather than preferring either the Microsoft/Watcom `(in|out)pw?` style,
or the Borland `(in|out)portb?` style, master.lib had to introduce its
own `(OUT|IN)P[BW]` naming scheme… Insert obligatory xkcd standards
comic.
Part of P0126, funded by [Anonymous] and Blue Bolt.
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)