Notes on run-time objects
There are two arrays of objects that I've started to decode. I'm naming them "Simple Objects" and "Complex Objects" for the lack of better terms. These names mostly reflect the size of data maintained for each. I have a more thorough listing of these notes and decompiled routines in this .txt file which I've checked into a fork of shygoo's mk64project repo.
Course_Objects.txt
Edit: Bonus gif to show modifying Mario Raceway object rotation function (8029AB60)
Simple Objects
The simple object array is stored at 8015F9B8-80162578 (0x64 number of objects, each 0x70 bytes long). Objects such as the following are stored there:
- shells
- bananas
- item boxes
- trees, cacti, shrubs
- piranha plants
- falling rocks
- spinning signs
- train, railroad crossing
The structure of each simple object contains the following. There is clear variation in data stored at offset 0x24, so I indicate those as "union". However, offset 0x08 is used as float in some cases, so the entire structure may be a union for each type. If you want to help, I could use some help decoding these values and better determining which types use them.
Code:
typedef struct {
i16 type; // 00: used in switch at 802A3568
i16 flags; // 02: non-zero if used, 0x800 = gone (tree hit by star)
i16 i16_04; // 04: 1 = visible, -1 = not visible, used as timer in KD RR crossing
i16 i16_06; // 06: TODO some sort of state: 3 = touched by kart?
i16 i16_08; // 08: TODO, also float in proc_8029817C(), proc_802A10F0(), proc_802B0E98()
i16 i16_0A; // 0A: TODO
float f32_0C; // 0C: float in fake item box
i16 rotX; // 10: rotation about X axis (e.g., rotating item boxes)
i16 rotY; // 12: rotation about Y (vertical) axis (e.g., Mario Raceway spinning signs)
i16 rotZ; // 14: rotation about Z axis (e.g., rotating item boxes)
// 16-17
float poxX; // 18: X position
float posY; // 1C: Y position
float posZ; // 20: Z position
union {
struct item_box {
float f32_24; // 24: TODO
float f32_28; // 28: TODO fake item box
};
struct piranha {
i16 i16_24; // 24: TODO: state in piranha plant?
i16 i16_26; // 26: TODO: state in piranha plant?
i16 i16_28; // 28: TODO: state in piranha plant?
i16 i16_2A; // 2A: TODO
};
struct triple_shell {
float f32_24; // 24: TODO
float f32_28; // 28: TODO
float f32_2C; // 2C: TODO
};
struct rocks {
float f32_24; // 24
float f32_28; // 28
float f32_2C; // 2C
// 30,34,38?
float f32_3C; // 3C
float f32_40; // 40
float f32_44; // 44
float f32_48; // 48
float f32_4C; // 4C
float f32_50; // 50
float f32_54; // 54
float f32_58; // 58
float f32_5C; // 5C
float f32_60; // 60
float f32_64; // 64
// 68,6C?
};
}
// ?-6F
} SimpleObject;
The halfword at offset 0x00 designates the type of object and is handled in function UpdateSimpleObjects/802A3548 which loops through all objects in the simple object array each cycles with a large switch statement. I have decoded all of the types handled here:
Type | Handler | Description |
0x02 | 80297D04 | trees in Mario Raceway (27x) |
0x03 | 80297D04 | trees in Yoshi Valley (13x) |
0x04 | 80297D04 | trees in Royal Raceway (24x) |
0x05 | 8029D188 | Choco Mountain (x3) falling rocks? |
0x06 | 802B2034 | single banana |
0x07 | 802B32C4 | single green shell |
0x08 | 802B4218 | single red shell |
0x09 | 80297BFC | Yoshi's Valley egg |
0x0A | 802981EC | piranha plant |
0x0C | 802A1600 | item box |
0x0D | 802A10F0 | fake item box |
0x0E | 802B0A28 | bunch of bananas |
0x0F | 8029817C | something in Kalimari Desert 2x(1x) engine? |
0x10 | 802981CC | something in Kalimari Desert 2x(1x) caboose? |
0x11 | 802981DC | something in Kalimari Desert 2x(5x) train cars? |
0x13 | 80297D04 | trees in Moo Moo Farm (21x) |
0x15 | 802B0E98 | triple green shell? |
0x16 | 802B0E98 | triple red shell |
0x17 | 8029AB60 | Mario Raceway spinning signs |
0x19 | 80297D04 | trees in Koopa Troopa Beach (12x) |
0x1A | 80297D04 | trees in Luigi Raceway (20x) |
0x1B | 80297D04 | TODO: couldn't find it used |
0x1C | 80297D04 | trees by castle in Royal Raceway? (8x) |
0x1D | 80297D04 | trees in Frappe Snowland (30x) |
0x1E | 80297D04 | cacti in Kalimari Desert (44x of 30, 31, 32) |
0x1F | 80297D04 | cacti in Kalimari Desert |
0x20 | 80297D04 | cacti in Kalimari Desert |
0x21 | 80297D04 | shrubs in Bowser's Castle (27x) |
0x23 | 8029AAC8 | Wario Stadium (3x) spinning sign |
0x26 | 8029816C | paddle wheel on boat in DK's |
0x27 | 8029AAD8 | something in Kalimari Desert (x4) railroad crossing? |
0x2A | 802B4218 | single blue shell |
0x2B | 802A156C | item box under balloon in Luigi Raceway |
0x2D | 80297D5C | kiwano fruit in DKJP |
Complex Objects
The complex object array is stored at 80165C18-80183D57 (0x226 objects, each 0xE0 bytes long). Objects such as the following are stored there:
- hot air balloon
- shell fire trails
- thwomps
- TODO: many more
The structure of each complex object contains the following and haven't seen any variation in the data types between elements yet, so it probably did not contain a union. Many of these elements are TODOs.
Code:
typedef struct {
float f32_00; // size (scale) read from *0x800EEB14 (0x3E99999A = 0.3)
float f32_04; // current X position?, from proc_8008BF18
float f32_08; // current Y position?, from proc_8008BF18
float f32_0C; // current Z position?, from proc_8008BF18
float f32_10; // base X position?
float f32_14; // base Y position?
float f32_18; // base Z position?
float f32_1C; // start of structure passed through A1 to proc_80042A20
float f32_20; //
float f32_24; //
float f32_28; // delta X position used in proc_80050E34 (04 = 10 + 28)
float f32_2C; // delta Y position used in proc_80050E34 (08 = 14 + 2C)
float f32_30; // delta Z position used in proc_80050E34 (0C = 18 + 30)
float f32_34;
float f32_38; // proc_80054E10
float f32_3C; // proc_80054E10, proc_80074924
float f32_40; // proc_80054E10
float f32_44; // proc_80054E10, proc_8004A6EC, proc_80055CCC, proc_800568A0
i32 i32_48; // proc_80073E18
i32 i32_4C; // init to -1 in proc_8006EE7C, used in proc_8007375C
u32 u32_50; // proc_8007278C
u32 u32_54;
u32 u32_58;
u32 u32_5C;
i32 i32_60; // init to -0x2128 in proc_8006EE7C
i32 i32_64; // init to -0x128 in proc_8006EE7C
i32 i32_68; // init to -0x2128 in proc_8006EE7C
i32 i32_6C; // init to -0x128 in proc_8006EE7C
u32 u32_70; // proc_80055164
u32 u32_74; // proc_800518F8, proc_800519D4, proc_80055164
// 78
// 7C
// 80
i16 i16_84; // proc_80053D74, proc_80054AFC, proc_80074924
i16 i16_86; // proc_80053D74, proc_80054AFC, proc_80074924
i16 i16_88; // proc_80053D74, proc_80054AFC, proc_80074924
i16 i16_8A; // proc_80053D74, proc_80074924
i16 i16_8C; // proc_80053D74, proc_80074924
i16 i16_8E; // proc_80053D74, proc_80074924
i16 i16_90; // proc_80053D74, proc_80074924
i16 i16_92; // proc_800528EC, proc_80074924
u16 u16_94; // proc_80085878, proc_80088364, proc_8008B284, proc_8008B620, proc_8008B6A4
i16 i16_96; // proc_80088364, proc_8008B284, proc_8008B620, proc_8008B6A4
u16 u16_98; // proc_8007AC9C, proc_8008B478, proc_8008B620, proc_8008B6A4
// 9A
i16 i16_9C; // casts f32_04 to u16 in proc_8008BFC0, read in proc_80051ABC, proc_80051C60
i16 i16_9E; // casts f32_08 to u16 in proc_8008BFC0, read in proc_80051ABC, proc_80051C60
i16 i16_A0; // opacity? starts at 0xFF and decays to 0x00
i16 i16_A2; // proc_80054324, proc_80055164
i16 i16_A4;
i16 i16_A6; // proc_80074E28, proc_80074EE8, proc_80075CA8
// i16? A8
i16 i16_AA; // proc_8007401C
i16 i16_AC; // proc_800738A8, proc_80073A10
i16 i16_AE; // proc_8008BFFC, proc_80074D94
i16 i16_B0; // proc_8007FB48, proc_8007FB48, proc_80087060
u16 u16_B2; // start of structure passed through A1 to proc_800484BC
u16 u16_B4;
u16 u16_B6;
u16 u16_B8; // start of structure passed through A1 to proc_80042E00
u16 u16_BA;
u16 u16_BC;
u16 u16_BE; // start of structure passed through A1 to proc_80042E00
u16 u16_C0; // proc_80055CCC, proc_80070250
u16 u16_C2; // proc_80055CCC, proc_8005A14C
// C4
// C8
u8 u08_CA; // is used flag?
u8 u08_CB; // proc_8007278C, proc_80072B48
i8 i08_CC; // proc_80072C00
i8 i08_CD; // proc_8007401C
i8 i08_CE; // proc_8007401C
u8 u08_CF;
i8 i08_D0; // proc_800738A8, proc_80073A10
i8 i08_D1; // proc_8007CC00, proc_8007FB48
i8 i08_D2; // proc_800557B4, proc_800747F0
i8 i08_D3; // proc_80073404, proc_8007466C, proc_800747F0
i8 i08_D4; // proc_80072C00
u8 u08_D5; // proc_800750D8, proc_80075698
u8 u08_D6; // proc_80073600, proc_80073654
u8 u08_D7; // proc_800724F8
u8 u08_D8; // proc_800557B4, proc_800723A4
u8 u08_D9; // proc_800518F8, proc_800519D4
u8 u08_DA; // proc_800518F8, proc_800519D4
u8 u08_DB; // proc_80073FAC
u8 u08_DC; // proc_8007381C
u8 u08_DD; // proc_8008275C, proc_800850B0
u8 u08_DE; // proc_8008BFFC
u8 i08_DF; // proc_80053870
} CourseComplexObj;
Unlike the simple objects that are passed around by pointer, the complex objects are passed by index and each subroutine indexes into the global complex course objects. For example, routines at 8008B7D4, 8008B80C, 8008B8BC initialize different sections of the structure to provided values as follows:
Code:
#define COURSE_OBJ_COUNT 550 // 0x226
CourseComplexObj course_objs[COURSE_OBJ_COUNT]; // stored at 0x80165C18
void proc_8008B7D4(int idx, float a1, float a2, float a3)
{
course_objs[idx].f32_10 = a1;
course_objs[idx].f32_14 = a2;
course_objs[idx].f32_18 = a3;
}
void proc_8008B80C(int idx, float a1, float a2, float a3)
{
course_objs[idx].f32_28 = a1;
course_objs[idx].f32_2C = a2;
course_objs[idx].f32_30 = a3;
}
void proc_8008B8BC(int idx, u16 a1, u16 a2, u16 a3)
{
course_objs[idx].u16_B2 = a1;
course_objs[idx].u16_B4 = a2;
course_objs[idx].u16_B6 = a3;
}