Recently, the 100% route has made a seemingly minor route change where the wrong warp from Fire Temple to Forest Temple uses the Bolero Warp Song cutscene instead of the Title Screen as the last cutscene pointer. However, this change opens things up to a potential crash, as seen below.
To begin understanding things, let's start off with some Wrong Warping basics
- Whenever you Wrong Warp, you're actually signaling the game to trigger a cutscene as soon as you spawn into the next area.
- The data for this next cutscene is suppose to be supplied by the destination area (i.e. the destination scene is suppose to contain a command that updates the cutscene pointer). Since Forest Temple is not one of these scenes, the previous cutscene watched affects where the game expects that next cutscene to be in ram
- In order to not crash the game in this situation, the cutscene pointer must either point to data that forms a seemingly valid cutscene, or it needs to fail the valid cutscene check. This check works by reading in the first 8 bytes of the data pointed by the cutscene pointer as two signed 32 bit integers. If either value is negative, no cutscene will play.
Thus, the likely cause of Pit's crash is that the valid cutscene check passed, but the data itself was invalid, causing a softlock. But this isn't enough to explain why the game only crashes some of the time.
Part of the puzzle is the Bolero Cutscene itself. This cutscene is actually a generic "warp in" cutscene used for all warp song spawns (except Prelude) that is stored in the code for actor Demo_Kankyo. This sets the cutscene pointer to a relatively low address, specifically 8020E490 when warping into Death Mountain Crater as an Adult, which makes it possible to overwrite the now stale cutscene data by loading actors or code for particle effects. Since the crash occurred only some of the time, logically it means that something is capable of overwriting the cutscene. But before I get into that, a quick explanation on memory allocation is in order:
There are a few different subdivisions of ram, but eventually they whittle down to something I call "Actor Space". It is a free block list that starts at 801DAA00 and manages the space left over after everything else has been allocated. It's within this space that actor overlays, actor instances, and particle effect overlays will be allocated.
Allocations on the free block list will occur by seeking out a free block large enough for the requested space staring from either a low address to a high address ("head-end"), or from a high address toward a low address ("tail-end"). Anything "tail-end" allocated will persist until the current scene is destructed. All actor instances and most actor overlays are "head-end" allocated, whereas Particle Effect Overlays and the overlays for certain Link weapons (Hookshot, Bombs, Boomerang, etc.) are "tail-end" allocated. Furthermore, there is special group of actor overlays that share a single 0x24E0 byte "tail-end" allocated block.
- Din's Fire (Spell Animation)
- Farore's Wind (Spell Animation)
- Nayru's Love (Spell Animation)
- Possibly the Elemental Arrows
- The music effect that appears around Link when playing one of the 7 non-warp songs
Knowing this, I used my program Spectrum to do a preliminary analysis of how memory was being allocated in the scenes that the player traverses through starting from spawning in Death Mountain Crater up until the moment the cutscene triggers in Forest Temple after the wrong warp.
- Although Death Mountain Crater is close to being able to manipulate 8020E490, it's unlikely this was the case due to the actors spawned by Pit.
- Spawning in Death Mountain Trail immediately overwrites 8020E490 with En_Goroiwa's (Rolling Boulder) overlay. The data written here will cause the valid cutscene check to fail though, which helps things.
- Unsurprisingly, Spawning in Forest Temple's main entrance did not immediately overwrite address 8020E490.
- In Volvagia's Boss room, address 8020E490 is too far away from the low and high ends of the "Actor Space" heap for any manipulations to overwrite the stale cutscene
- Last and most important is Fire Temple. As one progresses through Fire Temple, it's possible to overwrite 8020E490 by triggering enough "tail-end" allocations.
Project64D was the perfect tool for the job. With it, I was able to write a script capable of logging tail-end allocations after a few hours of locating a few necessary functions, and with the help of Fig, I was able to generate a log of all tail-end allocations that looked similar to this:
---- SCENE NEW ---- ENT: 165 Cutscene Pointer: 8020e490 ---- LOAD ROOM 0 ---- 8021a6d0 = c05200:c06030 80867920:80868750 //DOOR 8021a030 = ea88e0:ea8f50 80b2d1b0:80b2d820 //PARTICLE 01 //always loaded 802197d0 = ea80b0:ea88e0 80b2c980:80b2d1b0 //PARTICLE 00 //dust ---- LOAD ROOM 2 ---- 802187d0 = c32fd0:c33fa0 80895700:808966d0 //ROOM CHANGE PLANE 80216520 = c55c10:c57e90 808b8370:808ba5f0 //LIFTING DUNGEON DOOR ---- LOAD ROOM 0 ---- ---- LOAD ROOM 1 ---- ---- LOAD ROOM 20 ---- ---- LOAD ROOM 1 ---- ---- LOAD ROOM 19 ---- 80215790 = cad2c0:cae020 8090fb40:809108a0 //HOOKSHOT 80215210 = eae730:eaec80 80b33000:80b33550 //PARTICLE 15 //hit marker 80214ba0 = eacab0:ead0f0 80b31380:80b319c0 //PARTICLE 0F //floor tile breaking 80214690 = eb3570:eb3a50 80b37e40:80b38320 //PARTICLE 20 //Gold Skulltula Dying ---- LOAD ROOM 1 ---- 80213790 = c0e2d0:c0f1a0 80870a00:808718d0 //BOMB 802131b0 = eaa030:eaa5e0 80b2e900:80b2eeb0 //PARTICLE 05 //Bomb Smoke 80212850 = ea9370:ea9ca0 80b2dc40:80b2e570 //PARTICLE 03 //Bomb Explosion 80212490 = ea9ca0:eaa030 80b2e570:80b2e900 //PARTICLE 04 //Bomb Explosion Blast Ring 802121d0 = eabbc0:eabe50 80b30490:80b30720 //PARTICLE 0C //Lava Walk 80211110 = eb01e0:eb1270 80b34ab0:80b35b40 //PARTICLE 19 //Bombed wall ---- LOAD ROOM 22 ---- ---- LOAD ROOM 1 ---- ---- LOAD ROOM 21 ---- ---- LOAD ROOM 4 ---- ---- LOAD ROOM 5 ---- ---- LOAD ROOM 16 ---- 8020ec00 = cf9120 cfae20 8095ba90 8095d790 //FARORE'S WIND (Size 24E0) 8020e4d0 = eb1ec0 eb25c0 80b36790 80b36e90 //PARTICLE 1C //Link catching fire ---- LOAD ROOM 9 ---- ---- LOAD ROOM 10 ---- 8020ceb0 = c9c880 c9de70 808ff0c0 809006b0 //SPIN ATTACK ---- LOAD ROOM 24 ---- ---- LOAD ROOM 12 ---- ---- LOAD ROOM 13 ----
With this log, I was able to close in on the key allocations: the "Farore's Wind" block, Particle 0x1C's overlay (being burned), and the Spin Attack. Furthermore, if we compare the cutscene pointer (8020E490) to where Particle 0x1C is allocated (8020E4D0), and factor in the 0x30 byte node used to manage each allocated space (located at 8020E4A0, before Particle 0x1C), all but the very first 0x10 bytes of the cutscene end up being overwritten, before the Spin Attack overlay is spawned.
So how does all of this come together? Well...
Remember that spawning on Death Mountain Trail overwrites the "Bolero cutscene" with data that would fail the valid cutscene check. In order to not crash, we either simply have to not overwrite the Bolero Cutscene while in Fire Temple, or we have to overwrite it such that it still fails the valid cutscene check.
The simplest solution to this would be to avoid spawning the Spin Attack overlay, since as I just mentioned, every other tail end allocation that would be encountered with this route miraculously adds up to being just 0x10 bytes (the absolute minimum) of overwriting the critical part of the cutscene. However, this is not the only solution. In fact, the above log (with the Spin Attack loading) will not crash, as the last 0x10 bytes of the Spin Attack Overlay that overwrites the first 0x10 bytes of the cutscene will fail the cutscene check. Another workable ordering is to Spin Attack before casting Farore's Wind, but without getting burned until after FW has been cast.
As for Pit's crash, the fatal mistake he made was simply not getting burned before performing the Spin Attack to break open the pots.
Lastly, some final vital notes:
- The Spin Attack overlay will load any time you either hold B long enough to possibly start a quick spin, or when doing a quick spin, even if you do not have magic.
- Playing the Song of Time spawns Oceff_Wipe4, which shares the same allocation block with Farore's Wind. As such, playing the song to move the Song of Time block will change things up in ways that are harder to predict if you also accidentally spin attack later on