Mushihimesama Futari/Reverse engineering notes
This page contains notes gathered while reverse engineering the futari15 mame romset. Addresses will be different for other versions of the game.
Contents
Players
Most of the player1 data is stored in the range [0xc38aabc..0xc38ab73]. Setting 0xc38ab24 to 0x7fffffff makes the player invulnerable, which can be useful while debugging enemy patterns.
Enemy Bullets
Enemy bullets are stored in a doubly linked list (head: 0xc38a3b8, tail: 0xc38a3bc).
The physical data of the bullets is stored in an array of 2000 structures in [0xc3c64d8..0xc40e917].
0xc06db00: schedule_enemy_bullet
signature: ```struct enemy_bullet_plan * schedule_enemy_bullet(byte type, int x, int y, short speed, short angle)```
This function is used to create a single bullet or a simple pattern.
All bullets constructed on the current frame are temporarily stored in an array of 500 structures in [0xc40e934..0xc41b453].
This is a great place place to start from when trying to find the AI of an enemy: simply put a breakpoint in this function when the desired enemy is about to shoot a bullet and when the breakpoint gets triggered, step out of the function to find where it was called from (which is usually the AI of the enemy).
0xc06dc90: spawn_enemy_bullets
signature: ```void spawn_enemy_bullets(struct gamestate *state)```
This function spawns all the bullets planned by 0xc06db00. Notice that a single bullet plan can spawn multiple bullets.
0xc06e350: new_enemy_bullet
signature: ```struct enemy_bullet * new_enemy_bullet(int type,int x,int y,ushort speed,ushort angle,void *param_6,short param_7)```
This function spawns a single bullet.
Enemies
Enemies are stored in a doubly linked list (head: 0xc38a2c8, tail: 0xc38a2cc).
The logic data of enemies is stored in an array of 128 structures in [0xc41b560..0xc464560].
The physical data of the enemy is dynamically allocated in [0xc464560..0xc47bc5f] by the enemy_entity_malloc function at 0xc075990.
Additional data can be allocated dynamically in "closures" (allocated in [0xc491e18..0xc498217]).
Each enemy has a different constructor and uses its dynamically allocated memory in a different way (e.g. the position is not always at the same offset).
The pointers to the constructors of the enemies are listed at [0xc2857e0..0xc285bdf].
For example, enemy 88 is the flying lobster from stage 2, therefore its constructor is at 0xc2857e0 + 4*88 = 0xc285940.
Depending on the game mode (original=0, maniac=1, ultra=2), which is stored at 0xc38a580, different update logics are selected for the enemies. For example, the flying lobster has 3 different update functions for the different game modes: 0x0c0b5f20 for original 0xc0b6210 for maniac, 0xc0b6440 for ultra.
Bosses are more complicated, as their AI changes over time. Each phase is usually represented by a closure a function pointer to the updater function which is called once per frame together with its contextual data (e.g. number of frames to wait before shooting the next bullets). There are different closures for shooting AI, movement AI, rendering... These closures are used to implement a sort of parallelism even though this game runs on a single thread.
As an example, the shooting AI of the second pattern of the first phase of the stage 2 ultra boss is at 0xc174cf0, but the chain of function pointers that link the constructor to that pattern is 8 pointers long:
- 0xc15e890 stage2_ultra_boss_constructor()
- 0xc166ad0 (allocates 12 bytes closure for next function)
- 0xc1668e0 stage2_ultra_boss_spawning_animation_updater()
- 0xc16a5a0 (allocates 16 bytes closure for next function)
- 0xc16a620 stage2_ultra_boss_phase1_updater()
- 0xc16a8e0 (allocates 82 bytes closure for next function)
- 0xc16afa0 stage2_ultra_boss_phase1_pattern2()
- 0xc1747d0 (allocates 64 bytes closure for next function)
- 0xc174cf0 stage2_ultra_boss_phase1_pattern2_shoot()
Other useful functions
0xc0631f0: randint
signature: ```ushort randint(ushort max)```
Random number generator (RNG). Uses the standard C LCG