Commit Graph

330 Commits

Author SHA1 Message Date
nmlgc aac0108c63 [Separate translation units] [th02] Sound effect playback (undecompilable)
Reason: Same as hflip_lut_generate() – wants to be word-aligned, and
the function before it has an odd length :(

Part of P0132, funded by [Anonymous].
2021-01-05 19:29:10 +01:00
nmlgc 52fdb33d1d [Separate translation units] [th02] frame_delay()
Part of P0132, funded by [Anonymous].
2021-01-05 19:23:31 +01:00
nmlgc b4d500b2de [Decompilation] [th01] Bosses: Generic no-op callback
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.
2020-12-18 01:38:46 +01:00
nmlgc 64f872e5c5 [Decompilation] [th01] Difficulty-based value selection
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.
2020-12-18 00:59:41 +01:00
nmlgc aaea18aa8f [Decompilation] [th01] Retrieving and setting the default boss palette
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.
2020-12-18 00:55:21 +01:00
nmlgc 73f62a5ba2 [Decompilation] [th01] Stage objects: Background allocation and blitting
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.
2020-12-01 00:34:18 +01:00
nmlgc dc65b59dcc [Decompilation] [th05] frame_delay()
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].
2020-11-16 20:01:36 +01:00
nmlgc 7897bf166f [Separate translation units] [th04/th05] .CDG: Loading and freeing
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].
2020-11-16 20:01:35 +01:00
nmlgc 00f177e196 [Maintenance] [th03] Remove the input mode and delay function disassembly
MAIN.EXE now also caught back up with the decompilation.

Part of P0127, funded by [Anonymous].
2020-11-16 20:01:35 +01:00
nmlgc 8835d59eda [Build] [th03] Keep hflip_lut_generate() in ASM due to alignment issues
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].
2020-11-16 20:01:34 +01:00
nmlgc 5965930bd0 [Decompilation] [th03] .MRS: Persistent horizontal flipping
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.
2020-11-16 20:01:31 +01:00
nmlgc 08a09aabb2 [Reverse-engineering] [th03] .MRS: Bitplane pointers
Part of P0126, funded by [Anonymous] and Blue Bolt.
2020-11-16 20:01:26 +01:00
nmlgc 6c22af7e83 [Build] [th01] Only compile shared single-file translation units a single time
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)
2020-11-04 14:47:52 +01:00
nmlgc f6a3246071 [Reverse-engineering] Enabling and disabling the EGC
Which involves temporarily enabling a GDC mode change bit.

Completes P0125, funded by [Anonymous].
2020-11-02 23:42:04 +01:00
nmlgc 056b1c77c1 [Decompilation] [th04] Player character and shot type selection menu
Significantly more complex than the single menu in TH05!

Completes P0124, funded by [Anonymous] and Blue Bolt.
2020-11-02 22:41:51 +01:00
nmlgc 10666131ec [Decompilation] [th01] Reimu's animations: .BOS load function
Part of P0123, funded by Yanga.
2020-10-13 21:15:19 +02:00
nmlgc 61559859e3 [Decompilation] [th01] Shootout lasers: Spawn function
Part of P0122, funded by Yanga.
2020-10-06 14:16:40 +02:00
nmlgc eb89330f64 [Build] [th01] Move the -Z and -3 options onto the command line
Deactivating them is the exception, not the norm.

Part of P0122, funded by Yanga.
2020-10-06 13:27:33 +02:00
nmlgc 60ee6e94fc [Decompilation] [th01] 32×32 square inversion on a single bitplane
The main effect behind the discoloration seen in the bomb animation,
as well as the exploding squares at the end of Kikuri's and Sariel's
entrance animation.

Single-function segments are lovely, by the way!

Part of P0121, funded by Yanga.
2020-09-28 13:05:07 +02:00
nmlgc 453dd3ca7e [Decompilation] [th05] Starting the game
And that's the intended way to play back the hidden DEMO5.REC. Unlock
the Extra Stage with all 4 characters, then hold the left and right
arrow keys in the main menu while waiting the usual demo replay.

For a recording of that replay, see

	https://www.youtube.com/watch?v=iP2ywlW2u4U

Completes P0119, funded by [Anonymous] and -Tom-.
2020-09-21 15:00:44 +02:00
nmlgc 3c27fbc3bd [Decompilation] [th05] Player character selection menu
The TH04 one might have the same function structure, but the only thing
that's actually identical in both games is the picture darkening loop.

Part of P0119, funded by [Anonymous] and -Tom-.
2020-09-21 15:00:05 +02:00
nmlgc 791d6d6085 [Build] [th04/th05] 16-bit: Rebuild ZUN.COM by default
Shoutout to Egor, who already implemented the necessary wrappers in
2018, which means that I don't have to spend a push on that. 🙂

Part of P0117, funded by [Anonymous].
2020-09-16 22:30:56 +02:00
nmlgc c5852610f6 [th05/gjinit] Initial state
And with that, we finally dumped every single PC-98 Touhou binary!
Since it'd be overkill to merge bmp2arr into the re-baseline branch
though, we also have to start out with the raw image bytes here.

Part of P0117, funded by [Anonymous].
2020-09-16 22:29:55 +02:00
nmlgc 9695c8d277 [th05/memchk] Initial state
No libc anymore, yet still no memory freeing…?

Part of P0117, funded by [Anonymous].
2020-09-16 22:29:35 +02:00
nmlgc 90b7ace180 [th04/memchk] Initial state
Needlessly linked with TCC rather than TLINK, adding almost 4 KB of
completely unnecessary libc startup code.

