sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

level_script.c (23796B)


      1 #include <ultra64.h>
      2 #ifdef NO_SEGMENTED_MEMORY
      3 #include <string.h>
      4 #endif
      5 
      6 #include "sm64.h"
      7 #include "audio/external.h"
      8 #include "buffers/framebuffers.h"
      9 #include "buffers/zbuffer.h"
     10 #include "game/area.h"
     11 #include "game/game_init.h"
     12 #include "game/mario.h"
     13 #include "game/memory.h"
     14 #include "game/object_helpers.h"
     15 #include "game/object_list_processor.h"
     16 #include "game/profiler.h"
     17 #include "game/save_file.h"
     18 #include "game/sound_init.h"
     19 #include "goddard/renderer.h"
     20 #include "geo_layout.h"
     21 #include "graph_node.h"
     22 #include "level_script.h"
     23 #include "level_misc_macros.h"
     24 #include "math_util.h"
     25 #include "surface_collision.h"
     26 #include "surface_load.h"
     27 
     28 #define CMD_GET(type, offset) (*(type *) (CMD_PROCESS_OFFSET(offset) + (u8 *) sCurrentCmd))
     29 
     30 // These are equal
     31 #define CMD_NEXT ((struct LevelCommand *) ((u8 *) sCurrentCmd + (sCurrentCmd->size << CMD_SIZE_SHIFT)))
     32 #define NEXT_CMD ((struct LevelCommand *) ((sCurrentCmd->size << CMD_SIZE_SHIFT) + (u8 *) sCurrentCmd))
     33 
     34 struct LevelCommand {
     35     /*00*/ u8 type;
     36     /*01*/ u8 size;
     37     /*02*/ // variable sized argument data
     38 };
     39 
     40 enum ScriptStatus { SCRIPT_RUNNING = 1, SCRIPT_PAUSED = 0, SCRIPT_PAUSED2 = -1 };
     41 
     42 static uintptr_t sStack[32];
     43 
     44 static struct AllocOnlyPool *sLevelPool = NULL;
     45 
     46 static u16 sDelayFrames = 0;
     47 static u16 sDelayFrames2 = 0;
     48 
     49 static s16 sCurrAreaIndex = -1;
     50 
     51 static uintptr_t *sStackTop = sStack;
     52 static uintptr_t *sStackBase = NULL;
     53 
     54 static s16 sScriptStatus;
     55 static s32 sRegister;
     56 static struct LevelCommand *sCurrentCmd;
     57 
     58 static s32 eval_script_op(s8 op, s32 arg) {
     59     s32 result = 0;
     60 
     61     switch (op) {
     62         case 0:
     63             result = sRegister & arg;
     64             break;
     65         case 1:
     66             result = !(sRegister & arg);
     67             break;
     68         case 2:
     69             result = sRegister == arg;
     70             break;
     71         case 3:
     72             result = sRegister != arg;
     73             break;
     74         case 4:
     75             result = sRegister < arg;
     76             break;
     77         case 5:
     78             result = sRegister <= arg;
     79             break;
     80         case 6:
     81             result = sRegister > arg;
     82             break;
     83         case 7:
     84             result = sRegister >= arg;
     85             break;
     86     }
     87 
     88     return result;
     89 }
     90 
     91 static void level_cmd_load_and_execute(void) {
     92     main_pool_push_state();
     93     load_segment(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8), MEMORY_POOL_LEFT);
     94 
     95     *sStackTop++ = (uintptr_t) NEXT_CMD;
     96     *sStackTop++ = (uintptr_t) sStackBase;
     97     sStackBase = sStackTop;
     98 
     99     sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 12));
    100 }
    101 
    102 static void level_cmd_exit_and_execute(void) {
    103     void *targetAddr = CMD_GET(void *, 12);
    104 
    105     main_pool_pop_state();
    106     main_pool_push_state();
    107 
    108     load_segment(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8),
    109             MEMORY_POOL_LEFT);
    110 
    111     sStackTop = sStackBase;
    112     sCurrentCmd = segmented_to_virtual(targetAddr);
    113 }
    114 
    115 static void level_cmd_exit(void) {
    116     main_pool_pop_state();
    117 
    118     sStackTop = sStackBase;
    119     sStackBase = (uintptr_t *) *(--sStackTop);
    120     sCurrentCmd = (struct LevelCommand *) *(--sStackTop);
    121 }
    122 
    123 static void level_cmd_sleep(void) {
    124     sScriptStatus = SCRIPT_PAUSED;
    125 
    126     if (sDelayFrames == 0) {
    127         sDelayFrames = CMD_GET(s16, 2);
    128     } else if (--sDelayFrames == 0) {
    129         sCurrentCmd = CMD_NEXT;
    130         sScriptStatus = SCRIPT_RUNNING;
    131     }
    132 }
    133 
    134 static void level_cmd_sleep2(void) {
    135     sScriptStatus = SCRIPT_PAUSED2;
    136 
    137     if (sDelayFrames2 == 0) {
    138         sDelayFrames2 = CMD_GET(s16, 2);
    139     } else if (--sDelayFrames2 == 0) {
    140         sCurrentCmd = CMD_NEXT;
    141         sScriptStatus = SCRIPT_RUNNING;
    142     }
    143 }
    144 
    145 static void level_cmd_jump(void) {
    146     sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 4));
    147 }
    148 
    149 static void level_cmd_jump_and_link(void) {
    150     *sStackTop++ = (uintptr_t) NEXT_CMD;
    151     sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 4));
    152 }
    153 
    154 static void level_cmd_return(void) {
    155     sCurrentCmd = (struct LevelCommand *) *(--sStackTop);
    156 }
    157 
    158 static void level_cmd_jump_and_link_push_arg(void) {
    159     *sStackTop++ = (uintptr_t) NEXT_CMD;
    160     *sStackTop++ = CMD_GET(s16, 2);
    161     sCurrentCmd = CMD_NEXT;
    162 }
    163 
    164 static void level_cmd_jump_repeat(void) {
    165     s32 val = *(sStackTop - 1);
    166 
    167     if (val == 0) {
    168         sCurrentCmd = (struct LevelCommand *) *(sStackTop - 2);
    169     } else if (--val != 0) {
    170         *(sStackTop - 1) = val;
    171         sCurrentCmd = (struct LevelCommand *) *(sStackTop - 2);
    172     } else {
    173         sCurrentCmd = CMD_NEXT;
    174         sStackTop -= 2;
    175     }
    176 }
    177 
    178 static void level_cmd_loop_begin(void) {
    179     *sStackTop++ = (uintptr_t) NEXT_CMD;
    180     *sStackTop++ = 0;
    181     sCurrentCmd = CMD_NEXT;
    182 }
    183 
    184 static void level_cmd_loop_until(void) {
    185     if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) != 0) {
    186         sCurrentCmd = CMD_NEXT;
    187         sStackTop -= 2;
    188     } else {
    189         sCurrentCmd = (struct LevelCommand *) *(sStackTop - 2);
    190     }
    191 }
    192 
    193 static void level_cmd_jump_if(void) {
    194     if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) != 0) {
    195         sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 8));
    196     } else {
    197         sCurrentCmd = CMD_NEXT;
    198     }
    199 }
    200 
    201 static void level_cmd_jump_and_link_if(void) {
    202     if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) != 0) {
    203         *sStackTop++ = (uintptr_t) NEXT_CMD;
    204         sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 8));
    205     } else {
    206         sCurrentCmd = CMD_NEXT;
    207     }
    208 }
    209 
    210 static void level_cmd_skip_if(void) {
    211     if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) == 0) {
    212         do {
    213             sCurrentCmd = CMD_NEXT;
    214         } while (sCurrentCmd->type == 0x0F || sCurrentCmd->type == 0x10);
    215     }
    216 
    217     sCurrentCmd = CMD_NEXT;
    218 }
    219 
    220 static void level_cmd_skip(void) {
    221     do {
    222         sCurrentCmd = CMD_NEXT;
    223     } while (sCurrentCmd->type == 0x10);
    224 
    225     sCurrentCmd = CMD_NEXT;
    226 }
    227 
    228 static void level_cmd_skippable_nop(void) {
    229     sCurrentCmd = CMD_NEXT;
    230 }
    231 
    232 static void level_cmd_call(void) {
    233     typedef s32 (*Func)(s16, s32);
    234     Func func = CMD_GET(Func, 4);
    235     sRegister = func(CMD_GET(s16, 2), sRegister);
    236     sCurrentCmd = CMD_NEXT;
    237 }
    238 
    239 static void level_cmd_call_loop(void) {
    240     typedef s32 (*Func)(s16, s32);
    241     Func func = CMD_GET(Func, 4);
    242     sRegister = func(CMD_GET(s16, 2), sRegister);
    243 
    244     if (sRegister == 0) {
    245         sScriptStatus = SCRIPT_PAUSED;
    246     } else {
    247         sScriptStatus = SCRIPT_RUNNING;
    248         sCurrentCmd = CMD_NEXT;
    249     }
    250 }
    251 
    252 static void level_cmd_set_register(void) {
    253     sRegister = CMD_GET(s16, 2);
    254     sCurrentCmd = CMD_NEXT;
    255 }
    256 
    257 static void level_cmd_push_pool_state(void) {
    258     main_pool_push_state();
    259     sCurrentCmd = CMD_NEXT;
    260 }
    261 
    262 static void level_cmd_pop_pool_state(void) {
    263     main_pool_pop_state();
    264     sCurrentCmd = CMD_NEXT;
    265 }
    266 
    267 static void level_cmd_load_to_fixed_address(void) {
    268     load_to_fixed_pool_addr(CMD_GET(void *, 4), CMD_GET(void *, 8), CMD_GET(void *, 12));
    269     sCurrentCmd = CMD_NEXT;
    270 }
    271 
    272 static void level_cmd_load_raw(void) {
    273     load_segment(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8),
    274             MEMORY_POOL_LEFT);
    275     sCurrentCmd = CMD_NEXT;
    276 }
    277 
    278 static void level_cmd_load_mio0(void) {
    279     load_segment_decompress(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8));
    280     sCurrentCmd = CMD_NEXT;
    281 }
    282 
    283 static void level_cmd_load_mario_head(void) {
    284     // TODO: Fix these hardcoded sizes
    285     void *addr = main_pool_alloc(DOUBLE_SIZE_ON_64_BIT(0xE1000), MEMORY_POOL_LEFT);
    286     if (addr != NULL) {
    287         gdm_init(addr, DOUBLE_SIZE_ON_64_BIT(0xE1000));
    288         gd_add_to_heap(gZBuffer, sizeof(gZBuffer)); // 0x25800
    289         gd_add_to_heap(gFramebuffer0, 3 * sizeof(gFramebuffer0)); // 0x70800
    290         gdm_setup();
    291         gdm_maketestdl(CMD_GET(s16, 2));
    292     } else {
    293         CN_DEBUG_PRINTF(("face anime memory overflow\n"));
    294     }
    295 
    296     sCurrentCmd = CMD_NEXT;
    297 }
    298 
    299 static void level_cmd_load_mio0_texture(void) {
    300     load_segment_decompress_heap(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8));
    301     sCurrentCmd = CMD_NEXT;
    302 }
    303 
    304 static void level_cmd_init_level(void) {
    305     init_graph_node_start(NULL, (struct GraphNodeStart *) &gObjParentGraphNode);
    306     clear_objects();
    307     clear_areas();
    308     main_pool_push_state();
    309 
    310     sCurrentCmd = CMD_NEXT;
    311 }
    312 
    313 static void level_cmd_clear_level(void) {
    314     clear_objects();
    315     clear_area_graph_nodes();
    316     clear_areas();
    317     main_pool_pop_state();
    318 
    319     sCurrentCmd = CMD_NEXT;
    320 }
    321 
    322 static void level_cmd_alloc_level_pool(void) {
    323     if (sLevelPool == NULL) {
    324         sLevelPool = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool),
    325                                           MEMORY_POOL_LEFT);
    326     }
    327 
    328     sCurrentCmd = CMD_NEXT;
    329 }
    330 
    331 static void level_cmd_free_level_pool(void) {
    332     s32 i;
    333 
    334     alloc_only_pool_resize(sLevelPool, sLevelPool->usedSpace);
    335     sLevelPool = NULL;
    336 
    337     for (i = 0; i < 8; i++) {
    338         if (gAreaData[i].terrainData != NULL) {
    339             alloc_surface_pools();
    340             break;
    341         }
    342     }
    343 
    344     sCurrentCmd = CMD_NEXT;
    345 }
    346 
    347 static void level_cmd_begin_area(void) {
    348     u8 areaIndex = CMD_GET(u8, 2);
    349     void *geoLayoutAddr = CMD_GET(void *, 4);
    350 
    351     if (areaIndex < 8) {
    352         struct GraphNodeRoot *screenArea =
    353             (struct GraphNodeRoot *) process_geo_layout(sLevelPool, geoLayoutAddr);
    354         struct GraphNodeCamera *node = (struct GraphNodeCamera *) screenArea->views[0];
    355 
    356         sCurrAreaIndex = areaIndex;
    357         screenArea->areaIndex = areaIndex;
    358         gAreas[areaIndex].unk04 = screenArea;
    359 
    360         if (node != NULL) {
    361             gAreas[areaIndex].camera = (struct Camera *) node->config.camera;
    362         } else {
    363             gAreas[areaIndex].camera = NULL;
    364         }
    365     }
    366 
    367     sCurrentCmd = CMD_NEXT;
    368 }
    369 
    370 static void level_cmd_end_area(void) {
    371     sCurrAreaIndex = -1;
    372     sCurrentCmd = CMD_NEXT;
    373 }
    374 
    375 static void level_cmd_load_model_from_dl(void) {
    376     s16 val1 = CMD_GET(s16, 2) & 0x0FFF;
    377     s16 val2 = ((u16)CMD_GET(s16, 2)) >> 12;
    378     void *val3 = CMD_GET(void *, 4);
    379 
    380     if (val1 < 256) {
    381         gLoadedGraphNodes[val1] =
    382             (struct GraphNode *) init_graph_node_display_list(sLevelPool, 0, val2, val3);
    383     }
    384 
    385     sCurrentCmd = CMD_NEXT;
    386 }
    387 
    388 static void level_cmd_load_model_from_geo(void) {
    389     s16 arg0 = CMD_GET(s16, 2);
    390     void *arg1 = CMD_GET(void *, 4);
    391 
    392     if (arg0 < 256) {
    393         gLoadedGraphNodes[arg0] = process_geo_layout(sLevelPool, arg1);
    394     }
    395 
    396     sCurrentCmd = CMD_NEXT;
    397 }
    398 
    399 static void level_cmd_23(void) {
    400     union {
    401         s32 i;
    402         f32 f;
    403     } arg2;
    404 
    405     s16 model = CMD_GET(s16, 2) & 0x0FFF;
    406     s16 arg0H = ((u16)CMD_GET(s16, 2)) >> 12;
    407     void *arg1 = CMD_GET(void *, 4);
    408     // load an f32, but using an integer load instruction for some reason (hence the union)
    409     arg2.i = CMD_GET(s32, 8);
    410 
    411     if (model < 256) {
    412         // GraphNodeScale has a GraphNode at the top. This
    413         // is being stored to the array, so cast the pointer.
    414         gLoadedGraphNodes[model] =
    415             (struct GraphNode *) init_graph_node_scale(sLevelPool, 0, arg0H, arg1, arg2.f);
    416     }
    417 
    418     sCurrentCmd = CMD_NEXT;
    419 }
    420 
    421 static void level_cmd_init_mario(void) {
    422     vec3s_set(gMarioSpawnInfo->startPos, 0, 0, 0);
    423     vec3s_set(gMarioSpawnInfo->startAngle, 0, 0, 0);
    424 
    425     gMarioSpawnInfo->activeAreaIndex = -1;
    426     gMarioSpawnInfo->areaIndex = 0;
    427     gMarioSpawnInfo->behaviorArg = CMD_GET(u32, 4);
    428     gMarioSpawnInfo->behaviorScript = CMD_GET(void *, 8);
    429     gMarioSpawnInfo->model = gLoadedGraphNodes[CMD_GET(u8, 3)];
    430     gMarioSpawnInfo->next = NULL;
    431 
    432     sCurrentCmd = CMD_NEXT;
    433 }
    434 
    435 static void level_cmd_place_object(void) {
    436     u8 val7 = 1 << (gCurrActNum - 1);
    437     u16 model;
    438     struct SpawnInfo *spawnInfo;
    439 
    440     if (sCurrAreaIndex != -1 && ((CMD_GET(u8, 2) & val7) || CMD_GET(u8, 2) == 0x1F)) {
    441         model = CMD_GET(u8, 3);
    442         spawnInfo = alloc_only_pool_alloc(sLevelPool, sizeof(struct SpawnInfo));
    443 
    444         spawnInfo->startPos[0] = CMD_GET(s16, 4);
    445         spawnInfo->startPos[1] = CMD_GET(s16, 6);
    446         spawnInfo->startPos[2] = CMD_GET(s16, 8);
    447 
    448         spawnInfo->startAngle[0] = CMD_GET(s16, 10) * 0x8000 / 180;
    449         spawnInfo->startAngle[1] = CMD_GET(s16, 12) * 0x8000 / 180;
    450         spawnInfo->startAngle[2] = CMD_GET(s16, 14) * 0x8000 / 180;
    451 
    452         spawnInfo->areaIndex = sCurrAreaIndex;
    453         spawnInfo->activeAreaIndex = sCurrAreaIndex;
    454 
    455         spawnInfo->behaviorArg = CMD_GET(u32, 16);
    456         spawnInfo->behaviorScript = CMD_GET(void *, 20);
    457         spawnInfo->model = gLoadedGraphNodes[model];
    458         spawnInfo->next = gAreas[sCurrAreaIndex].objectSpawnInfos;
    459 
    460         gAreas[sCurrAreaIndex].objectSpawnInfos = spawnInfo;
    461     }
    462 
    463     sCurrentCmd = CMD_NEXT;
    464 }
    465 
    466 static void level_cmd_create_warp_node(void) {
    467     if (sCurrAreaIndex != -1) {
    468         struct ObjectWarpNode *warpNode =
    469             alloc_only_pool_alloc(sLevelPool, sizeof(struct ObjectWarpNode));
    470 
    471         warpNode->node.id = CMD_GET(u8, 2);
    472         warpNode->node.destLevel = CMD_GET(u8, 3) + CMD_GET(u8, 6);
    473         warpNode->node.destArea = CMD_GET(u8, 4);
    474         warpNode->node.destNode = CMD_GET(u8, 5);
    475 
    476         warpNode->object = NULL;
    477 
    478         warpNode->next = gAreas[sCurrAreaIndex].warpNodes;
    479         gAreas[sCurrAreaIndex].warpNodes = warpNode;
    480     }
    481 
    482     sCurrentCmd = CMD_NEXT;
    483 }
    484 
    485 static void level_cmd_create_instant_warp(void) {
    486     s32 i;
    487     struct InstantWarp *warp;
    488 
    489     if (sCurrAreaIndex != -1) {
    490         if (gAreas[sCurrAreaIndex].instantWarps == NULL) {
    491             gAreas[sCurrAreaIndex].instantWarps =
    492                 alloc_only_pool_alloc(sLevelPool, 4 * sizeof(struct InstantWarp));
    493 
    494             for (i = INSTANT_WARP_INDEX_START; i < INSTANT_WARP_INDEX_STOP; i++) {
    495                 gAreas[sCurrAreaIndex].instantWarps[i].id = 0;
    496             }
    497         }
    498 
    499         warp = gAreas[sCurrAreaIndex].instantWarps + CMD_GET(u8, 2);
    500 
    501         warp[0].id = 1;
    502         warp[0].area = CMD_GET(u8, 3);
    503 
    504         warp[0].displacement[0] = CMD_GET(s16, 4);
    505         warp[0].displacement[1] = CMD_GET(s16, 6);
    506         warp[0].displacement[2] = CMD_GET(s16, 8);
    507     }
    508 
    509     sCurrentCmd = CMD_NEXT;
    510 }
    511 
    512 static void level_cmd_set_terrain_type(void) {
    513     if (sCurrAreaIndex != -1) {
    514         gAreas[sCurrAreaIndex].terrainType |= CMD_GET(s16, 2);
    515     }
    516 
    517     sCurrentCmd = CMD_NEXT;
    518 }
    519 
    520 static void level_cmd_create_painting_warp_node(void) {
    521     s32 i;
    522     struct WarpNode *node;
    523 
    524     if (sCurrAreaIndex != -1) {
    525         if (gAreas[sCurrAreaIndex].paintingWarpNodes == NULL) {
    526             gAreas[sCurrAreaIndex].paintingWarpNodes =
    527                 alloc_only_pool_alloc(sLevelPool, 45 * sizeof(struct WarpNode));
    528 
    529             for (i = 0; i < 45; i++) {
    530                 gAreas[sCurrAreaIndex].paintingWarpNodes[i].id = 0;
    531             }
    532         }
    533 
    534         node = &gAreas[sCurrAreaIndex].paintingWarpNodes[CMD_GET(u8, 2)];
    535 
    536         node->id = 1;
    537         node->destLevel = CMD_GET(u8, 3) + CMD_GET(u8, 6);
    538         node->destArea = CMD_GET(u8, 4);
    539         node->destNode = CMD_GET(u8, 5);
    540     }
    541 
    542     sCurrentCmd = CMD_NEXT;
    543 }
    544 
    545 static void level_cmd_3A(void) {
    546     struct UnusedArea28 *val4;
    547 
    548     if (sCurrAreaIndex != -1) {
    549         if ((val4 = gAreas[sCurrAreaIndex].unused) == NULL) {
    550             val4 = gAreas[sCurrAreaIndex].unused =
    551                 alloc_only_pool_alloc(sLevelPool, sizeof(struct UnusedArea28));
    552         }
    553 
    554         val4->unk00 = CMD_GET(s16, 2);
    555         val4->unk02 = CMD_GET(s16, 4);
    556         val4->unk04 = CMD_GET(s16, 6);
    557         val4->unk06 = CMD_GET(s16, 8);
    558         val4->unk08 = CMD_GET(s16, 10);
    559     }
    560 
    561     sCurrentCmd = CMD_NEXT;
    562 }
    563 
    564 static void level_cmd_create_whirlpool(void) {
    565     struct Whirlpool *whirlpool;
    566     s32 index = CMD_GET(u8, 2);
    567     s32 beatBowser2 =
    568         (save_file_get_flags() & (SAVE_FLAG_HAVE_KEY_2 | SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR)) != 0;
    569 
    570     if (CMD_GET(u8, 3) == 0 || (CMD_GET(u8, 3) == 1 && !beatBowser2)
    571         || (CMD_GET(u8, 3) == 2 && beatBowser2) || (CMD_GET(u8, 3) == 3 && gCurrActNum >= 2)) {
    572         if (sCurrAreaIndex != -1 && index < 2) {
    573             if ((whirlpool = gAreas[sCurrAreaIndex].whirlpools[index]) == NULL) {
    574                 whirlpool = alloc_only_pool_alloc(sLevelPool, sizeof(struct Whirlpool));
    575                 gAreas[sCurrAreaIndex].whirlpools[index] = whirlpool;
    576             }
    577 
    578             vec3s_set(whirlpool->pos, CMD_GET(s16, 4), CMD_GET(s16, 6), CMD_GET(s16, 8));
    579             whirlpool->strength = CMD_GET(s16, 10);
    580         }
    581     }
    582 
    583     sCurrentCmd = CMD_NEXT;
    584 }
    585 
    586 static void level_cmd_set_blackout(void) {
    587     osViBlack(CMD_GET(u8, 2));
    588     sCurrentCmd = CMD_NEXT;
    589 }
    590 
    591 static void level_cmd_set_gamma(void) {
    592     osViSetSpecialFeatures(CMD_GET(u8, 2) == 0 ? OS_VI_GAMMA_OFF : OS_VI_GAMMA_ON);
    593     sCurrentCmd = CMD_NEXT;
    594 }
    595 
    596 static void level_cmd_set_terrain_data(void) {
    597     if (sCurrAreaIndex != -1) {
    598 #ifndef NO_SEGMENTED_MEMORY
    599         gAreas[sCurrAreaIndex].terrainData = segmented_to_virtual(CMD_GET(void *, 4));
    600 #else
    601         Collision *data;
    602         u32 size;
    603 
    604         // The game modifies the terrain data and must be reset upon level reload.
    605         data = segmented_to_virtual(CMD_GET(void *, 4));
    606         size = get_area_terrain_size(data) * sizeof(Collision);
    607         gAreas[sCurrAreaIndex].terrainData = alloc_only_pool_alloc(sLevelPool, size);
    608         memcpy(gAreas[sCurrAreaIndex].terrainData, data, size);
    609 #endif
    610     }
    611     sCurrentCmd = CMD_NEXT;
    612 }
    613 
    614 static void level_cmd_set_rooms(void) {
    615     if (sCurrAreaIndex != -1) {
    616         gAreas[sCurrAreaIndex].surfaceRooms = segmented_to_virtual(CMD_GET(void *, 4));
    617     }
    618     sCurrentCmd = CMD_NEXT;
    619 }
    620 
    621 static void level_cmd_set_macro_objects(void) {
    622     if (sCurrAreaIndex != -1) {
    623 #ifndef NO_SEGMENTED_MEMORY
    624         gAreas[sCurrAreaIndex].macroObjects = segmented_to_virtual(CMD_GET(void *, 4));
    625 #else
    626         // The game modifies the macro object data (for example marking coins as taken),
    627         // so it must be reset when the level reloads.
    628         MacroObject *data = segmented_to_virtual(CMD_GET(void *, 4));
    629         s32 len = 0;
    630         while (data[len++] != MACRO_OBJECT_END()) {
    631             len += 4;
    632         }
    633         gAreas[sCurrAreaIndex].macroObjects = alloc_only_pool_alloc(sLevelPool, len * sizeof(MacroObject));
    634         memcpy(gAreas[sCurrAreaIndex].macroObjects, data, len * sizeof(MacroObject));
    635 #endif
    636     }
    637     sCurrentCmd = CMD_NEXT;
    638 }
    639 
    640 static void level_cmd_load_area(void) {
    641     s16 areaIndex = CMD_GET(u8, 2);
    642     UNUSED void *unused = (u8 *) sCurrentCmd + 4;
    643 
    644     stop_sounds_in_continuous_banks();
    645     load_area(areaIndex);
    646 
    647     sCurrentCmd = CMD_NEXT;
    648 }
    649 
    650 static void level_cmd_unload_area(void) {
    651     unload_area();
    652     sCurrentCmd = CMD_NEXT;
    653 }
    654 
    655 static void level_cmd_set_mario_start_pos(void) {
    656     gMarioSpawnInfo->areaIndex = CMD_GET(u8, 2);
    657 
    658 #if IS_64_BIT
    659     vec3s_set(gMarioSpawnInfo->startPos, CMD_GET(s16, 6), CMD_GET(s16, 8), CMD_GET(s16, 10));
    660 #else
    661     vec3s_copy(gMarioSpawnInfo->startPos, CMD_GET(Vec3s, 6));
    662 #endif
    663     vec3s_set(gMarioSpawnInfo->startAngle, 0, CMD_GET(s16, 4) * 0x8000 / 180, 0);
    664 
    665     sCurrentCmd = CMD_NEXT;
    666 }
    667 
    668 static void level_cmd_2C(void) {
    669     unload_mario_area();
    670     sCurrentCmd = CMD_NEXT;
    671 }
    672 
    673 static void level_cmd_2D(void) {
    674     area_update_objects();
    675     sCurrentCmd = CMD_NEXT;
    676 }
    677 
    678 static void level_cmd_set_transition(void) {
    679     if (gCurrentArea != NULL) {
    680         play_transition(CMD_GET(u8, 2), CMD_GET(u8, 3), CMD_GET(u8, 4), CMD_GET(u8, 5), CMD_GET(u8, 6));
    681     }
    682     sCurrentCmd = CMD_NEXT;
    683 }
    684 
    685 static void level_cmd_nop(void) {
    686     CN_DEBUG_PRINTF(("BAD: seqBlankColor\n"));
    687     sCurrentCmd = CMD_NEXT;
    688 }
    689 
    690 static void level_cmd_show_dialog(void) {
    691     if (sCurrAreaIndex != -1) {
    692         if (CMD_GET(u8, 2) < 2) {
    693             gAreas[sCurrAreaIndex].dialog[CMD_GET(u8, 2)] = CMD_GET(u8, 3);
    694         }
    695     }
    696     sCurrentCmd = CMD_NEXT;
    697 }
    698 
    699 static void level_cmd_set_music(void) {
    700     if (sCurrAreaIndex != -1) {
    701         gAreas[sCurrAreaIndex].musicParam = CMD_GET(s16, 2);
    702         gAreas[sCurrAreaIndex].musicParam2 = CMD_GET(s16, 4);
    703     }
    704     sCurrentCmd = CMD_NEXT;
    705 }
    706 
    707 static void level_cmd_set_menu_music(void) {
    708     set_background_music(0, CMD_GET(s16, 2), 0);
    709     sCurrentCmd = CMD_NEXT;
    710 }
    711 
    712 static void level_cmd_38(void) {
    713     fadeout_music(CMD_GET(s16, 2));
    714     sCurrentCmd = CMD_NEXT;
    715 }
    716 
    717 static void level_cmd_get_or_set_var(void) {
    718     if (CMD_GET(u8, 2) == 0) {
    719         switch (CMD_GET(u8, 3)) {
    720             case 0:
    721                 gCurrSaveFileNum = sRegister;
    722                 break;
    723             case 1:
    724                 gCurrCourseNum = sRegister;
    725                 break;
    726             case 2:
    727                 gCurrActNum = sRegister;
    728                 break;
    729             case 3:
    730                 gCurrLevelNum = sRegister;
    731                 break;
    732             case 4:
    733                 gCurrAreaIndex = sRegister;
    734                 break;
    735         }
    736     } else {
    737         switch (CMD_GET(u8, 3)) {
    738             case 0:
    739                 sRegister = gCurrSaveFileNum;
    740                 break;
    741             case 1:
    742                 sRegister = gCurrCourseNum;
    743                 break;
    744             case 2:
    745                 sRegister = gCurrActNum;
    746                 break;
    747             case 3:
    748                 sRegister = gCurrLevelNum;
    749                 break;
    750             case 4:
    751                 sRegister = gCurrAreaIndex;
    752                 break;
    753         }
    754     }
    755 
    756     sCurrentCmd = CMD_NEXT;
    757 }
    758 
    759 static void (*LevelScriptJumpTable[])(void) = {
    760     /*00*/ level_cmd_load_and_execute,
    761     /*01*/ level_cmd_exit_and_execute,
    762     /*02*/ level_cmd_exit,
    763     /*03*/ level_cmd_sleep,
    764     /*04*/ level_cmd_sleep2,
    765     /*05*/ level_cmd_jump,
    766     /*06*/ level_cmd_jump_and_link,
    767     /*07*/ level_cmd_return,
    768     /*08*/ level_cmd_jump_and_link_push_arg,
    769     /*09*/ level_cmd_jump_repeat,
    770     /*0A*/ level_cmd_loop_begin,
    771     /*0B*/ level_cmd_loop_until,
    772     /*0C*/ level_cmd_jump_if,
    773     /*0D*/ level_cmd_jump_and_link_if,
    774     /*0E*/ level_cmd_skip_if,
    775     /*0F*/ level_cmd_skip,
    776     /*10*/ level_cmd_skippable_nop,
    777     /*11*/ level_cmd_call,
    778     /*12*/ level_cmd_call_loop,
    779     /*13*/ level_cmd_set_register,
    780     /*14*/ level_cmd_push_pool_state,
    781     /*15*/ level_cmd_pop_pool_state,
    782     /*16*/ level_cmd_load_to_fixed_address,
    783     /*17*/ level_cmd_load_raw,
    784     /*18*/ level_cmd_load_mio0,
    785     /*19*/ level_cmd_load_mario_head,
    786     /*1A*/ level_cmd_load_mio0_texture,
    787     /*1B*/ level_cmd_init_level,
    788     /*1C*/ level_cmd_clear_level,
    789     /*1D*/ level_cmd_alloc_level_pool,
    790     /*1E*/ level_cmd_free_level_pool,
    791     /*1F*/ level_cmd_begin_area,
    792     /*20*/ level_cmd_end_area,
    793     /*21*/ level_cmd_load_model_from_dl,
    794     /*22*/ level_cmd_load_model_from_geo,
    795     /*23*/ level_cmd_23,
    796     /*24*/ level_cmd_place_object,
    797     /*25*/ level_cmd_init_mario,
    798     /*26*/ level_cmd_create_warp_node,
    799     /*27*/ level_cmd_create_painting_warp_node,
    800     /*28*/ level_cmd_create_instant_warp,
    801     /*29*/ level_cmd_load_area,
    802     /*2A*/ level_cmd_unload_area,
    803     /*2B*/ level_cmd_set_mario_start_pos,
    804     /*2C*/ level_cmd_2C,
    805     /*2D*/ level_cmd_2D,
    806     /*2E*/ level_cmd_set_terrain_data,
    807     /*2F*/ level_cmd_set_rooms,
    808     /*30*/ level_cmd_show_dialog,
    809     /*31*/ level_cmd_set_terrain_type,
    810     /*32*/ level_cmd_nop,
    811     /*33*/ level_cmd_set_transition,
    812     /*34*/ level_cmd_set_blackout,
    813     /*35*/ level_cmd_set_gamma,
    814     /*36*/ level_cmd_set_music,
    815     /*37*/ level_cmd_set_menu_music,
    816     /*38*/ level_cmd_38,
    817     /*39*/ level_cmd_set_macro_objects,
    818     /*3A*/ level_cmd_3A,
    819     /*3B*/ level_cmd_create_whirlpool,
    820     /*3C*/ level_cmd_get_or_set_var,
    821 };
    822 
    823 struct LevelCommand *level_script_execute(struct LevelCommand *cmd) {
    824     sScriptStatus = SCRIPT_RUNNING;
    825     sCurrentCmd = cmd;
    826 
    827     while (sScriptStatus == SCRIPT_RUNNING) {
    828         CN_DEBUG_PRINTF(("%08X: ", sCurrentCmd));
    829         CN_DEBUG_PRINTF(("%02d\n", sCurrentCmd->type));
    830 
    831         LevelScriptJumpTable[sCurrentCmd->type]();
    832     }
    833 
    834     profiler_log_thread5_time(LEVEL_SCRIPT_EXECUTE);
    835     init_rcp();
    836     render_game();
    837     end_master_display_list();
    838     alloc_display_list(0);
    839 
    840     return sCurrentCmd;
    841 }