Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/engine/level_script.c
7857 views
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
#include "game/settings.h"
29
#include "behavior_data.h"
30
31
#define CMD_GET(type, offset) (*(type *) (CMD_PROCESS_OFFSET(offset) + (u8 *) sCurrentCmd))
32
33
// These are equal
34
#define CMD_NEXT ((struct LevelCommand *) ((u8 *) sCurrentCmd + (sCurrentCmd->size << CMD_SIZE_SHIFT)))
35
#define NEXT_CMD ((struct LevelCommand *) ((sCurrentCmd->size << CMD_SIZE_SHIFT) + (u8 *) sCurrentCmd))
36
37
struct LevelCommand {
38
/*00*/ u8 type;
39
/*01*/ u8 size;
40
/*02*/ // variable sized argument data
41
};
42
43
enum ScriptStatus { SCRIPT_RUNNING = 1, SCRIPT_PAUSED = 0, SCRIPT_PAUSED2 = -1 };
44
45
static uintptr_t sStack[32];
46
47
static struct AllocOnlyPool *sLevelPool = NULL;
48
49
static u16 sDelayFrames = 0;
50
static u16 sDelayFrames2 = 0;
51
52
static s16 sCurrAreaIndex = -1;
53
54
static uintptr_t *sStackTop = sStack;
55
static uintptr_t *sStackBase = NULL;
56
57
static s16 sScriptStatus;
58
static s32 sRegister;
59
static struct LevelCommand *sCurrentCmd;
60
61
#ifdef USE_SYSTEM_MALLOC
62
static struct MemoryPool *sMemPoolForGoddard;
63
#endif
64
65
static s32 eval_script_op(s8 op, s32 arg) {
66
s32 result = 0;
67
68
switch (op) {
69
case 0:
70
result = sRegister & arg;
71
break;
72
case 1:
73
result = !(sRegister & arg);
74
break;
75
case 2:
76
result = sRegister == arg;
77
break;
78
case 3:
79
result = sRegister != arg;
80
break;
81
case 4:
82
result = sRegister < arg;
83
break;
84
case 5:
85
result = sRegister <= arg;
86
break;
87
case 6:
88
result = sRegister > arg;
89
break;
90
case 7:
91
result = sRegister >= arg;
92
break;
93
}
94
95
return result;
96
}
97
98
static void level_cmd_load_and_execute(void) {
99
main_pool_push_state();
100
load_segment(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8), MEMORY_POOL_LEFT);
101
102
*sStackTop++ = (uintptr_t) NEXT_CMD;
103
*sStackTop++ = (uintptr_t) sStackBase;
104
sStackBase = sStackTop;
105
106
sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 12));
107
}
108
109
static void level_cmd_exit_and_execute(void) {
110
void *targetAddr = CMD_GET(void *, 12);
111
112
main_pool_pop_state();
113
main_pool_push_state();
114
115
load_segment(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8),
116
MEMORY_POOL_LEFT);
117
118
sStackTop = sStackBase;
119
sCurrentCmd = segmented_to_virtual(targetAddr);
120
}
121
122
static void level_cmd_exit(void) {
123
main_pool_pop_state();
124
125
sStackTop = sStackBase;
126
sStackBase = (uintptr_t *) *(--sStackTop);
127
sCurrentCmd = (struct LevelCommand *) *(--sStackTop);
128
}
129
130
static void level_cmd_sleep(void) {
131
sScriptStatus = SCRIPT_PAUSED;
132
133
if (sDelayFrames == 0) {
134
sDelayFrames = CMD_GET(s16, 2);
135
} else if (--sDelayFrames == 0) {
136
sCurrentCmd = CMD_NEXT;
137
sScriptStatus = SCRIPT_RUNNING;
138
}
139
}
140
141
static void level_cmd_sleep2(void) {
142
sScriptStatus = SCRIPT_PAUSED2;
143
144
if (sDelayFrames2 == 0) {
145
sDelayFrames2 = CMD_GET(s16, 2);
146
} else if (--sDelayFrames2 == 0) {
147
sCurrentCmd = CMD_NEXT;
148
sScriptStatus = SCRIPT_RUNNING;
149
}
150
}
151
152
static void level_cmd_jump(void) {
153
sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 4));
154
}
155
156
static void level_cmd_jump_and_link(void) {
157
*sStackTop++ = (uintptr_t) NEXT_CMD;
158
sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 4));
159
}
160
161
static void level_cmd_return(void) {
162
sCurrentCmd = (struct LevelCommand *) *(--sStackTop);
163
}
164
165
static void level_cmd_jump_and_link_push_arg(void) {
166
*sStackTop++ = (uintptr_t) NEXT_CMD;
167
*sStackTop++ = CMD_GET(s16, 2);
168
sCurrentCmd = CMD_NEXT;
169
}
170
171
static void level_cmd_jump_repeat(void) {
172
s32 val = *(sStackTop - 1);
173
174
if (val == 0) {
175
sCurrentCmd = (struct LevelCommand *) *(sStackTop - 2);
176
} else if (--val != 0) {
177
*(sStackTop - 1) = val;
178
sCurrentCmd = (struct LevelCommand *) *(sStackTop - 2);
179
} else {
180
sCurrentCmd = CMD_NEXT;
181
sStackTop -= 2;
182
}
183
}
184
185
static void level_cmd_loop_begin(void) {
186
*sStackTop++ = (uintptr_t) NEXT_CMD;
187
*sStackTop++ = 0;
188
sCurrentCmd = CMD_NEXT;
189
}
190
191
static void level_cmd_loop_until(void) {
192
if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) != 0) {
193
sCurrentCmd = CMD_NEXT;
194
sStackTop -= 2;
195
} else {
196
sCurrentCmd = (struct LevelCommand *) *(sStackTop - 2);
197
}
198
}
199
200
static void level_cmd_jump_if(void) {
201
if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) != 0) {
202
sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 8));
203
} else {
204
sCurrentCmd = CMD_NEXT;
205
}
206
}
207
208
static void level_cmd_jump_and_link_if(void) {
209
if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) != 0) {
210
*sStackTop++ = (uintptr_t) NEXT_CMD;
211
sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 8));
212
} else {
213
sCurrentCmd = CMD_NEXT;
214
}
215
}
216
217
static void level_cmd_skip_if(void) {
218
if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) == 0) {
219
do {
220
sCurrentCmd = CMD_NEXT;
221
} while (sCurrentCmd->type == 0x0F || sCurrentCmd->type == 0x10);
222
}
223
224
sCurrentCmd = CMD_NEXT;
225
}
226
227
static void level_cmd_skip(void) {
228
do {
229
sCurrentCmd = CMD_NEXT;
230
} while (sCurrentCmd->type == 0x10);
231
232
sCurrentCmd = CMD_NEXT;
233
}
234
235
static void level_cmd_skippable_nop(void) {
236
sCurrentCmd = CMD_NEXT;
237
}
238
239
static void level_cmd_call(void) {
240
typedef s32 (*Func)(s16, s32);
241
Func func = CMD_GET(Func, 4);
242
sRegister = func(CMD_GET(s16, 2), sRegister);
243
sCurrentCmd = CMD_NEXT;
244
}
245
246
static void level_cmd_call_loop(void) {
247
typedef s32 (*Func)(s16, s32);
248
Func func = CMD_GET(Func, 4);
249
sRegister = func(CMD_GET(s16, 2), sRegister);
250
251
if (sRegister == 0) {
252
sScriptStatus = SCRIPT_PAUSED;
253
} else {
254
sScriptStatus = SCRIPT_RUNNING;
255
sCurrentCmd = CMD_NEXT;
256
}
257
}
258
259
static void level_cmd_set_register(void) {
260
sRegister = CMD_GET(s16, 2);
261
sCurrentCmd = CMD_NEXT;
262
}
263
264
static void level_cmd_push_pool_state(void) {
265
main_pool_push_state();
266
sCurrentCmd = CMD_NEXT;
267
}
268
269
static void level_cmd_pop_pool_state(void) {
270
main_pool_pop_state();
271
sCurrentCmd = CMD_NEXT;
272
}
273
274
static void level_cmd_load_to_fixed_address(void) {
275
load_to_fixed_pool_addr(CMD_GET(void *, 4), CMD_GET(void *, 8), CMD_GET(void *, 12));
276
sCurrentCmd = CMD_NEXT;
277
}
278
279
static void level_cmd_load_raw(void) {
280
load_segment(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8),
281
MEMORY_POOL_LEFT);
282
sCurrentCmd = CMD_NEXT;
283
}
284
285
static void level_cmd_load_mio0(void) {
286
load_segment_decompress(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8));
287
sCurrentCmd = CMD_NEXT;
288
}
289
290
#ifdef USE_SYSTEM_MALLOC
291
static void *alloc_for_goddard(u32 size) {
292
return mem_pool_alloc(sMemPoolForGoddard, size);
293
}
294
295
static void free_for_goddard(void *ptr) {
296
mem_pool_free(sMemPoolForGoddard, ptr);
297
}
298
#endif
299
300
static void level_cmd_load_mario_head(void) {
301
#ifdef USE_SYSTEM_MALLOC
302
sMemPoolForGoddard = mem_pool_init(0, 0);
303
gdm_init(alloc_for_goddard, free_for_goddard);
304
gdm_setup();
305
gdm_maketestdl(CMD_GET(s16, 2));
306
#else
307
// TODO: Fix these hardcoded sizes
308
void *addr = main_pool_alloc(DOUBLE_SIZE_ON_64_BIT(0xE1000), MEMORY_POOL_LEFT);
309
if (addr != NULL) {
310
gdm_init(addr, DOUBLE_SIZE_ON_64_BIT(0xE1000));
311
gd_add_to_heap(gZBuffer, sizeof(gZBuffer)); // 0x25800
312
gd_add_to_heap(gFrameBuffer0, 3 * sizeof(gFrameBuffer0)); // 0x70800
313
gdm_setup();
314
gdm_maketestdl(CMD_GET(s16, 2));
315
} else {
316
}
317
#endif
318
319
sCurrentCmd = CMD_NEXT;
320
}
321
322
static void level_cmd_load_mio0_texture(void) {
323
load_segment_decompress_heap(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8));
324
sCurrentCmd = CMD_NEXT;
325
}
326
327
static void level_cmd_init_level(void) {
328
init_graph_node_start(NULL, (struct GraphNodeStart *) &gObjParentGraphNode);
329
clear_objects();
330
clear_areas();
331
main_pool_push_state();
332
333
sCurrentCmd = CMD_NEXT;
334
}
335
336
static void level_cmd_clear_level(void) {
337
clear_objects();
338
clear_area_graph_nodes();
339
clear_areas();
340
main_pool_pop_state();
341
342
sCurrentCmd = CMD_NEXT;
343
}
344
345
static void level_cmd_alloc_level_pool(void) {
346
if (sLevelPool == NULL) {
347
#ifdef USE_SYSTEM_MALLOC
348
sLevelPool = alloc_only_pool_init();
349
#else
350
sLevelPool = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool),
351
MEMORY_POOL_LEFT);
352
#endif
353
}
354
355
sCurrentCmd = CMD_NEXT;
356
}
357
358
static void level_cmd_free_level_pool(void) {
359
s32 i;
360
361
#ifndef USE_SYSTEM_MALLOC
362
alloc_only_pool_resize(sLevelPool, sLevelPool->usedSpace);
363
#endif
364
sLevelPool = NULL;
365
366
for (i = 0; i < 8; i++) {
367
if (gAreaData[i].terrainData != NULL) {
368
alloc_surface_pools();
369
break;
370
}
371
}
372
373
sCurrentCmd = CMD_NEXT;
374
}
375
376
static void level_cmd_begin_area(void) {
377
u8 areaIndex = CMD_GET(u8, 2);
378
void *geoLayoutAddr = CMD_GET(void *, 4);
379
380
if (areaIndex < 8) {
381
struct GraphNodeRoot *screenArea =
382
(struct GraphNodeRoot *) process_geo_layout(sLevelPool, geoLayoutAddr);
383
struct GraphNodeCamera *node = (struct GraphNodeCamera *) screenArea->views[0];
384
385
sCurrAreaIndex = areaIndex;
386
screenArea->areaIndex = areaIndex;
387
gAreas[areaIndex].unk04 = screenArea;
388
389
if (node != NULL) {
390
gAreas[areaIndex].camera = (struct Camera *) node->config.camera;
391
} else {
392
gAreas[areaIndex].camera = NULL;
393
}
394
}
395
396
sCurrentCmd = CMD_NEXT;
397
}
398
399
static void level_cmd_end_area(void) {
400
sCurrAreaIndex = -1;
401
sCurrentCmd = CMD_NEXT;
402
}
403
404
static void level_cmd_load_model_from_dl(void) {
405
s16 val1 = CMD_GET(s16, 2) & 0x0FFF;
406
s16 val2 = ((u16)CMD_GET(s16, 2)) >> 12;
407
void *val3 = CMD_GET(void *, 4);
408
409
if (val1 < 256) {
410
gLoadedGraphNodes[val1] =
411
(struct GraphNode *) init_graph_node_display_list(sLevelPool, 0, val2, val3);
412
}
413
414
sCurrentCmd = CMD_NEXT;
415
}
416
417
static void level_cmd_load_model_from_geo(void) {
418
s16 arg0 = CMD_GET(s16, 2);
419
void *arg1 = CMD_GET(void *, 4);
420
421
if (arg0 < 256) {
422
gLoadedGraphNodes[arg0] = process_geo_layout(sLevelPool, arg1);
423
}
424
425
sCurrentCmd = CMD_NEXT;
426
}
427
428
static void level_cmd_23(void) {
429
union {
430
s32 i;
431
f32 f;
432
} arg2;
433
434
s16 model = CMD_GET(s16, 2) & 0x0FFF;
435
s16 arg0H = ((u16)CMD_GET(s16, 2)) >> 12;
436
void *arg1 = CMD_GET(void *, 4);
437
// load an f32, but using an integer load instruction for some reason (hence the union)
438
arg2.i = CMD_GET(s32, 8);
439
440
if (model < 256) {
441
// GraphNodeScale has a GraphNode at the top. This
442
// is being stored to the array, so cast the pointer.
443
gLoadedGraphNodes[model] =
444
(struct GraphNode *) init_graph_node_scale(sLevelPool, 0, arg0H, arg1, arg2.f);
445
}
446
447
sCurrentCmd = CMD_NEXT;
448
}
449
450
static void level_cmd_init_mario(void) {
451
vec3s_set(gMarioSpawnInfo->startPos, 0, 0, 0);
452
vec3s_set(gMarioSpawnInfo->startAngle, 0, 0, 0);
453
454
gMarioSpawnInfo->activeAreaIndex = -1;
455
gMarioSpawnInfo->areaIndex = 0;
456
gMarioSpawnInfo->behaviorArg = CMD_GET(u32, 4);
457
gMarioSpawnInfo->behaviorScript = CMD_GET(void *, 8);
458
gMarioSpawnInfo->unk18 = gLoadedGraphNodes[CMD_GET(u8, 3)];
459
gMarioSpawnInfo->next = NULL;
460
461
sCurrentCmd = CMD_NEXT;
462
}
463
464
static void remain_mod_place_objects(u8 model, s16 px, s16 py, s16 pz,
465
s16 rx, s16 ry, s16 rz, u32 behArg, void *behScript)
466
{
467
struct SpawnInfo *spawnInfo = alloc_only_pool_alloc(sLevelPool, sizeof(struct SpawnInfo));
468
469
spawnInfo->startPos[0] = px;
470
spawnInfo->startPos[1] = py;
471
spawnInfo->startPos[2] = pz;
472
473
spawnInfo->startAngle[0] = rx * 0x8000 / 180;
474
spawnInfo->startAngle[1] = ry * 0x8000 / 180;
475
spawnInfo->startAngle[2] = rz * 0x8000 / 180;
476
477
spawnInfo->areaIndex = sCurrAreaIndex;
478
spawnInfo->activeAreaIndex = sCurrAreaIndex;
479
480
spawnInfo->behaviorArg = behArg;
481
spawnInfo->behaviorScript = behScript;
482
spawnInfo->unk18 = gLoadedGraphNodes[model];
483
spawnInfo->next = gAreas[sCurrAreaIndex].objectSpawnInfos;
484
485
gAreas[sCurrAreaIndex].objectSpawnInfos = spawnInfo;
486
}
487
488
static void remain_mod_objects(struct SpawnInfo *spawnInfo)
489
{
490
void *spinWarpAreaOne = (void *) bhvSpinAirborneWarp; // Warp that occurs at start of each level
491
492
if ((gCurrLevelNum == LEVEL_WF) && (spawnInfo->behaviorScript == spinWarpAreaOne))
493
{
494
remain_mod_place_objects(MODEL_NONE, 780, 3784, -625, 0, -45, 0, 0x000D0000, (void *) bhvFadingWarp);
495
}
496
if ((gCurrLevelNum == LEVEL_JRB) && (spawnInfo->behaviorScript == spinWarpAreaOne))
497
{
498
remain_mod_place_objects(MODEL_NONE, 5250, 1650, 2500, 0, -90, 0, 0x000B0000, (void *)bhvFadingWarp);
499
}
500
if (gCurrLevelNum == LEVEL_CCM)
501
{
502
// Load assets from Act 5 and Act 2+
503
if ((gCurrActNum == 1) && (sCurrAreaIndex == 1) && (spawnInfo->behaviorScript == (void *)bhvSnowmansHead))
504
{
505
remain_mod_place_objects(MODEL_CCM_SNOWMAN_BASE, 2560, 2662, -1122, 0, 0, 0, 0x00000000, (void *)bhvSnowmansBottom);
506
}
507
if ((gCurrActNum == 1) && (sCurrAreaIndex == 2) && (spawnInfo->behaviorScript == (void *)bhvPenguinRaceFinishLine))
508
{
509
remain_mod_place_objects(MODEL_PENGUIN, -4952, 6656, -6075, 0, 270, 0, 0x02000000, (void *)bhvRacingPenguin);
510
}
511
}
512
if ((gCurrLevelNum == LEVEL_LLL) && (spawnInfo->behaviorScript == spinWarpAreaOne))
513
{
514
remain_mod_place_objects(MODEL_NONE, 925, 359, 2150, 0, 0, 0, 0x000E0000, (void *)bhvFadingWarp);
515
}
516
if (gCurrLevelNum == LEVEL_SSL)
517
{
518
if (spawnInfo->behaviorScript == spinWarpAreaOne)
519
{
520
remain_mod_place_objects(MODEL_NONE, -2050, 456, 615, 0, 0, 0, 0x00210000, (void *)bhvFadingWarp);
521
}
522
// Below places a warp after the first object in Area 2
523
if ((spawnInfo->behaviorScript == (void *)bhvAirborneWarp)&&(spawnInfo->behaviorArg == 0x000A0000))
524
{
525
remain_mod_place_objects(MODEL_NONE, 0, 0, 6708, 0, 0, 0, 0x00220000, (void *)bhvFadingWarp);
526
}
527
}
528
if (gCurrLevelNum == LEVEL_DDD)
529
{
530
if ((spawnInfo->behaviorScript == spinWarpAreaOne) && (gCurrActNum == 1))
531
{
532
remain_mod_place_objects(MODEL_MANTA_RAY, -4640, -1380, 40, 0, 0, 0, 0x04000000, (void *)bhvMantaRay);
533
}
534
if (spawnInfo->behaviorScript == (void *)bhvBowserSubDoor) // The first object in Area 2
535
{
536
remain_mod_place_objects(MODEL_NONE, 5960, 310, 4200, 0, -135, 0, 0x000C0000, (void *)bhvFadingWarp);
537
538
if ((configBowsersSub) && (gCurrActNum != 1))
539
{
540
sCurrentCmd = CMD_NEXT; // skip BowserSub
541
sCurrentCmd = CMD_NEXT; // overwrite BowserSubDoor object with this one
542
543
spawnInfo->startPos[0] = CMD_GET(s16, 4);
544
spawnInfo->startPos[1] = CMD_GET(s16, 6);
545
spawnInfo->startPos[2] = CMD_GET(s16, 8);
546
547
spawnInfo->startAngle[0] = CMD_GET(s16, 10) * 0x8000 / 180;
548
spawnInfo->startAngle[1] = CMD_GET(s16, 12) * 0x8000 / 180;
549
spawnInfo->startAngle[2] = CMD_GET(s16, 14) * 0x8000 / 180;
550
551
spawnInfo->behaviorArg = CMD_GET(u32, 16);
552
spawnInfo->behaviorScript = CMD_GET(void *, 20);
553
spawnInfo->unk18 = gLoadedGraphNodes[CMD_GET(u8, 3)];
554
}
555
}
556
}
557
if ((gCurrLevelNum == LEVEL_SL) && (spawnInfo->behaviorScript == spinWarpAreaOne))
558
{
559
remain_mod_place_objects(MODEL_NONE, 4350, 1224, 2675, 0, 0, 0, 0x00200000, (void *) bhvFadingWarp);
560
}
561
if ((gCurrLevelNum == LEVEL_TTM) && (spawnInfo->behaviorScript == spinWarpAreaOne))
562
{
563
if (gCurrActNum == 1) // Load assets from Act 2
564
{
565
remain_mod_place_objects(MODEL_UKIKI, 729, 2307, 335, 0, 0, 0, 0x00000000, (void *)bhvUkiki);
566
remain_mod_place_objects(MODEL_TTM_STAR_CAGE, 2496, 1670, 1492, 0, 0, 0, 0x01000000, (void *)bhvUkikiCage);
567
}
568
}
569
if ((gCurrLevelNum == LEVEL_THI) && (spawnInfo->behaviorScript == spinWarpAreaOne))
570
{
571
if (spawnInfo->areaIndex == 1) // THI actually has two spinwarp entrances (big/small)
572
{
573
remain_mod_place_objects(MODEL_NONE, 350, 4091, -1515, 0, 90, 0, 0x000E0000, (void *)bhvFadingWarp);
574
575
if (gCurrActNum == 1) // Load assets from Act 3
576
{
577
remain_mod_place_objects(MODEL_KOOPA_WITH_SHELL, -1900, -511, 2400, 0, -30, 0, 0x02030000, (void *)bhvKoopa);
578
remain_mod_place_objects(MODEL_NONE, 7400, -1537, -6300, 0, 0, 0, 0x00000000, (void *)bhvKoopaRaceEndpoint);
579
}
580
}
581
}
582
if ((gCurrLevelNum == LEVEL_TTC) && (spawnInfo->behaviorScript == spinWarpAreaOne))
583
{
584
remain_mod_place_objects(MODEL_NONE, 1785, -4822, -720, 0, 0, 0, 0x000B0000, (void *)bhvFadingWarp);
585
remain_mod_place_objects(MODEL_NONE, 1535, -4622, -790, 0, 0, 0, 0x000C0000, (void *)bhvFadingWarp);
586
}
587
}
588
589
static void remain_mod_create_whirlpool(u8 index, u8 id, s16 pX, s16 pY, s16 pZ, s16 strength)
590
{
591
struct Whirlpool *whirlpool;
592
593
if (sCurrAreaIndex != -1 && index < 2)
594
{
595
if ((whirlpool = gAreas[sCurrAreaIndex].whirlpools[index]) == NULL)
596
{
597
whirlpool = alloc_only_pool_alloc(sLevelPool, sizeof(struct Whirlpool));
598
gAreas[sCurrAreaIndex].whirlpools[index] = whirlpool;
599
}
600
601
vec3s_set(whirlpool->pos, pX, pY, pZ);
602
whirlpool->strength = strength;
603
}
604
}
605
606
static void remain_mod_create_warp_nodes(u8 id, u8 destLevel, u8 destArea, u8 destNode)
607
{
608
struct ObjectWarpNode *warpNode = alloc_only_pool_alloc(sLevelPool, sizeof(struct ObjectWarpNode));
609
610
warpNode->node.id = id;
611
warpNode->node.destLevel = destLevel;
612
warpNode->node.destArea = destArea;
613
warpNode->node.destNode = destNode;
614
615
warpNode->object = NULL;
616
617
warpNode->next = gAreas[sCurrAreaIndex].warpNodes;
618
gAreas[sCurrAreaIndex].warpNodes = warpNode;
619
}
620
621
static void remain_mod_warp_nodes(struct ObjectWarpNode *warpNode)
622
{
623
u8 lastWarpNodeInArea = 0xF1;
624
625
if ((gCurrLevelNum == LEVEL_WF) && (warpNode->node.id == lastWarpNodeInArea))
626
{
627
remain_mod_create_warp_nodes(0x0D, LEVEL_WF, 0x01, 0x0D);
628
remain_mod_create_warp_nodes(0x0E, LEVEL_WF, 0x01, 0x0D);
629
}
630
if ((gCurrLevelNum == LEVEL_JRB) && (warpNode->node.id == lastWarpNodeInArea))
631
{
632
if (sCurrAreaIndex == 1)
633
{
634
remain_mod_create_warp_nodes(0x0B, LEVEL_JRB, 0x01, 0x0B);
635
636
if (gCurrActNum == 1)
637
{
638
// Create whirlpool with strength (-30) set to zero
639
remain_mod_create_whirlpool(0, 3, 4979, -5222, 2482, 0);
640
}
641
}
642
else if (sCurrAreaIndex == 2)
643
{
644
remain_mod_create_warp_nodes(0x0C, LEVEL_JRB, 0x01, 0x0B);
645
}
646
}
647
if ((gCurrLevelNum == LEVEL_LLL) && (warpNode->node.id == lastWarpNodeInArea))
648
{
649
if (sCurrAreaIndex == 1)
650
{
651
remain_mod_create_warp_nodes(0x0E, LEVEL_LLL, 0x01, 0x0E);
652
}
653
else if (sCurrAreaIndex == 2)
654
{
655
remain_mod_create_warp_nodes(0x0F, LEVEL_LLL, 0x01, 0x0E);
656
remain_mod_create_warp_nodes(0x20, LEVEL_LLL, 0x01, 0x0E);
657
}
658
}
659
if ((gCurrLevelNum == LEVEL_SSL) && (warpNode->node.id == lastWarpNodeInArea))
660
{
661
if (sCurrAreaIndex == 1)
662
{
663
remain_mod_create_warp_nodes(0x21, LEVEL_SSL, 0x01, 0x21);
664
}
665
else if (sCurrAreaIndex == 2)
666
{
667
remain_mod_create_warp_nodes(0x22, LEVEL_SSL, 0x01, 0x21);
668
}
669
else if (sCurrAreaIndex == 3)
670
{
671
remain_mod_create_warp_nodes(0x23, LEVEL_SSL, 0x01, 0x21);
672
}
673
}
674
if ((gCurrLevelNum == LEVEL_DDD) && (warpNode->node.id == lastWarpNodeInArea))
675
{
676
if (sCurrAreaIndex == 2)
677
{
678
remain_mod_create_warp_nodes(0x0B, LEVEL_DDD, 0x02, 0x0C);
679
remain_mod_create_warp_nodes(0x0C, LEVEL_DDD, 0x02, 0x0C);
680
681
if ((gCurrActNum == 1) && (configBowsersSub))
682
{
683
// Create whirlpool with strength (50) set to zero
684
remain_mod_create_whirlpool(1, 2, 3917, -2040, -6041, 0);
685
}
686
}
687
}
688
if ((gCurrLevelNum == LEVEL_SL) && (warpNode->node.id == lastWarpNodeInArea))
689
{
690
if (sCurrAreaIndex == 1)
691
{
692
remain_mod_create_warp_nodes(0x0F, LEVEL_SL, 0x01, 0x20);
693
remain_mod_create_warp_nodes(0x20, LEVEL_SL, 0x01, 0x20);
694
}
695
}
696
if ((gCurrLevelNum == LEVEL_THI) && (warpNode->node.id == lastWarpNodeInArea))
697
{
698
if (sCurrAreaIndex == 1)
699
{
700
remain_mod_create_warp_nodes(0x0E, LEVEL_THI, 0x01, 0x0E);
701
}
702
else if (sCurrAreaIndex == 3)
703
{
704
remain_mod_create_warp_nodes(0x0F, LEVEL_THI, 0x01, 0x0E);
705
}
706
}
707
if ((gCurrLevelNum == LEVEL_TTC) && (warpNode->node.id == lastWarpNodeInArea))
708
{
709
remain_mod_create_warp_nodes(0x0B, LEVEL_TTC, 0x01, 0x0C);
710
remain_mod_create_warp_nodes(0x0C, LEVEL_TTC, 0x01, 0x0C);
711
}
712
}
713
714
static void level_cmd_place_object(void) {
715
u8 val7 = 1 << (gCurrActNum - 1);
716
u16 model;
717
struct SpawnInfo *spawnInfo;
718
719
// This is a mess, you probably won't forgive me but I hope god will.
720
u8 canLoad = (
721
(gCurrLevelNum != LEVEL_DDD || !configBowsersSub)
722
&& (gCurrLevelNum != LEVEL_JRB)
723
&& (
724
(gCurrLevelNum == LEVEL_LLL)
725
|| (!(CMD_GET(u8, 2) & (1 << 0)) && (gCurrActNum != 1))
726
|| ((CMD_GET(u8, 2) & (1 << 0)) && (gCurrActNum == 1))
727
)
728
)
729
|| (
730
(gCurrLevelNum == LEVEL_DDD)
731
&& (configBowsersSub)
732
&& (save_file_get_flags() & (SAVE_FLAG_HAVE_KEY_2 | SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR))
733
)
734
|| (
735
(gCurrLevelNum == LEVEL_JRB)
736
&& (
737
((gCurrActNum == 1) && (CMD_GET(u8, 2) & (1 << 0)))
738
|| ((gCurrActNum != 1) && (CMD_GET(u8, 2) & (1 << 1)))
739
)
740
);
741
742
if (sCurrAreaIndex != -1
743
&& (((!configStayInCourse || gCurrLevelNum != LEVEL_JRB) && (CMD_GET(u8, 2) & val7)) || CMD_GET(u8, 2) == 0x1F || (configStayInCourse && canLoad))) {
744
745
model = CMD_GET(u8, 3);
746
spawnInfo = alloc_only_pool_alloc(sLevelPool, sizeof(struct SpawnInfo));
747
748
spawnInfo->startPos[0] = CMD_GET(s16, 4);
749
spawnInfo->startPos[1] = CMD_GET(s16, 6);
750
spawnInfo->startPos[2] = CMD_GET(s16, 8);
751
752
spawnInfo->startAngle[0] = CMD_GET(s16, 10) * 0x8000 / 180;
753
spawnInfo->startAngle[1] = CMD_GET(s16, 12) * 0x8000 / 180;
754
spawnInfo->startAngle[2] = CMD_GET(s16, 14) * 0x8000 / 180;
755
756
spawnInfo->areaIndex = sCurrAreaIndex;
757
spawnInfo->activeAreaIndex = sCurrAreaIndex;
758
759
spawnInfo->behaviorArg = CMD_GET(u32, 16);
760
spawnInfo->behaviorScript = CMD_GET(void *, 20);
761
spawnInfo->unk18 = gLoadedGraphNodes[model];
762
spawnInfo->next = gAreas[sCurrAreaIndex].objectSpawnInfos;
763
764
gAreas[sCurrAreaIndex].objectSpawnInfos = spawnInfo;
765
766
if (configStayInCourse == 2)
767
{
768
remain_mod_objects(spawnInfo);
769
}
770
}
771
772
sCurrentCmd = CMD_NEXT;
773
}
774
775
static void level_cmd_create_warp_node(void) {
776
if (sCurrAreaIndex != -1) {
777
struct ObjectWarpNode *warpNode =
778
alloc_only_pool_alloc(sLevelPool, sizeof(struct ObjectWarpNode));
779
780
warpNode->node.id = CMD_GET(u8, 2);
781
warpNode->node.destLevel = CMD_GET(u8, 3) + CMD_GET(u8, 6);
782
warpNode->node.destArea = CMD_GET(u8, 4);
783
warpNode->node.destNode = CMD_GET(u8, 5);
784
785
warpNode->object = NULL;
786
787
warpNode->next = gAreas[sCurrAreaIndex].warpNodes;
788
gAreas[sCurrAreaIndex].warpNodes = warpNode;
789
790
if (configStayInCourse == 2)
791
{
792
remain_mod_warp_nodes(warpNode);
793
}
794
}
795
796
sCurrentCmd = CMD_NEXT;
797
}
798
799
static void level_cmd_create_instant_warp(void) {
800
s32 i;
801
struct InstantWarp *warp;
802
803
if (sCurrAreaIndex != -1) {
804
if (gAreas[sCurrAreaIndex].instantWarps == NULL) {
805
gAreas[sCurrAreaIndex].instantWarps =
806
alloc_only_pool_alloc(sLevelPool, 4 * sizeof(struct InstantWarp));
807
808
for (i = INSTANT_WARP_INDEX_START; i < INSTANT_WARP_INDEX_STOP; i++) {
809
gAreas[sCurrAreaIndex].instantWarps[i].id = 0;
810
}
811
}
812
813
warp = gAreas[sCurrAreaIndex].instantWarps + CMD_GET(u8, 2);
814
815
warp[0].id = 1;
816
warp[0].area = CMD_GET(u8, 3);
817
818
warp[0].displacement[0] = CMD_GET(s16, 4);
819
warp[0].displacement[1] = CMD_GET(s16, 6);
820
warp[0].displacement[2] = CMD_GET(s16, 8);
821
}
822
823
sCurrentCmd = CMD_NEXT;
824
}
825
826
static void level_cmd_set_terrain_type(void) {
827
if (sCurrAreaIndex != -1) {
828
gAreas[sCurrAreaIndex].terrainType |= CMD_GET(s16, 2);
829
}
830
831
sCurrentCmd = CMD_NEXT;
832
}
833
834
static void level_cmd_create_painting_warp_node(void) {
835
s32 i;
836
struct WarpNode *node;
837
838
if (sCurrAreaIndex != -1) {
839
if (gAreas[sCurrAreaIndex].paintingWarpNodes == NULL) {
840
gAreas[sCurrAreaIndex].paintingWarpNodes =
841
alloc_only_pool_alloc(sLevelPool, 45 * sizeof(struct WarpNode));
842
843
for (i = 0; i < 45; i++) {
844
gAreas[sCurrAreaIndex].paintingWarpNodes[i].id = 0;
845
}
846
}
847
848
node = &gAreas[sCurrAreaIndex].paintingWarpNodes[CMD_GET(u8, 2)];
849
850
node->id = 1;
851
node->destLevel = CMD_GET(u8, 3) + CMD_GET(u8, 6);
852
node->destArea = CMD_GET(u8, 4);
853
node->destNode = CMD_GET(u8, 5);
854
}
855
856
sCurrentCmd = CMD_NEXT;
857
}
858
859
static void level_cmd_3A(void) {
860
struct UnusedArea28 *val4;
861
862
if (sCurrAreaIndex != -1) {
863
if ((val4 = gAreas[sCurrAreaIndex].unused28) == NULL) {
864
val4 = gAreas[sCurrAreaIndex].unused28 =
865
alloc_only_pool_alloc(sLevelPool, sizeof(struct UnusedArea28));
866
}
867
868
val4->unk00 = CMD_GET(s16, 2);
869
val4->unk02 = CMD_GET(s16, 4);
870
val4->unk04 = CMD_GET(s16, 6);
871
val4->unk06 = CMD_GET(s16, 8);
872
val4->unk08 = CMD_GET(s16, 10);
873
}
874
875
sCurrentCmd = CMD_NEXT;
876
}
877
878
static void level_cmd_create_whirlpool(void) {
879
struct Whirlpool *whirlpool;
880
s32 index = CMD_GET(u8, 2);
881
s32 beatBowser2 = (!configBowsersSub && ((save_file_get_flags() & (SAVE_FLAG_HAVE_KEY_2 | SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR)) != 0)) || (configBowsersSub && gCurrActNum >= 2);
882
883
if (CMD_GET(u8, 3) == 0 || (CMD_GET(u8, 3) == 1 && !beatBowser2)
884
|| (CMD_GET(u8, 3) == 2 && beatBowser2) || (CMD_GET(u8, 3) == 3 && gCurrActNum >= 2)) {
885
if (sCurrAreaIndex != -1 && index < 2) {
886
if ((whirlpool = gAreas[sCurrAreaIndex].whirlpools[index]) == NULL) {
887
whirlpool = alloc_only_pool_alloc(sLevelPool, sizeof(struct Whirlpool));
888
gAreas[sCurrAreaIndex].whirlpools[index] = whirlpool;
889
}
890
891
vec3s_set(whirlpool->pos, CMD_GET(s16, 4), CMD_GET(s16, 6), CMD_GET(s16, 8));
892
whirlpool->strength = CMD_GET(s16, 10);
893
}
894
}
895
896
sCurrentCmd = CMD_NEXT;
897
}
898
899
static void level_cmd_set_blackout(void) {
900
osViBlack(CMD_GET(u8, 2));
901
sCurrentCmd = CMD_NEXT;
902
}
903
904
static void level_cmd_set_gamma(void) {
905
osViSetSpecialFeatures(CMD_GET(u8, 2) == 0 ? OS_VI_GAMMA_OFF : OS_VI_GAMMA_ON);
906
sCurrentCmd = CMD_NEXT;
907
}
908
909
static void level_cmd_set_terrain_data(void) {
910
if (sCurrAreaIndex != -1) {
911
#ifndef NO_SEGMENTED_MEMORY
912
gAreas[sCurrAreaIndex].terrainData = segmented_to_virtual(CMD_GET(void *, 4));
913
#else
914
Collision *data;
915
u32 size;
916
917
// The game modifies the terrain data and must be reset upon level reload.
918
data = segmented_to_virtual(CMD_GET(void *, 4));
919
size = get_area_terrain_size(data) * sizeof(Collision);
920
gAreas[sCurrAreaIndex].terrainData = alloc_only_pool_alloc(sLevelPool, size);
921
memcpy(gAreas[sCurrAreaIndex].terrainData, data, size);
922
#endif
923
}
924
sCurrentCmd = CMD_NEXT;
925
}
926
927
static void level_cmd_set_rooms(void) {
928
if (sCurrAreaIndex != -1) {
929
gAreas[sCurrAreaIndex].surfaceRooms = segmented_to_virtual(CMD_GET(void *, 4));
930
}
931
sCurrentCmd = CMD_NEXT;
932
}
933
934
static void level_cmd_set_macro_objects(void) {
935
if (sCurrAreaIndex != -1) {
936
#ifndef NO_SEGMENTED_MEMORY
937
gAreas[sCurrAreaIndex].macroObjects = segmented_to_virtual(CMD_GET(void *, 4));
938
#else
939
// The game modifies the macro object data (for example marking coins as taken),
940
// so it must be reset when the level reloads.
941
MacroObject *data = segmented_to_virtual(CMD_GET(void *, 4));
942
s32 len = 0;
943
while (data[len++] != MACRO_OBJECT_END()) {
944
len += 4;
945
}
946
gAreas[sCurrAreaIndex].macroObjects = alloc_only_pool_alloc(sLevelPool, len * sizeof(MacroObject));
947
memcpy(gAreas[sCurrAreaIndex].macroObjects, data, len * sizeof(MacroObject));
948
#endif
949
}
950
sCurrentCmd = CMD_NEXT;
951
}
952
953
static void level_cmd_load_area(void) {
954
s16 areaIndex = CMD_GET(u8, 2);
955
UNUSED void *unused = (u8 *) sCurrentCmd + 4;
956
957
stop_sounds_in_continuous_banks();
958
load_area(areaIndex);
959
960
sCurrentCmd = CMD_NEXT;
961
}
962
963
static void level_cmd_unload_area(void) {
964
unload_area();
965
sCurrentCmd = CMD_NEXT;
966
}
967
968
static void level_cmd_set_mario_start_pos(void) {
969
gMarioSpawnInfo->areaIndex = CMD_GET(u8, 2);
970
971
#if IS_64_BIT
972
vec3s_set(gMarioSpawnInfo->startPos, CMD_GET(s16, 6), CMD_GET(s16, 8), CMD_GET(s16, 10));
973
#else
974
vec3s_copy(gMarioSpawnInfo->startPos, CMD_GET(Vec3s, 6));
975
#endif
976
vec3s_set(gMarioSpawnInfo->startAngle, 0, CMD_GET(s16, 4) * 0x8000 / 180, 0);
977
978
sCurrentCmd = CMD_NEXT;
979
}
980
981
static void level_cmd_2C(void) {
982
unload_mario_area();
983
sCurrentCmd = CMD_NEXT;
984
}
985
986
static void level_cmd_2D(void) {
987
area_update_objects();
988
sCurrentCmd = CMD_NEXT;
989
}
990
991
static void level_cmd_set_transition(void) {
992
if (gCurrentArea != NULL) {
993
play_transition(CMD_GET(u8, 2), CMD_GET(u8, 3), CMD_GET(u8, 4), CMD_GET(u8, 5), CMD_GET(u8, 6));
994
}
995
sCurrentCmd = CMD_NEXT;
996
}
997
998
static void level_cmd_nop(void) {
999
sCurrentCmd = CMD_NEXT;
1000
}
1001
1002
static void level_cmd_show_dialog(void) {
1003
if (sCurrAreaIndex != -1) {
1004
if (CMD_GET(u8, 2) < 2) {
1005
gAreas[sCurrAreaIndex].dialog[CMD_GET(u8, 2)] = CMD_GET(u8, 3);
1006
}
1007
}
1008
sCurrentCmd = CMD_NEXT;
1009
}
1010
1011
static void level_cmd_set_music(void) {
1012
if (sCurrAreaIndex != -1) {
1013
gAreas[sCurrAreaIndex].musicParam = CMD_GET(s16, 2);
1014
gAreas[sCurrAreaIndex].musicParam2 = CMD_GET(s16, 4);
1015
}
1016
sCurrentCmd = CMD_NEXT;
1017
}
1018
1019
static void level_cmd_set_menu_music(void) {
1020
set_background_music(0, CMD_GET(s16, 2), 0);
1021
sCurrentCmd = CMD_NEXT;
1022
}
1023
1024
static void level_cmd_38(void) {
1025
fadeout_music(CMD_GET(s16, 2));
1026
sCurrentCmd = CMD_NEXT;
1027
}
1028
1029
static void level_cmd_get_or_set_var(void) {
1030
if (CMD_GET(u8, 2) == 0) {
1031
switch (CMD_GET(u8, 3)) {
1032
case 0:
1033
gCurrSaveFileNum = sRegister;
1034
break;
1035
case 1:
1036
gCurrCourseNum = sRegister;
1037
break;
1038
case 2:
1039
gCurrActNum = sRegister;
1040
break;
1041
case 3:
1042
gCurrLevelNum = sRegister;
1043
break;
1044
case 4:
1045
gCurrAreaIndex = sRegister;
1046
break;
1047
}
1048
} else {
1049
switch (CMD_GET(u8, 3)) {
1050
case 0:
1051
sRegister = gCurrSaveFileNum;
1052
break;
1053
case 1:
1054
sRegister = gCurrCourseNum;
1055
break;
1056
case 2:
1057
sRegister = gCurrActNum;
1058
break;
1059
case 3:
1060
sRegister = gCurrLevelNum;
1061
break;
1062
case 4:
1063
sRegister = gCurrAreaIndex;
1064
break;
1065
}
1066
}
1067
1068
sCurrentCmd = CMD_NEXT;
1069
}
1070
1071
static void (*LevelScriptJumpTable[])(void) = {
1072
/*00*/ level_cmd_load_and_execute,
1073
/*01*/ level_cmd_exit_and_execute,
1074
/*02*/ level_cmd_exit,
1075
/*03*/ level_cmd_sleep,
1076
/*04*/ level_cmd_sleep2,
1077
/*05*/ level_cmd_jump,
1078
/*06*/ level_cmd_jump_and_link,
1079
/*07*/ level_cmd_return,
1080
/*08*/ level_cmd_jump_and_link_push_arg,
1081
/*09*/ level_cmd_jump_repeat,
1082
/*0A*/ level_cmd_loop_begin,
1083
/*0B*/ level_cmd_loop_until,
1084
/*0C*/ level_cmd_jump_if,
1085
/*0D*/ level_cmd_jump_and_link_if,
1086
/*0E*/ level_cmd_skip_if,
1087
/*0F*/ level_cmd_skip,
1088
/*10*/ level_cmd_skippable_nop,
1089
/*11*/ level_cmd_call,
1090
/*12*/ level_cmd_call_loop,
1091
/*13*/ level_cmd_set_register,
1092
/*14*/ level_cmd_push_pool_state,
1093
/*15*/ level_cmd_pop_pool_state,
1094
/*16*/ level_cmd_load_to_fixed_address,
1095
/*17*/ level_cmd_load_raw,
1096
/*18*/ level_cmd_load_mio0,
1097
/*19*/ level_cmd_load_mario_head,
1098
/*1A*/ level_cmd_load_mio0_texture,
1099
/*1B*/ level_cmd_init_level,
1100
/*1C*/ level_cmd_clear_level,
1101
/*1D*/ level_cmd_alloc_level_pool,
1102
/*1E*/ level_cmd_free_level_pool,
1103
/*1F*/ level_cmd_begin_area,
1104
/*20*/ level_cmd_end_area,
1105
/*21*/ level_cmd_load_model_from_dl,
1106
/*22*/ level_cmd_load_model_from_geo,
1107
/*23*/ level_cmd_23,
1108
/*24*/ level_cmd_place_object,
1109
/*25*/ level_cmd_init_mario,
1110
/*26*/ level_cmd_create_warp_node,
1111
/*27*/ level_cmd_create_painting_warp_node,
1112
/*28*/ level_cmd_create_instant_warp,
1113
/*29*/ level_cmd_load_area,
1114
/*2A*/ level_cmd_unload_area,
1115
/*2B*/ level_cmd_set_mario_start_pos,
1116
/*2C*/ level_cmd_2C,
1117
/*2D*/ level_cmd_2D,
1118
/*2E*/ level_cmd_set_terrain_data,
1119
/*2F*/ level_cmd_set_rooms,
1120
/*30*/ level_cmd_show_dialog,
1121
/*31*/ level_cmd_set_terrain_type,
1122
/*32*/ level_cmd_nop,
1123
/*33*/ level_cmd_set_transition,
1124
/*34*/ level_cmd_set_blackout,
1125
/*35*/ level_cmd_set_gamma,
1126
/*36*/ level_cmd_set_music,
1127
/*37*/ level_cmd_set_menu_music,
1128
/*38*/ level_cmd_38,
1129
/*39*/ level_cmd_set_macro_objects,
1130
/*3A*/ level_cmd_3A,
1131
/*3B*/ level_cmd_create_whirlpool,
1132
/*3C*/ level_cmd_get_or_set_var,
1133
};
1134
1135
struct LevelCommand *level_script_execute(struct LevelCommand *cmd) {
1136
sScriptStatus = SCRIPT_RUNNING;
1137
sCurrentCmd = cmd;
1138
1139
while (sScriptStatus == SCRIPT_RUNNING) {
1140
LevelScriptJumpTable[sCurrentCmd->type]();
1141
}
1142
1143
profiler_log_thread5_time(LEVEL_SCRIPT_EXECUTE);
1144
init_rcp();
1145
render_game();
1146
end_master_display_list();
1147
alloc_display_list(0);
1148
1149
return sCurrentCmd;
1150
}
1151