From z64 wiki
Revision as of 00:55, 6 June 2013 by Spinout (talk | contribs) (Actor Categories)
Jump to: navigation, search

Actor Categories

All overlays loaded through the actor table have a category, there are 12 categories total.

char * actor_types[12] = {
    "Unknown",	"Prop (1)",	"Player",	"Bomb",
    "NPC",	"Enemy",	"Prop (2)",	"Item/Action",
    "Misc",	"Boss",		"Door",	"Chests"
1Prop (1)

Majora's Mask may use 16 different categories.

Loaded Actors

What is currently known about the loading and execution of code from actors in Zelda 64 is that:

  • The first time an actor is loaded (spawned), its actor file is transfered to ROM
    • The transfered file has its offsets relocated
    • Finish this section
  • For each actor instance, actors are given a certain amount of memory, varying for each actor, but the minimum (for compliance with the game's engine) space required is 0x128 bytes, at least in the OoT games (MM may vary).
    • Within this given memory is information such as
      • Location
      • Rotation
      • Scale
      • Pointer to damage chart (if the actor has one)
      • Pointers to actor functions
        • Initialization - run when actor instance is loaded.
        • Main - always run during gameplay.
        • Drawing - to be run when actor is close enough to the camera.
      • Pointer to Next/Previous actor(s) of this actor's category
      • Whatever else that actor needs.

At 0x80213C50 within RAM (0x80212020 + 0x1C30) is a list of actor categories (see above), each entry being an integer and a pointer. The pointer points to the first allocated entry for an actor. The integer is the number of actors of that category, the first entry being type 0, and the last last being type 11 (chests). Actors after the first actor are pointed to through the next/previous pointers within each allocated actor.

For Ocarina of Time, the allocated actor structure is as follows. Alternate (better) versions here.

typedef struct {
	f32 x, y, z;
} Coord;

typedef struct {
	u16 x, y, z;
} rotation;

typedef struct
    /* TODO: finish this thing */
    u16 actorNo;
    /* 0x2 */
    u8 actorType;
    /* 0x3 */
    u8 actorStatus;
    /* 0x4 */
    u32 unk_01;
    /* 0x8 */
    Coord Pos1;
    /* 0x14 */
    rotation initRot;
    /* 0x1A */
    u16 unk_02;
    /* 0x1C */
    u16 actorVariable;
    /* 0x1E */
    u16 unk_03;
    /* 0x20 */
    u32 unk_04;
    /* 0x24 */
    Coord Pos2;
    /* 0x30 */
    char buff[8];
    /* 0x38 */
    Coord Pos3;
    /* 0x44 */
    rotation rot_1;
    /* 0x4A */
    u16 unk_05;
    /* 0x4C */
    f32 unk_06; /* I know this is a float from breakpointing it */
    /* 0x50 */
    Coord Scale;
    /* 0x5C */
    char buff2[0x58];
    /* 0xB4 */
    rotation rot_2;
    /* 0xBA */
    char buff3[0x66];
    /* 0x120 */
    u32 actor_prev; /* Previous z_ram_actor of this type */
    /* 0x124 */
    u32 actor_next; /* Next z_ram_actor of this type */
    /* 0x128 */
    u32 unk;
    /* 0x12C */
    u32 ActorInit;
    /* 0x130 */
    u32 ActorDraw;
    /* 0x134 */
    u32 ActorMain;
    /* 0x138 */
    u32 CodeEntry;
    /* 0x13C */
    /* From here on, the structure and size varies for each actor */
} z_ram_actor;

/* 8-10-2012 : Addition made by Jason777 */
/* For actors which contain a damage chart (example: Stalfos)... */
typedef struct
     /* 0x98 */
     u32 DamageChart;  /* Pointer to the actor's Damage Chart in RAM. */
     /* 0xAF */
     u8 Health;
} z_ram_actor;

The Majora's Mask actor structure is similar but not interchangeable.

Actor table

Note to self: Rewrite from here on

PPPPPPPP IIIIIIII NNNNNNNN 00000000                    Repeats for each entry