Or maybe not, since ZUN doesn't free the allocated memory himself, but
relies on libc to do that?

Part of P0117, funded by [Anonymous].
2020-09-16 22:20:34 +02:00
nmlgc 197202d8ee [th05/zuninit] Initial state
On the surface, Version1.02 of the `INTvector set program` seems to
be largely the same as Version1.01, just with fancier instructions,
some redundancy removed, and some slightly different wording in the
playful messages… or is there more to it? Stay tuned!

Part of P0117, funded by [Anonymous].
2020-09-16 22:17:18 +02:00
nmlgc f54cd0fe95 [th04/zuninit] Initial state
Yup, it's finally the right time to properly rebuild ZUN.COM. While
all of these small binaries would still need some RE attention, putting
in the few minutes to make them position-independent right now is
definitely worth it. Adding them to the PI calculation on the website
would take much longer 😅

Part of P0117, funded by [Anonymous].
2020-09-16 22:16:49 +02:00
nmlgc 03048c318d [Decompilation] [th04/th05] Cutscenes: Text blending
Completes P0116, funded by [Anonymous] and Lmocinemod.
2020-09-12 12:09:31 +02:00
nmlgc 4e6bedf20d [Decompilation] [th05] Staff roll: Space window coordinates
Part of P0115, funded by Lmocinemod and Blue Bolt.
2020-09-12 11:21:44 +02:00
nmlgc 967bb8b633 [Decompilation] [th03] Input mode and delay functions
Nice to see that Borland C++ optimizes bit-tests to cover just the high
or low byte of a word if possible, and that these don't have to be
two-byte structures after all.

Completes P0114, funded by Lmocinemod.
2020-09-07 21:18:40 +02:00
nmlgc aee8a13958 [Separate translation units] [th02] game_exit_to_dos(), zun_error(), graph_putsa_fx()
Oh, wait. Due to ridiculously unlucky alignment, we can't actually
approach TH03's OP.EXE from the top of code segment 2… without
covering way too many functions at once, that is.
At least TH02 works out with "just" three functions at once. *If* we
add seg2 back to OP.EXE, where we previously needed to delete it… 😵

Part of P0114, funded by Lmocinemod.
2020-09-07 21:18:40 +02:00
nmlgc 716a925636 [Decompilation] [th03] YUME.CFG loading and saving
Oh, I just needed something in TH03's OP.EXE before code segment 2.

Part of P0114, funded by Lmocinemod.
2020-09-07 21:18:40 +02:00
nmlgc 5a6ac29f50 [Separate translation units] [th03] CDG: Non-alpha display (undecompilable)
Reason: Self-modifying. -.-

Part of P0114, funded by Lmocinemod.
2020-09-07 21:18:39 +02:00
nmlgc ecc1372842 [Decompilation] [th03] Lookup table for horizontally flipping planar pixels
In which we exchange variable names for the ability to decompile more
than just 3 instructions here.
… yeah, "decompilation" is still a stretch.

Part of P0114, funded by Lmocinemod.
2020-09-07 21:18:38 +02:00
nmlgc 6204fdd450 [Separate translation units] [th02/th03/th04] frame_delay_2()
Completes P0113, funded by Lmocinemod.
2020-09-07 21:18:38 +02:00
nmlgc f0511221b7 [Maintenance] [th02/th03/th04/th05] Establish a common name for code segment 2
About time I finally developed this piece of tech. Towards TH05, this
segment got more and more undecompilable ASM functions mixed inbetween
C ones. Which means that pretty much all of the current ASM land
`#include`s in that segment will have to become translation units. And
we *really* don't want an additional layer of numbered, per-binary
translation units that just `#include` maybe one or two functions.

Also yeah, no _TEXT suffix, to drive home the point that this is a
"library" segment, and not really "owned" by any one file.

Part of P0113, funded by Lmocinemod.
2020-09-07 21:18:38 +02:00
nmlgc 1f1847de48 [Build] Assemble all .ASM files in the 32-bit build part
Yeah, why *were* we assembling them in the 16-bit part before?!
Possible reasons:
• In a time before Tup, it made no actual difference whether these
  little files were assembled in the 32-bit or 16-bit part. Now it sort
  of does, since we've temporarily given up on minimal rebuilds in the
  16-bit part.
• Emphasizing the temporary nature of the 32-bit part by deliberately
  moving everything to the 16-bit part as early as possible?
• It all started with the ZUN.COM ASM code, which doesn't include any
  other files, and can therefore be perfectly tracked by a Makefile.
  Which *was* superior than the exclusive dumb batch file we had in the
  past. And then I've simply cargo-culted all new .ASM translation
  units into the 16-bit part well.

Oh, and another positive side effect of temporarily not using 16-bit
TASM: The build process now also runs on Windows 95.

Part of P0113, funded by Lmocinemod.
2020-09-07 17:25:56 +02:00
nmlgc 51b775bd41 [Build] 16-bit: Don't explictly specify the .OBJ extension in TASM rules
If the output file already exists, TASM interprets a filename with
extension as a directory, and then concatenates the implicitly
generated output file name to that path… yup.

Part of P0001, funded by GhostPhanom.
2020-09-03 19:04:16 +02:00
nmlgc e5f28204a5 [Build] 32-bit: Actually create `bin/Pipeline` during the 32-bit part -.-
Lol, this led the bmp2arr compilation to fail on a clean checkout.
Lesson learned: Always test on a clean clone, not in your separate test
repo that you just `git pull` up to the final commit in a push, but
with all the build artifacts still in there…

