Commit Graph

881 Commits

Author SHA1 Message Date
nmlgc d62dd06ddf [Reverse-engineering] [th02/th04/th05] Shot type control functions
Now you can mix and match characters and shot types any way you want.

TH02 and TH05 simply use the regular shot sprites associated with the
character/shot type no matter which function is used, which quite
surprisingly works without glitches for the few combinations I've tried.
TH04, however, always uses the sprites that were originally intended to
be used with that shot function, regardless of the character you assign
the function to, meaning that it always loads all 4 shot sprite sheets
into memory. Not doing that would have been one way to reduce the game's
memory footprint so that it doesn't require an extender…

Funded by -Tom-.
2018-04-21 15:32:33 +02:00
nmlgc 82bacbf01d [Reverse-engineering] [th04/th05] Current player character ID
TH03, again, uses structures for this.

Funded by -Tom-.
2018-04-20 21:37:41 +02:00
nmlgc 755a0564ae [Reverse-engineering] [th02/th04/th05] Shot power levels
Funded by -Tom-.
2018-04-20 19:33:40 +02:00
nmlgc a67a55dc20 [Reverse-engineering] [th02/th04/th05] Current shot power
Funded by -Tom-.
2018-04-18 17:51:58 +02:00
nmlgc 26d5747d71 [Reverse-engineering] [th02/th05] Current live and bomb counts
TH01 was done in 3b175c8, TH03 obviously has two of those and uses a
structure for it, TH04 doesn't move it out of the resident structure.

Funded by -Tom-.
2018-04-17 19:30:14 +02:00
nmlgc 47e5601613 [Reverse-engineering] [th03/th04/th05] Frame-count-mod-2/4/8/16 globals
Mostly used for animations, but NOPping out the assignments causes
Ultra-like behavior in some (mid)bosses, at least in TH04 and TH05?

Funded by -Tom-.
2018-04-16 13:18:54 +02:00
nmlgc a4308d6c09 [Reduction] #712: js_sense
In which ZUN actively refuses help from master.lib and rips out
everything that isn't immediately related to reading the one joystick
port of the PC-9801-86 sound board.

Maybe there actually was a good reason for that?

Funded by -Tom-.
2018-04-15 23:51:49 +02:00
nmlgc 6c35094bea [Reduction] #711: master.lib joystick globals
Funded by -Tom-.
2018-04-15 23:51:49 +02:00
nmlgc 371fa21610 [Reduction] #710: js_start
Might be interesting to research why ZUN explicitly removed support for
the SAJ-98 there.

Funded by -Tom-.
2018-04-15 23:51:49 +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
Egor 88fdde46b1 [Readme] Recommend dynamic core and max cycles for faster compilation in DOSBox 2018-04-10 19:51:16 +02:00
nmlgc 843905ecea [Reverse-engineering] [th04/th05] ZUN Soft logo explosions
Which seemed a nicer and more debuggable intro to how ZUN implements
sprites than starting with a random structure from MAIN.EXE.

Funded by -Tom-.
2018-03-21 23:20:04 +01:00
nmlgc 6b0d0c6940 [Maintenance] Rename master.lib's speed parameter to avoid an upcoming conflict
TASM crashes if you try to define a structure member with the same name
as an existing numeric equate.

And yes, we should have solved this by finally librarifying master.lib,
but that'll be a rather involved subtask as well.
2018-03-20 19:26:44 +01:00
nmlgc dca8770533 [Build] Use Borland's real-mode MAKER.EXE
DOSBox-X seems to have some problems with it, or maybe that's just me
having configured something wrong. In any case, it's not like we need
that extra memory, so removing the need for the DPMI driver removes
one more potential source of problems.

Oh, and it has always been part of the devkit. :P
2018-03-19 23:52:42 +01:00
nmlgc 0fe6416a77 [Reverse-engineering] [th04/th05] Midboss timer
word_20616 in TH02's MAIN.EXE does the same, but uses a weird unit.
Half-seconds, maybe?

thcrap-like Skipgame functionality is still out of reach, unfortunately.
But eh, now you can practice midbosses while being underpowered, if you
want to.

Funded by -Tom-.
2018-03-18 21:27:20 +01:00
nmlgc 134804d0e5 [Readme] Add 16-bit TASM to the list of build requirements 2018-03-17 05:30:49 +01:00
nmlgc 4b8baf1413 [Reverse-engineering] [th02/th03/th04/th05] Random number ring buffer
Yes, you're reading that correctly. If the cursor is at 255, reading a
16-bit value will fill the upper 8 bits with the neighboring cursor
value, which always is 0xFF.

Funded by -Tom-.
2018-03-16 18:12:21 +01:00
nmlgc f98fba3c9d [Reverse-engineering] [th02/th04/th05] .map file loading
Funded by -Tom-.
2018-03-16 08:02:14 +01:00
nmlgc 13a6a1d64c [Reverse-engineering] [th04/th05] Stage number globals
TH03 is still rather confusing with apparently three of those, TH02 was
done in 17f6ac7d, TH01 doesn't even copy it out of the resident
structure.

