generalize send_function_call a bit
This commit is contained in:
+57
-11
@@ -27,10 +27,24 @@ bool function_compiler_available() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
string CompiledFunctionCode::generate_client_command(
|
const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
|
||||||
|
switch (arch) {
|
||||||
|
case CompiledFunctionCode::Architecture::POWERPC:
|
||||||
|
return "PowerPC";
|
||||||
|
case CompiledFunctionCode::Architecture::X86:
|
||||||
|
return "x86";
|
||||||
|
default:
|
||||||
|
throw logic_error("invalid architecture");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename FooterT, typename U16T>
|
||||||
|
string CompiledFunctionCode::generate_client_command_t(
|
||||||
const unordered_map<string, uint32_t>& label_writes,
|
const unordered_map<string, uint32_t>& label_writes,
|
||||||
const string& suffix) const {
|
const string& suffix) const {
|
||||||
S_ExecuteCode_Footer_GC_B2 footer;
|
FooterT footer;
|
||||||
footer.num_relocations = this->relocation_deltas.size();
|
footer.num_relocations = this->relocation_deltas.size();
|
||||||
footer.unused1.clear(0);
|
footer.unused1.clear(0);
|
||||||
footer.entrypoint_addr_offset = this->entrypoint_offset_offset;
|
footer.entrypoint_addr_offset = this->entrypoint_offset_offset;
|
||||||
@@ -57,7 +71,7 @@ string CompiledFunctionCode::generate_client_command(
|
|||||||
|
|
||||||
footer.relocations_offset = w.size();
|
footer.relocations_offset = w.size();
|
||||||
for (uint16_t delta : this->relocation_deltas) {
|
for (uint16_t delta : this->relocation_deltas) {
|
||||||
w.put_u16b(delta);
|
w.put<U16T>(delta);
|
||||||
}
|
}
|
||||||
if (this->relocation_deltas.size() & 1) {
|
if (this->relocation_deltas.size() & 1) {
|
||||||
w.put_u16(0);
|
w.put_u16(0);
|
||||||
@@ -67,13 +81,37 @@ string CompiledFunctionCode::generate_client_command(
|
|||||||
return move(w.str());
|
return move(w.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string CompiledFunctionCode::generate_client_command(
|
||||||
|
const unordered_map<string, uint32_t>& label_writes,
|
||||||
|
const string& suffix) const {
|
||||||
|
if (this->arch == Architecture::POWERPC) {
|
||||||
|
return this->generate_client_command_t<S_ExecuteCode_Footer_GC_B2, be_uint16_t>(
|
||||||
|
label_writes, suffix);
|
||||||
|
} else if (this->arch == Architecture::X86) {
|
||||||
|
return this->generate_client_command_t<S_ExecuteCode_Footer_PC_XB_BB_B2, le_uint16_t>(
|
||||||
|
label_writes, suffix);
|
||||||
|
} else {
|
||||||
|
throw logic_error("invalid architecture");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CompiledFunctionCode::is_big_endian() const {
|
||||||
|
return this->arch == Architecture::POWERPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
shared_ptr<CompiledFunctionCode> compile_function_code(
|
shared_ptr<CompiledFunctionCode> compile_function_code(
|
||||||
const string& directory, const string& name, const string& text) {
|
CompiledFunctionCode::Architecture arch,
|
||||||
|
const string& directory,
|
||||||
|
const string& name,
|
||||||
|
const string& text) {
|
||||||
#ifndef HAVE_RESOURCE_FILE
|
#ifndef HAVE_RESOURCE_FILE
|
||||||
|
(void)arch;
|
||||||
(void)directory;
|
(void)directory;
|
||||||
(void)name;
|
(void)name;
|
||||||
(void)text;
|
(void)text;
|
||||||
throw runtime_error("PowerPC assembler is not available");
|
throw runtime_error("function compiler is not available");
|
||||||
|
|
||||||
#else
|
#else
|
||||||
std::unordered_set<string> get_include_stack; // For mutual recursion detection
|
std::unordered_set<string> get_include_stack; // For mutual recursion detection
|
||||||
@@ -94,12 +132,17 @@ shared_ptr<CompiledFunctionCode> compile_function_code(
|
|||||||
};
|
};
|
||||||
|
|
||||||
shared_ptr<CompiledFunctionCode> ret(new CompiledFunctionCode());
|
shared_ptr<CompiledFunctionCode> ret(new CompiledFunctionCode());
|
||||||
|
ret->arch = arch;
|
||||||
ret->name = name;
|
ret->name = name;
|
||||||
ret->index = 0;
|
ret->index = 0;
|
||||||
|
|
||||||
auto assembled = PPC32Emulator::assemble(text, get_include);
|
if (arch == CompiledFunctionCode::Architecture::POWERPC) {
|
||||||
ret->code = move(assembled.code);
|
auto assembled = PPC32Emulator::assemble(text, get_include);
|
||||||
ret->label_offsets = move(assembled.label_offsets);
|
ret->code = move(assembled.code);
|
||||||
|
ret->label_offsets = move(assembled.label_offsets);
|
||||||
|
} else if (arch == CompiledFunctionCode::Architecture::X86) {
|
||||||
|
throw runtime_error("x86 assembler is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
set<uint32_t> reloc_indexes;
|
set<uint32_t> reloc_indexes;
|
||||||
for (const auto& it : ret->label_offsets) {
|
for (const auto& it : ret->label_offsets) {
|
||||||
@@ -149,7 +192,8 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
|
|||||||
try {
|
try {
|
||||||
string path = directory + "/" + filename;
|
string path = directory + "/" + filename;
|
||||||
string text = load_file(path);
|
string text = load_file(path);
|
||||||
auto code = compile_function_code(directory, name, text);
|
auto code = compile_function_code(
|
||||||
|
CompiledFunctionCode::Architecture::POWERPC, directory, name, text);
|
||||||
if (code->index != 0) {
|
if (code->index != 0) {
|
||||||
if (!this->index_to_function.emplace(code->index, code).second) {
|
if (!this->index_to_function.emplace(code->index, code).second) {
|
||||||
throw runtime_error(string_printf(
|
throw runtime_error(string_printf(
|
||||||
@@ -162,9 +206,11 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
|
|||||||
this->name_to_patch_function.emplace(name, code);
|
this->name_to_patch_function.emplace(name, code);
|
||||||
}
|
}
|
||||||
if (code->index) {
|
if (code->index) {
|
||||||
function_compiler_log.info("Compiled function %02X => %s", code->index, name.c_str());
|
function_compiler_log.info("Compiled function %02X => %s (%s)",
|
||||||
|
code->index, name.c_str(), name_for_architecture(code->arch));
|
||||||
} else {
|
} else {
|
||||||
function_compiler_log.info("Compiled function %s", name.c_str());
|
function_compiler_log.info("Compiled function %s (%s)",
|
||||||
|
name.c_str(), name_for_architecture(code->arch));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ bool function_compiler_available();
|
|||||||
// PPC32 because I haven't written an appropriate x86 assembler yet.
|
// PPC32 because I haven't written an appropriate x86 assembler yet.
|
||||||
|
|
||||||
struct CompiledFunctionCode {
|
struct CompiledFunctionCode {
|
||||||
|
enum class Architecture {
|
||||||
|
POWERPC = 0,
|
||||||
|
X86,
|
||||||
|
};
|
||||||
|
Architecture arch;
|
||||||
std::string code;
|
std::string code;
|
||||||
std::vector<uint16_t> relocation_deltas;
|
std::vector<uint16_t> relocation_deltas;
|
||||||
std::unordered_map<std::string, uint32_t> label_offsets;
|
std::unordered_map<std::string, uint32_t> label_offsets;
|
||||||
@@ -28,12 +33,21 @@ struct CompiledFunctionCode {
|
|||||||
uint32_t index; // 0 = unused (not registered in index_to_function)
|
uint32_t index; // 0 = unused (not registered in index_to_function)
|
||||||
uint32_t menu_item_id;
|
uint32_t menu_item_id;
|
||||||
|
|
||||||
|
bool is_big_endian() const;
|
||||||
|
|
||||||
|
template <typename FooterT, typename U16T>
|
||||||
|
std::string generate_client_command_t(
|
||||||
|
const std::unordered_map<std::string, uint32_t>& label_writes,
|
||||||
|
const std::string& suffix) const;
|
||||||
std::string generate_client_command(
|
std::string generate_client_command(
|
||||||
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
||||||
const std::string& suffix = "") const;
|
const std::string& suffix = "") const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* name_for_architecture(CompiledFunctionCode::Architecture arch);
|
||||||
|
|
||||||
std::shared_ptr<CompiledFunctionCode> compile_function_code(
|
std::shared_ptr<CompiledFunctionCode> compile_function_code(
|
||||||
|
CompiledFunctionCode::Architecture arch,
|
||||||
const std::string& directory,
|
const std::string& directory,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::string& text);
|
const std::string& text);
|
||||||
|
|||||||
+22
-15
@@ -217,9 +217,6 @@ void send_function_call(
|
|||||||
const std::string& suffix,
|
const std::string& suffix,
|
||||||
uint32_t checksum_addr,
|
uint32_t checksum_addr,
|
||||||
uint32_t checksum_size) {
|
uint32_t checksum_size) {
|
||||||
if (c->version != GameVersion::GC) {
|
|
||||||
throw logic_error("cannot send function calls to non-GameCube clients");
|
|
||||||
}
|
|
||||||
if (c->flags & Client::Flag::DOES_NOT_SUPPORT_SEND_FUNCTION_CALL) {
|
if (c->flags & Client::Flag::DOES_NOT_SUPPORT_SEND_FUNCTION_CALL) {
|
||||||
throw logic_error("client does not support function calls");
|
throw logic_error("client does not support function calls");
|
||||||
}
|
}
|
||||||
@@ -230,29 +227,39 @@ void send_function_call(
|
|||||||
string data;
|
string data;
|
||||||
uint32_t index = 0;
|
uint32_t index = 0;
|
||||||
if (code.get()) {
|
if (code.get()) {
|
||||||
// TODO: If we end up supporting B2 on non-GC platforms, we have to rework
|
|
||||||
// generate_client_command to be able to generate little-endian footers.
|
|
||||||
data = code->generate_client_command(label_writes, suffix);
|
data = code->generate_client_command(label_writes, suffix);
|
||||||
index = code->index;
|
index = code->index;
|
||||||
|
|
||||||
if (c->flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
|
if (c->flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
|
||||||
uint32_t key = random_object<uint32_t>();
|
uint32_t key = random_object<uint32_t>();
|
||||||
|
|
||||||
|
// This format was probably never used on any little-endian system, but we
|
||||||
|
// implement the way it would probably work there if it was used.
|
||||||
StringWriter w;
|
StringWriter w;
|
||||||
w.put_u32b(data.size());
|
if (code->is_big_endian()) {
|
||||||
w.put_u32b(key);
|
w.put_u32b(data.size());
|
||||||
|
w.put_u32b(key);
|
||||||
|
} else {
|
||||||
|
w.put_u32l(data.size());
|
||||||
|
w.put_u32l(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
data = prs_compress(data);
|
||||||
|
|
||||||
// Round size up to a multiple of 4 for encryption
|
// Round size up to a multiple of 4 for encryption
|
||||||
data.resize((data.size() + 3) & ~3);
|
data.resize((data.size() + 3) & ~3);
|
||||||
|
|
||||||
// For this format, the code section is decrypted without byteswapping on
|
|
||||||
// the client (GameCube), which is big-endian, so we have to treat the data
|
|
||||||
// the same way here (hence we can't just use crypt.encrypt).
|
|
||||||
data = prs_compress(data);
|
|
||||||
StringReader compressed_r(data);
|
|
||||||
PSOV2Encryption crypt(key);
|
PSOV2Encryption crypt(key);
|
||||||
while (!compressed_r.eof()) {
|
if (code->is_big_endian()) {
|
||||||
w.put_u32b(compressed_r.get_u32b() ^ crypt.next());
|
// The code section is decrypted without byteswapping on the client.
|
||||||
|
// Since PowerPC systems (including the GameCube) are usually
|
||||||
|
// big-endian, we have to treat the data the same way here (hence we
|
||||||
|
// can't just use crypt.encrypt).
|
||||||
|
StringReader compressed_r(data);
|
||||||
|
while (!compressed_r.eof()) {
|
||||||
|
w.put_u32b(compressed_r.get_u32b() ^ crypt.next());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
crypt.encrypt(data.data(), data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
data = move(w.str());
|
data = move(w.str());
|
||||||
|
|||||||
Reference in New Issue
Block a user