Also, yes, `mkdir` only supports backslashes.

Part of P0001, funded by GhostPhanom.
2020-09-03 19:04:15 +02:00
nmlgc 4ef4c9eeb0 [Maintenance] [th05] Split segment #1 before Reimu's bomb star animation
Nope, not going to figure this one out on ASM level.

Completes P0111, funded by [Anonymous] and Blue Bolt.
2020-08-28 15:08:21 +02:00
nmlgc 1c3e9097cc [Decompilation] [th05] EX-Alice: Fire wave management
Final unknown entity in TH05 that can collide with the player!
Get position independence hype!
(Also, no structure member comments necessary, thanks to the new
types!)

Part of P0111, funded by [Anonymous] and Blue Bolt.
2020-08-28 14:59:27 +02:00
nmlgc 7c4d31c02b [Decompilation] [th05] Line sets: Rendering
With all the memmove()-style shifting of individual lines, there's
probably a point to reserving 20 lines per set, but only ever rendering
4 of them. We'll see once I get to decompile all of these functions.

Part of P0110, funded by [Anonymous] and Blue Bolt.
2020-08-19 20:10:08 +02:00
nmlgc 2c7d86bc5b [Decompilation] [th05] Mai & Yuki: Smooth random movement
Wow, so the boss_stuff_t structure did exist in the original code after
all. This is the only function in all of TH04 and TH05 that confirms
it – in dea40ad, it just seemed like something I made up, out of
convenience.

Completes P0109, funded by [Anonymous] and Blue Bolt.
2020-08-16 21:41:34 +02:00
nmlgc 4f7a3a28c3 [Reverse-engineering] [th05] Difficulty- and rank-based bullet pattern tuning
If C allowed labels of other functions as `goto` targets, this *might*
have been decompilable into something useful to modders. But like this,
there's no point in even trying.

Yeah, you *really* don't want to base your fangame mod on TH05.

Part of P0109, funded by [Anonymous] and Blue Bolt.
2020-08-16 21:40:33 +02:00
nmlgc 829946a29e [Decompilation] [th04] Difficulty- and rank-based bullet pattern tuning
Because it's at least *possible* to decompile TH04's version of these
functions.

Part of P0109, funded by [Anonymous] and Blue Bolt.
2020-08-16 21:40:32 +02:00
nmlgc 0644857fcb [Decompilation] [th05] Bosses: Smoothly flying towards a point
Found a uth05win inaccuracy! Once the Y coordinate gets close enough to
the target point, it actually speeds up twice as much as the X
coordinate would. This might make uth05win a couple of frames slower in
all boss fights from Stage 3 on.

And yeah, got too used to decompilation to go back to splitting off
RE'd functions in ASM land 😛

Part of P0109, funded by [Anonymous] and Blue Bolt.
2020-08-16 21:40:31 +02:00
nmlgc 5f5eb8aa4e [Decompilation] [th01] .BOS: Unused and broken plane pointer reset function
Because that's not how you free dynamically allocated memory?

Part of P0106, funded by Yanga.
2020-08-12 17:49:34 +02:00
nmlgc 72538610ba [Decompilation] [th01] .GRC: Load function
Still no idea what those 7 unknown bytes in the .GRC and .BOS headers
could be.

Part of P0105, funded by Yanga.
2020-08-12 16:19:39 +02:00
nmlgc a75e0f8f53 [Maintenance] Compile all VRAM-accessing translation units as C++
Leading to slight complications in TH02's Music Room and shot type
selection menus. Thought about leaving those in C for a while, but I
still think it's worth it for the consistency we get with the VRAM
offset functions. Also, we'll have similar code for the main menus of
later games, and I'll surely won't be using C++ when starting out with
these.

Part of P0105, funded by Yanga.
2020-08-12 16:16:09 +02:00
nmlgc afd74fb8aa [Decompilation] [th01] 16-bit integer to string conversion
"Hey, let's have separate functions for uint16_t and int16_t… and then
just *not* support negative numbers in the latter" :zunpet:

Part of P0104, funded by Ember2528.
2020-07-27 17:22:16 +02:00
nmlgc edb70162e0 [Decompilation] [th01] Pellets: Manager class constructor
Note how Turbo C++ auto-generates that call to `operator new`, which
you don't see in the decompilation anymore. So yeah, as soon as you add
a constructor, Turbo C++ enforces heap allocation for any instance of
that class, even function-local ones that would otherwise be
stack-allocated.

That's where the bad reputation of C++ comes from, I guess?

Part of P0102, funded by Yanga.
2020-07-12 16:23:12 +02:00
nmlgc e3a78bd19b [Maintenance] Fix vector creation function declarations and calls
Part of P0099, funded by Ember2528.
2020-07-12 15:22:50 +02:00
nmlgc 3e3129567c [Decompilation] [th01] Pellet delay cloud blitting and unblitting
And immediately, we discover another two hardcoded sprites, with, of
course, another set of functions for blitting and unblitting them…

Part of P0099, funded by Ember2528.
2020-07-12 15:15:05 +02:00
nmlgc 442a92d32b [Decompilation] [th01] Player shots: Spawn function
Continuing to learn new things about Turbo C++'s code generation!

Part of P0098, funded by Yanga.
2020-06-13 21:15:31 +02:00
nmlgc 51de73bcc9 [Decompilation] [th01] Orb physics
"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.
2020-06-13 21:15:27 +02:00
nmlgc 6a5fa3aee9 [Maintenance] [th01] Decide on `unput` for VRAM page 1 pixel restoration
"Unblitting" reads better in commit descriptions, though 🤔

