Skip to content

Instantly share code, notes, and snippets.

@ExpHP
Last active June 22, 2020 16:04
Show Gist options
  • Save ExpHP/9547aee1b37682f934da399a7d2b41f4 to your computer and use it in GitHub Desktop.
Save ExpHP/9547aee1b37682f934da399a7d2b41f4 to your computer and use it in GitHub Desktop.
totally-random-thre-notes

Completely random Touhou reversing notes

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.

Early reversing:

(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's main.
  • 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:
    mov   dword [UPDATE_FUNC_REGISTRY], 0x0
    jmp   <somewhere>
    Look at the functions that call UPDATE_FUNC_REGISTRY. Two of these will have many more references than all of the others. These are register__on_tick and register__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 either UpdateFunc::operator new, malloc, or register__on_tick. (note: to help identify malloc, UpdateFuncs are 0x28 bytes in modern games)
  • Go through the references of register__on_tick. 90% of on_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) calls malloc, (2) TypeName::constructor (which often gets inlined), (3) calls TypeName::initialize, and (4), on failure, calls TypeName::destructor followed by free.
      • TypeName::constructor: Typically calls constructors of fields, then negates all of that hard work by doing a memset(this, 0, sizeof(*this)), and finally writes this to a global variable.
        • On BulletManager and ItemManager you'll see it calling initialize_vector(size, count, constructor, destructor).
      • TypeName::initialize: Often makes calls to anm_preload, register__on_tick and register__on_draw.
      • TypeName::on_tick, TypeName::on_draw: These are functions you see being pushed or written to fields shortly before calls to register__on_tick or register__on_draw.
      • Typename::destructor: This calls destructors of its fields, calls free on allocated pointers, and you will often see it entering the UpdateFuncRegistry's CRITICAL_SECTION so that it can call UpdateFuncRegistry::unregister.
    • Things are slightly different for objects that represent threads. These have a thread_start function. Inside either operator new or initialize, you'll see the thread_start callback being pushed or written to a field shortly before a call to some subroutine that calls CreateThread. For some of them there's no initialize function, and the update funcs are registered inside thread_start.
  • Supervisor::switch_gamemodes: You should encounter Ending pretty early on (somewhere around 4th to 10th depending on how many Bombs produce screenshake). When you look at what calls Ending::operator new, you'll find yourself in a case of a switch statement. This function is the member function Supervisor::switch_gamemodes.
  • Look at the previous cases in Supervisor::switch_gamemodes. Most of them will end in a call to GameThread::operator new. The 0x0 case will call LoadingThread::operator new.

Key concepts

Hardware input versus game input

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.

Linked lists and their mysterious fourth field

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, UpdateFuncs 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.

Flags

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.

on_tick priorities

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 Engine Table

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).

TH16 Enemy field comments

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;
};

Table of type names

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment