While looking into what it would take to relocate the general memory pool (0x8005C000-0x801C1000) to expansion RAM, I noticed something odd with Level script 0x16 that caused the game to crash. From shygoo's notes:
0x16 (8037EC54): Load raw data from ROM to absolute RAM address
16 10 00 00 [XX XX XX XX] [YY YY YY YY] [ZZ ZZ ZZ ZZ]
X = RAM address
Y = ROM address start
Z = ROM address end
Call 0x802786F0 (A0 = X, A1 = Y, A2 = Z)
This is 100% correct. However, the function it calls (FixedCopy/802786F0) makes a large assumption that the destination RAM address is within the memory pool range. SM64 only uses this level script for copying the main menu asm code to RAM. I spent some time decompiling the FixedCopy routine, which you can find below. There are two problems with the code for general use: 1. it calls _pool_alloc() and 2. it computes the length to copy from pool_tail - ram instead of rom_end - rom_start. Without some modifications, this makes it practically unusable for anything but what SM64 already uses it for.
I'm also attaching an updated decompiled level scripts C file which includes some more level scripts and all the _pool_alloc(), _pool_free() and related data structures.
Today I learned that you can actually load data directly from the ROM cartridge and put it into a register. I had always just assumed that you needed to use the DMACopy function if you wanted to use any data that is stored on the cartridge. This means that we can have jump tables inside the ROM file itself and not have to worry about where to put them in RAM. This makes it trivial to add in new custom level script commands into the game.
I've written some example code for creating a new custom levelscript command. This simple code does 3 main things:
Modifies function 0x803805C8 to read from a jump table at 0xB07E0000 instead of 0x8038B8B8.
I overwrote the unused function 0x8024B940 to be the code for my new levelscript command.
I copied the table data from ROM address 0x108638 to 0x7E0000, and then added a new entry at the end of the list. I chose 0x7E0000 since that region is not being used by the original 8MB ROM or the extended ROM.
The new levelscript command I created doesn't do anything important. My new 4-byte levelscript 0x3D command takes the value located at 0x80370000 and adds it with the signed halfword in the 0x3D command. Basically, [ 3D 04 01 00 ] is the same as: (*80370000) += 0x100. The region around 0x80370000 is empty and not being used by anything, so it will have no effect in-game.
Spoiler: Source code
// Assemble with Armips v0.9
// This function is modified to get the function pointer for a command from the table at 0xB07E0000. .orga0x0FD348
LevelScriptLoad: // begin 803805C8 (0FD348) addiusp, sp, -0x18 swra, 0x14(sp) swa0, 0x18(sp) addiut6, r0, 0x1 luiat, 0x8039 sht6, -0x41e0(at) lwt7, 0x18(sp) luiat, 0x8039 swt7, -0x41d8(at) lht8, 0x8038be20; lui t8, 0x8039/lh t8, -0x41e0t8 addiuat, r0, 0x1 bnet8, at, LevelScriptLoad_70 nop
LevelScriptLoad_38: ; 80380600 lwt9, 0x8038be28; lui t9, 0x8039/lw t9, -0x41d8t9 lbut0, 0x00(t9) luit9, 0xB07E sllt1, t0, 0x2 addut9, t9, t1 lwt9, 0x00(t9); Get address from new jump table at 0xB07E0000 (ROM address 0x7E0000) jalrt9 nop lht2, 0x8038be20; lui t2, 0x8039/lh t2, -0x41e0t2 addiuat, r0, 0x1 beqt2, at, LevelScriptLoad_38 nop
LevelScriptLoad_70: ; 80380638 jal0x8027E3E0 addiua0, r0, 0x1 jal0x80247CCC nop jal0x8027B3B4 nop jal0x80247D14; CleanupDisplayList nop jal0x80278F2C movea0, r0 luiv0, 0x8039 b LevelScriptLoad_AC lwv0, -0x41d8(v0) b LevelScriptLoad_AC nop
LevelScriptLoad_AC: ; 80380674 lwra, 0x14(sp) jrra addiusp, sp, 0x18
// New levelscript command 0x3D .orga0x6940; Overwrite the unused function 0x8024B940 .area0x64; Set data import limit to 0x64 bytes
LevelScript3D: addiusp, sp, -0x18 swra, 0x14(sp) lwt0, 0x8038be28; lui t0, 0x8039/lw t0, -0x41d8t0 lht1, 0x02(t0) luit2, 0x8037 lwt3, 0x00(t2) addt3, t3, t1 swt3, 0x00(t2)
LevelScript3D_increment: ; You NEED to include this increment code in every levelscript command luiat, 0x8039 lbut8, 0x1(t0) addut9, t0, t8 swt9, -0x41d8(at)
LevelScript3D_end: lwra, 0x14(sp) jrra addiusp, sp, 0x18 .endarea
I've basically figured out what the commands 0x1E & 0x1C do. Nothing exciting though, sadly.
Command 0x1E is used to allocate data from the pool and reset a couple variables:
Reallocates memory for the geometry layout node system.
Allocates 0xDAC0 bytes and stores pointer to 0x8038EE98. Not sure what this is, but it is related to collision.
Allocates memory for collision data. Vanilla SM64 allocates enough space for only 2,300 collision triangles. Newer versions of Skelux's SM64 editor allocate enough space for 32,767 triangles.
Sets halfword at address 0x8036125C to 0. (0x8036125C seems to be just a flag that is set when Mario enters into area #2 of any level)
Sets byte at address 0x803613FE to 0. (0x803613FE seems to be a read-only byte that tells you how many red coins Mario has collected so far)
A couple days ago I looked into how the inside castle did its room culling, and I discovered how it worked. Its a fairly simple process that involves the level script command 0x2F, and the geometry layout switch command (0xE).
The levels "Inside Castle", "Big Boo's Haunt", and "Hazy Maze Cave" contain a switch command in their geometry layouts that uses the ASM function 0x8029DBD4.
Spoiler: Geometry Layout for Inside Castle area 1
[0E001400] 08 00 00 0A 00A0 0078 00A0 0078 // Set screen rendering area (center X = 160, center Y = 120, Width = 320, Height = 240)
[0E00140C] 04 00 00 00 // Open New Node
[0E001410] 0C 00 00 00 // Disable Z-Buffer
[0E001414] 04 00 00 00 // Open New Node
[0E001418] 09 00 00 64 // Unknown command
[0E00141C] 04 00 00 00 // Open New Node
[0E001420] 19 00 0001 00000000 // Draw solid color background. Color = (0,0,0)
[0E001428] 05 00 00 00 // Close Node
[0E00142C] 05 00 00 00 // Close Node
[0E001430] 0C 01 00 00 // Enable Z-Buffer
[0E001434] 04 00 00 00 // Open New Node
[0E001438] 0A 01 0040 0032 1B58 8029AA3C // Set camera frustum (FOV = 64, Near = 50, Far = 7000)
[0E001444] 04 00 00 00 // Open New Node
[0E001448] 0F 00 000D 0000 07D0 1770 0000 0000 0000 80287D30 // Unknown command
[0E00145C] 04 00 00 00 // Open New Node [0E001460] 0E 00 00 11 8029DBD4 // Switch case with following display lists using ASM function 0x8029DBD4
[0E001468] 04 00 00 00 // Open New Node
[0E00146C] 02 01 00 00 0E000F30 // Branch geometry layout to address 0x0E000F30
[0E000F30] 0B 00 00 00 // Start geometry layout
[0E000F34] 04 00 00 00 // Open New Node
[0E000F38] 15 01 00 00 07028FD0 // Load display list 0x07028FD0 into layer 1
[0E000F40] 15 04 00 00 07029578 // Load display list 0x07029578 into layer 4
[0E000F48] 15 01 00 00 0702A650 // Load display list 0x0702A650 into layer 1
[0E000F50] 15 06 00 00 0702AA10 // Load display list 0x0702AA10 into layer 6
[0E000F58] 15 04 00 00 0702AB20 // Load display list 0x0702AB20 into layer 4
[0E000F60] 18 00 0000 802D2360 // Create display list(s) from the ASM function 0x802D2360 (a0 = 0)
[0E000F68] 05 00 00 00 // Close Node
[0E000F6C] 03 00 00 00 // Return from branch
[0E001474] 02 01 00 00 0E000F70 // Branch geometry layout to address 0x0E000F70
[0E000F70] 0B 00 00 00 // Start geometry layout
[0E000F74] 04 00 00 00 // Open New Node
[0E000F78] 15 01 00 00 0702E408 // Load display list 0x0702E408 into layer 1
[0E000F80] 05 00 00 00 // Close Node
[0E000F84] 03 00 00 00 // Return from branch
[0E00147C] 02 01 00 00 0E000F88 // Branch geometry layout to address 0x0E000F88
[0E000F88] 0B 00 00 00 // Start geometry layout
[0E000F8C] 04 00 00 00 // Open New Node
[0E000F90] 15 01 00 00 0702FD30 // Load display list 0x0702FD30 into layer 1
[0E000F98] 15 01 00 00 07023DB0 // Load display list 0x07023DB0 into layer 1
[0E000FA0] 05 00 00 00 // Close Node
[0E000FA4] 03 00 00 00 // Return from branch
[0E001484] 02 01 00 00 0E000FA8 // Branch geometry layout to address 0x0E000FA8
[0E000FA8] 0B 00 00 00 // Start geometry layout
[0E000FAC] 04 00 00 00 // Open New Node
[0E000FB0] 15 01 00 00 07031588 // Load display list 0x07031588 into layer 1
[0E000FB8] 15 04 00 00 07031720 // Load display list 0x07031720 into layer 4
[0E000FC0] 15 04 00 00 07031830 // Load display list 0x07031830 into layer 4
[0E000FC8] 05 00 00 00 // Close Node
[0E000FCC] 03 00 00 00 // Return from branch
[0E00148C] 02 01 00 00 0E000FD0 // Branch geometry layout to address 0x0E000FD0
[0E000FD0] 0B 00 00 00 // Start geometry layout
[0E000FD4] 04 00 00 00 // Open New Node
[0E000FD8] 15 01 00 00 07032FC0 // Load display list 0x07032FC0 into layer 1
[0E000FE0] 15 04 00 00 07033158 // Load display list 0x07033158 into layer 4
[0E000FE8] 18 00 0000 802D5D0C // Create display list(s) from the ASM function 0x802D5D0C (a0 = 0)
[0E000FF0] 18 00 0100 802D5B98 // Create display list(s) from the ASM function 0x802D5B98 (a0 = 256)
[0E000FF8] 05 00 00 00 // Close Node
[0E000FFC] 03 00 00 00 // Return from branch
[0E001494] 02 01 00 00 0E001000 // Branch geometry layout to address 0x0E001000
[0E001000] 0B 00 00 00 // Start geometry layout
[0E001004] 04 00 00 00 // Open New Node
[0E001008] 15 01 00 00 07034D88 // Load display list 0x07034D88 into layer 1
[0E001010] 15 04 00 00 07035178 // Load display list 0x07035178 into layer 4
[0E001018] 15 04 00 00 07035288 // Load display list 0x07035288 into layer 4
[0E001020] 18 00 0000 802D5D0C // Create display list(s) from the ASM function 0x802D5D0C (a0 = 0)
[0E001028] 18 00 0102 802D5B98 // Create display list(s) from the ASM function 0x802D5B98 (a0 = 258)
[0E001030] 05 00 00 00 // Close Node
[0E001034] 03 00 00 00 // Return from branch
[0E00149C] 02 01 00 00 0E001038 // Branch geometry layout to address 0x0E001038
[0E001038] 0B 00 00 00 // Start geometry layout
[0E00103C] 04 00 00 00 // Open New Node
[0E001040] 15 01 00 00 07036D88 // Load display list 0x07036D88 into layer 1
[0E001048] 15 01 00 00 07037988 // Load display list 0x07037988 into layer 1
[0E001050] 15 01 00 00 07037BF8 // Load display list 0x07037BF8 into layer 1
[0E001058] 15 05 00 00 07037DE8 // Load display list 0x07037DE8 into layer 5
[0E001060] 15 05 00 00 07038240 // Load display list 0x07038240 into layer 5
[0E001068] 15 04 00 00 07038350 // Load display list 0x07038350 into layer 4
[0E001070] 18 00 0000 802D5D0C // Create display list(s) from the ASM function 0x802D5D0C (a0 = 0)
[0E001078] 18 00 0103 802D5B98 // Create display list(s) from the ASM function 0x802D5B98 (a0 = 259)
[0E001080] 05 00 00 00 // Close Node
[0E001084] 03 00 00 00 // Return from branch
[0E0014A4] 02 01 00 00 0E001088 // Branch geometry layout to address 0x0E001088
[0E001088] 0B 00 00 00 // Start geometry layout
[0E00108C] 04 00 00 00 // Open New Node
[0E001090] 15 01 00 00 0703A6C8 // Load display list 0x0703A6C8 into layer 1
[0E001098] 15 04 00 00 0703A808 // Load display list 0x0703A808 into layer 4
[0E0010A0] 15 01 00 00 070234C0 // Load display list 0x070234C0 into layer 1
[0E0010A8] 15 01 00 00 07023520 // Load display list 0x07023520 into layer 1
[0E0010B0] 18 00 0000 802D5D0C // Create display list(s) from the ASM function 0x802D5D0C (a0 = 0)
[0E0010B8] 18 00 0101 802D5B98 // Create display list(s) from the ASM function 0x802D5B98 (a0 = 257)
[0E0010C0] 05 00 00 00 // Close Node
[0E0010C4] 03 00 00 00 // Return from branch
[0E0014AC] 02 01 00 00 0E0010C8 // Branch geometry layout to address 0x0E0010C8
[0E0010C8] 0B 00 00 00 // Start geometry layout
[0E0010CC] 04 00 00 00 // Open New Node
[0E0010D0] 15 01 00 00 07028FD0 // Load display list 0x07028FD0 into layer 1
[0E0010D8] 15 04 00 00 07029578 // Load display list 0x07029578 into layer 4
[0E0010E0] 15 01 00 00 0702A650 // Load display list 0x0702A650 into layer 1
[0E0010E8] 15 06 00 00 0702AA10 // Load display list 0x0702AA10 into layer 6
[0E0010F0] 15 04 00 00 0702AB20 // Load display list 0x0702AB20 into layer 4
[0E0010F8] 18 00 0000 802D2360 // Create display list(s) from the ASM function 0x802D2360 (a0 = 0)
[0E001100] 15 01 00 00 0703BA08 // Load display list 0x0703BA08 into layer 1
[0E001108] 05 00 00 00 // Close Node
[0E00110C] 03 00 00 00 // Return from branch
[0E0014B4] 02 01 00 00 0E001110 // Branch geometry layout to address 0x0E001110
[0E001110] 0B 00 00 00 // Start geometry layout
[0E001114] 04 00 00 00 // Open New Node
[0E001118] 15 01 00 00 07028FD0 // Load display list 0x07028FD0 into layer 1
[0E001120] 15 04 00 00 07029578 // Load display list 0x07029578 into layer 4
[0E001128] 15 01 00 00 0702A650 // Load display list 0x0702A650 into layer 1
[0E001130] 15 06 00 00 0702AA10 // Load display list 0x0702AA10 into layer 6
[0E001138] 15 04 00 00 0702AB20 // Load display list 0x0702AB20 into layer 4
[0E001140] 18 00 0000 802D2360 // Create display list(s) from the ASM function 0x802D2360 (a0 = 0)
[0E001148] 15 01 00 00 0702E408 // Load display list 0x0702E408 into layer 1
[0E001150] 05 00 00 00 // Close Node
[0E001154] 03 00 00 00 // Return from branch
[0E0014BC] 02 01 00 00 0E001158 // Branch geometry layout to address 0x0E001158
[0E001158] 0B 00 00 00 // Start geometry layout
[0E00115C] 04 00 00 00 // Open New Node
[0E001160] 15 01 00 00 07028FD0 // Load display list 0x07028FD0 into layer 1
[0E001168] 15 04 00 00 07029578 // Load display list 0x07029578 into layer 4
[0E001170] 15 01 00 00 0702A650 // Load display list 0x0702A650 into layer 1
[0E001178] 15 06 00 00 0702AA10 // Load display list 0x0702AA10 into layer 6
[0E001180] 15 04 00 00 0702AB20 // Load display list 0x0702AB20 into layer 4
[0E001188] 18 00 0000 802D2360 // Create display list(s) from the ASM function 0x802D2360 (a0 = 0)
[0E001190] 15 01 00 00 0702FD30 // Load display list 0x0702FD30 into layer 1
[0E001198] 15 01 00 00 07023DB0 // Load display list 0x07023DB0 into layer 1
[0E0011A0] 05 00 00 00 // Close Node
[0E0011A4] 03 00 00 00 // Return from branch
[0E0014C4] 02 01 00 00 0E0011A8 // Branch geometry layout to address 0x0E0011A8
[0E0011A8] 0B 00 00 00 // Start geometry layout
[0E0011AC] 04 00 00 00 // Open New Node
[0E0011B0] 15 01 00 00 07028FD0 // Load display list 0x07028FD0 into layer 1
[0E0011B8] 15 04 00 00 07029578 // Load display list 0x07029578 into layer 4
[0E0011C0] 15 01 00 00 0702A650 // Load display list 0x0702A650 into layer 1
[0E0011C8] 15 06 00 00 0702AA10 // Load display list 0x0702AA10 into layer 6
[0E0011D0] 15 04 00 00 0702AB20 // Load display list 0x0702AB20 into layer 4
[0E0011D8] 18 00 0000 802D2360 // Create display list(s) from the ASM function 0x802D2360 (a0 = 0)
[0E0011E0] 15 01 00 00 07031588 // Load display list 0x07031588 into layer 1
[0E0011E8] 15 04 00 00 07031720 // Load display list 0x07031720 into layer 4
[0E0011F0] 15 04 00 00 07031830 // Load display list 0x07031830 into layer 4
[0E0011F8] 05 00 00 00 // Close Node
[0E0011FC] 03 00 00 00 // Return from branch
[0E0014CC] 02 01 00 00 0E001200 // Branch geometry layout to address 0x0E001200
[0E001200] 0B 00 00 00 // Start geometry layout
[0E001204] 04 00 00 00 // Open New Node
[0E001208] 15 01 00 00 07028FD0 // Load display list 0x07028FD0 into layer 1
[0E001210] 15 04 00 00 07029578 // Load display list 0x07029578 into layer 4
[0E001218] 15 01 00 00 0702A650 // Load display list 0x0702A650 into layer 1
[0E001220] 15 06 00 00 0702AA10 // Load display list 0x0702AA10 into layer 6
[0E001228] 15 04 00 00 0702AB20 // Load display list 0x0702AB20 into layer 4
[0E001230] 18 00 0000 802D2360 // Create display list(s) from the ASM function 0x802D2360 (a0 = 0)
[0E001238] 15 01 00 00 07032FC0 // Load display list 0x07032FC0 into layer 1
[0E001240] 15 04 00 00 07033158 // Load display list 0x07033158 into layer 4
[0E001248] 18 00 0000 802D5D0C // Create display list(s) from the ASM function 0x802D5D0C (a0 = 0)
[0E001250] 18 00 0100 802D5B98 // Create display list(s) from the ASM function 0x802D5B98 (a0 = 256)
[0E001258] 05 00 00 00 // Close Node
[0E00125C] 03 00 00 00 // Return from branch
[0E0014D4] 02 01 00 00 0E001260 // Branch geometry layout to address 0x0E001260
[0E001260] 0B 00 00 00 // Start geometry layout
[0E001264] 04 00 00 00 // Open New Node
[0E001268] 15 01 00 00 07028FD0 // Load display list 0x07028FD0 into layer 1
[0E001270] 15 04 00 00 07029578 // Load display list 0x07029578 into layer 4
[0E001278] 15 01 00 00 0702A650 // Load display list 0x0702A650 into layer 1
[0E001280] 15 06 00 00 0702AA10 // Load display list 0x0702AA10 into layer 6
[0E001288] 15 04 00 00 0702AB20 // Load display list 0x0702AB20 into layer 4
[0E001290] 18 00 0000 802D2360 // Create display list(s) from the ASM function 0x802D2360 (a0 = 0)
[0E001298] 15 01 00 00 07034D88 // Load display list 0x07034D88 into layer 1
[0E0012A0] 15 04 00 00 07035178 // Load display list 0x07035178 into layer 4
[0E0012A8] 15 04 00 00 07035288 // Load display list 0x07035288 into layer 4
[0E0012B0] 18 00 0000 802D5D0C // Create display list(s) from the ASM function 0x802D5D0C (a0 = 0)
[0E0012B8] 18 00 0102 802D5B98 // Create display list(s) from the ASM function 0x802D5B98 (a0 = 258)
[0E0012C0] 05 00 00 00 // Close Node
[0E0012C4] 03 00 00 00 // Return from branch
[0E0014DC] 02 01 00 00 0E0012C8 // Branch geometry layout to address 0x0E0012C8
[0E0012C8] 0B 00 00 00 // Start geometry layout
[0E0012CC] 04 00 00 00 // Open New Node
[0E0012D0] 15 01 00 00 07028FD0 // Load display list 0x07028FD0 into layer 1
[0E0012D8] 15 04 00 00 07029578 // Load display list 0x07029578 into layer 4
[0E0012E0] 15 01 00 00 0702A650 // Load display list 0x0702A650 into layer 1
[0E0012E8] 15 06 00 00 0702AA10 // Load display list 0x0702AA10 into layer 6
[0E0012F0] 15 04 00 00 0702AB20 // Load display list 0x0702AB20 into layer 4
[0E0012F8] 18 00 0000 802D2360 // Create display list(s) from the ASM function 0x802D2360 (a0 = 0)
[0E001300] 15 01 00 00 07036D88 // Load display list 0x07036D88 into layer 1
[0E001308] 15 01 00 00 07037988 // Load display list 0x07037988 into layer 1
[0E001310] 15 01 00 00 07037BF8 // Load display list 0x07037BF8 into layer 1
[0E001318] 15 05 00 00 07037DE8 // Load display list 0x07037DE8 into layer 5
[0E001320] 15 05 00 00 07038240 // Load display list 0x07038240 into layer 5
[0E001328] 15 04 00 00 07038350 // Load display list 0x07038350 into layer 4
[0E001330] 18 00 0000 802D5D0C // Create display list(s) from the ASM function 0x802D5D0C (a0 = 0)
[0E001338] 18 00 0103 802D5B98 // Create display list(s) from the ASM function 0x802D5B98 (a0 = 259)
[0E001340] 05 00 00 00 // Close Node
[0E001344] 03 00 00 00 // Return from branch
[0E0014E4] 02 01 00 00 0E001348 // Branch geometry layout to address 0x0E001348
[0E001348] 0B 00 00 00 // Start geometry layout
[0E00134C] 04 00 00 00 // Open New Node
[0E001350] 15 01 00 00 07028FD0 // Load display list 0x07028FD0 into layer 1
[0E001358] 15 04 00 00 07029578 // Load display list 0x07029578 into layer 4
[0E001360] 15 01 00 00 0702A650 // Load display list 0x0702A650 into layer 1
[0E001368] 15 06 00 00 0702AA10 // Load display list 0x0702AA10 into layer 6
[0E001370] 15 04 00 00 0702AB20 // Load display list 0x0702AB20 into layer 4
[0E001378] 18 00 0000 802D2360 // Create display list(s) from the ASM function 0x802D2360 (a0 = 0)
[0E001380] 15 01 00 00 0703A6C8 // Load display list 0x0703A6C8 into layer 1
[0E001388] 15 04 00 00 0703A808 // Load display list 0x0703A808 into layer 4
[0E001390] 15 01 00 00 070234C0 // Load display list 0x070234C0 into layer 1
[0E001398] 15 01 00 00 07023520 // Load display list 0x07023520 into layer 1
[0E0013A0] 18 00 0000 802D5D0C // Create display list(s) from the ASM function 0x802D5D0C (a0 = 0)
[0E0013A8] 18 00 0101 802D5B98 // Create display list(s) from the ASM function 0x802D5B98 (a0 = 257)
[0E0013B0] 05 00 00 00 // Close Node
[0E0013B4] 03 00 00 00 // Return from branch
[0E0014EC] 02 01 00 00 0E0013B8 // Branch geometry layout to address 0x0E0013B8
[0E0013B8] 0B 00 00 00 // Start geometry layout
[0E0013BC] 04 00 00 00 // Open New Node
[0E0013C0] 15 01 00 00 07028FD0 // Load display list 0x07028FD0 into layer 1
[0E0013C8] 15 04 00 00 07029578 // Load display list 0x07029578 into layer 4
[0E0013D0] 15 01 00 00 0702A650 // Load display list 0x0702A650 into layer 1
[0E0013D8] 15 06 00 00 0702AA10 // Load display list 0x0702AA10 into layer 6
[0E0013E0] 15 04 00 00 0702AB20 // Load display list 0x0702AB20 into layer 4
[0E0013E8] 18 00 0000 802D2360 // Create display list(s) from the ASM function 0x802D2360 (a0 = 0)
[0E0013F0] 15 01 00 00 0703BA08 // Load display list 0x0703BA08 into layer 1
[0E0013F8] 05 00 00 00 // Close Node
[0E0013FC] 03 00 00 00 // Return from branch
[0E0014F4] 05 00 00 00 // Close Node
[0E0014F8] 17 00 00 00 // Setup display lists for level objects
[0E0014FC] 18 00 0000 802761D0 // Create display list(s) from the ASM function 0x802761D0 (a0 = 0)
[0E001504] 05 00 00 00 // Close Node
[0E001508] 05 00 00 00 // Close Node
[0E00150C] 05 00 00 00 // Close Node
[0E001510] 05 00 00 00 // Close Node
[0E001514] 01 00 00 00 // End geometry layout
The function 0x8029DBD4 will find the collision triangle below Mario, and use the byte at offset 0x5 in the collision triangle structure to determine which switch case to use.
The purpose of the level script command 0x2F is to setup the pointer of an array of byte values that will populate the collision triangles. This pointer will be stored at offset 0x0C in the structure of the current area being processed. The length of this byte array is the same as the number of collision triangles inside the current area. If the level script doesn't have a 0x2F command, then each collision triangle will have zero as their offset 0x5.
Code:
typedef char s8;
void LevelScript2F()
{
// 0x8038B8AC = current area index being processed in the level scripts
if(*((short*)0x8038B8AC) != -1)
{
Area* t8 = *((Area**)0x8032DDC8); // 0x8032DDC8 = pointer to start of area struct array.
int t0 = (*((short*)0x8038B8AC) * 0x3C);
Area* area_struct = (Area*)(t8 + t0);
area_struct->_0xC = SegmentedToVirtual(*((int*)(*((int*)0x8038BE28) + 4)));
}
*((int*)0x8038BE28) += *((s8*)(*((int*)0x8038BE28) + 1)); // Go to the next level script command
}
The collision triangle's offset 0x05 is populated from a loop in the function 0x80383068, which is eventually called from the level initialize command ([ 11 08 0000 8024BCD8 ]) before the main level loop command starts ([ 12 08 0001 8024BCD8 ]).
Here's a video showing me editing the switch byte in the collision triangle underneath where Mario is standing:
(This post was last modified: 21-01-2018, 07:27 AM by David.)