Part of P0096, funded by Ember2528.
2020-06-13 21:13:48 +02:00
nmlgc 52b8414993 [Reverse-engineering] [th01] Reimu's X position
13 copies of the clamping branches… Quality.

Part of P0096, funded by Ember2528.
2020-06-13 21:13:47 +02:00
nmlgc dd89843fae [Decompilation] [th01] Pellet rendering
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.
2020-06-13 21:11:53 +02:00
nmlgc 8ddb77801d [Decompilation] [th01] GDC-powered graphics layer scrolling
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.
2020-05-31 17:46:46 +02:00
nmlgc 57a8487084 [Decompilation] [th01] FUUIN.EXE resident structure data retrieval
Completes P0094, funded by Yanga.
2020-05-25 15:22:53 +02:00
nmlgc 29c5a7365f [Build] Assemble piloadc as a separate translation unit
Restoring the originally released version here as well… except for
ZUN's custom format magic ID, of course.

Completes P0091, funded by Ember2528.
2020-05-12 15:06:13 +02:00
nmlgc 4f87ec8152 [Decompilation] [th01] Blitting full-width numbers onto VRAM
Part of P0091, funded by Ember2528.
2020-05-12 15:06:12 +02:00
nmlgc f2543c8336 [Decompilation] [th01] Keyboard input in REIIDEN.EXE
Yes, TH01's memory info screen will recurse into itself for every 3
frames the PgUp key is held, requiring one additional PgDown press per
recursion to actually get out of it.
You can, of course, also crash the system via a stack overflow this
way, if that's your thing.

Part of P0091, funded by Ember2528.
2020-05-12 15:06:11 +02:00
nmlgc 05a0e9b1c8 [Decompilation] [th01] Unused function to snap all dots with hardware color #4
That's… pretty specific. The only thing on the main menu with this
color is the "1996 ZUN" text at the bottom… probably part of an
effect that we never got to see. Every other idea would be baseless
speculation, given that the snapped buffer isn't used anywhere else.

Part of P0090, funded by Yanga.
2020-05-11 22:12:51 +02:00
nmlgc 90252cc59a [Decompilation] [th04/th05] Stage and BGM title popups
Completes P0089, funded by [Anonymous] and Blue Bolt.
2020-05-04 16:14:21 +02:00
nmlgc 70b4134af4 [Maintenance] [th04/th05] Assemble 16-bit .ASM files case-sensitively
Otherwise, TASM would simply convert all EXTRN declarations in those
files to uppercase. Then, the linker would expect them in uppercase,
forcing both the case-sensitive big 32-bit .ASM files *and* the entire
C land to declare them as uppercase as well.

For functions with __pascal convention which are always uppercased
anyway, this makes no difference. It does matter for regular __cdecl
variables, though, and the C declaration of [score_delta] in e6294c2
already showed that we'd then be forced to use macros if we wanted to
pretend that these names still had lowercase characters.

Doing this for every variable referenced in both C land and 16-bit ASM
land gets annoying quickly. So, no need to force this inconsistency if
we can get rid of it by slightly uglifying ASM land.

Part of P0089, funded by [Anonymous] and Blue Bolt.
2020-05-03 23:56:08 +02:00
nmlgc 02f0a0afcc [Build] Don't word-align everything by default
Again, 11 necessary workarounds, vs. forcing byte aligment in at least
18 places, and that number would have significantly grown in the
future.

Part of P0085, funded by -Tom-.
2020-04-03 17:35:57 +02:00
nmlgc ffad8cc897 [Build] Use the minimum possible size for enums by default
5 enums where code generation wants an `int`, vs. 11 cases where using
the minimum size is exactly the right default. So it's way more
idiomatic to force those 5 to 16 bits via a dummy element… except that
we can't give it a single, consistent name, because you can't redeclare
the same element in a different enum later.

Oh well, let's have this ugly naming convention instead, which makes it
totally clear that the force element not, in fact, a valid value of
that enum.

Part of P0085, funded by -Tom-.
2020-04-03 17:33:58 +02:00
nmlgc 580fa8301a [Decompilation] [th02] Random number ring buffer reads, instance #2
And that's how you decompile from the beginning of a segment.

Part of P0085, funded by -Tom-.
2020-04-03 17:32:49 +02:00
nmlgc c338305a61 [Decompilation] REYHI*.DAT loading and recreation
With master.lib file I/O in REIIDEN.EXE, and POSIX file I/O in
FUUIN.EXE… yup.

