This is a wide variety of completely random stuff that I eventually wanted to put on webpages once they got fleshed out enough, but don't have the time to continue working on atm so I'm just dumping it all here
Most notably this has complete lists of on_tick
priorities for almost every STG (just missing a few of the point titles like GFW and ISC)... not that there's much you can do with that information.
(this section is just personal notes describing what to do early on, because I still need to do this for ISC and GFW)
The key to getting started is to identify the following funcs:
main
. Binary Ninja automatically identifies the entry point as_start
, which is the C runtime start. Somewhere near the end of it should be a call to a 16-byte aligned function. It should contain calls to APIs for creating dialogues and stuff. That'smain
.register__on_tick
. 99% of all high-level reversing work is just going through the code references to this function. There's numerous ways you can find it.- In all modern games, if you scroll to the bottom of Binary Ninja's Graph View for
main
, the basic block at the very bottom will be:
Look at the functions that callmov dword [UPDATE_FUNC_REGISTRY], 0x0 jmp <somewhere>
UPDATE_FUNC_REGISTRY
. Two of these will have many more references than all of the others. These areregister__on_tick
andregister__on_draw
(they have 44 and 83 references respectively in TH17).- When that doesn't work (e.g. TH10 and earlier), I just boot up the game and use Cheat Engine to locate the instruction that decrements the life counter on death. Then I keep looking back up through the cross references until I find a function that is pushed instead of called; that function is an
on_tick
callback. Any nearby functions you see being called are eitherUpdateFunc::operator new
,malloc
, orregister__on_tick
. (note: to help identifymalloc
, UpdateFuncs are 0x28 bytes in modern games)
- In all modern games, if you scroll to the bottom of Binary Ninja's Graph View for
- Go through the references of
register__on_tick
. 90% ofon_tick
callbacks are associated with global objects (the rest are typically all associated with a single type,ScreenEffect
). The vast majority of them will be listed in alphebetical order by ZUN's names (e.g.AsciiInf
,BackgroundInf
,BulletInf
...). Be aware of the general structure of how these objects are initialized:TypeName::operator new
: (1) callsmalloc
, (2)TypeName::constructor
(which often gets inlined), (3) callsTypeName::initialize
, and (4), on failure, callsTypeName::destructor
followed byfree
.TypeName::constructor
: Typically calls constructors of fields, then negates all of that hard work by doing amemset(this, 0, sizeof(*this))
, and finally writesthis
to a global variable.- On
BulletManager
andItemManager
you'll see it callinginitialize_vector
(size, count, constructor, destructor)
.
- On
TypeName::initialize
: Often makes calls toanm_preload
,register__on_tick
andregister__on_draw
.TypeName::on_tick
,TypeName::on_draw
: These are functions you see being pushed or written to fields shortly before calls toregister__on_tick
orregister__on_draw
.Typename::destructor
: This calls destructors of its fields, callsfree
on allocated pointers, and you will often see it entering the UpdateFuncRegistry'sCRITICAL_SECTION
so that it can callUpdateFuncRegistry::unregister
.
- Things are slightly different for objects that represent threads. These have a
thread_start
function. Inside eitheroperator new
orinitialize
, you'll see thethread_start
callback being pushed or written to a field shortly before a call to some subroutine that callsCreateThread
. For some of them there's noinitialize
function, and the update funcs are registered insidethread_start
.
Supervisor::switch_gamemodes
: You should encounterEnding
pretty early on (somewhere around 4th to 10th depending on how many Bombs produce screenshake). When you look at what callsEnding::operator new
, you'll find yourself in a case of a switch statement. This function is the member functionSupervisor::switch_gamemodes
.- Look at the previous cases in
Supervisor::switch_gamemodes
. Most of them will end in a call toGameThread::operator new
. The0x0
case will callLoadingThread::operator new
.
If you look in a modern Touhou game you will find lots of variables describing input. All modern Touhou games (and probably pre-MoF games as well) have a distinction between two types of variables representing player input.
- Hardware input is the set of variables that are set according to the keys currently pressed on the keyboard. It is a bitfield integer with bits corresponding to every key that has any function; this of course includes the arrow keys, Z, X, C, Ctrl, and Shift, but it also includes Esc, R, P (screenshot), Enter, and etc.
- Game input is the input that controls the player character. These variables do not include Esc, R, and etc. During replay playback, this is read from the replay file.
When you try to identify the input variables in memory, you'll typically find at least two variables that almost always appear to have the same value. One of these is actually previous frame input. The game also stores rising edge (INPUT & ~INPUT_PREV
) and falling edge (INPUT_PREV & ~INPUT
) variables that it uses to determine when a key was just pressed.
For many, many scenarios, ZUN uses a form of doubly-linked list. There is a common data structure for this:
struct ListNode {
void* entry;
ListNode* next;
ListNode* prev;
ListNode* __field_c; // TD onwards
}
These list nodes are often—but not always—placed directly on the structure in question; for instance, UpdateFunc
s have a ListNode
field, where the entry
field points to the beginning of the ListNode
. Sometimes, such as for Bullets, a ListNode
is placed at offset 0 and the Bullet is accessed by a pointer cast; in this case entry
field is left unused or possibly even repurposed to hold something else.
The fourth field is not present in UFO, DS, or GFW, but is present from TD onwards. During some list operations, it is written only if it is already non-null. For instance, here is the pseudocode for a frequently-inlined function that inserts into a list:
// Example at th13.exe(v1.00c)+0x4709f1
void ListNode::insert_after(ListNode* node) {
if (node->next) {
this->next = node->next;
node->next->prev = this;
}
// ??????????????????
if (node->__field_c) {
node->__field_c = this;
}
node->next = this;
this->prev = node;
}
Despite continued efforts I still have no idea what this field is or if it is ever even used. I tried running TD and using a program to scan for possible usage of __field_c
by finding addresses such that [[addr + 4] + 8] == addr && [addr + 0xc] != 0
. Unfortunately, the results were inconclusive; I was unable to identify any results from ZUN's code because there were too many things from other libraries.
IN Enemy
flags
Bits | Mask | Inv Mask | |
---|---|---|---|
0x3324:0 |
0x00000001 |
0xfffffffe |
Marks the Enemy as "in use." (since there's no freelist) |
0x3324:1 |
0x00000002 |
0xfffffffd |
|
0x3324:2 |
0x00000004 |
0xfffffffb |
ECL flag 2, negated (i.e. hitbox active) (note: there's some unknown extra funny business in flagSet ...) |
0x3324:3 |
0x00000008 |
0xfffffff7 |
ECL flag 4, negated (i.e. vulnerable) |
0x3324:4 |
0x00000010 |
0xffffffef |
ECL flag 8, normal (i.e. intangible) |
0x3324:5 |
0x00000020 |
0xffffffdf |
|
0x3324:6 |
0x00000040 |
0xffffffbf |
ECL flag 1, negated (i.e. hurtbox active) |
0x3324:7 |
0x00000080 |
0xffffff7f |
(set by ECL 157. If this is set, ECL ret logs a message "error : no Stack Ret" .) |
0x3324:8 |
0x00000100 |
0xfffffeff |
(set on familiars) |
0x3324:11 |
0x00000800 |
0xfffff7ff |
(used by etFan and friends somehow. cleared on new familiars if player is youkai) |
0x3324:(12-14) |
0x00003000 |
0xffffcfff |
0b10 = movePosTime , 0b01 = moveDir , 0b11 = moveCircle |
0x3324:(14-17) |
0x0001c000 |
0xfffe3fff |
Position interpolation mode (movePosTime and friends) |
0x3324:17 |
0x00020000 |
0xfffdffff |
etDelay (et instructions do not immediately shoot) |
0x3324:18 |
0x00040000 |
0xfffbffff |
Mirror x |
0x3324:19 |
0x00080000 |
0xfff7ffff |
moveLimit enabled |
0x3324:24 |
0x01000000 |
0xfeffffff |
(used at 0x42cc50 ) |
0x3324:25 |
0x02000000 |
0xfdffffff |
anmRotSet |
0x3324:26 |
0x04000000 |
0xfbffffff |
(set by ECL 151) |
0x3324:27 |
0x08000000 |
0xf7ffffff |
(spellTimeout ) |
0x3324:28 |
0x10000000 |
0xefffffff |
ECL flag 16, normal (i.e. don't delete at screen edge) |
0x3324:29 |
0x20000000 |
0xdfffffff |
|
0x3324:30 |
0x40000000 |
0xbfffffff |
(set by ECL 173 as well as playerNullify . Possibly indicates enemy should be frozen during spell break?) |
0x3324:31 |
0x80000000 |
0x7fffffff |
(set by ECL 183) |
0x3328:0 |
0x00000001 |
0xfffffffe |
|
0x3328:1 |
0x00000002 |
0xfffffffd |
trailFamiliarSet |
0x3328:2 |
0x00000004 |
0xfffffffb |
(set by anmSetBoss/anmSetBossSlot , cleared by anmSet/anmSetSlot ) |
0x3328:6 |
0x00000040 |
0xffffffbf |
ECL flag 32, normal (unknown) |
**LoLK Globals.flags
(0x4e7794)
TH16 GameThread flags
Bits | Mask | Inv Mask | |
---|---|---|---|
0 | 0x00000001 |
0xfffffffe |
|
1 | 0x00000002 |
0xfffffffd |
(0x43c45c ) |
2 | 0x00000004 |
0xfffffffb |
(gets enabled once high score is achieved) |
3 | 0x00000008 |
0xfffffff7 |
(sometimes tested together with 0; bitmask 0x9 . 0 resets CONTINUES_USED somewhere) |
4-5 | 0x00000030 |
0xffffffcf |
0b01 Practice. 0b10 Spell practice. |
6 | 0x00000040 |
0xfffffffb |
|
8-9 | 0x00000300 |
0xfffffcff |
Pointdevice related? If nonzero, misses are not counted. |
TH16 GameThread flags
Bits | Mask | Inv Mask | |
---|---|---|---|
10 | 0x00004000 |
0xffffbfff |
Set during Game Clear bonus and fadeout at game end. |
this |
TH10 | TH11 | TH12 | TH12.5 | TH13 | TH14 | TH15 | TH16 | TH17 | Meta-responsibilities |
---|---|---|---|---|---|---|---|---|---|---|
Supervisor |
0x01 |
0x01 |
0x01 |
0x01 |
0x01 |
0x01 |
0x01 |
0x01 |
0x01 |
|
LoadingThread |
0x03 |
0x03 |
0x03 |
0x03 |
0x03 |
0x03 |
0x03 |
0x03 |
0x03 |
|
AsciiManager |
0x04 |
0x04 |
0x04 |
0x04 |
0x04 |
0x04 |
0x04 |
0x04 |
0x05 |
|
DebugSprtView |
0x05 |
0x05 |
0x05 |
0x05 |
0x05 |
0x05 |
0x05 |
|||
DebugSptTest |
0x05 |
0x05 |
0x05 |
0x05 |
0x05 |
0x05 |
0x05 |
|||
MainMenuThread |
0x06 |
0x06 |
0x06 |
0x06 |
0x06 |
0x06 |
0x06 |
0x06 |
0x07 |
|
DebugSoundTest |
0x07 |
0x07 |
0x07 |
0x07 |
0x08 |
|||||
AnmManager (1) |
0x08 |
0x08 |
0x08 |
0x08 |
0x08 |
0x08 |
0x09 |
0x09 |
0x0a |
|
PauseMenu |
0x09 |
0x09 |
0x09 |
0x09 |
0x09 |
0x09 |
0x0a |
0x0a |
0x0b |
|
HelpManual |
0x0a |
0x0a |
0x0a |
0x0b |
0x0b |
0x0c |
||||
TrophyManager |
0x0e |
|||||||||
GameThread |
0x0a |
0x0a |
0x0a |
0x0b |
0x0b |
0x0b |
0x0f |
0x0f |
0x10 |
Pause cutoff |
ReplayManager (1) |
0x0b |
0x0b |
0x0b |
0x0c |
0x0c |
0x0c |
0x10 |
0x10 |
0x11 |
|
Stage |
0x0c |
0x0c |
0x0c |
0x0d |
0x0d |
0x0d |
0x11 |
0x11 |
0x12 |
|
Stage (2) |
0x0d |
|||||||||
TranceManager |
0x0f |
|||||||||
ScreenEffect |
0x0e |
0x0e |
0x0e |
0x0f |
0x10 |
0x10 |
0x14 |
0x13 |
0x14 |
|
PopupManager |
0x0f |
0x0f |
0x0f |
0x10 |
0x11 |
0x11 |
0x15 |
0x14 |
0x15 |
|
Player |
0x10 |
0x10 |
0x10 |
0x11 |
0x12 |
0x12 |
0x17 |
0x16 |
0x17 |
|
(pl0X.anm) Bomb |
0x11 |
0x11 |
0x11 |
0x14 |
0x14 |
0x19 |
0x18 |
0x19 |
||
(pl0Xsub.anm) Bomb |
0x19 |
|||||||||
PhotoManager |
0x12 |
|||||||||
EnemyManager |
0x12 |
0x12 |
0x12 |
0x14 |
0x15 |
0x15 |
0x1a |
0x1a |
0x1b |
|
Ufo |
0x13 |
|||||||||
LaserManager |
0x13 |
0x13 |
0x14 |
0x16 |
0x16 |
0x16 |
0x1b |
0x1b |
0x1c |
|
BulletManager |
0x14 |
0x14 |
0x15 |
0x17 |
0x17 |
0x17 |
0x1c |
0x1c |
0x1d |
|
ItemManager |
0x15 |
0x15 |
0x16 |
0x18 |
0x18 |
0x18 |
0x1d |
0x1d |
0x1e |
|
SpiritItemManager |
0x19 |
|||||||||
GoastManager |
0x1f |
|||||||||
Spellcard |
0x16 |
0x16 |
0x17 |
0x19 |
0x1a |
0x1a |
0x1f |
0x1e |
0x20 |
|
EffectManager |
0x17 |
0x17 |
0x18 |
0x1a |
0x1b |
0x1b |
0x20 |
0x1f |
0x21 |
|
Gui |
0x18 |
0x18 |
0x19 |
0x1b |
0x1c |
0x1c |
0x21 |
0x20 |
0x22 |
|
HintManager |
0x19 |
0x19 |
0x1a |
|||||||
AnmManager (2) |
0x1a |
0x1a |
0x1b |
0x1d |
0x1d |
0x1d |
0x22 |
0x21 |
0x23 |
|
ReplayManager (2) |
0x1b |
0x1b |
0x1c |
0x1e |
0x1e |
0x1e |
0x23 |
0x22 |
0x24 |
Replay speedup |
Ending |
0x1c |
0x1c |
0x1d |
0x1f |
0x1f |
0x24 |
0x23 |
0x25 |
And their purposes:
this |
Comment |
---|---|
Supervisor |
Reads raw keyboard input. Switches gamemodes. |
LoadingThread |
...something about window resolution. This type might be mis-named, but I've seen references to sig.anm near it. |
AsciiManager |
Simply ticks down remaining_time (+0x12c in TH16) fields on strings and deletes expired ones. |
DebugSprtView |
Unused. |
DebugSptTest |
Unused. |
MainMenuThread |
Implements main menu. |
DebugSoundTest |
Unused. |
AnmManager (1) |
Run ANM VMs in layers 36 to 42. (pre TH17) Also rebuild their layer lists. |
PauseMenu |
Implements Pause and Game Over menu. |
HelpManual |
Implements Help Manual. |
TrophyManager |
|
GameThread |
Stage/Game clear bonus display. Probably handles stage transitions. Prevents all higher priority funcs from running by returning 3 when the game is paused. |
ReplayManager (1) |
This one has two different possible functions. One records gameplay for a replay. The other plays back a replay. Both set the game INPUT globals. |
Stage (1) |
Plays STD script, does... things! Things it does! |
Stage (2) |
Identical. Probably used during stage transitions. All modern Touhous have two Stage objects, but only MoF gives them two separate update functions. |
TranceManager |
Implements manual trances. (Death trances don't appear to depend on this!) |
ScreenEffect |
Implements e.g. screen shake and fades to black. |
PopupManager |
Updates those tiny little score, PIV, power and graze popup texts written in kanji numerals. |
Player |
HUGE. Watches input, moves, initiates bombs, shoots, moves player bullets, precomputes hurtbox... |
(pl0X.anm) Bomb |
Runs per-tick logic of a bomb if the player is using one. |
(pl0Xsub.anm) Bomb |
Same but for season releases. |
PhotoManager |
Darkens the screen during camera shooting, and appears to be responsible for grabbing the actual screenshot. Not responsible for charging the camera or displaying/moving the... uh... frustum. |
EnemyManager |
For each enemy, runs ECL, takes damage from the Player, maybe kills the player, and lots, lots more. |
Ufo |
Watches for full tokens and summons a UFO. Probably ticks live UFOs. Not responsible for tokens. |
LaserManager |
Runs game logic for each laser, similar to bullets. |
BulletManager |
Moves enemy bullets. Check graze conditions, maybe kill the player. |
ItemManager |
Moves items, checks for PoC/autocollect/attraction/pickup, performs item pickup effects. |
SpiritItemManager |
Makes divine spirits collectable and disappear after a while. |
GoastManager |
Makes tokens move. Not sure if this is responsible for otter hypers, or if that's done in Player::on_tick . |
Spellcard |
Messes with anms, updates spell bonus, checks for bomb usage. |
EffectManager |
TH12-14: Noop. TH15-17: Checks the list of running effect anm ids and zeros out any that have finished. This may seem pointless since the VMs already clean up after themselves on completion, but in TH15 this bookkeeping was necessary for Pointdevice. |
Gui |
Refreshes all counters on the right side of the screen and many HUD elements. Might also be responsible for boss dialogue? I dunno it's very big leave me alone |
HintManager |
Old help thing before 12.5. (but do any games actually use it?) |
AnmManager (2) |
Run ANM VMs in layers 36 to 42. (pre TH17) Also rebuild their layer lists. |
ReplayManager (2) |
Implements replay speedup by causing all of the above on_tick funcs to repeat 7 times in one frame. |
Ending |
Shows the ending and staff roll. |
In TH08 and earlier, there are no global lists of ANM VMs, and presumably no concept of layers (not that I would know). Each global object holds their own set of ANM VMs, updating them in on_tick
and drawing them in on_draw
. This makes it a fair bit more obvious why modern games contain so many "noop" on_draw
functions, and why some seemingly useless types like EffectManager
exist.
this |
TH08 Prio. |
Known non-obvious differences from TH10+ |
---|---|---|
Supervisor |
0x00 |
|
AsciiManager |
0x01 |
Is now at least partly responsible for implementing the Pause menu. |
Lolidfk |
0x02 |
Uhhhh. This is important. Its on_tick is huge. The game crashes without it. Based on alphabetical placement, it could have something to do with loading? But it's not a thread like in modern games, and its on_tick runs during the entire game. When the game is paused, this is the last on_tick that runs. |
ScreenEffect |
0x03 |
|
MusicRoom |
0x04 |
(merged with MainMenu in later games) |
MainMenu |
0x04 |
|
Ending |
0x05 |
|
ReplayManager |
0x06 |
Used during replay playback. Presumably reads input from the replay file. |
ReplayManager |
0x07 |
Used during live gameplay. Presumably records input. |
Stage |
0x08 |
|
Player |
0x09 |
Additionally ticks state of player bombs. (responsibility of Bomb in later games) |
EnemyManager |
0x0b |
|
Spellcard |
0x0c |
Additionally handles the spellcard-like visual effects of player bombs. (removed in later games) |
EffectManager |
0x0d |
Now we see that this type once actually had responsibilities! Modern games simply create particles and forget about them, letting them live as orphans in the global ANM lists. In TH08, EffectManager is a home for these particles. |
BulletManager |
0x0e |
Additionally calls a function on ItemManager to update all items. (in later games, ItemManager has its own on_tick ) |
Gui |
0x0f |
|
ResultScreen |
0x10 |
That screen that asks to save a replay, and anything else with that background (e.g. Result on the Main Menu). |
ReplayManager |
0x11 |
Used during live gameplay. Actually does something unlike in modern games; no clue what. |
ReplayManager |
0x12 |
Used during replay playback. Implements replay speedup (and presumably slowdown as well). |
Note: These comments apply virtually wholesale to at least TH15 and TH17. Don't know yet how similar/different TH14 is.
struct Enemy {
// A PosVel is a complicated data structure that supports various modes
// of motion (fixed position, fixed velocity, interpolation).
/* 0x0 */ struct PosVel prev_final_pos;
/* 0x44 */ struct PosVel final_pos;
/* 0x88 */ struct PosVel abs_pos;
/* 0xcc */ struct PosVel rel_pos;
/* 0x110 */ struct Float2 hurtbox_size;
/* 0x118 */ struct Float2 hitbox_size;
// Rotation of rectangular hitboxes and hurtboxes.
// (FIXME: any other effect? I know I looked into this, but I forgot...)
/* 0x120 */ float rotation;
// ANM ids for all anms. Strangely, even though the size of this array is clearly 16,
// there are some loops in TH16 that seem to only go over the first 14 items...
/* 0x124 */ int32_t anm_ids[16];
// (FIXME I forget what this is)
/* 0x164 */ struct Float3 anm_pos_array[16];
// (FIXME this one gets used at th16+0x41d37b but I don't know what it is)
/* 0x224 */ int32_t __anm_related[16];
/* 0x264 */ int32_t selected_anm_index;
/* 0x268 */ int32_t anm_slot_0_anm_index;
/* 0x26c */ int32_t anm_slot_0_script;
/* 0x270 */ // 12 unknown bytes
/* 0x27c */ int32_t anm_layers;
/* 0x280 */ // 12 unknown bytes
// Enemy-local ECL vars.
/* 0x28c */ int32_t ecl_int_vars[4];
/* 0x29c */ float ecl_float_vars[8];
/* 0x2bc */ struct Timer time;
/* 0x2d0 */ struct Timer __timer_2d0; // no clue. It counts up
// Setting this variable causes the enemy to behave as if GAME_SPEED has been
// multiplied by a factor of `(1 - slowdown)`.
//
// To my knowledge, no ECL instruction sets this in TH16.
/* 0x2e4 */ float slowdown;
/* 0x2e8 */ struct EnemyList node_in_global_storage;
// These are interpolators for the various moveFooTime commands.
// (TODO: document these structs)
/* 0x2f8 */ struct InterpStrange1 abs_pos_i;
/* 0x360 */ struct InterpStrange1 rel_pos_i;
/* 0x3c8 */ struct InterpFloat abs_angle_i;
/* 0x3f8 */ struct InterpFloat abs_speed_i;
/* 0x428 */ struct InterpFloat rel_angle_i;
/* 0x458 */ struct InterpFloat rel_speed_i;
/* 0x488 */ struct InterpFloat2 abs_radial_dist_i;
/* 0x4cc */ struct InterpFloat2 rel_radial_dist_i;
/* 0x510 */ struct InterpFloat2 abs_ellipse_i;
/* 0x554 */ struct InterpFloat2 rel_ellipse_i;
// (I know little about these, I think I took them from 32th's notes)
/* 0x598 */ struct BulletMgrProps bullet_mgrs[16];
/* 0x3d98 */ int32_t __et_ex_index[16];
/* 0x3dd8 */ struct BulletOffset bullet_mgr_offsets[16];
/* 0x3e98 */ struct BulletOffset bullet_mgr_origins[16];
/* 0x3f58 */ struct Float2 __anm__unknown_306;
/* 0x3f60 */ struct Float2 move_limit_center;
/* 0x3f68 */ struct Float2 move_limit_size;
/* 0x3f70 */ int32_t score_reward;
/* 0x3f74 */ struct EnemyLife life;
/* 0x3f90 */ struct EnemyDrop drops;
/* 0x3fe0 */ int32_t __field_3fe0__some_kind_of_extra_damage;
/* 0x3fe4 */ uint32_t death_sound;
/* 0x3fe8 */ int32_t death_anm_script;
/* 0x3fec */ int32_t death_anm_index;
/* 0x3ff0 */ // 8 unknown bytes
/* 0x3ff8 */ int32_t hit_sound;
/* 0x3ffc */ struct Time set_invuln;
/* 0x4010 */ struct Time no_hitbox_dur;
/* 0x4024 */ struct Time __timer_4024__counts_down;
/* 0x4038 */ float bomb_damage_multiplier;
/* 0x403c */ struct EnemyDropSeason drop_season;
// Yes, there are two dwords full of flags. The upper dword is mostly empty.
/* 0x4060 */ int32_t flags_low;
/* 0x4064 */ int32_t flags_high;
/* 0x4068 */ // 8 unknown bytes
/* 0x4070 */ int32_t own_boss_id;
/* 0x4074 */ float et_protect_range;
// Used by both setNext and setTimeout.
/* 0x4078 */ struct EnemySetNextSlot set_next[8];
// Points to `this - 0x120c`, the beginning of the zEnemyFull structure that this Enemy is embedded in.
/* 0x44b8 */ struct EnemyFull* full;
/* 0x44bc */ struct EnemyFog fog;
/* 0x44d8 */ char set_death[64];
/* 0x4518 */ void* func_from_ecl_func_set;
/* 0x451c */ uint32_t is_func_set_2;
/* 0x4520 */ void* func_from_ecl_flag_ext_dmg;
/* 0x4524 */ void* func_from_ecl_unknown_634;
// This is the chapter that the enemy was spawned during. It also gets updated by ECL 524 setChapter
// (that is to say, an enemy that starts a new chapter is considered to "belong to" the new chapter).
//
// (TODO: discuss chapters)
/* 0x4528 */ int32_t own_chapter;
/* 0x452c */ int32_t __bool_452c; // cleared by ECL 570
} /* 0x4530 bytes large */
Interior types:
struct PosVel __packed
{
struct Float3 pos;
char __unknown[0xc];
float speed;
float angle;
float radial_dist;
float radial_speed;
float __field_28__some_angle;
float __field_2c;
float __field_30__some_angle;
struct Float3 velocity;
int32_t flags;
};
struct EnemyDrop __packed
{
int32_t main_type;
int32_t extra_counts[17];
struct zFloat2 area;
};
struct EnemyDropSeason __packed
{
// These fields describe season items dropped on death,
// as configured by the first special ECL instruction
struct Timer bonus_timer;
int32_t max_time;
int32_t min_count; // count dropped when timer reaches max time
// note: max count is not stored here, it's stored in the
// element of EnemyDrop that corresponds to the
// season item index
// These fields describe items dropped by dealing damage,
// as configured by the second special ECL instruction
int32_t damage_per_season_drop;
int32_t damage_accounted_for_season_drops;
};
struct EnemyFog __packed
{
struct Fog* fog_ptr;
char __unknown[4];
float fog_radius;
float __fog_field_c__init_16f;
int32_t fog_color;
float __fog_angle_44d0;
float __fog_angle_44d4;
struct zCOMMENT __exact_size_known[0];
};
struct EnemyLife __packed
{
int32_t current;
int32_t maximum;
int32_t remaining_for_cur_attack;
// This is used instead as the current HP if is_spell is true.
int32_t current_scaled_by_seven;
int32_t starting_value_for_next_attack;
int32_t total_damage_including_ignored;
int32_t is_spell;
};
Interpolators:
struct InterpStrange1 __packed
{
struct Float3 current;
struct Float3 initial;
struct Float3 goal;
struct Float3 bezier_1;
struct Float3 bezier_2;
struct Timer time;
int32_t end_time;
int32_t method_for_1d;
uint32_t __field_58;
uint32_t __field_5c;
int32_t method_for_3d;
int32_t flag_1d;
};
// zInterpFloat, zInterpFloat2, zInterpInt, zInterpInt2, and zInterpInt3
// all look exactly like this, just replacing Float3
struct InterpFloat3 __packed
{
struct Float3 initial;
struct Float3 goal;
struct Float3 bezier_1;
struct Float3 bezier_2;
struct Float3 current;
struct Timer time;
int32_t end_time;
int32_t method;
};
Some earlier games have a bunch of type names in static memory. Some of these strings are even used in leftover debugging statements. Others can also be identified partly with the knowledge that the binary mostly contains object code from cpp
files listed in alphabetical order; so many of the methods appear vaguely in alphabetical order by type name.
My name | ZUN's name |
---|---|
(global) AsciiManager |
AsciiInf |
(global) TranceManager (TH13) |
AstralInf |
(global) Stage |
BackgroundInf |
(global) Bomb (ShotType in older documents) |
BombInf |
(global) BulletManager |
BulletInf (Pre-Mof) EtamaInf |
(global) Spellcard |
CardInf |
(global) Ending |
EndingInf |
(global) EffectManager |
EffectInf |
(global) EnemyManager |
EnemyCtrlInf |
(global) Gui |
FrontInf (Pre-MoF) FRScreenImplInf |
(global) GameThread |
GameTaskInf |
(global) Lolidfk (-TH08) |
Possibly GlobalInf |
(global) HelpManual (TH13-) |
HelpInf |
(global) HintManager (-TH12) |
HintInf |
(global) ItemManager |
ItemInf |
(global) LaserManager |
LaserInf |
(global) SpiritItemManager (TH13) |
LGodsInf |
(global) LoadingThread |
LoadingInf |
(global) Supervisor |
Probably MotherInf , judging from alphabetical ordering... |
(global) PauseMenu |
PauseInf |
(global) Player |
PlayerInf |
(global) DebugSprtView |
Presumably SprtViewInf but haven't seen such a string... |
(global) ResultScreen (pre-MoF) |
ResultSysInf |
(global) DebugSptTest |
SptTestInf |
(global) MainMenuThread |
TitleInf |
(global) DebugSoundTest |
SoundTestInf |
(global) AnmManager |
Probably SprtCtrlInf |
(global) TrophyManager (TH17-) |
|
(global) ReplayManager |
ReplayInf |
(global) PopupManager |
SmallScoreInf |
(global) Ufo (TH12) |
|
(global) GoastManager (TH17) |
|
(global) UpdateFuncRegistry |
FuncCtrlInf (Pre-MoF) funcChainInf |
(struct) PosVel |
PositionInf |
(unsure) | EclViewInf |
(unsure) | EclResourceInf |
(struct) ScreenEffect |
ScreenInf . Yes. Evidence at th08.exe+0x45b91b . |
(laser-related) LaserBaseClass |
LaserCurveInf |
(laser-related) LaserCurve |
LaserCurveInf |
(laser-related) LaserInfinite |
LaserInfiniteInf |
(laser-related) LaserLine |
LaserLineInf |
(laser-related) LaserBeam |
LaserBeamInf |
(laser-related) unknown | LaserCurveVertexInf |
(laser-related) unknown | CurveEquationInf |
(anm-related) AnmId |
SprtId |
(anm-related) | SprtGroupTexInf |
(anm-related) | SprtGroupInf |
(anm-related) | SprtInf |
(anm-related) | SptInf |
(anm-related) | SptBaseInf |
(hint-related) | HintDataInf |
(replay-related) ReplayStageData |
ReplayStageDataHeaderInf |
(replay-related) possibly ReplayChunk |
ReplayRecDataInf |
(unsure) | FPSInf |