add $nativecall command

This commit is contained in:
Martin Michelsen
2025-02-22 20:52:47 -08:00
parent 11d539042c
commit 66e00d5136
4 changed files with 133 additions and 0 deletions
+1
View File
@@ -558,6 +558,7 @@ Some commands only work on the game server and not on the proxy server. The chat
* The rest of the commands in this section are enabled on the game server. (They are always enabled on the proxy server.)
* `$readmem <address>` (game server only): Read 4 bytes from the given address and show you the values.
* `$writemem <address> <data>` (game server only): Write data to the given address. Data is not required to be any specific size.
* `$nativecall <address> [arg1 ...]` (game server only, GC only): Call a native function on your client. Only arguments passed in registers are supported; calling functions that take many arguments is not supported.
* `$quest <number>` (game server only): Load a quest by quest number. Can be used to load battle or challenge quests with only one player present. Debug is not required to be enabled if the specified quest has the AllowStartFromChatCommand field set in its metadata file.
* `$qcall <function-id>`: Call a quest function on your client.
* `$qcheck <flag-num>` (game server only): Show the value of a quest flag. This command can be used without debug mode enabled. If you're in a game, show the value of the flag in that game; if you're in the lobby, show the saved value of that quest flag for your character (BB only).
+50
View File
@@ -2830,6 +2830,56 @@ ChatCommandDefinition cc_writemem(
},
unavailable_on_proxy_server);
ChatCommandDefinition cc_nativecall(
{"$nativecall"},
+[](const ServerArgs& a) -> void {
a.check_debug_enabled();
// TODO: $nativecall is not implemented on x86 (yet) because there are
// multiple calling conventions used within the executable (at least on
// Xbox and BB), so we would need a way to specify which calling
// convention to use, which would be annoying
if (is_x86(a.c->version())) {
throw precondition_failed("Command not supported\non x86 clients");
}
auto tokens = phosg::split(a.text, ' ');
if (tokens.size() < 1) {
throw precondition_failed("Incorrect arguments");
}
uint32_t addr = stoul(tokens[0], nullptr, 16);
if (!console_address_in_range(a.c->version(), addr)) {
throw precondition_failed("$C4Function address\nout of range");
}
unordered_map<string, uint32_t> label_writes{{"call_addr", addr}};
for (size_t z = 0; z < tokens.size() - 1; z++) {
label_writes.emplace(phosg::string_printf("arg%zu", z), stoull(tokens[z + 1], nullptr, 16));
}
prepare_client_for_patches(a.c, [wc = weak_ptr<Client>(a.c), label_writes = std::move(label_writes)]() {
auto c = wc.lock();
if (!c) {
return;
}
try {
auto s = c->require_server_state();
const char* function_name = is_dc(c->version())
? "CallNativeFunctionDC"
: is_gc(c->version())
? "CallNativeFunctionGC"
: "CallNativeFunctionX86";
auto fn = s->function_code_index->name_to_function.at(function_name);
send_function_call(c, fn, label_writes);
c->function_call_response_queue.emplace_back(empty_function_call_response_handler);
} catch (const out_of_range&) {
throw precondition_failed("Invalid patch name");
}
});
},
unavailable_on_proxy_server);
////////////////////////////////////////////////////////////////////////////////
// Dispatch methods
@@ -0,0 +1,30 @@
# This function implements the $nativecall chat command on DC clients.
entry_ptr:
reloc0:
.offsetof start
start:
sts.l -[r15], pr
mov.l r0, [call_addr]
mov.l r4, [arg0]
mov.l r5, [arg1]
mov.l r6, [arg2]
mov.l r7, [arg3]
calls r0
nop
lds.l pr, [r15]+
rets
nop
.align 4
call_addr:
.zero
arg0:
.zero
arg1:
.zero
arg2:
.zero
arg3:
.zero
@@ -0,0 +1,52 @@
# This function implements the $nativecall chat command on GameCube clients.
entry_ptr:
reloc0:
.offsetof start
start:
mflr r0
stw [r1 + 4], r0
stwu [r1 - 0x20], r1
bl read
call_addr:
.zero
arg0:
.zero
arg1:
.zero
arg2:
.zero
arg3:
.zero
arg4:
.zero
arg5:
.zero
arg6:
.zero
arg7:
.zero
arg8:
.zero
arg9:
.zero
resume:
mflr r12
lwz r0, [r12] # call_addr
lwz r3, [r12 + 0x04] # arg0
lwz r4, [r12 + 0x08] # arg1
lwz r5, [r12 + 0x0C] # arg2
lwz r6, [r12 + 0x10] # arg3
lwz r7, [r12 + 0x14] # arg4
lwz r8, [r12 + 0x18] # arg5
lwz r9, [r12 + 0x1C] # arg6
lwz r10, [r12 + 0x20] # arg7
lwz r11, [r12 + 0x24] # arg8
lwz r12, [r12 + 0x28] # arg9
mtctr r0
bctrl
addi r1, r1, 0x20
lwz r0, [r1 + 4]
mtlr r0
blr