From a6442c6208d7ba495ac783a77d6fdf615b332995 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Thu, 11 Aug 2022 00:23:49 -0700 Subject: [PATCH] generalize send_function_call a bit --- src/FunctionCompiler.cc | 68 ++++++++++++++++++++++++++++++++++------- src/FunctionCompiler.hh | 14 +++++++++ src/SendCommands.cc | 37 +++++++++++++--------- 3 files changed, 93 insertions(+), 26 deletions(-) diff --git a/src/FunctionCompiler.cc b/src/FunctionCompiler.cc index 12b384f5..821696f0 100644 --- a/src/FunctionCompiler.cc +++ b/src/FunctionCompiler.cc @@ -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 +string CompiledFunctionCode::generate_client_command_t( const unordered_map& label_writes, const string& suffix) const { - S_ExecuteCode_Footer_GC_B2 footer; + FooterT footer; footer.num_relocations = this->relocation_deltas.size(); footer.unused1.clear(0); footer.entrypoint_addr_offset = this->entrypoint_offset_offset; @@ -57,7 +71,7 @@ string CompiledFunctionCode::generate_client_command( footer.relocations_offset = w.size(); for (uint16_t delta : this->relocation_deltas) { - w.put_u16b(delta); + w.put(delta); } if (this->relocation_deltas.size() & 1) { w.put_u16(0); @@ -67,13 +81,37 @@ string CompiledFunctionCode::generate_client_command( return move(w.str()); } +string CompiledFunctionCode::generate_client_command( + const unordered_map& label_writes, + const string& suffix) const { + if (this->arch == Architecture::POWERPC) { + return this->generate_client_command_t( + label_writes, suffix); + } else if (this->arch == Architecture::X86) { + return this->generate_client_command_t( + label_writes, suffix); + } else { + throw logic_error("invalid architecture"); + } +} + +bool CompiledFunctionCode::is_big_endian() const { + return this->arch == Architecture::POWERPC; +} + + + shared_ptr 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 + (void)arch; (void)directory; (void)name; (void)text; - throw runtime_error("PowerPC assembler is not available"); + throw runtime_error("function compiler is not available"); #else std::unordered_set get_include_stack; // For mutual recursion detection @@ -94,12 +132,17 @@ shared_ptr compile_function_code( }; shared_ptr ret(new CompiledFunctionCode()); + ret->arch = arch; ret->name = name; ret->index = 0; - auto assembled = PPC32Emulator::assemble(text, get_include); - ret->code = move(assembled.code); - ret->label_offsets = move(assembled.label_offsets); + if (arch == CompiledFunctionCode::Architecture::POWERPC) { + auto assembled = PPC32Emulator::assemble(text, get_include); + 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 reloc_indexes; for (const auto& it : ret->label_offsets) { @@ -149,7 +192,8 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) { try { string path = directory + "/" + filename; 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 (!this->index_to_function.emplace(code->index, code).second) { throw runtime_error(string_printf( @@ -162,9 +206,11 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) { this->name_to_patch_function.emplace(name, code); } 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 { - 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) { diff --git a/src/FunctionCompiler.hh b/src/FunctionCompiler.hh index e13edd28..63361c5b 100644 --- a/src/FunctionCompiler.hh +++ b/src/FunctionCompiler.hh @@ -20,6 +20,11 @@ bool function_compiler_available(); // PPC32 because I haven't written an appropriate x86 assembler yet. struct CompiledFunctionCode { + enum class Architecture { + POWERPC = 0, + X86, + }; + Architecture arch; std::string code; std::vector relocation_deltas; std::unordered_map label_offsets; @@ -28,12 +33,21 @@ struct CompiledFunctionCode { uint32_t index; // 0 = unused (not registered in index_to_function) uint32_t menu_item_id; + bool is_big_endian() const; + + template + std::string generate_client_command_t( + const std::unordered_map& label_writes, + const std::string& suffix) const; std::string generate_client_command( const std::unordered_map& label_writes = {}, const std::string& suffix = "") const; }; +const char* name_for_architecture(CompiledFunctionCode::Architecture arch); + std::shared_ptr compile_function_code( + CompiledFunctionCode::Architecture arch, const std::string& directory, const std::string& name, const std::string& text); diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 2b29077b..d575b65d 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -217,9 +217,6 @@ void send_function_call( const std::string& suffix, uint32_t checksum_addr, 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) { throw logic_error("client does not support function calls"); } @@ -230,29 +227,39 @@ void send_function_call( string data; uint32_t index = 0; 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); index = code->index; if (c->flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) { uint32_t key = random_object(); + // 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; - w.put_u32b(data.size()); - w.put_u32b(key); + if (code->is_big_endian()) { + 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 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); - while (!compressed_r.eof()) { - w.put_u32b(compressed_r.get_u32b() ^ crypt.next()); + if (code->is_big_endian()) { + // 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());