#include "smlua.h" #include "pc/mods/mods.h" #include "audio/external.h" u8 gSmLuaConvertSuccess = false; #define VEC3F_BUFFER_COUNT 64 static Vec3f sVec3fBuffer[VEC3F_BUFFER_COUNT] = { 0 }; static u8 sVec3fBufferIndex = 0; #define VEC3S_BUFFER_COUNT 64 static Vec3s sVec3sBuffer[VEC3S_BUFFER_COUNT] = { 0 }; static u8 sVec3sBufferIndex = 0; #define VEC4F_BUFFER_COUNT 64 static Vec4f sVec4fBuffer[VEC4F_BUFFER_COUNT] = { 0 }; static u8 sVec4fBufferIndex = 0; #define VEC4S_BUFFER_COUNT 64 static Vec4s sVec4sBuffer[VEC4S_BUFFER_COUNT] = { 0 }; static u8 sVec4sBufferIndex = 0; #define COLOR_BUFFER_COUNT 64 static Color sColorBuffer[COLOR_BUFFER_COUNT] = { 0 }; static u8 sColorBufferIndex = 0; f32* smlua_get_vec3f_from_buffer(void) { if (sVec3fBufferIndex >= VEC3F_BUFFER_COUNT) { sVec3fBufferIndex = 0; } return sVec3fBuffer[sVec3fBufferIndex++]; } s16* smlua_get_vec3s_from_buffer(void) { if (sVec3sBufferIndex >= VEC3S_BUFFER_COUNT) { sVec3sBufferIndex = 0; } return sVec3sBuffer[sVec3sBufferIndex++]; } f32* smlua_get_vec4f_from_buffer(void) { if (sVec4fBufferIndex >= VEC4F_BUFFER_COUNT) { sVec4fBufferIndex = 0; } return sVec4fBuffer[sVec4fBufferIndex++]; } s16* smlua_get_vec4s_from_buffer(void) { if (sVec4sBufferIndex >= VEC4S_BUFFER_COUNT) { sVec4sBufferIndex = 0; } return sVec4sBuffer[sVec4sBufferIndex++]; } u8* smlua_get_color_from_buffer(void) { if (sColorBufferIndex >= COLOR_BUFFER_COUNT) { sColorBufferIndex = 0; } return sColorBuffer[sColorBufferIndex++]; } f32 *smlua_get_vec3f_for_play_sound(f32 *pos) { if (pos < (f32 *) sVec3fBuffer || pos >= (f32 *) (sVec3fBuffer + VEC3F_BUFFER_COUNT)) { return pos; } if (memcmp(pos, gGlobalSoundSource, sizeof(Vec3f)) == 0) { return gGlobalSoundSource; } for (s32 i = 0; i != MAX_PLAYERS; ++i) { if (gMarioStates[i].marioObj && memcmp(pos, gMarioStates[i].marioObj->header.gfx.cameraToObject, sizeof(Vec3f)) == 0) { return gMarioStates[i].marioObj->header.gfx.cameraToObject; } } return pos; } /////////////////////////////////////////////////////////////////////////////////////////// void smlua_bind_function(lua_State* L, const char* name, void* func) { lua_pushcfunction(L, func); lua_setglobal(L, name); } bool smlua_is_table_empty(int index) { lua_pushnil(gLuaState); // key if (lua_next(gLuaState, index)) { lua_pop(gLuaState, 2); return false; } return true; } /////////////////////////////////////////////////////////////////////////////////////////// bool smlua_to_boolean(lua_State* L, int index) { if (lua_type(L, index) != LUA_TBOOLEAN) { LOG_LUA_LINE("smlua_to_boolean received improper type '%s'", luaL_typename(L, index)); gSmLuaConvertSuccess = false; return 0; } return lua_toboolean(L, index) ? true : false; } lua_Integer smlua_to_integer(lua_State* L, int index) { if (lua_type(L, index) == LUA_TBOOLEAN) { gSmLuaConvertSuccess = true; return lua_toboolean(L, index) ? 1 : 0; } else if (lua_type(L, index) != LUA_TNUMBER) { LOG_LUA_LINE("smlua_to_integer received improper type '%s'", luaL_typename(L, index)); gSmLuaConvertSuccess = false; return 0; } gSmLuaConvertSuccess = true; lua_Integer val = lua_tointeger(L, index); return (val == 0) ? lua_tonumber(L, index) : val; } lua_Number smlua_to_number(lua_State* L, int index) { if (lua_type(L, index) == LUA_TBOOLEAN) { gSmLuaConvertSuccess = true; return lua_toboolean(L, index) ? 1 : 0; } else if (lua_type(L, index) != LUA_TNUMBER) { LOG_LUA_LINE("smlua_to_number received improper type '%s'", luaL_typename(L, index)); gSmLuaConvertSuccess = false; return 0; } gSmLuaConvertSuccess = true; return lua_tonumber(L, index); } const char* smlua_to_string(lua_State* L, int index) { if (lua_type(L, index) != LUA_TSTRING) { LOG_LUA_LINE("smlua_to_string received improper type '%s'", luaL_typename(L, index)); gSmLuaConvertSuccess = false; return 0; } gSmLuaConvertSuccess = true; return lua_tostring(L, index); } LuaFunction smlua_to_lua_function(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNIL) { return 0; } if (lua_type(L, index) != LUA_TFUNCTION) { LOG_LUA_LINE("smlua_to_lua_function received improper type '%s'", luaL_typename(L, index)); gSmLuaConvertSuccess = false; return 0; } gSmLuaConvertSuccess = true; lua_pushvalue(L, index); return luaL_ref(L, LUA_REGISTRYINDEX); } bool smlua_is_cobject(lua_State* L, int index, UNUSED u16 lot) { return lua_isuserdata(L, index); } void* smlua_to_cobject(lua_State* L, int index, u16 lot) { s32 indexType = lua_type(L, index); if (indexType != LUA_TUSERDATA) { LOG_LUA_LINE("smlua_to_cobject received improper type '%s'", lua_typename(L, indexType)); gSmLuaConvertSuccess = false; return NULL; } CObject *cobject = luaL_checkudata(L, index, "CObject"); if (lot != cobject->lot) { LOG_LUA_LINE("smlua_to_cobject received improper LOT. Expected '%s', received '%s'", smlua_get_lot_name(lot), smlua_get_lot_name(cobject->lot)); gSmLuaConvertSuccess = false; return NULL; } if (cobject->pointer == NULL) { LOG_LUA_LINE("smlua_to_cobject received null pointer."); gSmLuaConvertSuccess = false; return NULL; } gSmLuaConvertSuccess = true; return cobject->pointer; } void* smlua_to_cpointer(lua_State* L, int index, u16 lvt) { s32 indexType = lua_type(L, index); if (indexType != LUA_TUSERDATA) { LOG_LUA_LINE("smlua_to_cpointer received improper type '%s'", lua_typename(L, indexType)); gSmLuaConvertSuccess = false; return NULL; } CPointer *cpointer = luaL_checkudata(L, index, "CPointer"); if (lvt != cpointer->lvt) { LOG_LUA_LINE("smlua_to_cpointer received improper LOT. Expected '%s', received '%s'", smlua_get_lvt_name(lvt), smlua_get_lvt_name(cpointer->lvt)); gSmLuaConvertSuccess = false; return NULL; } if (cpointer->pointer == NULL) { LOG_LUA_LINE("smlua_to_cpointer received null pointer."); gSmLuaConvertSuccess = false; return NULL; } gSmLuaConvertSuccess = true; return cpointer->pointer; } struct LSTNetworkType smlua_to_lnt(lua_State* L, int index) { struct LSTNetworkType lnt = { 0 }; int valueType = lua_type(L, index); if (valueType == LUA_TNUMBER) { lnt.type = LST_NETWORK_TYPE_INTEGER; lnt.value.integer = lua_tointeger(L, index); lnt.size = sizeof(u8) + sizeof(long long); if (lnt.value.integer == 0) { lnt.type = LST_NETWORK_TYPE_NUMBER; lnt.value.number = lua_tonumber(L, index); lnt.size = sizeof(u8) + sizeof(double); if (lnt.value.number == 0) { lnt.type = LST_NETWORK_TYPE_INTEGER; lnt.size = sizeof(u8) + sizeof(long long); } } gSmLuaConvertSuccess = true; return lnt; } if (valueType == LUA_TBOOLEAN) { lnt.type = LST_NETWORK_TYPE_BOOLEAN; lnt.value.boolean = lua_toboolean(L, index); lnt.size = sizeof(u8) + sizeof(u8); gSmLuaConvertSuccess = true; return lnt; } if (valueType == LUA_TSTRING) { lnt.type = LST_NETWORK_TYPE_STRING; lnt.value.string = (char*)lua_tostring(L, index); if (lnt.value.string == NULL || strlen(lnt.value.string) > 256) { LOG_LUA_LINE("smlua_to_lnt on invalid string value: '%s'", (lnt.value.string == NULL) ? "" : lnt.value.string); gSmLuaConvertSuccess = false; return lnt; } lnt.size = sizeof(u8) + sizeof(u16) + sizeof(u8) * strlen(lnt.value.string); gSmLuaConvertSuccess = true; return lnt; } if (valueType == LUA_TNIL) { lnt.type = LST_NETWORK_TYPE_NIL; lnt.size = sizeof(u8); gSmLuaConvertSuccess = true; return lnt; } LOG_LUA_LINE("smlua_to_lnt on invalid type: '%d'", valueType); gSmLuaConvertSuccess = false; return lnt; } /////////////////////////////////////////////////////////////////////////////////////////// bool packet_write_lnt(struct Packet* p, struct LSTNetworkType* lnt) { u8 lntType = lnt->type; packet_write(p, &lntType, sizeof(u8)); switch (lnt->type) { case LST_NETWORK_TYPE_NUMBER: { f64 number = lnt->value.number; packet_write(p, &number, sizeof(f64)); return true; } case LST_NETWORK_TYPE_INTEGER: { s64 integer = lnt->value.integer; packet_write(p, &integer, sizeof(s64)); return true; } case LST_NETWORK_TYPE_BOOLEAN: { packet_write(p, &lnt->value.boolean, sizeof(u8)); return true; } case LST_NETWORK_TYPE_STRING: { u16 valueLength = strlen(lnt->value.string); if (valueLength < 1 || valueLength > 256) { LOG_ERROR("attempted to send lua variable with invalid string length: %u", valueLength); return false; } packet_write(p, &valueLength, sizeof(u16)); packet_write(p, lnt->value.string, valueLength * sizeof(u8)); return true; } case LST_NETWORK_TYPE_NIL: { // no-op return true; } default: break; } LOG_ERROR("attempted to send lua variable with invalid lnt type: %d", lnt->type); return false; } bool packet_read_lnt(struct Packet* p, struct LSTNetworkType* lnt) { packet_read(p, &lnt->type, sizeof(u8)); switch (lnt->type) { case LST_NETWORK_TYPE_NUMBER: packet_read(p, &lnt->value.number, sizeof(f64)); return true; case LST_NETWORK_TYPE_INTEGER: packet_read(p, &lnt->value.integer, sizeof(s64)); return true; case LST_NETWORK_TYPE_BOOLEAN: packet_read(p, &lnt->value.boolean, sizeof(u8)); return true; case LST_NETWORK_TYPE_STRING: { u16 valueLength = 0; packet_read(p, &valueLength, sizeof(u16)); if (valueLength < 1 || valueLength > 256) { LOG_ERROR("received lua variable with invalid value length: %d", valueLength); return false; } lnt->value.string = calloc(valueLength + 1, sizeof(char)); packet_read(p, lnt->value.string, valueLength * sizeof(u8)); return true; } case LST_NETWORK_TYPE_NIL: // no-op return true; default: break; } LOG_ERROR("received lua variable with invalid type: %d", lnt->type); return false; } /////////////////////////////////////////////////////////////////////////////////////////// CObject *smlua_push_object(lua_State* L, u16 lot, void* p, void *extraInfo) { if (p == NULL) { lua_pushnil(L); return; } LUA_STACK_CHECK_BEGIN_NUM(1); uintptr_t key = (lot * 0x9E3779B97F4A7C15) ^ ((uintptr_t)p >> 3); lua_rawgeti(L, LUA_REGISTRYINDEX, gSmLuaCObjects); lua_pushinteger(L, key); lua_gettable(L, -2); if (lua_isuserdata(L, -1)) { const CObject *cobj = lua_touserdata(L, -1); if (cobj && cobj->lot == lot && cobj->pointer == p) { lua_remove(L, -2); // Remove gSmLuaCObjects table return; } } lua_pop(L, 1); CObject *cobject = lua_newuserdata(L, sizeof(CObject)); cobject->pointer = p; cobject->lot = lot; cobject->freed = false; cobject->info = extraInfo; lua_rawgeti(L, LUA_REGISTRYINDEX, gSmLuaCObjectMetatable); lua_setmetatable(L, -2); lua_pushinteger(L, key); lua_pushvalue(L, -2); // Duplicate userdata lua_settable(L, -4); lua_remove(L, -2); // Remove gSmLuaCObjects table LUA_STACK_CHECK_END(); return cobject; } CPointer *smlua_push_pointer(lua_State* L, u16 lvt, void* p, void *extraInfo) { if (p == NULL) { lua_pushnil(L); return; } LUA_STACK_CHECK_BEGIN_NUM(1); uintptr_t key = (lvt * 0x9E3779B97F4A7C15) ^ ((uintptr_t)p >> 3); lua_rawgeti(L, LUA_REGISTRYINDEX, gSmLuaCPointers); lua_pushinteger(L, key); lua_gettable(L, -2); if (lua_isuserdata(L, -1)) { const CPointer *cptr = lua_touserdata(L, 1); if (cptr && cptr->lvt == lvt && cptr->pointer == p) { lua_remove(L, -2); // Remove gSmLuaCPointers table return; } } lua_pop(L, 1); CPointer *cpointer = lua_newuserdata(L, sizeof(CPointer)); cpointer->pointer = p; cpointer->lvt = lvt; cpointer->freed = false; cpointer->info = extraInfo; lua_rawgeti(L, LUA_REGISTRYINDEX, gSmLuaCPointerMetatable); lua_setmetatable(L, -2); lua_pushinteger(L, key); lua_pushvalue(L, -2); // Duplicate userdata lua_settable(L, -4); lua_remove(L, -2); // Remove gSmLuaCPointers table LUA_STACK_CHECK_END(); return cpointer; } void smlua_push_integer_field(int index, const char* name, lua_Integer val) { lua_pushinteger(gLuaState, val); lua_setfield(gLuaState, index, name); } void smlua_push_number_field(int index, const char* name, lua_Number val) { lua_pushnumber(gLuaState, val); lua_setfield(gLuaState, index, name); } void smlua_push_string_field(int index, const char* name, const char* val) { lua_pushstring(gLuaState, val); lua_setfield(gLuaState, index, name); } void smlua_push_nil_field(int index, const char* name) { lua_pushnil(gLuaState); lua_setfield(gLuaState, index, name); } void smlua_push_table_field(int index, const char* name) { lua_newtable(gLuaState); lua_setfield(gLuaState, index, name); } /////////////////////////////////////////////////////////////////////////////////////////// void smlua_push_lnt(struct LSTNetworkType* lnt) { lua_State* L = gLuaState; switch (lnt->type) { case LST_NETWORK_TYPE_INTEGER: lua_pushinteger(L, lnt->value.integer); break; case LST_NETWORK_TYPE_NUMBER: lua_pushnumber(L, lnt->value.number); break; case LST_NETWORK_TYPE_BOOLEAN: lua_pushboolean(L, lnt->value.boolean); break; case LST_NETWORK_TYPE_STRING: lua_pushstring(L, lnt->value.string); break; case LST_NETWORK_TYPE_NIL: lua_pushnil(L); break; default: SOFT_ASSERT(false); } } /////////////////////////////////////////////////////////////////////////////////////////// lua_Integer smlua_get_integer_field(int index, const char* name) { if (lua_type(gLuaState, index) != LUA_TTABLE && lua_type(gLuaState, index) != LUA_TUSERDATA) { LOG_LUA_LINE("smlua_get_integer_field received improper type '%s'", luaL_typename(gLuaState, index)); gSmLuaConvertSuccess = false; return 0; } lua_getfield(gLuaState, index, name); lua_Integer val = smlua_to_integer(gLuaState, -1); lua_pop(gLuaState, 1); return val; } lua_Number smlua_get_number_field(int index, const char* name) { if (lua_type(gLuaState, index) != LUA_TTABLE && lua_type(gLuaState, index) != LUA_TUSERDATA) { LOG_LUA_LINE("smlua_get_number_field received improper type '%s'", luaL_typename(gLuaState, index)); gSmLuaConvertSuccess = false; return 0; } lua_getfield(gLuaState, index, name); lua_Number val = smlua_to_number(gLuaState, -1); lua_pop(gLuaState, 1); return val; } const char* smlua_get_string_field(int index, const char* name) { if (lua_type(gLuaState, index) != LUA_TTABLE && lua_type(gLuaState, index) != LUA_TUSERDATA) { LOG_LUA_LINE("smlua_get_string_field received improper type '%s'", luaL_typename(gLuaState, index)); gSmLuaConvertSuccess = false; return 0; } lua_getfield(gLuaState, index, name); const char* val = smlua_to_string(gLuaState, -1); lua_pop(gLuaState, 1); return val; } LuaFunction smlua_get_function_field(int index, const char *name) { if (lua_type(gLuaState, index) != LUA_TTABLE && lua_type(gLuaState, index) != LUA_TUSERDATA) { LOG_LUA_LINE("smlua_get_function_field received improper type '%s'", luaL_typename(gLuaState, index)); gSmLuaConvertSuccess = false; return 0; } lua_getfield(gLuaState, index, name); LuaFunction val = smlua_to_lua_function(gLuaState, -1); lua_pop(gLuaState, 1); return val; } /////////////////////////////////////////////////////////////////////////////////////////// s64 smlua_get_integer_mod_variable(u16 modIndex, const char* variable) { lua_State* L = gLuaState; if (!gActiveMods.entries) { LOG_ERROR("Could not find mod list entries"); return 0; } if (modIndex >= gActiveMods.entryCount) { LOG_ERROR("Could not find mod list entry"); return 0; } // figure out entry struct Mod* mod = gActiveMods.entries[modIndex]; if (mod == NULL) { LOG_ERROR("Could not find mod list entry for modIndex: %u", modIndex); return 0; } u8 prevSuppress = gSmLuaSuppressErrors; int prevTop = lua_gettop(L); lua_getglobal(L, "_G"); // get global table lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); // get the file's "global" table gSmLuaSuppressErrors = true; s64 value = smlua_get_integer_field(-1, (char*)variable); lua_settop(L, prevTop); // return variable gSmLuaSuppressErrors = prevSuppress; return value; } s64 smlua_get_any_integer_mod_variable(const char* variable) { lua_State* L = gLuaState; u8 prevSuppress = gSmLuaSuppressErrors; s64 value = 0; for (s32 i = 0; i < gActiveMods.entryCount; i++) { // figure out entry struct Mod* mod = gActiveMods.entries[i]; int prevTop = lua_gettop(L); lua_getglobal(L, "_G"); // get global table lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); // get the file's "global" table gSmLuaSuppressErrors = true; value = smlua_get_integer_field(-1, (char*)variable); lua_settop(L, prevTop); if (gSmLuaConvertSuccess) { gSmLuaSuppressErrors = prevSuppress; return value; } } // return variable gSmLuaSuppressErrors = prevSuppress; return value; } LuaFunction smlua_get_function_mod_variable(u16 modIndex, const char *variable) { lua_State *L = gLuaState; if (modIndex >= gActiveMods.entryCount) { LOG_ERROR("Could not find mod list entry for modIndex: %u", modIndex); return 0; } // figure out entry struct Mod *mod = gActiveMods.entries[modIndex]; if (mod == NULL) { LOG_ERROR("Could not find mod list entry for modIndex: %u", modIndex); return 0; } u8 prevSuppress = gSmLuaSuppressErrors; int prevTop = lua_gettop(L); lua_getglobal(L, "_G"); // get global table lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); // get the file's "global" table gSmLuaSuppressErrors = true; LuaFunction value = smlua_get_function_field(-1, (char *)variable); lua_settop(L, prevTop); // return variable gSmLuaSuppressErrors = prevSuppress; return value; } LuaFunction smlua_get_any_function_mod_variable(const char *variable) { lua_State *L = gLuaState; u8 prevSuppress = gSmLuaSuppressErrors; LuaFunction value = 0; for (s32 i = 0; i < gActiveMods.entryCount; i++) { // figure out entry struct Mod *mod = gActiveMods.entries[i]; int prevTop = lua_gettop(L); lua_getglobal(L, "_G"); // get global table lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); // get the file's "global" table gSmLuaSuppressErrors = true; value = smlua_get_function_field(-1, (char *)variable); lua_settop(L, prevTop); if (gSmLuaConvertSuccess) { gSmLuaSuppressErrors = prevSuppress; return value; } } // return variable gSmLuaSuppressErrors = prevSuppress; return value; } /////////////////////////////////////////////////////////////////////////////////////////// const char* smlua_lnt_to_str(struct LSTNetworkType* lnt) { static char sLntStr[32] = ""; switch (lnt->type) { case LST_NETWORK_TYPE_INTEGER: snprintf(sLntStr, 32, "%lld", lnt->value.integer); break; case LST_NETWORK_TYPE_NUMBER: snprintf(sLntStr, 32, "%f", lnt->value.number); break; case LST_NETWORK_TYPE_BOOLEAN: snprintf(sLntStr, 32, "%u", lnt->value.boolean); break; case LST_NETWORK_TYPE_STRING: snprintf(sLntStr, 32, "%s", lnt->value.string); break; case LST_NETWORK_TYPE_NIL: snprintf(sLntStr, 32, ""); break; default: SOFT_ASSERT_RETURN(false, "UNKNOWN"); } return sLntStr; } void smlua_dump_stack(void) { lua_State* L = gLuaState; int top = lua_gettop(L); printf("--------------\n"); for (int i = 1; i <= top; i++) { printf("%d\t%s\t", i, luaL_typename(L, i)); switch (lua_type(L, i)) { case LUA_TNUMBER: printf("%g\n", lua_tonumber(L, i)); break; case LUA_TSTRING: printf("%s\n", lua_tostring(L, i)); break; case LUA_TBOOLEAN: printf("%s\n", (lua_toboolean(L, i) ? "true" : "false")); break; case LUA_TNIL: printf("%s\n", "nil"); break; default: printf("%p\n", lua_topointer(L, i)); break; } } printf("--------------\n"); } void smlua_dump_globals(void) { lua_State* L = gLuaState; printf("--------------\n"); lua_pushglobaltable(L); // table is in the stack at index 't' lua_pushnil(L); // first key while (lua_next(L, -2) != 0) { // uses 'key' (at index -2) and 'value' (at index -1) if (lua_type(L, -2) == LUA_TSTRING) { printf("%s - %s\n", lua_tostring(L, -2), lua_typename(L, lua_type(L, -1))); } else { printf("%s - %s\n", lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1))); } // removes 'value'; keeps 'key' for next iteration lua_pop(L, 1); } lua_pop(L, 1); // remove global table(-1) printf("--------------\n"); } void smlua_dump_table(int index) { lua_State* L = gLuaState; printf("--------------\n"); // table is in the stack at index 't' lua_pushnil(L); // first key while (lua_next(L, index) != 0) { // uses 'key' (at index -2) and 'value' (at index -1) if (lua_type(L, -2) == LUA_TSTRING) { printf("%s - %s\n", lua_tostring(L, -2), lua_typename(L, lua_type(L, -1))); } else { printf("%s - %s\n", lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1))); } // removes 'value'; keeps 'key' for next iteration lua_pop(L, 1); } printf("--------------\n"); } void smlua_logline(void) { lua_State* L = gLuaState; lua_Debug info; int level = 0; while (lua_getstack(L, level, &info)) { lua_getinfo(L, "nSl", &info); // Get the folder and file // in the format: "folder/file.lua" const char* src = info.source; int slashCount = 0; const char* folderStart = NULL; for (const char* p = src + strlen(src); p > src; --p) { if (*p == '/' || *p == '\\') { if (++slashCount == 2) { folderStart = p + 1; break; } } } LOG_LUA(" [%d] '%s':%d -- %s [%s]", level, (folderStart ? folderStart : info.short_src), info.currentline, (info.name ? info.name : ""), info.what); ++level; } } // If an object is freed that Lua has a CObject to, // Lua is able to use-after-free that pointer // todo figure out a better way to do this void smlua_free(void *ptr) { if (ptr && gLuaState) { lua_State *L = gLuaState; LUA_STACK_CHECK_BEGIN(); u16 lot = LOT_SURFACE; // Assuming this is a surface uintptr_t key = lot ^ (uintptr_t) ptr; lua_rawgeti(L, LUA_REGISTRYINDEX, gSmLuaCObjects); lua_pushinteger(L, key); lua_gettable(L, -2); CObject *obj = (CObject *) lua_touserdata(L, -1); if (obj && obj->pointer == ptr) { obj->freed = true; lua_pop(L, 1); lua_pushinteger(L, key); lua_pushnil(L); lua_settable(L, -3); } else { lua_pop(L, 1); } lua_pop(L, 1); LUA_STACK_CHECK_END(); } free(ptr); }