Funded by -Tom-.
2018-03-15 07:44:32 +01:00
nmlgc 1f752d739d [Build] Keep TLINK from accidentally creating 1.MAP on every build 2018-03-10 20:46:00 +01:00
nmlgc 4ddd9e1377 [Maintenance] readme.md → README.md
Because there will be a CONTRIBUTING.md as well.
2017-10-26 22:56:29 +02:00
Egor 7b0ebbbb86 .gitignore target binaries 2017-10-24 21:33:59 +03:00
nmlgc cae1be46a4 [Arrays] Remaining references to the sine and cosine tables 2017-01-10 20:23:57 +01:00
nmlgc f8d8d56cd1 [Reduction] Bouncing Music Room polygons 2017-01-09 21:10:33 +01:00
nmlgc 39c169c30d [Reduction] Separate functions for 1D and 2D vector construction
Look at that TH05 vector2_at_opt function. What the hell, the caller is
supposed to set up the stack frame for the function? How do you even get
a compiler to do this (and no, I haven't found a compiler switch)? No
way around writing a separate "optimizer" as part of the compilation
pipeline, it seems.
2017-01-08 21:01:13 +01:00
nmlgc 6759d346d5 [Reduction] Lookup table for horizontally flipping planar pixels
OK, let's not identify the arrays in a file-based fashion just yet, and
first reduce all shared ZUN code that uses arrays. Less stressful, we'll
have to do this anyway, and I just can't resist the urge to immediately
reverse-engineer everything I find.
2017-01-07 22:30:46 +01:00
nmlgc 7a309919aa [Reverse-engineering] [th02/maine] *Actually* identify all array references -.-
And already, the script begins to crumble, reminding me of what a
terrible idea it actually was. Like, if you did it for real, you'd get
so many false positives that the script stops being useful, since
every raw number above 0x90 (the size of the _DATA segment of the
Borland C++ DOS startup code) can potentially be a memory reference.

I do think that the script now covers the sweet spot between full-blown
emulation and shallow parsing though, so going to do at least a few
more files.
2017-01-05 23:54:17 +01:00
nmlgc 915f780e73 [Reverse-engineering] [th02/maine] Identify all remaining global arrays
Which is the last step on the way to completely position-independent
code, with no random hex numbers that should have been data pointers,
but weren't automatically turned into data pointers by IDA because
they're only ever addressed in the indirect fashion of

	mov bx, [bp-array_index]
	mov ax, [bx+0D00h] ; 0D00h is obviously an array of some sort

Removing all of these makes it practicable to add or delete code without
breaking the game in the process. Basic "modding", so to speak.

Automatically catching all possible cases where this happens actually
amounts to emulating the entire game, and *even then*, we're not
guaranteed that the *size* of the array just falls out as a byproduct
of this emulation and the tons of heuristics I would have thrown on top
of that. ZUN hates proper bounds checking and the correct size of each
array may simply never be implied anywhere.

So, rather than going through all that trouble of that (and hell, I
haven't even finished *parsing* this nasty MASM assembly format), and
since nothing really has happened in this project for almost two years,
I chose to just turn this into a text manipulation issue and figure out
the rest manually. Yeah, quick and dirty, and it probably won't scale if
I ever end up doing the same for PC-98 Policenauts, but it'd better work
at least for the rest of PC-98 Touhou.

Trying to do one of those per day from now on. Probably won't make it
due to the reverse-engineering effort required for the big main
executables of each game, but it'd sure be cool if I did.
2017-01-04 20:52:21 +01:00
nmlgc 58e1e142a5 [Maintenance] clip[bss].asm actually covers 16 bytes, not 8 -.- 2017-01-03 21:42:14 +01:00
nmlgc bfa2fb23bb [Maintenance] Mark all batch and binary files as executable
For all files without a +x flag, git on Cygwin actually sets the Windows ACLs
to explicitly deny execution.
2016-03-02 08:26:13 +01: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 697407d658 [Maintenance] [th02] Split off HUUHI.DAT pre-save encoding into a separate file
Necessary for ZUN_RES.COM as well.
2015-09-09 17:08:42 +02:00
nmlgc 547dafe071 [Maintenance] [th02] Move the menu options in huuma_cfg_t to their own structure
Necessary for ZUN_RES.COM.
2015-09-08 17:19:32 +02:00
nmlgc 43001161e3 [Maintenance] Fix any whitespace issues in our own code 2015-09-07 15:44:48 +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 c5f53d9cf1 [Maintenance] Rename snd_kaja_func() to snd_kaja_interrupt()
Oh, right, these functions can have parameters. So, let's turn snd_kaja_func()
into a macro that combines the function number and the parameter into the AX
value for the driver.
2015-03-15 23:51:11 +01:00
nmlgc de491f225d [Maintenance] Move the sound driver function slices from hardware/ to snd/
And renaming them all to the short filenames they will be decompiled to for
consistency. These functions aren't really immediately hardware-related, as
we've established earlier in the decompilation.
2015-03-15 23:01:31 +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 f3b8ae2851 [C decompilation] [th02/op] Second set of sound driver calls
So yeah, after ignoring this issue for a week, we indeed have no choice but to
decompile these functions into this horrible mess of C and inline assembly.
And you know what? Since the compiled result still matches with ZUN's binary,
it's entirely possible that this *was* the original format this code was
written in! Seriously, how intoxicated do you have to be to write (or rather,
slur) code like this?

Keeping these functions entirely in assembly would have surely been better.
However, it would have made linking practically impossible, especially for the
later games which still need them in the current assembly slice format.
2015-03-12 12:46:51 +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 519e24c459 Rename the *_copy_region_* functions to *_copy_rect_*
TH01 copies a lot of different shapes from plane 1 to 0, so "region" feels
awfully unspecific.
2015-03-10 14:18:28 +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 3b175c8980 [Reverse-engineering] [th01] ReiidenConfig structure 2015-03-06 23:04:25 +01:00