mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +00:00
Some checks are pending
Build coop / build-linux (push) Waiting to run
Build coop / build-steamos (push) Waiting to run
Build coop / build-windows-opengl (push) Waiting to run
Build coop / build-windows-directx (push) Waiting to run
Build coop / build-macos-arm (push) Waiting to run
Build coop / build-macos-intel (push) Waiting to run
I didn't add standard Lua require() because I've always been afraid of it. I'm not sure we can guarantee which files it will read (or not read). Instead, here is a custom implementation. It should work more or less the same and allow for more modular code. For backwards compatibility reasons, all of the lua files in the base mod folder will be loaded as in the past. Aka one at a time and alphabetically. However, now coop will look for Lua files in subdirectories and will load them in when another Lua file calls require(). The file search order is more reasonable than normal Lua require(). It will first look for files relative to the currently running script. If there is no matching relative file, it will pick from any Lua file that is in any of the mod's subdirectories. --------- Co-authored-by: MysterD <myster@d>
309 lines
9 KiB
Python
309 lines
9 KiB
Python
import sys
|
|
import re
|
|
from vec_types import *
|
|
|
|
|
|
HOOK_RETURN_NEVER = "HOOK_RETURN_NEVER"
|
|
HOOK_RETURN_ON_SUCCESSFUL_CALL = "HOOK_RETURN_ON_SUCCESSFUL_CALL"
|
|
HOOK_RETURN_ON_OUTPUT_SET = "HOOK_RETURN_ON_OUTPUT_SET"
|
|
|
|
|
|
SMLUA_CALL_EVENT_HOOKS_BEGIN = """
|
|
bool smlua_call_event_hooks_{hook_type}({parameters}) {{
|
|
lua_State *L = gLuaState;
|
|
if (L == NULL) {{ return false; }}{define_hook_result}
|
|
|
|
struct LuaHookedEvent *hook = &sHookedEvents[{hook_type}];
|
|
for (int i = 0; i < hook->count; i++) {{
|
|
s32 prevTop = lua_gettop(L);
|
|
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
"""
|
|
|
|
SMLUA_CALL_EVENT_HOOKS_DEFINE_HOOK_RESULT = """
|
|
bool hookResult = false;"""
|
|
|
|
SMLUA_CALL_EVENT_HOOKS_SET_HOOK_RESULT = """
|
|
hookResult = true;"""
|
|
|
|
SMLUA_CALL_EVENT_HOOKS_CALLBACK = """
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, {n_inputs}, {n_outputs}, 0, hook->mod[i], hook->modFile[i])) {{
|
|
LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[{hook_type}]);
|
|
continue;
|
|
}}{set_hook_result}
|
|
"""
|
|
|
|
SMLUA_CALL_EVENT_HOOKS_RETURN_ON_OUTPUT_SET = """
|
|
lua_settop(L, prevTop);
|
|
return true;"""
|
|
|
|
SMLUA_CALL_EVENT_HOOKS_RETURN_ON_SUCCESSFUL_CALL = """
|
|
return true;"""
|
|
|
|
SMLUA_CALL_EVENT_HOOKS_END = """
|
|
lua_settop(L, prevTop);{return_on_successful_call}
|
|
}}
|
|
return {hook_result};
|
|
}}
|
|
"""
|
|
|
|
SMLUA_INTEGER_TYPES = {
|
|
"input": """
|
|
// push {name}
|
|
lua_pushinteger(L, {name});
|
|
""",
|
|
"output": """
|
|
// return {name}
|
|
if (lua_type(L, -{output_index}) == LUA_TNUMBER) {{
|
|
*{name} = smlua_to_integer(L, -{output_index});{return_on_output_set}
|
|
}}
|
|
"""
|
|
}
|
|
|
|
SMLUA_FLOAT_TYPES = {
|
|
"input": """
|
|
// push {name}
|
|
lua_pushnumber(L, {name});
|
|
""",
|
|
"output": """
|
|
// return {name}
|
|
if (lua_type(L, -{output_index}) == LUA_TNUMBER) {{
|
|
*{name} = smlua_to_number(L, -{output_index});{return_on_output_set}
|
|
}}
|
|
"""
|
|
}
|
|
|
|
SMLUA_TYPES = {
|
|
**{
|
|
type_name: {
|
|
"input": """
|
|
// push {name}
|
|
extern void smlua_new_%s(%s src);
|
|
smlua_new_%s({name});
|
|
""" % (type_name.lower(), type_name, type_name.lower()),
|
|
"output": """
|
|
// return {name}
|
|
if (lua_type(L, -{output_index}) == LUA_TTABLE) {{
|
|
extern void smlua_get_%s(%s dest, int index);
|
|
smlua_get_%s(*{name}, -{output_index});{return_on_output_set}
|
|
}}
|
|
""" % (type_name.lower(), type_name, type_name.lower())
|
|
}
|
|
for type_name in VEC_TYPES
|
|
},
|
|
"u8": SMLUA_INTEGER_TYPES,
|
|
"u16": SMLUA_INTEGER_TYPES,
|
|
"u32": SMLUA_INTEGER_TYPES,
|
|
"u64": SMLUA_INTEGER_TYPES,
|
|
"s8": SMLUA_INTEGER_TYPES,
|
|
"s16": SMLUA_INTEGER_TYPES,
|
|
"s32": SMLUA_INTEGER_TYPES,
|
|
"s64": SMLUA_INTEGER_TYPES,
|
|
"f32": SMLUA_FLOAT_TYPES,
|
|
"f64": SMLUA_FLOAT_TYPES,
|
|
"bool": {
|
|
"input": """
|
|
// push {name}
|
|
lua_pushboolean(L, {name});
|
|
""",
|
|
"output": """
|
|
// return {name}
|
|
if (lua_type(L, -{output_index}) == LUA_TBOOLEAN) {{
|
|
*{name} = smlua_to_boolean(L, -{output_index});{return_on_output_set}
|
|
}}
|
|
"""
|
|
},
|
|
"constchar*": {
|
|
"input": """
|
|
// push {name}
|
|
lua_pushstring(L, {name});
|
|
""",
|
|
"output": """
|
|
// return {name}
|
|
if (lua_type(L, -{output_index}) == LUA_TSTRING) {{
|
|
*{name} = smlua_to_string(L, -{output_index});{return_on_output_set}
|
|
}}
|
|
"""
|
|
},
|
|
"structMarioState*": {
|
|
"input": """
|
|
// push {name}
|
|
lua_getglobal(L, "gMarioStates");
|
|
lua_pushinteger(L, {name}->playerIndex);
|
|
lua_gettable(L, -2);
|
|
lua_remove(L, -2);
|
|
"""
|
|
},
|
|
"structWarpDest": {
|
|
"output": """
|
|
// if the hook returns a table, use it to override the warp parameters
|
|
if (lua_istable(L, -1)) {{
|
|
|
|
lua_getfield(L, -1, "destLevel");
|
|
if (lua_isnumber(L, -1)) {{
|
|
{name}->levelNum = smlua_to_integer(L, -1);
|
|
}}
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, -1, "destArea");
|
|
if (lua_isnumber(L, -1)) {{
|
|
{name}->areaIdx = smlua_to_integer(L, -1);
|
|
}}
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, -1, "destWarpNode");
|
|
if (lua_isnumber(L, -1)) {{
|
|
{name}->nodeId = smlua_to_integer(L, -1);
|
|
}}
|
|
lua_pop(L, 1);
|
|
{return_on_output_set}
|
|
}}
|
|
"""
|
|
},
|
|
}
|
|
|
|
|
|
def init():
|
|
|
|
# HACK: build LOT types from smlua_cobject_autogen.c
|
|
with open('src/pc/lua/smlua_cobject_autogen.c') as f:
|
|
file = f.read()
|
|
begin = file.find("const char *sLuaLotNames[] = {") + len("const char *sLuaLotNames[] = {")
|
|
end = file.find("};", begin)
|
|
types = [list(filter(None, re.split(r'\W+', line.strip()))) for line in file[begin:end].split('\n') if line.strip()]
|
|
|
|
for lot, name in types:
|
|
typename = f"struct{name}*"
|
|
if typename not in SMLUA_TYPES:
|
|
SMLUA_TYPES[typename] = {
|
|
"input": """
|
|
// push {name}
|
|
smlua_push_object(L, %s, {name}, NULL);
|
|
""" % (lot)
|
|
}
|
|
|
|
|
|
def extract_hook_event(line: str):
|
|
if line.startswith("//"):
|
|
return None
|
|
if not line.startswith("SMLUA_EVENT_HOOK"):
|
|
return None
|
|
|
|
tokens = [token.strip() for token in line.replace('(', ',').replace(')', ',').split(',') if token.strip()]
|
|
if len(tokens) < 3:
|
|
return None
|
|
|
|
hook_event_type = tokens[1]
|
|
if not hook_event_type.startswith("HOOK_"):
|
|
return None
|
|
|
|
hook_event_return = tokens[2]
|
|
if not hook_event_return.startswith("HOOK_RETURN_"):
|
|
return None
|
|
|
|
parameters = ", ".join(token.replace("OUTPUT ", "") for token in tokens[3:])
|
|
inputs = []
|
|
outputs = []
|
|
for token in tokens[3:]:
|
|
param = [x.strip() for x in token.split() if x.strip()]
|
|
if len(param) < 2:
|
|
continue
|
|
|
|
is_output = param[0] == "OUTPUT"
|
|
if is_output:
|
|
param = param[1:]
|
|
if len(param) < 2:
|
|
continue
|
|
|
|
param_type = " ".join(param[:-1]) if param[0] != "enum" else "s32"
|
|
param_name = param[-1]
|
|
stars = param_name.count('*')
|
|
param_name = param_name[stars:]
|
|
param_type += "*" * stars
|
|
param_type = "".join(param_type.split())
|
|
|
|
if is_output:
|
|
outputs.append({
|
|
"type": param_type[:-1],
|
|
"name": param_name
|
|
})
|
|
else:
|
|
inputs.append({
|
|
"type": param_type,
|
|
"name": param_name
|
|
})
|
|
|
|
return {
|
|
"type": hook_event_type,
|
|
"return": hook_event_return,
|
|
"parameters": parameters,
|
|
"inputs": inputs,
|
|
"outputs": outputs
|
|
}
|
|
|
|
|
|
def main():
|
|
verbose = len(sys.argv) > 1 and (sys.argv[1] == "-v" or sys.argv[1] == "--verbose")
|
|
with open("src/pc/lua/smlua_hook_events.inl", "r") as f:
|
|
lines = f.readlines()
|
|
|
|
hook_events = []
|
|
for line in lines:
|
|
hook_event = extract_hook_event(line)
|
|
if hook_event:
|
|
if verbose:
|
|
print(hook_event)
|
|
hook_events.append(hook_event)
|
|
|
|
generated = (
|
|
"/* THIS FILE IS AUTO-GENERATED */\n" +
|
|
"/* DO NOT EDIT IT MANUALLY */\n\n"
|
|
)
|
|
|
|
for hook_event in hook_events:
|
|
hook_return = hook_event["return"]
|
|
define_hook_result = SMLUA_CALL_EVENT_HOOKS_DEFINE_HOOK_RESULT if hook_return == HOOK_RETURN_NEVER else ""
|
|
set_hook_result = SMLUA_CALL_EVENT_HOOKS_SET_HOOK_RESULT if hook_return == HOOK_RETURN_NEVER else ""
|
|
return_on_successful_call = SMLUA_CALL_EVENT_HOOKS_RETURN_ON_SUCCESSFUL_CALL if hook_return == HOOK_RETURN_ON_SUCCESSFUL_CALL else ""
|
|
return_on_output_set = SMLUA_CALL_EVENT_HOOKS_RETURN_ON_OUTPUT_SET if hook_return == HOOK_RETURN_ON_OUTPUT_SET else ""
|
|
hook_result = "hookResult" if hook_return == HOOK_RETURN_NEVER else "false"
|
|
|
|
generated += SMLUA_CALL_EVENT_HOOKS_BEGIN.format(
|
|
hook_type=hook_event["type"],
|
|
parameters=hook_event["parameters"],
|
|
define_hook_result=define_hook_result
|
|
)
|
|
|
|
for input in hook_event["inputs"]:
|
|
generated += SMLUA_TYPES[input["type"]]["input"].format(
|
|
name=input["name"]
|
|
)
|
|
|
|
generated += SMLUA_CALL_EVENT_HOOKS_CALLBACK.format(
|
|
n_inputs=len(hook_event["inputs"]),
|
|
n_outputs=len(hook_event["outputs"]),
|
|
hook_type=hook_event["type"],
|
|
set_hook_result=set_hook_result
|
|
)
|
|
|
|
for i, output in enumerate(hook_event["outputs"]):
|
|
generated += SMLUA_TYPES[output["type"]]["output"].format(
|
|
name=output["name"],
|
|
output_index=i+1,
|
|
return_on_output_set=return_on_output_set
|
|
)
|
|
|
|
generated += SMLUA_CALL_EVENT_HOOKS_END.format(
|
|
return_on_successful_call=return_on_successful_call,
|
|
hook_result=hook_result
|
|
)
|
|
|
|
with open("src/pc/lua/smlua_hook_events_autogen.inl", "w", encoding="utf-8", newline="\n") as f:
|
|
f.write(generated)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
init()
|
|
main()
|