This breaks down into-
X: Start offset of actor file
Y: End offset of actor file
A: Start Virtual Address of file
B: End Virtual Address of file
P: Address of actor file in ram (If that actor is loaded, is 00000000 otherwise (always 00000000 in ROM)
I: Address of actor info within actor (virtual address)
N: Address of actor filename (only used in debugger's ROM)


Here is a example, the stalfo's entry, at 0x000B8D480 in Debugger's ROM.

00C2E420 00C33CD0 8085F650 80864F00
00000000 80864550 80137048 00000000

So, what can we get from this...
Stalfo's actor lies at 0x00C2E420-0x00C33CD0 in ROM, has a virtual address of 0x8085F650-0x8086F000 (Hint: N64's RAM only goes to 0x00800000 ;)), has actor info at 0x80864550 within the virtual address of the actor, and when loaded into RAM it's name is at 0x80137048

So, how to make use of some of this...

First off, If we want to find our actor's number (though the game puts them in order :P) or it's corresponding object, we take our info virtual address ("I") and subtract it from the virtual begin address ("A"). For the stalfo, this would give us the result of 0x4F00 (0x80864550-0x8085F650=0x4F00). Now, if we go into the stalfo's file (which lies at 0x00C2E420 - 0x00C33CD0) and go to 0x4F00, we should see this:

00020500 00000015 00320000 00000928

Now, I'm not completely sure what this all is, but I do know that the first halfword (0x0002) is the actor number. The upper byte of the second half word (0x05) determines actor category. The fith halfword (0x0032) is the object number.

So what?

Well hey, look here:

0002 0032 Stalfos

(Taken from The actor list)
The actor and object numbers match up

Actor headers

Now we will take a look at what I like to call "actor headers". The actor structure is similar to the elf file structure, in the sense that there are the following parts:

Text block
Data block
Rodata block
BSS block
Relocation block

Back to the stalfo example, if we go to the last word in the actor, we will read 0x00000640. This is the distance we go "up" in the file to get to the header. If your hex editor does not have go to relative up, or you are writing a program, you can take the end of the file address and subtract the value of the header pointer from it to get the pointer. So in the stalfo, we can do:


At 0x5270 in the actor file, we see:

00004EC0 000001F0 000001C0 00000000 

This is to be read word-by-word.

  • The first word (.text) is the amount of bytes (0x4EC0) of [mips r4000] machine code there is, starting at offset 0x0.
  • The second word (.data) is the amount of bytes of data which is accessed by the machine code that varies with or as the actor's routine progresses. (R/W) This can also include inline model data, which can be seen in element arrows and ganon to name a few. This data directly follows the text block.
  • The third word is the read-only (.rodata) block. It usually contains text which is used with the n64 debugging print function (0x80002130) thus contains empty format strings. This data directly follows the data block.
  • The next word is the BSS data, and it is rarely used in the actor file.
  • The last word is the amount of one-word entries of relocation data. Format:

Where: S = section vals:

0 = .text 
1 = .data
2 = .rodata
3 = .bss

T = type vals:

0 = unused?
1 = unused?
2 = 32 bit pointer
3 = unused?
4 = jump target
5 = lui/ori pt. 1
6 = lui/ori pt. 2
7+ = unused?

O = offset (relative to start of section S) This data follows the header and is the last thing in the actor file besides the header pointer.

Custom Actors

ZZT32 decided to get off his ass one day and write nOVL, a tool which converts MIPS elf binaries to Zelda 64 Overlays, also known as actors. Few actors have been (re)written using mips-gcc + nOVL, but there are a few:

  • En_Anim - An actor written to load any animation.
  • En_AnimVar - A fork of the above actor, uses variables.
  • en_vase - The simplest actor in OoT, re-written in C to test nOVL and to document functions.
  • En_Bird - The simplest animated actor in OoT, re-written for the same reason as En_Vase.

zovldis is the opposite of nOVL - it takes an overlay and disassembles it to an assembly file, which, combined with a proper makefile, produces an identical actor as the one which was disassembled. This makes hacking existing actors much more convenient. An example of actor modification using this approach is here (video)


Every actor in Debug MQ disassembled


Cendamos - Actor table information, misc stuff that got the ball rolling
Euler - Actor file structure
JayTheHam - His early work with Cendamos on actor information
ZZT32 - Explained some actor table stuff to me
spinout - Writing this article