Part of P0084, funded by Yanga.
2020-03-22 10:16:08 +01:00
nmlgc 9e676ce3ef [Decompilation] [th01] .PTN snap functions
Which repurpose the .PTN image slots to store the background of
frequently updated VRAM sections, like all the numbers in the HUD.
Future games would simply use the text RAM and gaiji for numbers. Which
would have worked just fine for TH01 as well (especially since all the
functions we've seen so far are aligned to the 8-pixel byte grid), but
it looks as if ZUN simply wasn't aware of gaiji during the development
of TH01.

Part of P0083, funded by Yanga.
2020-03-18 20:33:58 +01:00
nmlgc f6cbff0bf9 [Decompilation] [th01] .GRP file loading and display
All the weird double returns in FUUIN.EXE just magically appear with
-O-! 😮

And yeah, it's a bowl of global state spaghetti once again. 🍝 Named
the functions in a way that would make sense to a user of the API, who
should be aware of typical side effects, like, y'know, a changed
hardware palette… That's how you end up with the supposed "main"
function getting a "_palette_show" suffix.

Completes P0082, funded by Ember2528.
2020-03-13 19:48:38 +01:00
nmlgc 8adbeb76b6 [Decompilation] [th01] .GRP palette fades
It's optimization barrier time again \o/

Part of P0082, funded by Ember2528.
2020-03-13 19:14:32 +01:00
nmlgc 00050d0e5e [Decompilation] [th01] VRAM text typing
Yeah, that 8×16 text RAM grid is so restricting.

Part of P0082, funded by Ember2528.
2020-03-13 19:09:12 +01:00
nmlgc 5ac9b301f1 [Asset pipeline] Add a .GRZ viewer
… have to hardcode that palette in any stand-alone viewers or
converters.

Completes P0081, funded by Ember2528.
2020-03-07 21:43:08 +01:00
nmlgc fd6a8bae81 [Decompilation] [th01] .GRZ file loading and display
Yet another run-length encoded graphics format, this one being
exclusively used to wastefully store Konngara's sword slash and kuji-in
kill "animation".

But for once, the terrible code generated by inline functions with
non-literal parameters perfectly matches what ZUN wrote here.

Part of P0081, funded by Ember2528.
2020-03-07 21:43:00 +01:00
nmlgc 0252da2a71 [Decompilation] [th01] Game init and exit functions
Yes, that's the code that forgot reactivating the text cursor before
returning to the DOS prompt!

Completes P0080, funded by Ember2528 and Splashman.
2020-03-03 13:12:00 +01:00
nmlgc ebd214bbb2 [Decompilation] [th05] RES_KSO.COM
And of course, TH05 ruins the consistency once again. Sure, the added
file error handling is nice, but we also have changes in the playful
messages (lol), and now need a third distinct optimization barrier
(🤦)… But as it turns out, inlined calls to empty functions work as
well. They also seem closer to what ZUN might have actually written
there, given that their function body could have been removed by the
preprocessor, similar to the logging functions in the Windows Touhou
games. (With the difference that the latter infamously *aren't*
inlined…)

Part of P0077, funded by Splashman and -Tom-.
2020-02-23 17:53:17 +01:00
nmlgc 4e53b97552 [Decompilation] [th04] RES_HUMA.COM
Part of P0077, funded by Splashman and -Tom-.
2020-02-23 17:53:17 +01:00
nmlgc 803c079a5d [Decompilation] [th03] RES_YUME.COM
Huh, C++ wants its `char`s to be unsigned in order to *not* sign-extend
them to 16 bits for comparison against ASCII literals?!
Anyway, that completes TH03's ZUN.COM, with bascially no new C code.

Part of P0077, funded by Splashman and -Tom-.
2020-02-23 17:53:16 +01:00
nmlgc 942373e640 [Maintenance] [th02] Split ZUN_RES.COM into three translation units
Necessary to make string literals from the first one end up at their
correct positions in the data segment even after the upcoming
deduplication…

Part of P0076, funded by [Anonymous] and -Tom-.
2020-02-23 17:12:26 +01:00
nmlgc 6363a37d7a [Maintenance] Move TH02's sound functions to a separate header
Oh hey, guarding declarations with complicated types via #ifdef limits
the header files we additionally have to #include!

Part of P0076, funded by [Anonymous] and -Tom-.
2020-02-23 16:51:45 +01:00
nmlgc b0c832bdee [Decompilation] [th01] Restorable line drawing
Yes, when clipping the start and end points to the screen area, ZUN
uses an integer division to calculate the line slopes, rather than a
floating-point one. Doesn't seem like it actually causes any incorrect
lines to be drawn, though; that case is only hit in the Mima boss
fight, which draws a few lines with a bottom coordinate of 400 rather
than 399. It *might* also restore the wrong pixels at parts of the
YuugenMagan fight, causing weird flickering, but seriously, that's an
issue everywhere you look in this game.

Part of P0069, funded by [Anonymous] and Yanga.
2020-01-14 22:12:08 +01:00
wintiger0222 4d13d7f7e9 [Decompilation] [th01] graph_printf_fx 2020-01-14 22:08:44 +01:00
nmlgc e55a48b700 [Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)

Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.

And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.

And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐

And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.

Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 20:23:27 +01:00
nmlgc 24fdd31192 [Reverse-engineering] [th04] Extra Stage selectability
Final structure in TH04's OP.EXE! 🎉 Position independence now
reached here as well.

Part of P0064, funded by Touhou Patch Center.
2019-12-29 21:15:43 +01:00
nmlgc f46fc914c1 [Maintenance] [th04/th05] Define a OP/MAIN/MAINE macro
Seems to be the best way to handle all those implementation differences
in the GENSOU.SCR functions.

Part of P0063, funded by -Tom-.
2019-12-28 12:27:47 +01:00
nmlgc 83f422c61a [Decompilation] [th05] Character-independent shot type functions
Part of P0062, funded by Touhou Patch Center.
2019-12-22 15:37:36 +01:00
nmlgc c060df171e [Decompilation] [th05] Reimu's shot control functions
That took less than 1½ hours, even with deduplication. Too easy.

Part of P0062, funded by Touhou Patch Center.
2019-12-22 15:35:58 +01:00
wintiger0222 02d1c04858 [Reverse-engineering] [th05] Character selection and unlock variables 2019-12-17 23:27:00 +01:00
nmlgc 5f4f5d87dc [Decompilation] [th03] Shot update and render functions
Meh, can't overload arithmetic operators that take a Subpixel without
generating a needless load and store, even with -Z. But heck, slightly
uglifying subpixel/subpixel arithmetic is exactly the right trade-off.

Completes P0061, funded by Touhou Patch Center.
2019-12-05 21:41:31 +01:00
nmlgc b6e0330ff6 [Decompilation] [th03] Sprite display calls
Yes, decompilation, of something that was so obviously originally
written in ASM. We're still left with two un-decompilable instructions
here, but I'm amazed at how nicely I was able to abstract away all of
the gory register details, leading to pretty clear, readable, and dare
I say *portable* code?! Turbo C++ was once again pretty helpful here:

• `static_cast<char>(_BX) = _AL` actually compiles into `MOV BL, AL`,
  as you would have intended,
• and no-op assignments like _DI = _DI are optimized away, allowing
  us to leave them in for clarity, so that we can have all parameter
  assignments for the SPRITE16 display call in a single place.

I love this compiler.

Part of P0060, funded by Touhou Patch Center.
2019-11-28 23:14:21 +01:00
wintiger0222 01de2900dd [Decompilation] [th01] frame_delay
Closes #7.
2019-11-18 21:29:43 +01:00
nmlgc e7e1cbcaaf [Decompilation] [th05] Marisa's shot control functions
Yeah… such fun pretending that the original code wasn't copy-pasted.
And yes, Reimu will have to wait until the next one.

Completes P0037, funded by zorg.
2019-10-14 23:54:37 +02:00
nmlgc 1276a0c94a [Decompilation] [th05] Mima's shot control functions
And if I don't manage to cover Reimu in this push, it's because ZUN
switched around the cases in half of the functions here… 😵  Here's
some macros instead, to make the code at least *look* as table-driven
and readable as it should have been in the first place.

Part of P0037, funded by zorg.
2019-10-14 23:54:37 +02:00
nmlgc da27eb4c31 [Decompilation] [th05] Yuuka's shot control functions
Ooh, shot position being determined by RNG at lower shot levels? That's
some RNG manipulation TAS potential right there! Maybe.

Part of P0037, funded by zorg.
2019-10-14 23:54:29 +02:00
nmlgc a533b5d3ea [Reverse-engineering] [th04/th05] Player sprite area invalidation
And once again, the TH05 version is un-decompilable. :/ It was pretty
close this time, though, as the entire block between PUSH DI and POP DI
kind of resembles a separate inlined function, in accordance with Turbo
C++'s  automatic backup of the DI register, as researched in 7f971a0.

Except that it contains a loop, and Turbo C++ refuses to inline any
function with `do`, `while`, `for`, or `goto`. If it didn't, it would
have totally worked.

Also, yes, C++ class methods are treated identically in this regard.

Oh well. Shot type control functions next, finally!

Completes P0035, funded by zorg.
2019-09-24 22:04:26 +02:00
nmlgc 6cdd2296bb [Reverse-engineering] [th04/th05] Input → player movement
Completes P0033, funded by zorg.
2019-09-21 14:01:51 +02:00
nmlgc f8213c5a32 [Reverse-engineering] [th04/th05] HUD bar display
So it's *_put(), inherited from master.lib, for everything just writing
to text RAM, and *_render() for everything more involved? But what
about master.lib's own graphics RAM functions like super_put()? Need to
fix that inconsistency some day.

Once again no decompilation, because…

Part of P0033, funded by zorg.
2019-09-21 14:01:50 +02:00
nmlgc e6294c2c1a [Reverse-engineering] [th02/th04/th05] Score update and display
The TH02 version is a piece of cake…

… but TH04 starts turning it into this un-decompilable piece of
unnecessarily micro-optimized ZUN code. Couldn't have chosen anything
better for the first separate ASM translation unit.

Aside from now having to convert names of exported *variables* to
uppercase for visibility in ASM translation units, the most notable
lesson in this was the one about avoiding fixup overflows. From the
Borland C++ Version 4.0 User's Guide:

	"In an assembly language program, a fixup overflow frequently
	 occurs if you have declared an external variable within a
	 segment definition, but this variable actually exists in a
	 different segment."

Can't be restated often enough.

Completes P0032, funded by zorg.
2019-09-21 14:01:47 +02:00
nmlgc dea40ad770 [Decompilation] [th05] Stage setup
"Yeah, let's do this real quick, how can this possibly be hard, it's
just MOVs and a few function calls"…

…except that these MOVs access quite a lot of data, which we now all
have to declare in the C world, hooray.
Once it came to midbosses and bosses, I just turned them into C structs
after all. Despite what I said in 260edd8… after all, the ASM world
doesn't care about the representation in the C world, so they don't
necessarily have to be the same.

Since these structs can't contain everything related to midbosses and
bosses (really, why did all those variables have to be spread out like
this, ZUN?), it also made for a nice occasion to continue the "stuff"
naming scheme, describing "an obviously incomplete collection of
variables related to a thing", first seen in 160d4eb.

Also, PROCDESC apparently is the only syntactically correct option to
declare an extern near proc?

Also, that `boss_phase_timed_out` variable only needs to be here
already because TCC enforces word alignment for the .data segment…
yeah, it's technically not related to this commit, but why waste time
working around it if we can just include that one variable.

Completes P0030, funded by zorg.
2019-09-15 20:35:15 +02:00
nmlgc 9d121c7cce [Decompilation] [th04/th05] Handle subpixels at the C++ type level
I've had the idea to hide this implementation detail and improve code
readability for some time now, but it obviously must still all inline,
to be indistinguishable from a direct assignment of the correct value…

… which, amazingly, it does! Even the static_cast from float to int.
The latter allows us to exclusively implement this for float, since we
do have to express the occasional value smaller than 16.

Who needs macros anyway. Yay, C++ in TH04 and TH05 after all!

Part of P0030, funded by zorg.
2019-09-15 20:30:35 +02:00
nmlgc c7fc4ca41d [Decompilation] [th05] Committing the score delta
Completes P0029, funded by zorg.
2019-09-15 20:29:09 +02:00
Egor 8ec888f30a [zuncom] Get rid of moveup.asm
one less invokation of tasm+tlink == one less headache
2018-04-15 20:22:41 +03:00
Egor 16766e28ac [zuncom] Simplified build of zun.com
Now it uses a single utility (zungen) to generate the header and
concatenate all the files
2018-04-15 20:07:40 +03:00
Egor 3eaa16a0fd [th04/th05] Rebuild ZUN.COM 2018-04-15 17:37:06 +03:00
Egor a1effa1c42 [th02/th03] Rebuild ZUN.COM
Two DOS utilities were made for this:
- gensize: generates TASM macro definitions with filesizes.
- copycat: similar to copy/b a+b+c d, except a+b+c is specified in a
separate file to avoid command line length limitations.

th02/zun.com is bit-perfect
th03/zun.com is almost there, with insignificant differences in
zunsp.com and res_yume.com.
2018-04-14 20:21:27 +03:00
nmlgc aa56a7cb18 [C decompilation] [th02] ZUN_RES.COM
This, hands down, has been the single worst stretch of decompilation so far.
Three extremely difficult functions that each still required inline assembly.
And no, this didn't even work out with any of the optimization features in
Borland C++ that aren't included in Turbo C++.
2015-09-17 03:43:28 +02:00
nmlgc 14e69ceb6d [C decompilation] [th01] VSync interrupt handler
Time to get back into this.
2015-09-05 22:33:07 +02:00
nmlgc 75b8765e44 [C decompilation] Finish TH02's OP.EXE 2015-03-16 22:36:50 +01:00
nmlgc 92979e8f31 [C decompilation] [th02] Code segment #2 of all three executables
Only one code segment left in both OP and FUUIN! its-happening.gif

Yeah, that commit is way larger than I'm comfortable with, but none of these
functions is particularly large or difficult to decompile (with the exception
of graph_putsa_fx(), which I actually did weeks ago), and OP and MAIN have
their own unique functions in between the shared ones, so…
2015-03-14 23:25:50 +01:00
nmlgc 44327e9305 [C decompilation] [th01/reiiden] 2D vector construction
Which, for some reason, is also found in the MAIN.EXE of every later game
in between completely unrelated hardware and file format functions.

Separate commit because it has its own segment in REIIDEN.EXE, and because
coming up with the nice function names took pretty long, since I haven't done
anything involving trigonometry in the past 5 years...
2015-03-13 23:03:39 +01:00
nmlgc e0d90dbdc3 [C decompilation] [th01] Text mode functions
Yet another set of questionable C reimplementations of master.lib functions to
waste my time. And half of them, including z_text_(v)putsa, aren't even called
anywhere.
2015-03-11 23:29:58 +01:00
nmlgc 6d2fa9f077 [C decompilation] [th01/reiiden] Randomly shaped VRAM copy functions, #1
So apparently, TH01 isn't double-buffered in the usual sense, and instead uses
the second hardware framebuffer (page 1) exclusively to keep the background
image and any non-animated sprites, including the cards. Then, in order to
limit flickering when animating the bullet, character and boss sprites on top
of that (or just to the limit number of VRAM accesses, who knows), ZUN goes to
great lengths and tries to make sure to only copy back the pixels that were
modified on plane 0 in the last frame.

(Which doesn't work that well though. When you play the game, you still notice
tons of flickering whenever sprites overlap.)

And by "great lengths", I mean "having a separate counterpart function for
each shape and sprite animated which recalculates and copies back the same
pixels from plane 1 to plane 0", because that's what the new functions here
lead me to believe. Both of them are only called at one place: the wave
function on the second half of Elis' entrance animation, and the horizontal
masked line function for Reimu's X attack animations.
2015-03-10 17:39:00 +01:00
nmlgc 44ad3eb4bc [C decompilation] [th01/fuuin] Slow 2x VRAM region scaling
This function raises one of those essential questions about the eventual ports
we'd like to do. I'll explain everything more thoroughly here, since people
who might complain about the ports not being faithful enough need to
understand this.

----

The original plan was aim for "100% frame-perfect" ports and advertise them as
such. However, the PC-98 is not a console with fixed specs. As the name
implies, it's a computer architecture, and a plethora of different, more and
more powerful PC-98 models were released during its lifespan. Even if we only
consider the subset of products that fulfills the minimum requirements to run
the PC-98 Touhou games, that's still a sizable number of systems.

Therefore, the only true definition of a *frame* can be "everything that is
drawn between two Vsync wait calls". Such a *frame* may contain certain
expensive function calls, and certain systems may run these functions slower
than the developer expected, thus effectively leading to more *frames* than
the developer explicitly specified.

This is one of those functions.

Here, we have a scaling function that appears to be written deliberately to
run very slow, which ends up creating the rolling effect you see in the route
selection and the high score and continue screens of TH01. However, that
doesn't change the fact that the function is still CPU-bound, and neither
waits for Vsync nor is iteratively called by something that does. The faster
your CPU, the faster the rolling effect gets… until ultimately, it's faster
than one frame and therefore vanishes altogether. Mind you, this is true on
both emulators and real hardware. The final PC-98 model, the Ra43, had a CPU
clocked at 433 Mhz, and it may have even been instant there.
If you use more optimized algorithm, it also runs faster on the same CPU (I
tried this, and it worked beautifully)… you get the idea.

Still, it may very well be that this algorithm was not a deliberate choice and
simply resulted from a lack of experience, especially since this was ZUN's
first game.

That leaves us with two approaches to porting functions like these:

1) Look at the recommended system requirements ZUN specified, configure the
   PC-98 emulator accordingly, measure how much of the work is done in each
   frame, then rewrite the function to be bound to that specific frame rate…
