Stale Reference Manipulation is a glitch in Ocarina of Time and Majora's Mask (as well as their 3D remakes) that allows you to overwrite nearly anything stored on the Actor Heap with the position and angle data of an unloaded actor that is currently attached to a loaded actor.
The actor heap is a region of memory set aside to contain all the currently loaded actors and their code. It is structured as a doubly-linked list which is always aligned to the nearest 0x10.
We can manipulate the actor heap by allocating actors (such as dropping bombs) deallocating actors (such as destroying pots) and loading new rooms (via loading plane or door, etc). This process is called a heap manipulation or heap manip for short. It is used to make sure the unloaded actor is writing to the desired part of memory.
Type | Alternate Name | Contains | General Computer Science Term |
---|---|---|---|
Actor | Actor Instance | Data | Object |
Overlay | Actor File | Code and Data | Class |
Other | N/A | Extra Data | N/A |
Builds | Link Size |
---|---|
Japanese N64 | 0x30 |
International N64 and all GameCube | 0x10 |
Link's Address is given as the address of his Actor Instance for convenience (since we frequently care about data inside him). All other addresses are where the link header will allocate. These two types of address can easily be converted to the other by either subtracting or adding the link header size for your version, respectively.
Tatl is included because she only allocates if you have acquired her (cutscene where Deku Link shrugs his shoulders in Pre-Clocktower). If we were somehow able to skip this in the future, the usable start of the heap would shift downwards to where Tatl usually is. More realistically, if you're using KZ (the MM practice ROM), and you warp away from Lost Woods without triggering that cutscene, Tatl will not allocate either, so the heap start address will be different. Note there is a setting in KZ (File, Have Tatl) that you can set to make the heap how it would normally be in a run.
Build | Build Date | Link (Actor) | Tatl (header) | Start of Usable Portion (header) |
---|---|---|---|---|
JP 1.0 | 00-03-31 02:22:11 | 0x803FFFA0 | 0x80406580 | 0x8040B3B0 |
JP 1.1 | 00-04-04 09:34:16 | 0x80400260 | 0x80406840 | 0x8040B670 |
US 1.0 | 00-07-31 17:04:16 | 0x803FFDB0 | 0x80406350 | 0x8040B140 |
PAL 1.0 | 00-09-25 11:16:53 | 0x803F7250 | 0x803FD7F0 | 0x804025E0 |
PAL 1.1 | 00-09-29 09:29:41 | 0x803F75F0 | 0x803FDB90 | 0x80402980 |
US GCN | 03-08-26 04:20:25 | 0x8039A4F0 | 0x803A0A90 | 0x803A5880 |
PAL GCN | 03-10-04 00:40:20 | 0x80392140 | 0x803986E0 | 0x8039D4D0 |
JP GCN | 03-11-06 01:25:18 | 0x8039A4E0 | 0x803A0A80 | 0x803A5870 |
Stale references can be obtained in a variety of ways. The main constraint is that there must be a loaded actor that is attached to an unloaded actor. As such, the different methods are distinguished by which loaded actor is attached (and how, in the case of ambiguity).
By far the easiest method to achieve SRM is via an unloaded actor that Link is carrying above his head. The simplicity of this arrangement comes from the fact that you can adjust the unloaded actor's position and angle by simply moving Link around.
While a loaded actor can be carried in Link's hands by simply standing in front of it, then pressing A to pick it up, obtaining an unloaded actor requires specific conditions.
One option requires the actor you wish to unload to not be on camera as you move through a loading plane (actor that loads a new room within the scene by walking through it). This causes it to "cull" (stop drawing), which causes the game to not load the actor in the new room, but for Link to somehow still be carrying it. This appears visually in the game as Link carrying nothing above his head.
To achieve this constraint, the only method known to be possible in MM is to do a superslide off the actor we wish to carry an unloaded version of through a loading plane while the actor is off camera. Most places we can do Superslide Carry SRM require us to get Inverted Camera prior to supersliding in order to achieve this, however there is at least one exception (Deku Palace). Before sliding backwards through the loading plane, we must release target so that the camera does not snap back to where the actor is.
Superslide Carry SRM was originally discovered prior to SRM being fully understood.
It's also possible to acquire a held stale reference by picking up an actor during/after a night transition. This method is not thought to be useful, but it has been known about long before SRM was fully researched. The long-known method is to perform grab delay on the SCT dog during the day, then initiate the pick-up animation once the night transition happens (which causes the dog to unload).
Another method is to hold an explosive in your hands, get frozen in some way (Either by bringing up a textbox or getting screamed at by a Gibdo/ReDead), let the explosion finish, load something else in the explosive's place, then get unfrozen. Loading something else in the explosive's place is more challenging in MM, since we neither have a highly manipulable camera like OoT, nor do we have a combined heap like MM3D (our heap contains only actors), however it is possible to load a collectible item drop in its place, shown here with hearts.
Note that there are other known ways to achieve Carry SRM in other Zelda 64 games. None of these methods are thought to be possible in Majora's Mask, but they may be in the future.
In OoT, a method very similar in function to superslide SRM is used. This works by using the Walking While Talking glitch to lock the camera far away as you pick up an actor, causing you to pick up a culled version of that actor. After that, walking into a loading plane will unload the culled actor, yielding a stale reference. This method of Carry SRM is thought to be impossible in MM since there's no known way to achieve Walking While Talking in any scenes with a grabbable actor and a loading plane.
In OoT, there is another method of obtaining a carried stale reference that is similar to superslide SRM. This method works by first obtaining the type of Get Item Delay which causes the item model to be inside Link's body, breaking a regrowable grass and picking it up while broken, then going through a loading plane while the grass is culled (off camera). Notably, this method results in a form of Carry SRM that only writes the unloaded grass's angle, whereas other methods also write position. This method is not thought to be possible in MM since there no known way to get this type of GID in a scene that has regrowable grass and a loading plane.
Zora Link's fins can grab onto something, and thus they can serve as the loaded actor in the SRM pair.
To achieve SRM with a Zora fin, the fin must latch onto something "permanent" in the room (such as a rupee that never unloads, a gold skulltula token, or a heart that appears in place of a heart piece once you've already collected it) and must carry it back to Link who must be standing in a different room. The actor latched on to by the Zora Fin must be off camera (culled) when it enters the new room.
Zora Fin SRM is the only form that was not previously known prior to the SRM Renaissance. The earliest known example of it in a video was an MM investigation following the original discovery in OoT.
This method is much harder to control than Carry SRM, since we must control where the attached actor drops in order to set the position/angle to a useful value. Since the fins are constantly moving towards Link until he catches them, this adds a significant amount of complexity to any setup.
On the positive side though, Zora Fin SRM is the only variant known to be possible in rooms that use different types of loading actors besides loading planes (such as doors or staircases). This is because these other types of actors act as collision in most cases (except for pressing A to open a door, or facing the staircase as you walk into it). But since the Zora Fins follow Link wherever he goes, he's able to divert attention away from them long enough to pass through these other types of loading actors.
Arrows shot from the bow are able to attach themselves to targets, which allows them to be used as the loaded actor in the SRM pair. It might be possible to attach an arrow to other actors besides targets as well, but that is not currently known.
The only place where Arrow SRM is known to be possible is during Boat Archery. You can shoot an arrow at Koume's target right as she carries it through the loading plane, causing it to unload. This will visually appear as an arrow floating in midair (since it's attached to an unloaded target).
After you go through the loading plane, something will load in at the address of the unloaded target. Once that arrow unloads, the next arrow shot will load in at the same address, enabling it to modify the very same data.
The earliest known example is a clip of fullgrowngaming. In this case, the actor heap lined up in such a way that the door to the boathouse loaded at the exact address the target used to be. As such, each time a new arrow was shot, the X, Y, and Z position of the door would be modified to be that of the new arrow. The door would continue moving alongside the arrow until it collided with something, at which point both the door and arrow remained motionless. Subsequent arrow shots would not move the door until the arrow that previously modified the door unloaded (which happens about 5s after being shot).
Note that Deku Bubbles are the same actor as Arrows. This means that if a Deku Bubble could attach itself to another actor (like Arrows can attach themselves to Targets), doing SRM as Deku would be theoretically possible. This can be verified by hacking the attached actor pointer (offset 0x264) of a Deku Bubble to be nonzero. However, at this time, there are no known actors that Deku Bubble can attach to, rendering this form of SRM significantly less useful.
Since Arrow SRM is currently thought to only be possible during boat archery, it is most likely completely useless, since finishing boat archery loads a new scene before you regain control, so it would be impossible to benefit from the results of the SRM.
Some more details on this form of SRM are provided here:
Perhaps the least understood method, but also one of the methods that has been known about long before SRM was properly researched, it is possible to acquire a stale reference by riding Epona into a loading plane to unload her. In this case, Link serves as the loaded actor and Epona serves as this unloaded actor. However, unlike Carry SRM, the unloaded actor is not being carried by Link, but rather is being ridden by him.
Note that the other methods of SRM require the presence of a particular type of actor (either grabbable, boomerangable, or arrow-attachable), but this method should be possible anywhere there's a loading plane, assuming we are able to bring Epona there. Unfortunately, however, Epona SRM seems to do very little writing to memory, and instead does a lot of reading of memory. These reads tend to be corrupt due to the fact that Epona has been unloaded, so the data is garbage. This can lead to lots of interesting effects like in the video.
Some more details on this form of SRM are provided here:
The attachment between Link and the unloaded grabbable actor is by way of a pointer located at Link + 0x388.
Offset | Name | Size | Type | Value Written |
---|---|---|---|---|
0x90 | Scene Collision Flags (bgCheckFlags) | 16-bit | integer | Current Value & 0xFF00 |
0x120 | Parent Actor | 32-bit | pointer | Link's Address |
Offset | Name | Size | Type |
---|---|---|---|
0x24 | X Position | 32-bit | floating-point |
0x28 | Y Position | 32-bit | floating-point |
0x2C | Z Position | 32-bit | floating-point |
0x32 | Y (roll/vertical) Angle | 16-bit | integer |
0xBE | Y (roll/vertical) Facing Angle | 16-bit | integer |
Note that if the high bit of Byte 5 in the stale reference is set, then Link will lose the ability to drop it (A button will always say Throw, even if standing still).
Offset | Name | Size | Type | Value Written (Thrown) | Value Written (Dropped) | Value Written (Shield Dropped) |
---|---|---|---|---|---|---|
0x68 | Y Velocity | 32-bit | floating-point | 0x41400000 | 0x00000000 | N/A |
0x70 | Horizontal Speed | 32-bit | floating-point | 0x41000000 | 0x00000000 | N/A |
0x120 | Parent Actor | 32-bit | pointer | 0x00000000 | 0x00000000 | 0x00000000 |
The facing angle (offset 0xBE) is the preferred value to use to write our desired data with. Due to being an integer, it's possible to achieve any arbitrary 16-bit value with relative ease. Furthermore, it is also far from the floating-point fields which are not possible to achieve arbitrary 32-bit values with (although we can control some subset of the 32 bits).
However it is important to note that we cannot always use facing angle to write our desired data due to the alignment of the heap. The heap is always aligned to multiples of 0x10, which means that the low nybble/hexit of the offset of the field we wish to manipulate entirely determines which field of our unloaded carried actor we must use to write the data. Fortunately, due to the spread of the offsets of the various fields, we are able to manipulate any part of an actor/overlay whose offset does not end in 0x0 or 0x1 using Carry SRM.
The attachment between the fins and the unloaded actor is by way of a pointer located at Fin + 0x1C8.
N/A
Offset | Name | Size | Type |
---|---|---|---|
0x24 | X Position | 32-bit | floating-point |
0x28 | Y Position | 32-bit | floating-point |
0x2C | Z Position | 32-bit | floating-point |
Offset | Name | Size | Type | Value Written |
---|---|---|---|---|
0x4 | Flags | 32-bit | integer | Current Value & 0x0000DFFF |
The attachment between the arrow and the unloaded actor is by way of a pointer located at Arrow + 0x264.
N/A
Offset | Name | Size | Type |
---|---|---|---|
0x24 | X Position | 32-bit | floating-point |
0x28 | Y Position | 32-bit | floating-point |
0x2C | Z Position | 32-bit | floating-point |
Offset | Name | Size | Type | Value Written |
---|---|---|---|---|
0x4 | Flags | 32-bit | integer | Current Value & 0xFFFF7FFF |
The attachment between Link and the unloaded climbable actor is by way of a pointer located at Link + 0x390.
Offset | Name | Size | Type | Value Written (FD, child Epona) | Value Written (Goron) | Value Written (Zora) | Value Written (Deku) | Value Written (Human, adult Epona) |
---|---|---|---|---|---|---|---|---|
0x116 | TextID | 16-bit | integer | 0x1804 | 0x1804 | 0x1805 | 0x1806 | 0x1806 |
SRM is an extremely powerful and versatile glitch. As such, it is important to classify the different things we can achieve with SRM for the purpose of determining what is and is not allowed in various speedrun categories.
The Actor Heap contain lots of different data. This level includes modifying any data contained in an Actor or Overlay or generic allocation except for fields containing pointers (see Level 2). It is very easy to determine whether a field contains a pointer because a pointer is always a 32-bit value beginning in 0x80, whereas other 32-bit values tend to be floating-point which means they begin with either a 0x4 or 0xC.
This is the least powerful thing currently known to be possible to do with SRM. As such, it is allowed in every category that allows SRM.
The Actor Heap also contains pointers to functions. Modifying these allows us to alter which function gets called when various in-game events happen (such as looking at an actor which is normally supposed to call the draw function, but we can make it call other code instead).
Note that instead of calling a different function using this method, we can also cause a jump into the middle of a function. This is called Partial Function Calling (PFC). It is a stronger subset of FPM that enables register modifications occurring prior to the call to be preserved inside the function.
As this technique is quite a bit more powerful (due to allowing us to run different code than usual) than Level 1, it is currently banned in every category that does not also allow Level 3 (ACE).
It is also possible to modify the code section of an Overlay, or to hijack a function pointer using Level 2 to point at some data we've written (often the specific filename we chose), which will cause the game to execute that data as code.
This level is distinguished by being able to write and execute the same regions of memory. Modern consoles such as the 3DS have protections against this, but the N64 does not.
Executing player-written code is the single most broken glitch any game can have. As such, this level is banned in all categories except Any%, Low%, and NSR (No Source Requirement).