How to add new built-in functions for Sunscript
It is possible to expand the scripting capabilities of Super Mario Sunshine with custom code so that you can use them when writing sunscript code. This page lays out the general idea.
Most SPC (scripting system of Sunshine) functions are registered in initUserBuiltin (Address: 0x80289200 in NTSC-U) using bindSystemDataToSymbol. The arguments of bindSystemDataToSymbol are a pointer to TSCPBinary, the name of the function as a string and the pointer to the function. Example: bindSystemDataToSymbol(tscpbinaryPtr, "MyCustomFunction", &MyCustomFunction).
Once registered, the function can be used in SunScript by specifying it as a builtin, e.g. builtin MyCustomFunction(argument1, argument2);
In C or C++, your new function should have the following signature: void MyCustomFunction(TParm* tparm, uint32 argCount);
argCount is the amount of arguments your function was called with in the script, you can use that to confirm the function wasn't called with more arguments than what you designed it for.
TParm is the script engine context. Any arguments for the functions come from TParm, and any values returned are put back into TParm. The scripting of Sunshine is stack-based.
The following structs describes the most important parts of TParm:
struct TParm { uint32 _x0; uint32 _x4; uint32 _x8; uint32 _xC; uint32 _x10; uint32 _x14; uint32 stackMax; // 0x18 uint32 stackCount; // 0x1C struct StackItem* stackStart; // 0x20 };
struct StackItem { uint32 type; uint32 value; };
For the stack item, a type of 0 means the value is an integer, a type of 1 means float. It is recommended to write functions for handling stack operations. Example:
struct StackItem popItem(struct TParm* tparm) { tparm->stackCount -= 1; return tparm->stackStart[tparm->stackCount]; }
struct StackItem pushItem(struct TParm* tparm, uint32 value, uint32 type) { struct StackItem item; item.type = type; item.value = value; tparm->stackStart[tparm->stackCount] = item; tparm->stackCount += 1; }
The final custom function can look like this now:
void MyCustomFunction(struct TParm* tparm, uint32 argCount) { if (tparm->stackCount > 1) { OSReport("StackCount is %i", tparm->stackCount); struct StackItem arg2 = popItem(tparm); struct StackItem arg1 = popItem(tparm); if (tparm->stackCount < tparm->stackMax) { pushItem(tparm, arg1.value+arg2.value, SPC_INT); // Return a value else { OSReport("Failed to put value on stack, stack is full"); } } }