2) …or just continue using a CPU-bound algorithm, which will pretty much
   complete instantly on any modern system.

I'd argue that 2) is actually the more "faithful" approach. It will run faster
than the typical clock speeds people emulate the games at, and maybe draw a
bit of criticism because of that, but it seems a lot more rational than the
approximation provided by 1). Not to mention that it's undeniably easier to
implement, and hey, a faster game feels a lot better than a slower one, right?

… Oh well, maybe we'll still encounter some kind of CPU-bound animation that
is so essential to the experience that we do want to lock it to a certain
frame rate…
2015-03-09 17:58:30 +01:00
nmlgc 160d4eb69f [C decompilation] [th01/op] [th01/reiiden] Random resident structure stuff 2015-03-07 17:43:39 +01:00
nmlgc 0fd7f14784 [C decompilation] [th01/op] Archive functions
Fuck TH02 and above and their bizarre assembly code that indeed appears to be,
uh, playfully "optimized" in the most inadequate of places, far away from the
innermost loop. It's ALWAYS just these one or two instructions I just can't
fucking get out of the C compiler, which lead to the conclusion that these
functions must have either been first compiled to assembly, then "fine-tuned"
and then linked into the executable…

… or I'm really just missing some obscure compiler setting.

At least with TH01, you can tell that the source language must have undeniably
been C++, and the decompilation is a breeze.
2015-03-05 23:12:14 +01:00
nmlgc a3ae0095f0 [C decompilation] [th02] PI display 2015-03-04 04:28:16 +01:00
nmlgc ed0437f80e [C decompilation] [th02] First set of sound driver calls 2015-03-04 02:47:22 +01:00
nmlgc 404044f32b [C decompilation] [th02/op] [th03/op] [th04/op] Frame delay #1 2015-03-04 02:47:16 +01:00
nmlgc a8384c925f [C decompilation] [th02/maine] HUUMA.CFG loading 2015-03-03 07:40:29 +01:00
nmlgc 63299cdf42 [C decompilation] [th02/op] High score screen 2015-03-03 04:25:19 +01:00
nmlgc 87b1fb9e14 [C decompilation] [th02/maine] High score screen
MAIN.EXE shares most of the code in this segment, but I can't remove it from
there right now due to the weird ordering of the data segments in that
executable…

