sm64coopdx/src/pc/lua/smlua_cobject.c
2022-03-29 20:45:56 -07:00

590 lines
18 KiB
C

#include "smlua.h"
#include "game/level_update.h"
#include "game/area.h"
#include "game/mario.h"
#include "audio/external.h"
#include "object_fields.h"
#include "pc/djui/djui_hud_utils.h"
#include "pc/lua/smlua.h"
#include "pc/lua/utils/smlua_anim_utils.h"
#include "pc/lua/utils/smlua_collision_utils.h"
#include "pc/lua/utils/smlua_obj_utils.h"
#include "pc/mods/mods.h"
#define LUA_VEC3S_FIELD_COUNT 3
static struct LuaObjectField sVec3sFields[LUA_VEC3S_FIELD_COUNT] = {
{ "x", LVT_S16, sizeof(s16) * 0, false, LOT_NONE },
{ "y", LVT_S16, sizeof(s16) * 1, false, LOT_NONE },
{ "z", LVT_S16, sizeof(s16) * 2, false, LOT_NONE },
};
#define LUA_VEC3F_FIELD_COUNT 3
static struct LuaObjectField sVec3fFields[LUA_VEC3F_FIELD_COUNT] = {
{ "x", LVT_F32, sizeof(f32) * 0, false, LOT_NONE },
{ "y", LVT_F32, sizeof(f32) * 1, false, LOT_NONE },
{ "z", LVT_F32, sizeof(f32) * 2, false, LOT_NONE },
};
struct LuaObjectTable sLuaObjectTable[LOT_MAX] = {
{ LOT_NONE, NULL, 0 },
{ LOT_VEC3S, sVec3sFields, LUA_VEC3S_FIELD_COUNT },
{ LOT_VEC3F, sVec3fFields, LUA_VEC3F_FIELD_COUNT },
};
struct LuaObjectField* smlua_get_object_field(u16 lot, const char* key) {
if (lot > LOT_AUTOGEN_MIN) {
return smlua_get_object_field_autogen(lot, key);
}
struct LuaObjectTable* ot = &sLuaObjectTable[lot];
// binary search
s32 min = 0;
s32 max = ot->fieldCount - 1;
s32 i = (min + max) / 2;
while (true) {
s32 rc = strcmp(key, ot->fields[i].key);
if (rc == 0) {
return &ot->fields[i];
} else if (rc < 0) {
max = i - 1;
i = (min + max) / 2;
} else if (rc > 0) {
min = i + 1;
i = (min + max) / 2;
}
if (min > max || max < min) {
return NULL;
}
}
return NULL;
}
bool smlua_valid_lot(u16 lot) {
if (lot > LOT_NONE && lot < LOT_MAX) { return true; }
if (lot > LOT_AUTOGEN_MIN && lot < LOT_AUTOGEN_MAX) { return true; }
return false;
}
bool smlua_valid_lvt(u16 lvt) {
return (lvt < LVT_MAX);
}
//////////////////
// obj behavior //
//////////////////
#define CUSTOM_FIELD_MAX 11
#define CUSTOM_FIELD_ITEM_LEN 48
struct CustomFieldItem {
char key[CUSTOM_FIELD_ITEM_LEN];
enum LuaValueType lvt;
struct CustomFieldItem* next;
};
static void smlua_add_custom_field_linked(struct CustomFieldItem** head, struct CustomFieldItem* item, const char* key, enum LuaValueType lvt) {
snprintf(item->key, CUSTOM_FIELD_ITEM_LEN, "%s", key);
item->lvt = lvt;
item->next = NULL;
if (*head == NULL) {
item->next = *head;
*head = item;
return;
}
struct CustomFieldItem* prev = NULL;
struct CustomFieldItem* node = *head;
while (node != NULL) {
if (strcmp(node->key, item->key) > 0) {
if (prev == NULL) {
item->next = *head;
*head = item;
return;
} else {
item->next = prev->next;
prev->next = item;
return;
}
}
prev = node;
node = node->next;
}
if (prev != NULL) {
item->next = prev->next;
prev->next = item;
}
}
static int smlua_func_define_custom_obj_fields(lua_State* L) {
LUA_STACK_CHECK_BEGIN();
if (!smlua_functions_valid_param_count(L, 1)) { return 0; }
if (lua_type(L, 1) != LUA_TTABLE) {
LOG_LUA("Invalid parameter for define_custom_obj_fields()");
return 0;
}
if (gLuaLoadingMod == NULL) {
LOG_LUA("define_custom_obj_fields() can only be called on load.");
return 0;
}
struct CustomFieldItem* customFieldsHead = NULL;
struct CustomFieldItem customFields[CUSTOM_FIELD_MAX] = { 0 };
u16 customFieldCount = 0;
// get _custom_object_fields
lua_getglobal(L, "_G"); // get global table
lua_getfield(L, LUA_REGISTRYINDEX, gLuaLoadingMod->relativePath); // push file's "global" table
int fileGlobalIndex = lua_gettop(L);
lua_getfield(L, fileGlobalIndex, "_custom_object_fields");
lua_remove(L, -2); // remove file's "global" table
lua_remove(L, -2); // remove global table
int customObjectFieldsIndex = lua_gettop(L);
// table is in the stack at index 't'
lua_pushnil(L); // first key
s32 iterationTop = lua_gettop(L);
while (lua_next(L, 1) != 0) {
int keyIndex = lua_gettop(L) - 1;
int valueIndex = lua_gettop(L) - 0;
// uses 'key' (at index -2) and 'value' (at index -1)
if (lua_type(L, keyIndex) != LUA_TSTRING) {
LOG_LUA("Invalid key type for define_custom_obj_fields() : %u", lua_type(L, keyIndex));
lua_settop(L, iterationTop);
continue;
}
if (lua_type(L, valueIndex) != LUA_TSTRING) {
LOG_LUA("Invalid value type for define_custom_obj_fields() : %u", lua_type(L, valueIndex));
lua_settop(L, iterationTop);
continue;
}
const char* key = smlua_to_string(L, keyIndex);
if (key[0] != 'o') {
LOG_LUA("Invalid key name for define_custom_obj_fields()");
lua_settop(L, iterationTop);
continue;
}
if (strlen(key) >= CUSTOM_FIELD_ITEM_LEN) {
LOG_LUA("Too long of key name for define_custom_obj_fields()");
lua_settop(L, iterationTop);
continue;
}
const char* value = smlua_to_string(L, valueIndex);
enum LuaValueType lvt = LVT_U32;
if (!strcmp(value, "u32")) { lvt = LVT_U32; }
else if (!strcmp(value, "s32")) { lvt = LVT_S32; }
else if (!strcmp(value, "f32")) { lvt = LVT_F32; }
else {
LOG_LUA("Invalid value name for define_custom_obj_fields()");
return 0;
}
if (customFieldCount >= CUSTOM_FIELD_MAX) {
LOG_LUA("Ran out of custom fields!");
return 0;
}
smlua_add_custom_field_linked(&customFieldsHead, &customFields[customFieldCount], key, lvt);
customFieldCount++;
lua_settop(L, iterationTop);
}
lua_settop(L, iterationTop);
struct CustomFieldItem* node = customFieldsHead;
u32 fieldIndex = 0x1B;
while (node != NULL) {
// keep fieldIndex in range
if (fieldIndex < 0x1B) {
fieldIndex = 0x1B;
} else if (fieldIndex > 0x22 && fieldIndex < 0x48) {
fieldIndex = 0x48;
} else if (fieldIndex > 0x4A) {
LOG_LUA("Ran out of custom fields!");
return 0;
}
lua_pushvalue(L, customObjectFieldsIndex);
lua_pushstring(L, node->key);
lua_newtable(L);
{
// set fieldIndex
lua_pushstring(L, "_fieldIndex");
lua_pushinteger(L, fieldIndex);
lua_rawset(L, -3);
// set lvt
lua_pushstring(L, "_lvt");
lua_pushinteger(L, node->lvt);
lua_rawset(L, -3);
}
lua_settable(L, -3); // set _custom_object_fields
fieldIndex++;
node = node->next;
lua_settop(L, iterationTop);
}
lua_pop(L, 1); // pop key
lua_pop(L, 1); // pop _custom_object_fields
LUA_STACK_CHECK_END();
return 1;
}
struct LuaObjectField* smlua_get_custom_field(lua_State* L, u32 lot, int keyIndex) {
LUA_STACK_CHECK_BEGIN();
static struct LuaObjectField lof = { 0 };
if (lot != LOT_OBJECT) { return NULL; }
if (gLuaActiveMod == NULL) {
LOG_LUA("Failed to retrieve active mod entry.");
return NULL;
}
// get _custom_object_fields
lua_getglobal(L, "_G"); // get global table
lua_getfield(L, LUA_REGISTRYINDEX, gLuaActiveMod->relativePath); // push file's "global" table
int fileGlobalIndex = lua_gettop(L);
lua_getfield(L, fileGlobalIndex, "_custom_object_fields");
lua_remove(L, -2); // remove file's "global" table
lua_remove(L, -2); // remove global table
// get value table from key
lua_pushvalue(L, keyIndex);
lua_rawget(L, -2);
if (lua_type(L, -1) != LUA_TTABLE) {
lua_pop(L, 1); // pop value table
lua_pop(L, 1); // pop _custom_fields
LUA_STACK_CHECK_END();
return NULL;
}
// get _fieldIndex
lua_pushstring(L, "_fieldIndex");
lua_rawget(L, -2);
u32 fieldIndex = smlua_to_integer(L, -1);
lua_pop(L, 1);
bool validFieldIndex = (fieldIndex >= 0x1B && fieldIndex <= 0x22) || (fieldIndex >= 0x48 && fieldIndex <= 0x4A);
if (!gSmLuaConvertSuccess || !validFieldIndex) {
lua_pop(L, 1); // pop value table
lua_pop(L, 1); // pop _custom_fields
LUA_STACK_CHECK_END();
return NULL;
}
// get _lvt
lua_pushstring(L, "_lvt");
lua_rawget(L, -2);
u32 lvt = smlua_to_integer(L, -1);
lua_pop(L, 1);
bool validLvt = (lvt == LVT_U32 || lvt == LVT_S32 || lvt == LVT_F32);
if (!gSmLuaConvertSuccess || !validLvt) {
lua_pop(L, 1); // pop value table
lua_pop(L, 1); // pop _custom_fields
LUA_STACK_CHECK_END();
return NULL;
}
lof.immutable = false;
//lof.key = key;
lof.lot = LOT_NONE;
lof.valueOffset = offsetof(struct Object, rawData.asU32[fieldIndex]);
lof.valueType = lvt;
lua_pop(L, 1); // pop value table
lua_pop(L, 1); // pop _custom_fields
LUA_STACK_CHECK_END();
return &lof;
}
/////////////////////
// CObject get/set //
/////////////////////
static int smlua__get_field(lua_State* L) {
LUA_STACK_CHECK_BEGIN();
if (!smlua_functions_valid_param_count(L, 4)) { return 0; }
enum LuaObjectType lot = smlua_to_integer(L, 1);
if (!gSmLuaConvertSuccess) { return 0; }
u64 pointer = smlua_to_integer(L, 2);
if (!gSmLuaConvertSuccess) { return 0; }
const char* key = smlua_to_string(L, 3);
if (!gSmLuaConvertSuccess) { return 0; }
if (pointer == 0) {
LOG_LUA("_get_field on null pointer");
smlua_logline();
return 0;
}
if (!smlua_valid_lot(lot)) {
LOG_LUA("_get_field on invalid LOT '%u'", lot);
smlua_logline();
return 0;
}
if (!smlua_cobject_allowlist_contains(lot, pointer)) {
LOG_LUA("_get_field received a pointer not in allow list. '%u', '%llu", lot, (u64)pointer);
smlua_logline();
return 0;
}
struct LuaObjectField* data = smlua_get_object_field(lot, key);
if (data == NULL) {
data = smlua_get_custom_field(L, lot, 3);
}
if (data == NULL) {
LOG_LUA("_get_field on invalid key '%s', lot '%d'", key, lot);
smlua_logline();
return 0;
}
LUA_STACK_CHECK_END();
u8* p = ((u8*)(intptr_t)pointer) + data->valueOffset;
switch (data->valueType) {
case LVT_BOOL: lua_pushboolean(L, *(u8* )p); break;
case LVT_U8: lua_pushinteger(L, *(u8* )p); break;
case LVT_U16: lua_pushinteger(L, *(u16*)p); break;
case LVT_U32: lua_pushinteger(L, *(u32*)p); break;
case LVT_S8: lua_pushinteger(L, *(s8* )p); break;
case LVT_S16: lua_pushinteger(L, *(s16*)p); break;
case LVT_S32: lua_pushinteger(L, *(s32*)p); break;
case LVT_F32: lua_pushnumber( L, *(f32*)p); break;
case LVT_COBJECT: smlua_push_object(L, data->lot, p); break;
case LVT_COBJECT_P: smlua_push_object(L, data->lot, *(u8**)p); break;
case LVT_STRING: lua_pushstring(L, (char*)p); break;
case LVT_STRING_P: lua_pushstring(L, *(char**)p); break;
case LVT_BEHAVIORSCRIPT: lua_pushinteger(L, *(s32*)p); break;
case LVT_OBJECTANIMPOINTER: lua_pushinteger(L, *(s32*)p); break;
case LVT_COLLISION: lua_pushinteger(L, *(s32*)p); break;
// pointers
case LVT_U8_P:
case LVT_U16_P:
case LVT_U32_P:
case LVT_S8_P:
case LVT_S16_P:
case LVT_S32_P:
case LVT_F32_P:
case LVT_BEHAVIORSCRIPT_P:
case LVT_OBJECTANIMPOINTER_P:
case LVT_COLLISION_P:
smlua_push_pointer(L, data->valueType, *(u8**)p);
break;
default:
LOG_LUA("_get_field on unimplemented type '%d', key '%s'", data->valueType, key);
smlua_logline();
return 0;
}
return 1;
}
static int smlua__set_field(lua_State* L) {
LUA_STACK_CHECK_BEGIN();
if (!smlua_functions_valid_param_count(L, 5)) { return 0; }
enum LuaObjectType lot = smlua_to_integer(L, 1);
if (!gSmLuaConvertSuccess) { return 0; }
u64 pointer = smlua_to_integer(L, 2);
if (!gSmLuaConvertSuccess) { return 0; }
const char* key = smlua_to_string(L, 3);
if (!gSmLuaConvertSuccess) { return 0; }
if (pointer == 0) {
LOG_LUA("_set_field on null pointer");
smlua_logline();
return 0;
}
if (!smlua_valid_lot(lot)) {
LOG_LUA("_set_field on invalid LOT '%u'", lot);
smlua_logline();
return 0;
}
if (!smlua_cobject_allowlist_contains(lot, pointer)) {
LOG_LUA("_set_field received a pointer not in allow list. '%u', '%llu", lot, (u64)pointer);
smlua_logline();
return 0;
}
struct LuaObjectField* data = smlua_get_object_field(lot, key);
if (data == NULL) {
data = smlua_get_custom_field(L, lot, 3);
}
if (data == NULL) {
LOG_LUA("_set_field on invalid key '%s'", key);
smlua_logline();
return 0;
}
if (data->immutable) {
LOG_LUA("_set_field on immutable key '%s'", key);
smlua_logline();
return 0;
}
void* valuePointer = NULL;
u8* p = ((u8*)(intptr_t)pointer) + data->valueOffset;
switch (data->valueType) {
case LVT_BOOL:*(u8*) p = smlua_to_boolean(L, 4); break;
case LVT_U8: *(u8*) p = smlua_to_integer(L, 4); break;
case LVT_U16: *(u16*)p = smlua_to_integer(L, 4); break;
case LVT_U32: *(u32*)p = smlua_to_integer(L, 4); break;
case LVT_S8: *(s8*) p = smlua_to_integer(L, 4); break;
case LVT_S16: *(s16*)p = smlua_to_integer(L, 4); break;
case LVT_S32: *(s32*)p = smlua_to_integer(L, 4); break;
case LVT_F32: *(f32*)p = smlua_to_number(L, 4); break;
case LVT_COBJECT_P:
valuePointer = smlua_to_cobject(L, 4, data->lot);
if (gSmLuaConvertSuccess) {
*(u8**)p = valuePointer;
}
break;
// pointers
case LVT_U8_P:
case LVT_U16_P:
case LVT_U32_P:
case LVT_S8_P:
case LVT_S16_P:
case LVT_S32_P:
case LVT_F32_P:
case LVT_BEHAVIORSCRIPT_P:
case LVT_OBJECTANIMPOINTER_P:
case LVT_COLLISION_P:
valuePointer = smlua_to_cpointer(L, 4, data->valueType);
if (gSmLuaConvertSuccess) {
*(u8**)p = valuePointer;
}
break;
default:
LOG_LUA("_set_field on unimplemented type '%d', key '%s'", data->valueType, key);
smlua_logline();
return 0;
}
if (!gSmLuaConvertSuccess) {
LOG_LUA("_set_field failed to retrieve value type '%d', key '%s'", data->valueType, key);
smlua_logline();
return 0;
}
LUA_STACK_CHECK_END();
return 1;
}
//////////
// bind //
//////////
void smlua_cobject_init_globals(void) {
lua_State* L = gLuaState;
{
lua_newtable(L);
int t = lua_gettop(gLuaState);
for (s32 i = 0; i < MAX_PLAYERS; i++) {
lua_pushinteger(L, i);
smlua_push_object(L, LOT_MARIOSTATE, &gMarioStates[i]);
lua_settable(L, t);
}
lua_setglobal(L, "gMarioStates");
}
{
lua_newtable(L);
int t = lua_gettop(gLuaState);
for (s32 i = 0; i < MAX_PLAYERS; i++) {
lua_pushinteger(L, i);
smlua_push_object(L, LOT_NETWORKPLAYER, &gNetworkPlayers[i]);
lua_settable(L, t);
}
lua_setglobal(L, "gNetworkPlayers");
}
{
lua_newtable(L);
int t = lua_gettop(gLuaState);
for (s32 i = 0; i < CT_MAX; i++) {
lua_pushinteger(L, i);
smlua_push_object(L, LOT_CHARACTER, &gCharacters[i]);
lua_settable(L, t);
}
lua_setglobal(L, "gCharacters");
}
{
smlua_push_object(L, LOT_GLOBALTEXTURES, &gGlobalTextures);
lua_setglobal(L, "gTextures");
}
{
smlua_push_object(L, LOT_GLOBALOBJECTANIMATIONS, &gGlobalObjectAnimations);
lua_setglobal(L, "gObjectAnimations");
}
{
smlua_push_object(L, LOT_GLOBALOBJECTCOLLISIONDATA, &gGlobalObjectCollisionData);
lua_setglobal(L, "gGlobalObjectCollisionData");
}
{
smlua_push_object(L, LOT_LAKITUSTATE, &gLakituState);
lua_setglobal(L, "gLakituState");
}
{
smlua_push_object(L, LOT_SERVERSETTINGS, &gServerSettings);
lua_setglobal(L, "gServerSettings");
}
}
void smlua_cobject_init_per_file_globals(char* path) {
lua_State* L = gLuaState;
lua_getfield(L, LUA_REGISTRYINDEX, path); // push per-file globals
{
lua_pushstring(L, "_custom_object_fields");
lua_newtable(L);
lua_settable(L, -3);
}
lua_pop(L, 1); // pop per-file globals
}
void smlua_bind_cobject(void) {
lua_State* L = gLuaState;
smlua_bind_function(L, "define_custom_obj_fields", smlua_func_define_custom_obj_fields);
smlua_bind_function(L, "_get_field", smlua__get_field);
smlua_bind_function(L, "_set_field", smlua__set_field);
}