And yes, once again, those three seemingly random type casts in here are
*necessary* to build a bit-perfect binary.
2015-03-02 06:30:06 +01:00
nmlgc d058666929 [C decompilation] [th02/maine] Rotating rectangle animation
Small detour into MAINE.EXE because it has all the juicy algorithms that will
explain the remaining unknown members of the highscore data structure, and
there's this one code segment here we need to get out of the way first.
2015-02-28 22:37:40 +01:00
nmlgc 2f1b287f3d [C decompilation] [th01] VRAM region copy via EGC
The same function appears unused in TH02's MAINE.EXE. Separate commit because
this was painful enough and we can link the C version into FUUIN.EXE right
now.
2015-02-27 23:11:47 +01:00
nmlgc 1f514b5a6c [C decompilation] [th02/op] Shot type selection
Oh, OK, so this is what the PC-98 GRCG is all about. You call grcg_setcolor(),
and that puts the PC-98 hardware in some sort of "monochromatic mode". Then,
you just write your pixels into any *single* one of the 4 VRAM bitplanes. This
causes the hardware to automatically write to *all* bitplanes in such a way
that the final palette index for each of the 8, 16, or 32 pixels you just wrote
a 1 value to will actually end up to match the color you set earlier.

Don't forget to call grcg_off() at the end though, or you can't draw any
non-monochromatic graphics, heh.
2015-02-25 23:05:20 +01:00
nmlgc cd33367b51 [C decompilation] [th02/op] Music Room
Yes, all of it. Including the bouncing polygons, of course. And since it's
placed at the end of ZUN's code inside the executable, the code's already
position-independent and fully hackable.
2015-02-24 22:38:44 +01:00
nmlgc 436f1c5722 [C decompilation] [th01] MDRV2 calls
Still missing mdrv2_resident() though, which we currently can't slot in there
due to that string constant constructor syntax. :/
2015-02-21 20:48:58 +01:00
nmlgc ed8d0e28f5 [C decompilation] [th02/op] Title screen flashing animation 2015-02-21 14:16:27 +01:00
nmlgc 7836363019 Use a Makefile for the 16-bit part of the build process
Well, that became unbearable pretty quickly. Not sure whether I'm doing all
this Makefile business right, but this looks pretty nice.

It doesn't really help much at this point though because the 32-bit part is
still entirely separate and forces everything to rebuild all the time, but at
least it aborts on C compiler errors.
2015-02-21 11:28:56 +01:00