diff --git a/CMakeLists.txt b/CMakeLists.txt index 5773b953..fe3eea94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,10 +60,12 @@ set(SOURCES src/ChatCommands.cc src/ChoiceSearch.cc src/Client.cc + src/ClientFunctionIndex.cc src/CommonItemSet.cc src/Compression.cc src/DCSerialNumbers.cc src/DNSServer.cc + src/DOLFileIndex.cc src/DownloadSession.cc src/EnemyType.cc src/Episode3/AssistServer.cc @@ -79,7 +81,6 @@ set(SOURCES src/Episode3/Server.cc src/Episode3/Tournament.cc src/FileContentsCache.cc - src/FunctionCompiler.cc src/GameServer.cc src/GSLArchive.cc src/HTTPServer.cc diff --git a/README.md b/README.md index de03fe25..cc1e5048 100644 --- a/README.md +++ b/README.md @@ -594,6 +594,7 @@ Some commands only work for clients not in proxy sessions. The chat commands are * You'll see in-game messages from the server when you take some actions, like killing enemies, opening boxes, or flipping switches. * You'll see the rare seed value and floor variations when you join a game. * You'll be placed into the last available slot in lobbies and games instead of the first, unless you're joining a BB solo-mode game. + * You'll be able to run any client function with `$patch`, not only those that are marked visible. * You'll be able to join games with any PSO version, not only those for which cross-version play is normally enabled. See the "Cross-version play" section above for details on this. * `$readmem
`: Read 4 bytes from the given address and show you the values. * `$writemem
`: Write data to the given address. Data is not required to be any specific size. @@ -622,7 +623,7 @@ Some commands only work for clients not in proxy sessions. The chat commands are * `$ln [name-or-type]`: Set the lobby number. Visible only to you. This command exists because some non-lobby maps can be loaded as lobbies with invalid lobby numbers. See the "GC lobby types" and "Ep3 lobby types" entries in the information menu for acceptable values here. Note that non-lobby maps do not have a lobby counter, so there's no way to exit the lobby without using either `$ln` again or `$exit`. On the game server, `$ln` reloads the lobby immediately; on the proxy, it doesn't take effect until you load another lobby yourself (which means you'll like have to use `$exit` to escape). Run this command with no argument to return to the default lobby. * `$swa`: Enable or disable switch assist. When enabled, the server will unlock two-player and four-player doors in non-quest games when you step on any of the required switches. * `$exit`: If you're in a lobby, send you to the main menu (which ends your proxy session, if you're in one). If you're in a game or spectator team, send you to the lobby (but does not end your proxy session if you're in one). Does nothing if you're in a non-Episode 3 game and no quest is in progress. - * `$patch `: Run a patch on your client. `` must exactly match the name of a patch on the server. + * `$patch `: Run a client function. `` must exactly match the name of a client function on the server. * Character data commands (non-proxy only) * `$switchchar ` (BB only): Switch to a different character from your account without logging out. diff --git a/notes/generate-patches.py b/notes/generate-patches.py index 835a2db7..2542b4cb 100644 --- a/notes/generate-patches.py +++ b/notes/generate-patches.py @@ -4,7 +4,6 @@ import subprocess import sys from dataclasses import dataclass - version_tokens = ("3OJ2", "3OJ3", "3OJ4", "3OJ5", "3OE0", "3OE1", "3OE2", "3OP0") @@ -62,7 +61,7 @@ def write_patches_for_code( f.write("reloc0:\n") f.write(" .offsetof start\n") f.write("start:\n") - f.write(" .include WriteCodeBlocksGC\n") + f.write(" .include WriteCodeBlocks\n") for region in write_regions: f.write( f" # region @ {region.address:08X} ({len(region.data) * 4} bytes)\n" diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 3a242e69..116190ed 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -1111,9 +1111,9 @@ ChatCommandDefinition cc_exit( a.c->check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE)) { co_await prepare_client_for_patches(a.c); auto s = a.c->require_server_state(); - shared_ptr fn; + shared_ptr fn; try { - fn = s->function_code_index->get_patch("ExitAnywhere", a.c->specific_version); + fn = s->client_functions->get("ExitAnywhere", a.c->specific_version); } catch (const out_of_range&) { } if (fn) { @@ -1571,7 +1571,7 @@ ChatCommandDefinition cc_loadchar( auto send_set_extended_player_info = [&a, &s](const CharT& char_file) -> asio::awaitable { co_await prepare_client_for_patches(a.c); try { - auto fn = s->function_code_index->get_patch("SetExtendedPlayerInfo", a.c->specific_version); + auto fn = s->client_functions->get("SetExtendedPlayerInfo", a.c->specific_version); co_await send_function_call(a.c, fn, {}, &char_file, sizeof(CharT)); auto l = a.c->lobby.lock(); if (l) { @@ -1707,7 +1707,7 @@ ChatCommandDefinition cc_makeobj( co_await prepare_client_for_patches(a.c); auto s = a.c->require_server_state(); - auto fn = s->function_code_index->get_patch("CreateObject", a.c->specific_version); + auto fn = s->client_functions->get("CreateObject", a.c->specific_version); co_await send_function_call(a.c, fn, label_writes); }); @@ -1847,14 +1847,31 @@ ChatCommandDefinition cc_patch( try { auto s = a.c->require_server_state(); // Note: We can't look this up before prepare_client_for_patches because specific_version may not be set - auto fn = s->function_code_index->get_patch(patch_name, a.c->specific_version); + auto fn = s->client_functions->get(patch_name, a.c->specific_version); + + switch (fn->visibility) { + case ClientFunctionIndex::Function::Visibility::DEBUG_ONLY: + case ClientFunctionIndex::Function::Visibility::PATCHES_MENU_ONLY: + a.check_debug_enabled(); + break; + case ClientFunctionIndex::Function::Visibility::CHAT_COMMAND_ONLY_WITH_CHEAT_MODE: + a.check_cheats_enabled_or_allowed(true); + break; + case ClientFunctionIndex::Function::Visibility::CHAT_COMMAND_ONLY: + case ClientFunctionIndex::Function::Visibility::PATCHES_MENU_AND_CHAT_COMMAND: + break; + default: + throw std::logic_error("Invalid client function visibility"); + } + auto ret = co_await send_function_call(a.c, fn, label_writes); if (fn->show_return_value) { send_text_message_fmt(a.c, "$C6Return value:$C7\nInt: {}\nHex: {:08X}\nFloat: {:g}", ret.return_value.load(), ret.return_value.load(), std::bit_cast(ret.return_value.load())); } + } catch (const out_of_range&) { - send_text_message(a.c, "$C6Invalid patch name"); + send_text_message(a.c, "$C6Invalid function"); } co_return; }); @@ -2277,15 +2294,10 @@ ChatCommandDefinition cc_readmem( co_await prepare_client_for_patches(a.c); - shared_ptr fn; + shared_ptr fn; try { auto s = a.c->require_server_state(); - const char* function_name = is_dc(a.c->version()) - ? "ReadMemoryWordDC" - : is_gc(a.c->version()) - ? "ReadMemoryWordGC" - : "ReadMemoryWordX86"; - fn = s->function_code_index->name_to_function.at(function_name); + fn = s->client_functions->get("ReadMemoryWord", a.c->specific_version); } catch (const out_of_range&) { throw precondition_failed("Invalid patch name"); } @@ -3139,12 +3151,7 @@ ChatCommandDefinition cc_writemem( try { auto s = a.c->require_server_state(); - const char* function_name = is_dc(a.c->version()) - ? "WriteMemoryDC" - : is_gc(a.c->version()) - ? "WriteMemoryGC" - : "WriteMemoryX86"; - auto fn = s->function_code_index->name_to_function.at(function_name); + auto fn = s->client_functions->get("WriteMemory", a.c->specific_version); unordered_map label_writes{{"dest_addr", addr}, {"size", data.size()}}; co_await send_function_call(a.c, fn, label_writes, data.data(), data.size()); } catch (const out_of_range&) { @@ -3184,12 +3191,7 @@ ChatCommandDefinition cc_nativecall( try { auto s = a.c->require_server_state(); - const char* function_name = is_dc(a.c->version()) - ? "CallNativeFunctionDC" - : is_gc(a.c->version()) - ? "CallNativeFunctionGC" - : "CallNativeFunctionX86"; - auto fn = s->function_code_index->name_to_function.at(function_name); + auto fn = s->client_functions->get("CallNativeFunction", a.c->specific_version); co_await send_function_call(a.c, fn, label_writes); } catch (const out_of_range&) { throw precondition_failed("Invalid patch name"); diff --git a/src/Client.hh b/src/Client.hh index e9f44733..09c896f3 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -6,11 +6,11 @@ #include "Account.hh" #include "AsyncUtils.hh" #include "Channel.hh" +#include "ClientFunctionIndex.hh" #include "CommandFormats.hh" #include "Episode3/BattleRecord.hh" #include "Episode3/Tournament.hh" #include "FileContentsCache.hh" -#include "FunctionCompiler.hh" #include "PSOEncryption.hh" #include "PSOProtocol.hh" #include "PatchFileIndex.hh" diff --git a/src/ClientFunctionIndex.cc b/src/ClientFunctionIndex.cc new file mode 100644 index 00000000..abb39a73 --- /dev/null +++ b/src/ClientFunctionIndex.cc @@ -0,0 +1,558 @@ +#include "ClientFunctionIndex.hh" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "CommandFormats.hh" +#include "CommonFileFormats.hh" +#include "Compression.hh" +#include "Loggers.hh" + +using namespace std; + +using Arch = ClientFunctionIndex::Function::Architecture; + +const char* name_for_architecture(Arch arch) { + switch (arch) { + case Arch::SH4: + return "SH-4"; + case Arch::POWERPC: + return "PowerPC"; + case Arch::X86: + return "x86"; + default: + throw logic_error("invalid architecture"); + } +} + +uint32_t specific_version_for_architecture(Arch arch) { + switch (arch) { + case Arch::SH4: + return SPECIFIC_VERSION_SH4_INDETERMINATE; + case Arch::POWERPC: + return SPECIFIC_VERSION_PPC_INDETERMINATE; + case Arch::X86: + return SPECIFIC_VERSION_X86_INDETERMINATE; + default: + throw logic_error("invalid architecture"); + } +} + +Arch architecture_for_specific_version(uint32_t specific_version) { + if (specific_version == SPECIFIC_VERSION_SH4_INDETERMINATE) { + return Arch::SH4; + } else if (specific_version == SPECIFIC_VERSION_PPC_INDETERMINATE) { + return Arch::POWERPC; + } else if (specific_version == SPECIFIC_VERSION_X86_INDETERMINATE) { + return Arch::X86; + } else if (specific_version_is_dc(specific_version)) { + return Arch::SH4; + } else if (specific_version_is_gc(specific_version)) { + return Arch::POWERPC; + } else { + return Arch::X86; + } +} + +static inline std::string cache_key(const std::string& name, uint32_t specific_version) { + return std::format("{}-{:08X}", name, specific_version); +} + +template +const T& get_with_sv_fallback( + const std::unordered_map& index, const std::string& name, uint32_t specific_version) { + try { + return index.at(cache_key(name, specific_version)); + } catch (const std::out_of_range&) { + } + uint32_t arch_specific_version = specific_version_for_architecture(architecture_for_specific_version( + specific_version)); + if (arch_specific_version != specific_version) { + try { + return index.at(cache_key(name, arch_specific_version)); + } catch (const std::out_of_range&) { + } + } + return index.at(name); +} + +template +string ClientFunctionIndex::Function::generate_client_command_t( + const unordered_map& label_writes, + const void* suffix_data, + size_t suffix_size, + uint32_t override_relocations_offset) const { + using FooterT = RELFileFooterT; + + FooterT footer; + footer.num_relocations = this->relocation_deltas.size(); + footer.unused1.clear(0); + footer.root_offset = this->entrypoint_offset_offset; + footer.unused2.clear(0); + + phosg::StringWriter w; + if (!label_writes.empty()) { + string modified_code = this->code; + for (const auto& it : label_writes) { + size_t offset = this->label_offsets.at(it.first); + if (offset > modified_code.size() - 4) { + throw runtime_error("label out of range"); + } + *reinterpret_cast*>(modified_code.data() + offset) = it.second; + } + w.write(modified_code); + } else { + w.write(this->code); + } + if (suffix_size) { + w.write(suffix_data, suffix_size); + } + while (w.size() & 3) { + w.put_u8(0); + } + + footer.relocations_offset = w.size(); + + // Always write at least 4 bytes even if there are no relocations + if (this->relocation_deltas.empty()) { + w.put_u32(0); + } + + if (override_relocations_offset) { + footer.relocations_offset = override_relocations_offset; + } else { + for (uint16_t delta : this->relocation_deltas) { + w.put>(delta); + } + if (this->relocation_deltas.size() & 1) { + w.put_u16(0); + } + } + + w.put(footer); + return std::move(w.str()); +} + +string ClientFunctionIndex::Function::generate_client_command( + const unordered_map& label_writes, + const void* suffix_data, + size_t suffix_size, + uint32_t override_relocations_offset) const { + if (this->is_big_endian()) { + return this->generate_client_command_t(label_writes, suffix_data, suffix_size, override_relocations_offset); + } else if ((this->arch == Architecture::X86) || (this->arch == Architecture::SH4)) { + return this->generate_client_command_t(label_writes, suffix_data, suffix_size, override_relocations_offset); + } else { + throw logic_error("invalid architecture"); + } +} + +static unordered_map preprocess_function_code(const std::string& text) { + std::unordered_set all_specific_versions; + struct Line { + std::string text; + std::unordered_map new_specific_versions; // Nonempty iff line is a .versions directive + bool enable_all_versions = false; + }; + + std::vector lines; + for (auto& line_text : phosg::split(text, '\n')) { + auto& line = lines.emplace_back(); + line.text = std::move(line_text); + + string stripped_line = line.text; + phosg::strip_whitespace(stripped_line); + + if (stripped_line == ".all_versions") { + line.enable_all_versions = true; + } else if (stripped_line.starts_with(".versions ")) { + for (auto& vers_token : phosg::split(stripped_line.substr(10), ' ')) { + phosg::strip_whitespace(vers_token); + if (!vers_token.empty()) { + uint32_t specific_version = specific_version_for_str(vers_token); + size_t version_index = line.new_specific_versions.size(); + all_specific_versions.emplace(specific_version); + line.new_specific_versions.emplace(std::move(specific_version), version_index); + } + } + } + } + + static const std::string empty_str = ""; + + unordered_map ret; + for (uint32_t specific_version : all_specific_versions) { + std::deque version_lines; + bool include_current_line = true; + size_t current_vers_index = all_specific_versions.size(); + for (size_t line_znum = 0; line_znum < lines.size(); line_znum++) { + const auto& line = lines[line_znum]; + + if (line.enable_all_versions) { + include_current_line = true; + current_vers_index = all_specific_versions.size(); + version_lines.emplace_back(empty_str); + + } else if (!line.new_specific_versions.empty()) { + auto it = line.new_specific_versions.find(specific_version); + if (it == line.new_specific_versions.end()) { + include_current_line = false; + current_vers_index = all_specific_versions.size(); + } else { + include_current_line = true; + current_vers_index = it->second; + } + version_lines.emplace_back(empty_str); + + } else if (!include_current_line) { + version_lines.emplace_back(empty_str); + + } else { + std::string line_text = line.text; + size_t vers_offset = line_text.find("', vers_offset + 6); + if (end_offset == string::npos) { + throw runtime_error(std::format("(version {}) (line {}) unterminated replacement", + str_for_specific_version(specific_version), line_znum + 1)); + } + auto tokens = phosg::split(line_text.substr(vers_offset + 6, end_offset - vers_offset - 6), ' '); + if (current_vers_index >= tokens.size()) { + throw runtime_error(std::format("(version {}) (line {}) invalid replacement", + str_for_specific_version(specific_version), line_znum + 1)); + } + line_text = line_text.substr(0, vers_offset) + tokens[current_vers_index] + line_text.substr(end_offset + 1); + vers_offset = line_text.find(" source_files; + std::function add_directory = [&](const std::string& dir) -> void { + for (const auto& item : std::filesystem::directory_iterator(dir)) { + string item_name = item.path().filename().string(); + string item_path = dir.ends_with("/") ? (dir + item_name) : (dir + "/" + item_name); + if (std::filesystem::is_directory(item_path)) { + add_directory(item_path); + } else if (item_path.ends_with(".s") && std::filesystem::is_regular_file(item_path)) { + client_functions_log.debug_f("Adding {} from {}", item_name, item_path); + if (!source_files.emplace(item_name, phosg::load_file(item_path)).second) { + throw std::runtime_error(std::format("Duplicate source filename: {}", item_name)); + } + } else if (item_path.ends_with(".bin") && std::filesystem::is_regular_file(item_path)) { + client_functions_log.debug_f("Adding {} from {}", item_name, item_path); + if (!source_files.emplace(item_name, phosg::load_file(item_path)).second) { + throw std::runtime_error(std::format("Duplicate binary filename: {}", item_name)); + } + } else { + client_functions_log.debug_f("Ignoring {}", item_path); + } + } + }; + add_directory(root_dir); + + unordered_map include_cache; + uint32_t last_menu_item_id = 0; + for (const auto& [source_filename, source] : source_files) { + if (!source_filename.ends_with(".s")) { + client_functions_log.debug_f("Skipping root compile for {} because it is not a .s file", source_filename); + continue; + } + if (source_filename.ends_with(".inc.s")) { + client_functions_log.debug_f("Skipping root compile for {} because it is an include", source_filename); + continue; + } + + std::unordered_map preprocessed; + try { + preprocessed = preprocess_function_code(source); + } catch (const std::exception& e) { + throw std::runtime_error(std::format("({} preprocessing) {}", source_filename, e.what())); + } + + for (const auto& [specific_version, source] : preprocessed) { + shared_ptr fn = make_shared(); + fn->short_name = source_filename.substr(0, source_filename.size() - 2); + fn->specific_version = specific_version; + fn->menu_item_id = ++last_menu_item_id; + fn->arch = architecture_for_specific_version(fn->specific_version); + + try { + unordered_set get_include_stack; + function get_include_for_sv = [&include_cache, &source_files, &get_include_stack, &get_include_for_sv](const string& name, uint32_t specific_version) -> string { + try { + return get_with_sv_fallback(include_cache, name, specific_version); + } catch (const std::out_of_range&) { + } + if (client_functions_log.should_log(phosg::LogLevel::L_DEBUG)) { + client_functions_log.debug_f("({}) Include {}-{} needs to be compiled", + get_include_stack.size(), name, str_for_specific_version(specific_version)); + } + + auto it = source_files.find(name + ".inc.s"); + if (it != source_files.end()) { + if (!get_include_stack.emplace(name).second) { + throw runtime_error("Mutual recursion between includes: " + name); + } + for (const auto& [include_specific_version, include_source] : preprocess_function_code(it->second)) { + ResourceDASM::EmulatorBase::AssembleResult ret; + auto get_include = std::bind(get_include_for_sv, std::placeholders::_1, include_specific_version); + switch (architecture_for_specific_version(include_specific_version)) { + case Arch::POWERPC: + ret = ResourceDASM::PPC32Emulator::assemble(include_source, get_include); + break; + case Arch::X86: + ret = ResourceDASM::X86Emulator::assemble(include_source, get_include); + break; + case Arch::SH4: + ret = ResourceDASM::SH4Emulator::assemble(include_source, get_include); + break; + default: + throw runtime_error("unknown architecture"); + } + if (client_functions_log.should_log(phosg::LogLevel::L_DEBUG)) { + client_functions_log.debug_f("({}) Compiled include {}-{}", + get_include_stack.size(), name, str_for_specific_version(include_specific_version)); + } + include_cache.emplace(cache_key(name, include_specific_version), std::move(ret.code)); + } + get_include_stack.erase(name); + + } else { + it = source_files.find(name + ".inc.bin"); + if (it != source_files.end()) { + include_cache.emplace(name, it->second).first->second; + client_functions_log.debug_f("({}) Cached binary include {}", get_include_stack.size(), name); + } + } + + try { + return get_with_sv_fallback(include_cache, name, specific_version); + } catch (const std::out_of_range&) { + } + throw runtime_error(std::format( + "Data not found for include {} ({})", name, str_for_specific_version(specific_version))); + }; + + try { + ResourceDASM::EmulatorBase::AssembleResult assembled; + auto get_include = std::bind(get_include_for_sv, std::placeholders::_1, specific_version); + switch (fn->arch) { + case Arch::POWERPC: + assembled = ResourceDASM::PPC32Emulator::assemble(source, get_include); + break; + case Arch::X86: + assembled = ResourceDASM::X86Emulator::assemble(source, get_include); + break; + case Arch::SH4: + assembled = ResourceDASM::SH4Emulator::assemble(source, get_include); + break; + default: + throw runtime_error("invalid architecture"); + } + + fn->code = std::move(assembled.code); + fn->label_offsets = std::move(assembled.label_offsets); + for (const auto& [key, value] : assembled.metadata_keys) { + if (key == "visibility") { + if (value == "hidden") { + fn->visibility = Function::Visibility::DEBUG_ONLY; + } else if (value == "cheat") { + fn->visibility = Function::Visibility::CHAT_COMMAND_ONLY_WITH_CHEAT_MODE; + } else if (value == "chat") { + fn->visibility = Function::Visibility::CHAT_COMMAND_ONLY; + } else if (value == "menu") { + fn->visibility = Function::Visibility::PATCHES_MENU_ONLY; + } else if (value == "all") { + fn->visibility = Function::Visibility::PATCHES_MENU_AND_CHAT_COMMAND; + } else { + throw std::runtime_error("Invalid visibility value"); + } + } else if (key == "key") { + fn->short_name = value; + } else if (key == "name") { + fn->long_name = value; + } else if (key == "description") { + fn->description = value; + } else if (key == "client_flag") { + fn->client_flag = stoull(value, nullptr, 0); + } else if (key == "show_return_value") { + fn->show_return_value = true; + } else { + throw runtime_error("unknown metadata key: " + key); + } + } + + try { + fn->entrypoint_offset_offset = fn->label_offsets.at("entry_ptr"); + } catch (const out_of_range&) { + throw runtime_error("code does not contain entry_ptr label"); + } + + set reloc_indexes; + for (const auto& it : fn->label_offsets) { + if (it.first.starts_with("reloc")) { + reloc_indexes.emplace(it.second / 4); + } + } + uint32_t prev_index = 0; + for (const auto& it : reloc_indexes) { + uint32_t delta = it - prev_index; + if (delta > 0xFFFF) { + throw runtime_error("relocation delta too far away"); + } + fn->relocation_deltas.emplace_back(delta); + prev_index = it; + } + + } catch (const exception& e) { + if (raise_on_any_failure) { + throw; + } + client_functions_log.warning_f("Failed to compile function {} ({}): {}", + fn->short_name, str_for_specific_version(specific_version), e.what()); + } + + auto key = cache_key(fn->short_name, specific_version); + if (!this->all_functions.emplace(key, fn).second) { + throw std::runtime_error("Duplicate function key: " + key); + } + this->functions_by_specific_version[specific_version].emplace(key, fn); + this->functions_by_menu_item_id.emplace(fn->menu_item_id, fn); + + client_functions_log.debug_f("Compiled function {} ({}; {}; {})", + fn->short_name, str_for_specific_version(fn->specific_version), name_for_architecture(fn->arch), + phosg::name_for_enum(fn->visibility)); + } catch (const std::exception& e) { + throw std::runtime_error(std::format( + "({}-{}) {}", fn->short_name, str_for_specific_version(specific_version), e.what())); + } + } + } +} + +shared_ptr ClientFunctionIndex::patch_switches_menu( + uint32_t specific_version, + const std::unordered_set& server_auto_patches_enabled, + const std::unordered_set& client_auto_patches_enabled) const { + auto ret = make_shared(MenuID::PATCH_SWITCHES, "Patches"); + ret->items.emplace_back(PatchesMenuItemID::GO_BACK, "Go back", "Return to the\nmain menu", 0); + + auto map_it = this->functions_by_specific_version.find(specific_version); + if (map_it != this->functions_by_specific_version.end()) { + for (auto [name, fn] : map_it->second) { + if (fn->appears_in_patches_menu() && server_auto_patches_enabled.count(fn->short_name)) { + string item_text; + item_text.push_back(client_auto_patches_enabled.count(fn->short_name) ? '*' : '-'); + item_text += fn->long_name.empty() ? fn->short_name : fn->long_name; + ret->items.emplace_back( + fn->menu_item_id, item_text, fn->description, MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE); + } + } + } + return ret; +} + +bool ClientFunctionIndex::patch_menu_empty(uint32_t specific_version) const { + uint32_t mask = specific_version_is_indeterminate(specific_version) ? 0xFF000000 : 0xFFFFFFFF; + auto it = this->functions_by_specific_version.lower_bound(specific_version & mask); + return ((it == this->functions_by_specific_version.end()) || ((it->first & mask) != (specific_version & mask))); +} + +std::shared_ptr ClientFunctionIndex::get( + const std::string& name, uint32_t specific_version) const { + return get_with_sv_fallback(this->all_functions, name, specific_version); +} + +std::shared_ptr ClientFunctionIndex::get( + const std::string& name, Arch arch) const { + return get_with_sv_fallback(this->all_functions, name, specific_version_for_architecture(arch)); +} + +std::shared_ptr ClientFunctionIndex::get(const std::string& name) const { + return this->all_functions.at(name); +} + +std::shared_ptr ClientFunctionIndex::get_by_menu_item_id( + uint32_t menu_item_id) const { + return this->functions_by_menu_item_id.at(menu_item_id); +} + +uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum) { + static unordered_map checksum_to_specific_version; + if (checksum_to_specific_version.empty()) { + struct { + char system_code = 'G'; + char game_code1 = 'P'; + char game_code2; + char region_code; + char developer_code1 = '8'; + char developer_code2 = 'P'; + uint8_t disc_number = 0; + uint8_t version_code; + } __attribute__((packed)) data; + for (const char* game_code2 = "OS"; *game_code2; game_code2++) { + data.game_code2 = *game_code2; + for (const char* region_code = "JEP"; *region_code; region_code++) { + data.region_code = *region_code; + for (uint8_t version_code = 0; version_code < 8; version_code++) { + data.version_code = version_code; + uint32_t checksum = phosg::crc32(&data, sizeof(data)); + uint32_t specific_version = 0x33000030 | (*game_code2 << 16) | (*region_code << 8) | version_code; + if (!checksum_to_specific_version.emplace(checksum, specific_version).second) { + throw logic_error("multiple specific_versions have same header checksum"); + } + } + } + { + // Generate entries for Trial Editions + data.region_code = 'J'; + data.system_code = 'D'; + data.version_code = 0; + uint32_t checksum = phosg::crc32(&data, sizeof(data)); + uint32_t specific_version = 0x33004A54 | (*game_code2 << 16); + if (!checksum_to_specific_version.emplace(checksum, specific_version).second) { + throw logic_error("multiple specific_versions have same header checksum"); + } + data.system_code = 'G'; + } + } + } + return checksum_to_specific_version.at(header_checksum); +} + +template <> +const char* phosg::name_for_enum( + ClientFunctionIndex::Function::Visibility vis) { + switch (vis) { + case ClientFunctionIndex::Function::Visibility::DEBUG_ONLY: + return "DEBUG_ONLY"; + case ClientFunctionIndex::Function::Visibility::CHAT_COMMAND_ONLY_WITH_CHEAT_MODE: + return "CHAT_COMMAND_ONLY_WITH_CHEAT_MODE"; + case ClientFunctionIndex::Function::Visibility::CHAT_COMMAND_ONLY: + return "CHAT_COMMAND_ONLY"; + case ClientFunctionIndex::Function::Visibility::PATCHES_MENU_ONLY: + return "PATCHES_MENU_ONLY"; + case ClientFunctionIndex::Function::Visibility::PATCHES_MENU_AND_CHAT_COMMAND: + return "PATCHES_MENU_AND_CHAT_COMMAND"; + default: + throw std::logic_error("Invalid client function visibility"); + } +} diff --git a/src/ClientFunctionIndex.hh b/src/ClientFunctionIndex.hh new file mode 100644 index 00000000..ddba51b4 --- /dev/null +++ b/src/ClientFunctionIndex.hh @@ -0,0 +1,96 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Menu.hh" + +class ClientFunctionIndex { +public: + struct Function { + enum class Architecture { + UNKNOWN = 0, + POWERPC, // GC + X86, // PC, XB, BB + SH4, // Dreamcast + }; + Architecture arch = Architecture::UNKNOWN; + std::string code; + std::vector relocation_deltas; + std::unordered_map label_offsets; + uint32_t entrypoint_offset_offset = 0; + std::string short_name; // Based on filename + std::string long_name; // From .meta name directive + std::string description; // From .meta description directive + uint64_t client_flag = 0; // From .meta client_flag directive + uint32_t menu_item_id = 0; + enum class Visibility { + DEBUG_ONLY = 0, + CHAT_COMMAND_ONLY_WITH_CHEAT_MODE, + CHAT_COMMAND_ONLY, + PATCHES_MENU_ONLY, + PATCHES_MENU_AND_CHAT_COMMAND, + }; + Visibility visibility; + bool show_return_value = false; + uint32_t specific_version; + + inline bool appears_in_patches_menu() const { + return (this->visibility == Visibility::PATCHES_MENU_ONLY) || + (this->visibility == Visibility::PATCHES_MENU_AND_CHAT_COMMAND); + } + inline bool allowed_via_chat_command(bool cheat_mode_enabled) const { + return (cheat_mode_enabled && (this->visibility == Visibility::CHAT_COMMAND_ONLY_WITH_CHEAT_MODE)) || + (this->visibility == Visibility::CHAT_COMMAND_ONLY) || + (this->visibility == Visibility::PATCHES_MENU_AND_CHAT_COMMAND); + } + + inline bool is_big_endian() const { + return (this->arch == Architecture::POWERPC); + } + + template + std::string generate_client_command_t( + const std::unordered_map& label_writes, + const void* suffix_data = nullptr, + size_t suffix_size = 0, + uint32_t override_relocations_offset = 0) const; + std::string generate_client_command( + const std::unordered_map& label_writes = {}, + const void* suffix_data = nullptr, + size_t suffix_size = 0, + uint32_t override_relocations_offset = 0) const; + }; + + ClientFunctionIndex() = default; + ClientFunctionIndex(const std::string& directory, bool raise_on_any_failure); + + std::unordered_map> all_functions; // Key is "PatchName-SpecificVersion" + std::map>> functions_by_specific_version; + std::map> functions_by_menu_item_id; + + std::shared_ptr patch_switches_menu( + uint32_t specific_version, + const std::unordered_set& server_auto_patches_enabled, + const std::unordered_set& client_auto_patches_enabled) const; + bool patch_menu_empty(uint32_t specific_version) const; + + std::shared_ptr get(const std::string& name, uint32_t specific_version) const; + std::shared_ptr get(const std::string& name, Function::Architecture arch) const; + std::shared_ptr get(const std::string& name) const; + std::shared_ptr get_by_menu_item_id(uint32_t menu_item_id) const; +}; + +const char* name_for_architecture(ClientFunctionIndex::Function::Architecture arch); +uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum); + +template <> +const char* phosg::name_for_enum( + ClientFunctionIndex::Function::Visibility vis); diff --git a/src/DOLFileIndex.cc b/src/DOLFileIndex.cc new file mode 100644 index 00000000..f7f7f6a6 --- /dev/null +++ b/src/DOLFileIndex.cc @@ -0,0 +1,94 @@ +#include "DOLFileIndex.hh" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "CommandFormats.hh" +#include "CommonFileFormats.hh" +#include "Compression.hh" +#include "Loggers.hh" + +using namespace std; + +DOLFileIndex::DOLFileIndex(const string& directory) { + if (!std::filesystem::is_directory(directory)) { + client_functions_log.info_f("DOL file directory is missing"); + return; + } + + auto menu = make_shared(MenuID::PROGRAMS, "Programs"); + this->menu = menu; + menu->items.emplace_back(ProgramsMenuItemID::GO_BACK, "Go back", "Return to the\nmain menu", 0); + + uint32_t next_menu_item_id = 0; + for (const auto& item : std::filesystem::directory_iterator(directory)) { + string filename = item.path().filename().string(); + bool is_dol = filename.ends_with(".dol"); + bool is_compressed_dol = filename.ends_with(".dol.prs"); + if (!is_dol && !is_compressed_dol) { + continue; + } + string name = filename.substr(0, filename.size() - (is_compressed_dol ? 8 : 4)); + + try { + auto dol = make_shared(); + dol->menu_item_id = next_menu_item_id++; + dol->name = name; + + string path = directory + "/" + filename; + string file_data = phosg::load_file(path); + + string description; + if (is_compressed_dol) { + size_t decompressed_size = prs_decompress_size(file_data); + + phosg::StringWriter w; + w.put_u32b(file_data.size()); + w.put_u32b(decompressed_size); + w.write(file_data); + while (w.size() & 3) { + w.put_u8(0); + } + dol->data = std::move(w.str()); + + string compressed_size_str = phosg::format_size(file_data.size()); + string decompressed_size_str = phosg::format_size(decompressed_size); + client_functions_log.debug_f("Loaded compressed DOL file {} ({} -> {})", + dol->name, compressed_size_str, decompressed_size_str); + description = std::format("$C6{}$C7\n{}\n{} (orig)", dol->name, compressed_size_str, decompressed_size_str); + + } else { + phosg::StringWriter w; + w.put_u32b(0); + w.put_u32b(file_data.size()); + w.write(file_data); + while (w.size() & 3) { + w.put_u8(0); + } + dol->data = std::move(w.str()); + + string size_str = phosg::format_size(dol->data.size()); + client_functions_log.debug_f("Loaded DOL file {} ({})", filename, size_str); + description = std::format("$C6{}$C7\n{}", dol->name, size_str); + } + + this->name_to_file.emplace(dol->name, dol); + this->item_id_to_file.emplace_back(dol); + + menu->items.emplace_back(dol->menu_item_id, dol->name, description, MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE); + + } catch (const exception& e) { + client_functions_log.warning_f("Failed to load DOL file {}: {}", filename, e.what()); + } + } +} diff --git a/src/DOLFileIndex.hh b/src/DOLFileIndex.hh new file mode 100644 index 00000000..2dad09c7 --- /dev/null +++ b/src/DOLFileIndex.hh @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include "Menu.hh" + +struct DOLFileIndex { + struct File { + uint32_t menu_item_id; + std::string name; + std::string data; + bool is_compressed; + }; + + std::vector> item_id_to_file; + std::unordered_map> name_to_file; + std::shared_ptr menu; + + DOLFileIndex() = default; + explicit DOLFileIndex(const std::string& directory); + + inline bool empty() const { + return this->name_to_file.empty() && this->item_id_to_file.empty(); + } +}; diff --git a/src/FunctionCompiler.cc b/src/FunctionCompiler.cc deleted file mode 100644 index cf1e7446..00000000 --- a/src/FunctionCompiler.cc +++ /dev/null @@ -1,612 +0,0 @@ -#include "FunctionCompiler.hh" - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "CommandFormats.hh" -#include "CommonFileFormats.hh" -#include "Compression.hh" -#include "Loggers.hh" - -using namespace std; - -const char* name_for_architecture(CompiledFunctionCode::Architecture arch) { - switch (arch) { - case CompiledFunctionCode::Architecture::POWERPC: - return "PowerPC"; - case CompiledFunctionCode::Architecture::X86: - return "x86"; - case CompiledFunctionCode::Architecture::SH4: - return "SH-4"; - default: - throw logic_error("invalid architecture"); - } -} - -template -string CompiledFunctionCode::generate_client_command_t( - const unordered_map& label_writes, - const void* suffix_data, - size_t suffix_size, - uint32_t override_relocations_offset) const { - using FooterT = RELFileFooterT; - - FooterT footer; - footer.num_relocations = this->relocation_deltas.size(); - footer.unused1.clear(0); - footer.root_offset = this->entrypoint_offset_offset; - footer.unused2.clear(0); - - phosg::StringWriter w; - if (!label_writes.empty()) { - string modified_code = this->code; - for (const auto& it : label_writes) { - size_t offset = this->label_offsets.at(it.first); - if (offset > modified_code.size() - 4) { - throw runtime_error("label out of range"); - } - *reinterpret_cast*>(modified_code.data() + offset) = it.second; - } - w.write(modified_code); - } else { - w.write(this->code); - } - if (suffix_size) { - w.write(suffix_data, suffix_size); - } - while (w.size() & 3) { - w.put_u8(0); - } - - footer.relocations_offset = w.size(); - - // Always write at least 4 bytes even if there are no relocations - if (this->relocation_deltas.empty()) { - w.put_u32(0); - } - - if (override_relocations_offset) { - footer.relocations_offset = override_relocations_offset; - } else { - for (uint16_t delta : this->relocation_deltas) { - w.put>(delta); - } - if (this->relocation_deltas.size() & 1) { - w.put_u16(0); - } - } - - w.put(footer); - return std::move(w.str()); -} - -string CompiledFunctionCode::generate_client_command( - const unordered_map& label_writes, - const void* suffix_data, - size_t suffix_size, - uint32_t override_relocations_offset) const { - if (this->arch == Architecture::POWERPC) { - return this->generate_client_command_t(label_writes, suffix_data, suffix_size, override_relocations_offset); - } else if ((this->arch == Architecture::X86) || (this->arch == Architecture::SH4)) { - return this->generate_client_command_t(label_writes, suffix_data, suffix_size, override_relocations_offset); - } else { - throw logic_error("invalid architecture"); - } -} - -bool CompiledFunctionCode::is_big_endian() const { - return (this->arch == Architecture::POWERPC); -} - -static unordered_map preprocess_function_code(const std::string& text) { - auto parse_specific_version_list = +[](std::string&& text) -> vector { - phosg::strip_whitespace(text); - vector ret; - for (auto& vers_token : phosg::split(text, ' ')) { - phosg::strip_whitespace(vers_token); - if (vers_token.empty()) { - continue; - } - if (vers_token.size() != 4) { - throw std::runtime_error("invalid specific_version: " + vers_token); - } - ret.emplace_back(*reinterpret_cast(vers_token.data())); - } - return ret; - }; - - // Find a .versions directive and populate specific_versions - vector specific_versions; - auto lines = phosg::split(text, '\n'); - for (auto& line : lines) { - if (line.starts_with(".versions ")) { - if (!specific_versions.empty()) { - throw std::runtime_error("multiple .versions directives in file"); - } - specific_versions = parse_specific_version_list(line.substr(10)); - if (specific_versions.empty()) { - throw std::runtime_error(".versions directive does not specify any versions"); - } - line.clear(); - } - } - - // If there's no .versions directive, just return the text as-is - if (specific_versions.empty()) { - return {{0, std::move(text)}}; - } - - vector> version_lines; - version_lines.resize(specific_versions.size()); - - size_t line_num = 1; - vector current_only_versions; - unordered_set current_only_versions_set; - auto add_blank_line = [&]() -> void { - for (size_t vers_index = 0; vers_index < specific_versions.size(); vers_index++) { - version_lines[vers_index].emplace_back(""); - } - }; - for (auto& line : lines) { - phosg::strip_whitespace(line); - if (line.starts_with(".only_versions ")) { - current_only_versions = parse_specific_version_list(line.substr(15)); - current_only_versions_set.clear(); - for (uint32_t specific_version : current_only_versions) { - current_only_versions_set.emplace(specific_version); - } - add_blank_line(); - - } else if (line == ".all_versions") { - current_only_versions.clear(); - current_only_versions_set.clear(); - add_blank_line(); - - } else { - size_t vers_offset = line.find("', vers_offset + 6); - if (end_offset == string::npos) { - throw runtime_error(std::format("(line {}) unterminated replacement", line_num)); - } - auto tokens = phosg::split(version_line.substr(vers_offset + 6, end_offset - vers_offset - 6), ' '); - if (tokens.size() <= token_index) { - throw runtime_error(std::format("(line {}) invalid replacement", line_num)); - } - version_line = version_line.substr(0, vers_offset) + tokens.at(token_index) + version_line.substr(end_offset + 1); - vers_offset = version_line.find(" ret; - for (size_t z = 0; z < specific_versions.size(); z++) { - ret.emplace(specific_versions[z], phosg::join(version_lines.at(z), "\n")); - } - return ret; -} - -static vector> compile_function_code( - CompiledFunctionCode::Architecture arch, - const string& function_directory, - const string& system_directory, - const string& name, - const string& text, - bool raise_on_any_failure) { - unordered_set get_include_stack; - function get_include = [&](const string& name) -> string { - const char* arch_name_token; - switch (arch) { - case CompiledFunctionCode::Architecture::POWERPC: - arch_name_token = "ppc"; - break; - case CompiledFunctionCode::Architecture::X86: - arch_name_token = "x86"; - break; - case CompiledFunctionCode::Architecture::SH4: - arch_name_token = "sh4"; - break; - default: - throw runtime_error("unknown architecture"); - } - - // Look in the function directory first, then the system directory - string asm_filename = std::format("{}/{}.{}.inc.s", function_directory, name, arch_name_token); - if (!std::filesystem::is_regular_file(asm_filename)) { - asm_filename = std::format("{}/{}.{}.inc.s", system_directory, name, arch_name_token); - } - if (std::filesystem::is_regular_file(asm_filename)) { - if (!get_include_stack.emplace(name).second) { - throw runtime_error("mutual recursion between includes: " + name); - } - ResourceDASM::EmulatorBase::AssembleResult ret; - switch (arch) { - case CompiledFunctionCode::Architecture::POWERPC: - ret = ResourceDASM::PPC32Emulator::assemble(phosg::load_file(asm_filename), get_include); - break; - case CompiledFunctionCode::Architecture::X86: - ret = ResourceDASM::X86Emulator::assemble(phosg::load_file(asm_filename), get_include); - break; - case CompiledFunctionCode::Architecture::SH4: - ret = ResourceDASM::SH4Emulator::assemble(phosg::load_file(asm_filename), get_include); - break; - default: - throw runtime_error("unknown architecture"); - } - get_include_stack.erase(name); - return ret.code; - } - - string bin_filename = function_directory + "/" + name + ".inc.bin"; - if (std::filesystem::is_regular_file(bin_filename)) { - return phosg::load_file(bin_filename); - } - bin_filename = system_directory + "/" + name + ".inc.bin"; - if (std::filesystem::is_regular_file(bin_filename)) { - return phosg::load_file(bin_filename); - } - throw runtime_error("data not found for include: " + name + " (from " + asm_filename + " or " + bin_filename + ")"); - }; - - auto version_texts = preprocess_function_code(text); - - vector> ret; - for (const auto& [specific_version, version_text] : version_texts) { - try { - ResourceDASM::EmulatorBase::AssembleResult assembled; - if (arch == CompiledFunctionCode::Architecture::POWERPC) { - assembled = ResourceDASM::PPC32Emulator::assemble(version_text, get_include); - } else if (arch == CompiledFunctionCode::Architecture::X86) { - assembled = ResourceDASM::X86Emulator::assemble(version_text, get_include); - } else if (arch == CompiledFunctionCode::Architecture::SH4) { - assembled = ResourceDASM::SH4Emulator::assemble(version_text, get_include); - } else { - throw runtime_error("invalid architecture"); - } - - auto compiled = ret.emplace_back(make_shared()); - compiled->arch = arch; - compiled->short_name = name; - compiled->specific_version = specific_version; - compiled->code = std::move(assembled.code); - compiled->label_offsets = std::move(assembled.label_offsets); - for (const auto& it : assembled.metadata_keys) { - if (it.first == "hide_from_patches_menu") { - compiled->hide_from_patches_menu = true; - } else if (it.first == "name") { - compiled->long_name = it.second; - } else if (it.first == "description") { - compiled->description = it.second; - } else if (it.first == "client_flag") { - compiled->client_flag = stoull(it.second, nullptr, 0); - } else if (it.first == "show_return_value") { - compiled->show_return_value = true; - } else { - throw runtime_error("unknown metadata key: " + it.first); - } - } - - set reloc_indexes; - for (const auto& it : compiled->label_offsets) { - if (it.first.starts_with("reloc")) { - reloc_indexes.emplace(it.second / 4); - } - } - - try { - compiled->entrypoint_offset_offset = compiled->label_offsets.at("entry_ptr"); - } catch (const out_of_range&) { - throw runtime_error("code does not contain entry_ptr label"); - } - - uint32_t prev_index = 0; - for (const auto& it : reloc_indexes) { - uint32_t delta = it - prev_index; - if (delta > 0xFFFF) { - throw runtime_error("relocation delta too far away"); - } - compiled->relocation_deltas.emplace_back(delta); - prev_index = it; - } - - } catch (const exception& e) { - string version_str = specific_version ? (" (" + str_for_specific_version(specific_version) + ")") : ""; - if (raise_on_any_failure) { - throw; - } - function_compiler_log.warning_f("Failed to compile function {}{}: {}", name, version_str, e.what()); - } - } - - return ret; -} - -FunctionCodeIndex::FunctionCodeIndex(const string& directory, bool raise_on_any_failure) { - string system_dir_path = directory.ends_with("/") ? (directory + "System") : (directory + "/System"); - - uint32_t next_menu_item_id = 1; - for (const auto& item : std::filesystem::directory_iterator(directory)) { - string subdir_name = item.path().filename().string(); - string subdir_path = directory.ends_with("/") ? (directory + subdir_name) : (directory + "/" + subdir_name); - - auto add_file = [&](string filename) -> void { - try { - if (!filename.ends_with(".s")) { - return; - } - - string name = filename.substr(0, filename.size() - 2); - if (name.ends_with(".inc")) { - return; - } - - bool is_patch = name.ends_with(".patch"); - if (is_patch) { - name.resize(name.size() - 6); - } - - // Figure out the version or specific_version - CompiledFunctionCode::Architecture arch = CompiledFunctionCode::Architecture::UNKNOWN; - uint32_t specific_version = 0; - string short_name = name; - if (name.ends_with(".ppc")) { - arch = CompiledFunctionCode::Architecture::POWERPC; - name.resize(name.size() - 4); - short_name = name; - } else if (name.ends_with(".x86")) { - arch = CompiledFunctionCode::Architecture::X86; - name.resize(name.size() - 4); - short_name = name; - } else if (name.ends_with(".sh4")) { - arch = CompiledFunctionCode::Architecture::SH4; - name.resize(name.size() - 4); - short_name = name; - } else if (is_patch && (name.size() >= 5) && (name[name.size() - 5] == '.')) { - specific_version = (name[name.size() - 4] << 24) | (name[name.size() - 3] << 16) | (name[name.size() - 2] << 8) | name[name.size() - 1]; - if (specific_version_is_dc(specific_version)) { - arch = CompiledFunctionCode::Architecture::SH4; - } else if (specific_version_is_gc(specific_version)) { - arch = CompiledFunctionCode::Architecture::POWERPC; - } else if (specific_version_is_pc_v2(specific_version) || - specific_version_is_xb(specific_version) || - specific_version_is_bb(specific_version)) { - arch = CompiledFunctionCode::Architecture::X86; - } else { - throw runtime_error("unable to determine architecture from specific_version"); - } - short_name = name.substr(0, name.size() - 5); - } - - if (arch == CompiledFunctionCode::Architecture::UNKNOWN) { - throw runtime_error("unable to determine architecture"); - } - - string path = subdir_path + "/" + filename; - string text = phosg::load_file(path); - for (auto code : compile_function_code(arch, subdir_path, system_dir_path, name, text, raise_on_any_failure)) { - if (code->specific_version == 0) { - code->specific_version = specific_version; - } - code->source_path = path; - code->short_name = short_name; - this->name_to_function.emplace(name, code); - if (is_patch) { - code->menu_item_id = next_menu_item_id++; - this->menu_item_id_and_specific_version_to_patch_function.emplace( - static_cast(code->menu_item_id) << 32 | code->specific_version, code); - this->name_and_specific_version_to_patch_function.emplace( - std::format("{}-{:08X}", code->short_name, code->specific_version), code); - } - - string patch_prefix = is_patch ? std::format("[{:08X}] ", code->menu_item_id) : ""; - function_compiler_log.debug_f("Compiled function {}{} ({}; {})", - patch_prefix, name, str_for_specific_version(code->specific_version), name_for_architecture(code->arch)); - } - - } catch (const exception& e) { - if (raise_on_any_failure) { - throw runtime_error(format("({}) {}", filename, e.what())); - } - function_compiler_log.warning_f("Failed to compile function {}: {}", filename, e.what()); - } - }; - - if (std::filesystem::is_regular_file(subdir_path)) { - add_file(subdir_path); - } else if (std::filesystem::is_directory(subdir_path)) { - for (const auto& item : std::filesystem::directory_iterator(subdir_path)) { - string filename = item.path().filename().string(); - add_file(filename); - } - } else { - function_compiler_log.warning_f("Skipping {} (unknown file type)", subdir_name); - continue; - } - } -} - -shared_ptr FunctionCodeIndex::patch_switches_menu( - uint32_t specific_version, - const std::unordered_set& server_auto_patches_enabled, - const std::unordered_set& client_auto_patches_enabled) const { - auto suffix = std::format("-{:08X}", specific_version); - - auto ret = make_shared(MenuID::PATCH_SWITCHES, "Patches"); - ret->items.emplace_back(PatchesMenuItemID::GO_BACK, "Go back", "Return to the\nmain menu", 0); - for (const auto& it : this->name_and_specific_version_to_patch_function) { - const auto& fn = it.second; - if (fn->hide_from_patches_menu || !it.first.ends_with(suffix) || server_auto_patches_enabled.count(fn->short_name)) { - continue; - } - string name; - name.push_back(client_auto_patches_enabled.count(fn->short_name) ? '*' : '-'); - name += fn->long_name.empty() ? fn->short_name : fn->long_name; - ret->items.emplace_back(fn->menu_item_id, name, fn->description, MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE); - } - return ret; -} - -bool FunctionCodeIndex::patch_menu_empty(uint32_t specific_version) const { - uint32_t mask = specific_version_is_indeterminate(specific_version) ? 0xFF000000 : 0xFFFFFFFF; - for (const auto& it : this->menu_item_id_and_specific_version_to_patch_function) { - if ((it.first & mask) == (specific_version & mask)) { - return false; - } - } - return true; -} - -std::shared_ptr FunctionCodeIndex::get_patch( - const std::string& name, uint32_t specific_version) const { - return this->name_and_specific_version_to_patch_function.at(std::format("{}-{:08X}", name, specific_version)); -} - -DOLFileIndex::DOLFileIndex(const string& directory) { - if (!std::filesystem::is_directory(directory)) { - function_compiler_log.info_f("DOL file directory is missing"); - return; - } - - auto menu = make_shared(MenuID::PROGRAMS, "Programs"); - this->menu = menu; - menu->items.emplace_back(ProgramsMenuItemID::GO_BACK, "Go back", "Return to the\nmain menu", 0); - - uint32_t next_menu_item_id = 0; - for (const auto& item : std::filesystem::directory_iterator(directory)) { - string filename = item.path().filename().string(); - bool is_dol = filename.ends_with(".dol"); - bool is_compressed_dol = filename.ends_with(".dol.prs"); - if (!is_dol && !is_compressed_dol) { - continue; - } - string name = filename.substr(0, filename.size() - (is_compressed_dol ? 8 : 4)); - - try { - auto dol = make_shared(); - dol->menu_item_id = next_menu_item_id++; - dol->name = name; - - string path = directory + "/" + filename; - string file_data = phosg::load_file(path); - - string description; - if (is_compressed_dol) { - size_t decompressed_size = prs_decompress_size(file_data); - - phosg::StringWriter w; - w.put_u32b(file_data.size()); - w.put_u32b(decompressed_size); - w.write(file_data); - while (w.size() & 3) { - w.put_u8(0); - } - dol->data = std::move(w.str()); - - string compressed_size_str = phosg::format_size(file_data.size()); - string decompressed_size_str = phosg::format_size(decompressed_size); - function_compiler_log.debug_f("Loaded compressed DOL file {} ({} -> {})", - dol->name, compressed_size_str, decompressed_size_str); - description = std::format("$C6{}$C7\n{}\n{} (orig)", dol->name, compressed_size_str, decompressed_size_str); - - } else { - phosg::StringWriter w; - w.put_u32b(0); - w.put_u32b(file_data.size()); - w.write(file_data); - while (w.size() & 3) { - w.put_u8(0); - } - dol->data = std::move(w.str()); - - string size_str = phosg::format_size(dol->data.size()); - function_compiler_log.debug_f("Loaded DOL file {} ({})", filename, size_str); - description = std::format("$C6{}$C7\n{}", dol->name, size_str); - } - - this->name_to_file.emplace(dol->name, dol); - this->item_id_to_file.emplace_back(dol); - - menu->items.emplace_back(dol->menu_item_id, dol->name, description, MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE); - - } catch (const exception& e) { - function_compiler_log.warning_f("Failed to load DOL file {}: {}", filename, e.what()); - } - } -} - -uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum) { - static unordered_map checksum_to_specific_version; - if (checksum_to_specific_version.empty()) { - struct { - char system_code = 'G'; - char game_code1 = 'P'; - char game_code2; - char region_code; - char developer_code1 = '8'; - char developer_code2 = 'P'; - uint8_t disc_number = 0; - uint8_t version_code; - } __attribute__((packed)) data; - for (const char* game_code2 = "OS"; *game_code2; game_code2++) { - data.game_code2 = *game_code2; - for (const char* region_code = "JEP"; *region_code; region_code++) { - data.region_code = *region_code; - for (uint8_t version_code = 0; version_code < 8; version_code++) { - data.version_code = version_code; - uint32_t checksum = phosg::crc32(&data, sizeof(data)); - uint32_t specific_version = 0x33000030 | (*game_code2 << 16) | (*region_code << 8) | version_code; - if (!checksum_to_specific_version.emplace(checksum, specific_version).second) { - throw logic_error("multiple specific_versions have same header checksum"); - } - } - } - { - // Generate entries for Trial Editions - data.region_code = 'J'; - data.system_code = 'D'; - data.version_code = 0; - uint32_t checksum = phosg::crc32(&data, sizeof(data)); - uint32_t specific_version = 0x33004A54 | (*game_code2 << 16); - if (!checksum_to_specific_version.emplace(checksum, specific_version).second) { - throw logic_error("multiple specific_versions have same header checksum"); - } - data.system_code = 'G'; - } - } - } - return checksum_to_specific_version.at(header_checksum); -} diff --git a/src/FunctionCompiler.hh b/src/FunctionCompiler.hh deleted file mode 100644 index 04e32663..00000000 --- a/src/FunctionCompiler.hh +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#include "Menu.hh" - -// TODO: Support x86 and SH4 function calls in the future. Currently we only -// support PPC32 because I haven't written an appropriate x86 assembler yet. - -struct CompiledFunctionCode { - enum class Architecture { - UNKNOWN = 0, - POWERPC, // GC - X86, // PC, XB, BB - SH4, // Dreamcast - }; - Architecture arch; - std::string code; - std::vector relocation_deltas; - std::unordered_map label_offsets; - uint32_t entrypoint_offset_offset = 0; - std::string source_path; // Path to source file from newserv root - std::string short_name; // Based on filename - std::string long_name; // From .meta name directive - std::string description; // From .meta description directive - uint64_t client_flag = 0; // From .meta client_flag directive - uint32_t menu_item_id = 0; - bool hide_from_patches_menu = false; - bool show_return_value = false; - uint32_t specific_version = 0; // 0 = not a client-selectable patch - - bool is_big_endian() const; - - template - std::string generate_client_command_t( - const std::unordered_map& label_writes, - const void* suffix_data = nullptr, - size_t suffix_size = 0, - uint32_t override_relocations_offset = 0) const; - std::string generate_client_command( - const std::unordered_map& label_writes = {}, - const void* suffix_data = nullptr, - size_t suffix_size = 0, - uint32_t override_relocations_offset = 0) const; -}; - -const char* name_for_architecture(CompiledFunctionCode::Architecture arch); - -struct FunctionCodeIndex { - FunctionCodeIndex() = default; - FunctionCodeIndex(const std::string& directory, bool raise_on_any_failure); - - std::unordered_map> name_to_function; - std::unordered_map> index_to_function; - std::unordered_map> menu_item_id_and_specific_version_to_patch_function; - // Key here is e.g. "PATCHNAME-SPECIFICVERSION", with the latter in hex - std::map> name_and_specific_version_to_patch_function; - - std::shared_ptr patch_switches_menu( - uint32_t specific_version, - const std::unordered_set& server_auto_patches_enabled, - const std::unordered_set& client_auto_patches_enabled) const; - bool patch_menu_empty(uint32_t specific_version) const; - - std::shared_ptr get_patch(const std::string& name, uint32_t specific_version) const; -}; - -struct DOLFileIndex { - struct File { - uint32_t menu_item_id; - std::string name; - std::string data; - bool is_compressed; - }; - - std::vector> item_id_to_file; - std::unordered_map> name_to_file; - std::shared_ptr menu; - - DOLFileIndex() = default; - explicit DOLFileIndex(const std::string& directory); - - inline bool empty() const { - return this->name_to_file.empty() && this->item_id_to_file.empty(); - } -}; - -uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum); diff --git a/src/Loggers.cc b/src/Loggers.cc index 52d26857..28f36338 100644 --- a/src/Loggers.cc +++ b/src/Loggers.cc @@ -5,11 +5,11 @@ using namespace std; phosg::PrefixedLogger channel_exceptions_log("[Channel] ", phosg::LogLevel::L_USE_DEFAULT); +phosg::PrefixedLogger client_functions_log("[ClientFunctionIndex] ", phosg::LogLevel::L_USE_DEFAULT); phosg::PrefixedLogger client_log("", phosg::LogLevel::L_USE_DEFAULT); phosg::PrefixedLogger command_data_log("[Commands] ", phosg::LogLevel::L_USE_DEFAULT); phosg::PrefixedLogger config_log("[Config] ", phosg::LogLevel::L_USE_DEFAULT); phosg::PrefixedLogger dns_server_log("[DNSServer] ", phosg::LogLevel::L_USE_DEFAULT); -phosg::PrefixedLogger function_compiler_log("[FunctionCompiler] ", phosg::LogLevel::L_USE_DEFAULT); phosg::PrefixedLogger ip_stack_simulator_log("[IPStackSimulator] ", phosg::LogLevel::L_USE_DEFAULT); phosg::PrefixedLogger lobby_log("", phosg::LogLevel::L_USE_DEFAULT); phosg::PrefixedLogger patch_index_log("[PatchFileIndex] ", phosg::LogLevel::L_USE_DEFAULT); @@ -30,11 +30,11 @@ static void set_log_level_from_json( void set_all_log_levels(phosg::LogLevel level) { channel_exceptions_log.min_level = level; + client_functions_log.min_level = level; client_log.min_level = level; command_data_log.min_level = level; config_log.min_level = level; dns_server_log.min_level = level; - function_compiler_log.min_level = level; ip_stack_simulator_log.min_level = level; lobby_log.min_level = level; patch_index_log.min_level = level; @@ -47,11 +47,11 @@ void set_all_log_levels(phosg::LogLevel level) { void set_log_levels_from_json(const phosg::JSON& json) { set_log_level_from_json(channel_exceptions_log, json, "ChannelExceptions"); + set_log_level_from_json(client_functions_log, json, "ClientFunctionIndex"); set_log_level_from_json(client_log, json, "Clients"); set_log_level_from_json(command_data_log, json, "CommandData"); set_log_level_from_json(config_log, json, "Config"); set_log_level_from_json(dns_server_log, json, "DNSServer"); - set_log_level_from_json(function_compiler_log, json, "FunctionCompiler"); set_log_level_from_json(ip_stack_simulator_log, json, "IPStackSimulator"); set_log_level_from_json(lobby_log, json, "Lobbies"); set_log_level_from_json(patch_index_log, json, "PatchFileIndex"); diff --git a/src/Loggers.hh b/src/Loggers.hh index 79989fda..0f627dd7 100644 --- a/src/Loggers.hh +++ b/src/Loggers.hh @@ -4,11 +4,11 @@ #include extern phosg::PrefixedLogger channel_exceptions_log; +extern phosg::PrefixedLogger client_functions_log; extern phosg::PrefixedLogger client_log; extern phosg::PrefixedLogger command_data_log; extern phosg::PrefixedLogger config_log; extern phosg::PrefixedLogger dns_server_log; -extern phosg::PrefixedLogger function_compiler_log; extern phosg::PrefixedLogger ip_stack_simulator_log; extern phosg::PrefixedLogger lobby_log; extern phosg::PrefixedLogger patch_index_log; diff --git a/src/Main.cc b/src/Main.cc index 0975c864..ccd1827b 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -27,6 +27,7 @@ #include "Compression.hh" #include "DCSerialNumbers.hh" #include "DNSServer.hh" +#include "DOLFileIndex.hh" #include "DownloadSession.hh" #include "GSLArchive.hh" #include "GameServer.hh" @@ -1751,18 +1752,22 @@ Action a_assemble_quest_script( write_output_data(args, result_data.data(), result_data.size(), compress ? "bin" : "bind"); }); -Action a_assemble_all_patches( - "assemble-all-patches", "\ - assemble-all-patches [--skip-encrypted]\n\ +Action a_assemble_all_client_functions( + "assemble-all-client-functions", "\ + assemble-all-client-functions [--skip-encrypted] OUTPUT-DIRECTORY\n\ Assemble all patches in the system/client-functions directory, and produce\n\ - two compiled .bin files for each patch (one unencrypted, for most PSO\n\ + two compiled .bin files for each patch: one unencrypted, for most PSO\n\ versions, and one encrypted, for PSO GC JP v1.4, JP Ep3, and Ep3 Trial\n\ - Edition). The output files are saved in system/client-functions.\n", + Edition. If --skip-encrypted is given, only the unencrypted .bin files are\n\ + created.\n", +[](phosg::Arguments& args) { - auto fci = make_shared("system/client-functions", false); + auto fci = make_shared("system/client-functions", false); + + const std::string& output_dir = args.get(1); + std::filesystem::create_directories(output_dir); bool skip_encrypted = args.get("skip-encrypted"); - auto process_code = [&](shared_ptr code, + auto process_code = [&](shared_ptr code, uint32_t checksum_addr, uint32_t checksum_size, uint32_t override_start_addr) -> void { @@ -1775,34 +1780,18 @@ Action a_assemble_all_patches( code, {}, nullptr, 0, checksum_addr, checksum_size, override_start_addr, encrypted); w.put(PSOCommandHeaderDCV3{.command = 0xB2, .flag = 0x00, .size = data.size() + 4}); w.write(data); - string out_path = std::format("{}.{}.{}.bin", - code->source_path, str_for_specific_version(code->specific_version), (encrypted ? "enc" : "std")); + string out_path = std::format("{}/{}.{}.{}.bin", + output_dir, code->short_name, str_for_specific_version(code->specific_version), (encrypted ? "enc" : "std")); phosg::save_file(out_path, w.str()); phosg::fwrite_fmt(stderr, "... {}\n", out_path); } }; - for (const auto& it : fci->name_and_specific_version_to_patch_function) { - process_code(it.second, 0, 0, 0); + for (const auto& [_, fn] : fci->all_functions) { + process_code(fn, 0, 0, 0); } try { - process_code(fci->name_to_function.at("VersionDetectDC"), 0, 0, 0); - } catch (const out_of_range&) { - } - try { - process_code(fci->name_to_function.at("VersionDetectGC"), 0, 0, 0); - } catch (const out_of_range&) { - } - try { - process_code(fci->name_to_function.at("VersionDetectXB"), 0, 0, 0); - } catch (const out_of_range&) { - } - try { - process_code(fci->name_to_function.at("CacheClearFix-Phase1"), 0x80000000, 8, 0x7F2734EC); - } catch (const out_of_range&) { - } - try { - process_code(fci->name_to_function.at("CacheClearFix-Phase2"), 0, 0, 0); + process_code(fci->get("CacheClearFix-Phase1", SPECIFIC_VERSION_PPC_INDETERMINATE), 0x80000000, 8, 0x7F2734EC); } catch (const out_of_range&) { } }); @@ -3685,7 +3674,7 @@ Action a_check_client_functions( "check-client-functions", nullptr, +[](phosg::Arguments&) { set_all_log_levels(phosg::LogLevel::L_DEBUG); - FunctionCodeIndex fci("system/client-functions", true); + ClientFunctionIndex index("system/client-functions", true); phosg::fwrite_fmt(stdout, "All client functions compiled\n"); }); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index fa20086c..a6f41b10 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -132,7 +132,7 @@ static void send_main_menu(shared_ptr c) { main_menu->items.emplace_back(MainMenuItemID::DOWNLOAD_QUESTS, "Download quests", "Download quests", MenuItem::Flag::INVISIBLE_ON_DC_PROTOS | MenuItem::Flag::INVISIBLE_ON_PC_NTE | MenuItem::Flag::INVISIBLE_ON_BB); - if (!s->function_code_index->patch_menu_empty(c->specific_version)) { + if (!s->client_functions->patch_menu_empty(c->specific_version)) { main_menu->items.emplace_back(MainMenuItemID::PATCH_SWITCHES, "Patches", "Change game\nbehaviors", MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE); } @@ -240,14 +240,14 @@ static asio::awaitable send_auto_patches_if_needed(shared_ptr c) { c->set_flag(Client::Flag::HAS_AUTO_PATCHES); co_await prepare_client_for_patches(c); - unordered_set> functions_to_send; + unordered_set> functions_to_send; if (c->version() == Version::BB_V4) { for (const auto& patch_name : s->bb_required_patches) { try { - functions_to_send.emplace(s->function_code_index->get_patch(patch_name, c->specific_version)); + functions_to_send.emplace(s->client_functions->get(patch_name, c->specific_version)); } catch (const out_of_range&) { string message = std::format( - "Your client is not compatible with a\nrequired patch on this server.\n\nClient version: {:08X}\nPatch name: {}", c->specific_version, patch_name); + "Your client is not compatible with a\nrequired patch on this server.\n\nClient version: {}\nPatch name: {}", str_for_specific_version(c->specific_version), patch_name); send_message_box(c, message); c->channel->disconnect(); co_return; @@ -256,18 +256,18 @@ static asio::awaitable send_auto_patches_if_needed(shared_ptr c) { } for (const auto& patch_name : s->auto_patches) { try { - functions_to_send.emplace(s->function_code_index->get_patch(patch_name, c->specific_version)); + functions_to_send.emplace(s->client_functions->get(patch_name, c->specific_version)); } catch (const out_of_range&) { - c->log.warning_f("Server has auto patch {} enabled, but it is not available for specific_version {:08X}", - patch_name, c->specific_version); + c->log.warning_f("Server has auto patch {} enabled, but it is not available for specific_version {}", + patch_name, str_for_specific_version(c->specific_version)); } } for (const auto& patch_name : c->login->account->auto_patches_enabled) { try { - functions_to_send.emplace(s->function_code_index->get_patch(patch_name, c->specific_version)); + functions_to_send.emplace(s->client_functions->get(patch_name, c->specific_version)); } catch (const out_of_range&) { - c->log.warning_f("Client has auto patch {} enabled, but it is not available for specific_version {:08X}", - patch_name, c->specific_version); + c->log.warning_f("Client has auto patch {} enabled, but it is not available for specific_version {}", + patch_name, str_for_specific_version(c->specific_version)); } } @@ -1319,7 +1319,7 @@ static asio::awaitable on_9D_9E(shared_ptr c, Channel::Message& ms // not; we'll call on_login_complete once we receive the B3 response if (c->version() == Version::PC_V2) { try { - auto code = s->function_code_index->name_to_function.at("ReturnTokenX86"); + auto code = s->client_functions->get("ReturnToken", SPECIFIC_VERSION_X86_INDETERMINATE); unordered_map label_writes{{"token", c->login->account->account_id}}; auto resp = co_await send_function_call(c, code, label_writes, nullptr, 0, 0x00400000, 0x0000E000, 0, true); @@ -1342,7 +1342,8 @@ static asio::awaitable on_9D_9E(shared_ptr c, Channel::Message& ms c->specific_version = SPECIFIC_VERSION_PC_V2_INDETERMINATE; c->log.info_f("Version cannot be determined from PE header checksum {:08X}", resp.checksum); } - } catch (const out_of_range&) { + } catch (const out_of_range& e) { + c->log.info_f("Cannot determine PSO PC specific version: {}", e.what()); } } co_await on_login_complete(c); @@ -2560,7 +2561,7 @@ static asio::awaitable on_10_main_menu(shared_ptr c, uint32_t item // We have to prepare the client for patches here, even though we don't send them from this mennu, because we // need to know the client's specific_version before sending the menu. co_await prepare_client_for_patches(c); - send_menu(c, c->require_server_state()->function_code_index->patch_switches_menu(c->specific_version, s->auto_patches, c->login->account->auto_patches_enabled)); + send_menu(c, c->require_server_state()->client_functions->patch_switches_menu(c->specific_version, s->auto_patches, c->login->account->auto_patches_enabled)); break; } @@ -2914,13 +2915,12 @@ static void on_10_patch_switches(shared_ptr c, uint32_t item_id) { } auto s = c->require_server_state(); - uint64_t key = (static_cast(item_id) << 32) | c->specific_version; - auto fn = s->function_code_index->menu_item_id_and_specific_version_to_patch_function.at(key); + auto fn = s->client_functions->get_by_menu_item_id(item_id); if (!c->login->account->auto_patches_enabled.emplace(fn->short_name).second) { c->login->account->auto_patches_enabled.erase(fn->short_name); } c->login->account->save(); - send_menu(c, s->function_code_index->patch_switches_menu(c->specific_version, s->auto_patches, c->login->account->auto_patches_enabled)); + send_menu(c, s->client_functions->patch_switches_menu(c->specific_version, s->auto_patches, c->login->account->auto_patches_enabled)); } } diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 5708ef40..6c5256a2 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -335,7 +335,7 @@ asio::awaitable prepare_client_for_patches(shared_ptr c) { auto s = c->require_server_state(); if (!c->check_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH)) { - auto fn = s->function_code_index->name_to_function.at("CacheClearFix-Phase1"); + auto fn = s->client_functions->get("CacheClearFix-Phase1", ClientFunctionIndex::Function::Architecture::POWERPC); unordered_map label_writes; auto call1_res = co_await send_function_call(c, fn, label_writes, nullptr, 0, 0x80000000, 8, 0x7F2734EC); try { @@ -344,29 +344,29 @@ asio::awaitable prepare_client_for_patches(shared_ptr c) { } catch (const out_of_range&) { c->log.info_f("Could not detect specific version from header checksum {:08X}", call1_res.checksum); } - co_await send_function_call(c, s->function_code_index->name_to_function.at("CacheClearFix-Phase2")); + co_await send_function_call(c, s->client_functions->get("CacheClearFix-Phase2", ClientFunctionIndex::Function::Architecture::POWERPC)); c->log.info_f("Client cache behavior patched"); c->set_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH); } - const char* version_detect_name = nullptr; + ClientFunctionIndex::Function::Architecture arch = ClientFunctionIndex::Function::Architecture::UNKNOWN; if (c->version() == Version::DC_V2) { - version_detect_name = "VersionDetectDC"; + arch = ClientFunctionIndex::Function::Architecture::SH4; } else if (is_gc(c->version())) { - version_detect_name = "VersionDetectGC"; + arch = ClientFunctionIndex::Function::Architecture::POWERPC; } else if (c->version() == Version::XB_V3) { - version_detect_name = "VersionDetectXB"; + arch = ClientFunctionIndex::Function::Architecture::X86; } - if (version_detect_name && specific_version_is_indeterminate(c->specific_version)) { - auto vers_detect_res = co_await send_function_call( - c, s->function_code_index->name_to_function.at(version_detect_name)); + if ((arch != ClientFunctionIndex::Function::Architecture::UNKNOWN) && + specific_version_is_indeterminate(c->specific_version)) { + auto vers_detect_res = co_await send_function_call(c, s->client_functions->get("VersionDetect", arch)); c->specific_version = vers_detect_res.return_value; c->log.info_f("Version detected as {:08X}", c->specific_version); } } string prepare_send_function_call_data( - shared_ptr code, + shared_ptr code, const unordered_map& label_writes, const void* suffix_data, size_t suffix_size, @@ -416,7 +416,7 @@ string prepare_send_function_call_data( asio::awaitable send_function_call( shared_ptr c, - shared_ptr code, + shared_ptr code, const unordered_map& label_writes, const void* suffix_data, size_t suffix_size, @@ -445,7 +445,7 @@ asio::awaitable send_function_call( } asio::awaitable send_function_call_multi( - shared_ptr c, unordered_set> codes) { + shared_ptr c, unordered_set> codes) { if (codes.empty()) { co_return; } @@ -468,7 +468,7 @@ asio::awaitable send_function_call_multi( void send_function_call( shared_ptr ch, uint64_t client_enabled_flags, - shared_ptr code, + shared_ptr code, const unordered_map& label_writes, const void* suffix_data, size_t suffix_size, @@ -525,7 +525,7 @@ asio::awaitable send_protected_command(std::shared_ptr c, const vo co_await prepare_client_for_patches(c); try { - auto fn = s->function_code_index->get_patch("CallProtectedHandler", c->specific_version); + auto fn = s->client_functions->get("CallProtectedHandler", c->specific_version); unordered_map label_writes{{"size", size}}; co_await send_function_call(c, fn, label_writes, data, size); auto l = echo_to_lobby ? c->lobby.lock() : nullptr; @@ -549,7 +549,7 @@ asio::awaitable send_dol_file(shared_ptr c, shared_ptr label_writes{{"address", 0x80000034}}; // ArenaHigh from GC globals auto addr_ret = co_await send_function_call( - c, s->function_code_index->name_to_function.at("ReadMemoryWordGC"), label_writes); + c, s->client_functions->get("ReadMemoryWord", c->specific_version), label_writes); uint32_t dol_base_addr = (addr_ret.return_value - dol->data.size()) & (~3); // Write the file in multiple chunks @@ -561,7 +561,7 @@ asio::awaitable send_dol_file(shared_ptr c, shared_ptrdata.substr(offset, bytes_to_send); auto s = c->require_server_state(); - auto fn = s->function_code_index->name_to_function.at("WriteMemoryGC"); + auto fn = s->client_functions->get("WriteMemory", c->specific_version); label_writes = {{"dest_addr", (dol_base_addr + offset)}, {"size", bytes_to_send}}; co_await send_function_call(c, fn, label_writes, data_to_send.data(), data_to_send.size()); @@ -572,7 +572,7 @@ asio::awaitable send_dol_file(shared_ptr c, shared_ptrfunction_code_index->name_to_function.at("RunDOL"); + auto fn = s->client_functions->get("RunDOL", c->specific_version); label_writes = {{"dol_base_ptr", dol_base_addr}}; co_await send_function_call(c, fn, label_writes); // The client will stop running PSO after this, so disconnect them @@ -2456,7 +2456,7 @@ asio::awaitable send_get_player_info(shared_ptr c, } try { auto s = c->require_server_state(); - auto fn = s->function_code_index->get_patch("GetExtendedPlayerInfo", c->specific_version); + auto fn = s->client_functions->get("GetExtendedPlayerInfo", c->specific_version); send_function_call(c->channel, c->enabled_flags, fn); c->function_call_response_queue.emplace_back(make_shared>()); full_req_sent = true; diff --git a/src/SendCommands.hh b/src/SendCommands.hh index 2acc8dce..c4fad5a4 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -9,8 +9,8 @@ #include #include "Client.hh" +#include "ClientFunctionIndex.hh" #include "CommandFormats.hh" -#include "FunctionCompiler.hh" #include "Lobby.hh" #include "Menu.hh" #include "Quest.hh" @@ -150,7 +150,7 @@ void send_patch_change_to_directory( asio::awaitable prepare_client_for_patches(std::shared_ptr c); std::string prepare_send_function_call_data( - std::shared_ptr code, + std::shared_ptr code, const std::unordered_map& label_writes, const void* suffix_data, size_t suffix_size, @@ -163,7 +163,7 @@ std::string prepare_send_function_call_data( void send_function_call( std::shared_ptr ch, uint64_t client_enabled_flags, - std::shared_ptr code, + std::shared_ptr code, const std::unordered_map& label_writes = {}, const void* suffix_data = nullptr, size_t suffix_size = 0, @@ -173,7 +173,7 @@ void send_function_call( bool ignore_actually_runs_code_flag = false); asio::awaitable send_function_call( std::shared_ptr c, - std::shared_ptr code, + std::shared_ptr code, const std::unordered_map& label_writes = {}, const void* suffix_data = nullptr, size_t suffix_size = 0, @@ -182,7 +182,7 @@ asio::awaitable send_function_call( uint32_t override_relocations_offset = 0, bool ignore_actually_runs_code_flag = false); asio::awaitable send_function_call_multi( - std::shared_ptr c, std::unordered_set> codes); + std::shared_ptr c, std::unordered_set> codes); asio::awaitable send_protected_command(std::shared_ptr c, const void* data, size_t size, bool echo_to_lobby); asio::awaitable send_dol_file(std::shared_ptr c, std::shared_ptr dol); diff --git a/src/ServerState.cc b/src/ServerState.cc index 6b57b66e..a4ef9b8d 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -2231,7 +2231,7 @@ void ServerState::load_quest_index(bool raise_on_any_failure) { void ServerState::compile_functions(bool raise_on_any_failure) { config_log.info_f("Compiling client functions"); - this->function_code_index = make_shared("system/client-functions", raise_on_any_failure); + this->client_functions = make_shared("system/client-functions", raise_on_any_failure); } void ServerState::load_dol_files() { diff --git a/src/ServerState.hh b/src/ServerState.hh index e3556399..084576af 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -11,11 +11,12 @@ #include "Account.hh" #include "Client.hh" +#include "ClientFunctionIndex.hh" #include "CommonItemSet.hh" #include "DNSServer.hh" +#include "DOLFileIndex.hh" #include "Episode3/DataIndexes.hh" #include "Episode3/Tournament.hh" -#include "FunctionCompiler.hh" #include "GSLArchive.hh" #include "IPV4RangeSet.hh" #include "ItemNameIndex.hh" @@ -183,7 +184,7 @@ struct ServerState : public std::enable_shared_from_this { std::vector> bb_private_keys; std::shared_ptr> bb_default_keyboard_config; std::shared_ptr> bb_default_joystick_config; - std::shared_ptr function_code_index; + std::shared_ptr client_functions; std::shared_ptr pc_patch_file_index; std::shared_ptr bb_patch_file_index; std::unordered_map> map_file_for_source_hash; diff --git a/src/Version.cc b/src/Version.cc index fa87a99a..4e2021fb 100644 --- a/src/Version.cc +++ b/src/Version.cc @@ -140,7 +140,7 @@ uint32_t default_sub_version_for_version(Version version) { uint32_t default_specific_version_for_version(Version version, int64_t sub_version) { // For versions that don't support send_function_call by default, we need to set the specific_version based on // sub_version. Fortunately, all versions that share sub_version values also support send_function_call, so for those - // versions we get the specific_version later by sending VersionDetectDC, VersionDetectGC, or VersionDetectXB. + // versions we get the specific_version later by sending VersionDetect. switch (version) { case Version::DC_NTE: return SPECIFIC_VERSION_DC_NTE; // 1OJ1 (NTE) @@ -159,7 +159,7 @@ uint32_t default_specific_version_for_version(Version version, int64_t sub_versi return SPECIFIC_VERSION_DC_V1_INDETERMINATE; } case Version::DC_V2: - return SPECIFIC_VERSION_DC_V2_INDETERMINATE; // 2___; need to send VersionDetectDC + return SPECIFIC_VERSION_DC_V2_INDETERMINATE; // 2___; need to send VersionDetect case Version::PC_NTE: return SPECIFIC_VERSION_PC_V2_NTE; // 2OJT case Version::PC_V2: @@ -184,7 +184,7 @@ uint32_t default_specific_version_for_version(Version version, int64_t sub_versi case 0x30: // GC Ep1&2 GameJam demo, GC Ep1&2 Trial Edition, GC Ep1&2 JP v1.2, at least one version of PSO XB case 0x31: // GC Ep1&2 US v1.0, GC US v1.1, XB US default: - return SPECIFIC_VERSION_GC_V3_INDETERMINATE; // 3O__; need to send VersionDetectGC + return SPECIFIC_VERSION_GC_V3_INDETERMINATE; // 3O__; need to send VersionDetect } throw logic_error("this should be impossible"); case Version::GC_EP3_NTE: @@ -199,10 +199,10 @@ uint32_t default_specific_version_for_version(Version version, int64_t sub_versi case -1: // Initial check (before sub_version recognition) case 0x40: // GC Ep3 trial and GC Ep3 JP default: - return SPECIFIC_VERSION_GC_EP3_INDETERMINATE; // 3SJ_; need to send VersionDetectGC + return SPECIFIC_VERSION_GC_EP3_INDETERMINATE; // 3SJ_; need to send VersionDetect } case Version::XB_V3: - return SPECIFIC_VERSION_XB_V3_INDETERMINATE; // 4O__; need to send VersionDetectXB + return SPECIFIC_VERSION_XB_V3_INDETERMINATE; // 4O__; need to send VersionDetect case Version::BB_V4: return SPECIFIC_VERSION_BB_V4_INDETERMINATE; // 5___; we should be able to determine version from initial login default: @@ -244,6 +244,23 @@ bool specific_version_is_bb(uint32_t specific_version) { return ((specific_version & 0xFF000000) == 0x35000000); } +uint32_t specific_version_for_str(const std::string& s) { + switch (s.size()) { + case 0: + return 0; + case 1: + return (s[0] << 24); + case 2: + return (s[0] << 24) | (s[1] << 16); + case 3: + return (s[0] << 24) | (s[1] << 16) | (s[2] << 8); + case 4: + return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; + default: + throw std::runtime_error("Invalid specific_version string"); + } +} + string str_for_specific_version(uint32_t specific_version) { string ret; for (size_t z = 0; z < 4; z++) { diff --git a/src/Version.hh b/src/Version.hh index 6b7f5738..7831f829 100644 --- a/src/Version.hh +++ b/src/Version.hh @@ -184,6 +184,10 @@ constexpr bool uses_utf16(Version version) { (version == Version::BB_V4); } +constexpr uint32_t SPECIFIC_VERSION_SH4_INDETERMINATE = 0x53483400; // SH4_ +constexpr uint32_t SPECIFIC_VERSION_PPC_INDETERMINATE = 0x50504300; // PPC_ +constexpr uint32_t SPECIFIC_VERSION_X86_INDETERMINATE = 0x58383600; // X86_ + constexpr uint32_t SPECIFIC_VERSION_DC_NTE = 0x314F4A31; // 1OJ1 constexpr uint32_t SPECIFIC_VERSION_DC_11_2000_PROTOTYPE = 0x314F4A32; // 1OJ2 constexpr uint32_t SPECIFIC_VERSION_DC_V1_JP = 0x314F4A46; // 1OJF @@ -220,6 +224,7 @@ bool specific_version_is_gc(uint32_t specific_version); bool specific_version_is_xb(uint32_t specific_version); bool specific_version_is_bb(uint32_t specific_version); +uint32_t specific_version_for_str(const std::string& specific_version); std::string str_for_specific_version(uint32_t specific_version); enum class ServerBehavior { diff --git a/system/client-functions/AccurateKillCount/AccurateKillCount.5___.patch.s b/system/client-functions/AccurateKillCount.s similarity index 54% rename from system/client-functions/AccurateKillCount/AccurateKillCount.5___.patch.s rename to system/client-functions/AccurateKillCount.s index f1442194..60cbf459 100644 --- a/system/client-functions/AccurateKillCount/AccurateKillCount.5___.patch.s +++ b/system/client-functions/AccurateKillCount.s @@ -1,16 +1,76 @@ +.meta visibility="all" .meta name="Kill count fix" .meta description="Fixes client-side\nkill counts when\nmultiple enemies are\nkilled on the same\nframe" -.versions 59NJ 59NL - entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksBB + .include WriteCodeBlocks + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + + .label TItemWeapon_SealedJSword_count_kill_loc, + .data TItemWeapon_SealedJSword_count_kill_loc + .deltaof TItemWeapon_SealedJSword_count_kill, TItemWeapon_SealedJSword_count_kill_end + .address TItemWeapon_SealedJSword_count_kill_loc +TItemWeapon_SealedJSword_count_kill: # [std](TItemWeapon_SealedJSword* this @ r3) -> void + lwz r4, [r3 + 0xF0] # r4 = this->owner_player + lha r5, [r4 + 0x11A] # r5 = this->owner_player->num_kills_since_map_load + lha r6, [r3 + 0x1F8] # r6 = this->last_owner_player_kill_count + lhz r7, [r3 + 0xE8] # r7 = this->kill_count + cmp r6, r5 + bge TItemWeapon_SealedJSword_count_kill_skip_update + lwz r8, [r3 + 0xDC] + andi. r8, r8, 0x100 + beq TItemWeapon_SealedJSword_count_kill_skip_incr # if (!(flags & 0x100)) don't incr kill count + sub r8, r5, r6 + add r7, r7, r8 + sth [r3 + 0xE8], r7 +TItemWeapon_SealedJSword_count_kill_skip_incr: + sth [r3 + 0x1F8], r5 +TItemWeapon_SealedJSword_count_kill_skip_update: + cmplwi r7, 23000 + blt TItemWeapon_SealedJSword_count_kill_skip_set_flag + lwz r8, [r3 + 0xDC] + ori r8, r8, 0x200 + stw [r3 + 0xDC], r8 +TItemWeapon_SealedJSword_count_kill_skip_set_flag: + blr +TItemWeapon_SealedJSword_count_kill_end: + + + + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .data + .deltaof TItemWeapon_SealedJSword_count_kill, TItemWeapon_SealedJSword_count_kill_end + .address +TItemWeapon_SealedJSword_count_kill: + mov eax, [ecx + 0xF0] + movsx eax, word [eax + 0x11A] + movsx edx, word [ecx + 0x1F8] + sub edx, eax + jge TItemWeapon_SealedJSword_count_kill_skip_update + test dword [ecx + 0xDC], 0x100 + jz TItemWeapon_SealedJSword_count_kill_skip_incr + sub [ecx + 0xE8], dx +TItemWeapon_SealedJSword_count_kill_skip_incr: + mov [ecx + 0x1F8], ax +TItemWeapon_SealedJSword_count_kill_skip_update: + cmp word [ecx + 0xE8], 23000 + jb TItemWeapon_SealedJSword_count_kill_skip_set_flag + or dword [ecx + 0xDC], 0x200 +TItemWeapon_SealedJSword_count_kill_skip_set_flag: + ret +TItemWeapon_SealedJSword_count_kill_end: + + + + .versions 59NJ 59NL + .data .deltaof TItemUnitUnsealable_count_kill, TItemUnitUnsealable_count_kill_end .address @@ -34,8 +94,6 @@ TItemUnitUnsealable_count_kill_skip_update: jmp TItemUnitUnsealable_count_kill_end: - - .data .deltaof TItemWeapon_LameDArgent_count_kill, TItemWeapon_LameDArgent_count_kill_end .address @@ -59,8 +117,6 @@ TItemWeapon_LameDArgent_count_kill_skip_update: ret TItemWeapon_LameDArgent_count_kill_end: - - .data .deltaof TItemWeapon_SealedJSword_count_kill, TItemWeapon_SealedJSword_count_kill_end .address @@ -86,5 +142,7 @@ TItemWeapon_SealedJSword_count_kill_end: + .all_versions + .data 0x00000000 .data 0x00000000 diff --git a/system/client-functions/AccurateKillCount/AccurateKillCount.3___.patch.s b/system/client-functions/AccurateKillCount/AccurateKillCount.3___.patch.s deleted file mode 100644 index 5da3450e..00000000 --- a/system/client-functions/AccurateKillCount/AccurateKillCount.3___.patch.s +++ /dev/null @@ -1,42 +0,0 @@ -.meta name="Kill count fix" -.meta description="Fixes client-side\nkill counts when\nmultiple enemies are\nkilled on the same\nframe" - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - .label TItemWeapon_SealedJSword_count_kill_loc, - .data TItemWeapon_SealedJSword_count_kill_loc - .deltaof TItemWeapon_SealedJSword_count_kill, TItemWeapon_SealedJSword_count_kill_end - .address TItemWeapon_SealedJSword_count_kill_loc -TItemWeapon_SealedJSword_count_kill: # [std](TItemWeapon_SealedJSword* this @ r3) -> void - lwz r4, [r3 + 0xF0] # r4 = this->owner_player - lha r5, [r4 + 0x11A] # r5 = this->owner_player->num_kills_since_map_load - lha r6, [r3 + 0x1F8] # r6 = this->last_owner_player_kill_count - lhz r7, [r3 + 0xE8] # r7 = this->kill_count - cmp r6, r5 - bge TItemWeapon_SealedJSword_count_kill_skip_update - lwz r8, [r3 + 0xDC] - andi. r8, r8, 0x100 - beq TItemWeapon_SealedJSword_count_kill_skip_incr # if (!(flags & 0x100)) don't incr kill count - sub r8, r5, r6 - add r7, r7, r8 - sth [r3 + 0xE8], r7 -TItemWeapon_SealedJSword_count_kill_skip_incr: - sth [r3 + 0x1F8], r5 -TItemWeapon_SealedJSword_count_kill_skip_update: - cmplwi r7, 23000 - blt TItemWeapon_SealedJSword_count_kill_skip_set_flag - lwz r8, [r3 + 0xDC] - ori r8, r8, 0x200 - stw [r3 + 0xDC], r8 -TItemWeapon_SealedJSword_count_kill_skip_set_flag: - blr -TItemWeapon_SealedJSword_count_kill_end: - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/AccurateKillCount/AccurateKillCount.4___.patch.s b/system/client-functions/AccurateKillCount/AccurateKillCount.4___.patch.s deleted file mode 100644 index 6543c1de..00000000 --- a/system/client-functions/AccurateKillCount/AccurateKillCount.4___.patch.s +++ /dev/null @@ -1,35 +0,0 @@ -.meta name="Kill count fix" -.meta description="Fixes client-side\nkill counts when\nmultiple enemies are\nkilled on the same\nframe" - -.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksXB - - .data - .deltaof TItemWeapon_SealedJSword_count_kill, TItemWeapon_SealedJSword_count_kill_end - .address -TItemWeapon_SealedJSword_count_kill: - mov eax, [ecx + 0xF0] - movsx eax, word [eax + 0x11A] - movsx edx, word [ecx + 0x1F8] - sub edx, eax - jge TItemWeapon_SealedJSword_count_kill_skip_update - test dword [ecx + 0xDC], 0x100 - jz TItemWeapon_SealedJSword_count_kill_skip_incr - sub [ecx + 0xE8], dx -TItemWeapon_SealedJSword_count_kill_skip_incr: - mov [ecx + 0x1F8], ax -TItemWeapon_SealedJSword_count_kill_skip_update: - cmp word [ecx + 0xE8], 23000 - jb TItemWeapon_SealedJSword_count_kill_skip_set_flag - or dword [ecx + 0xDC], 0x200 -TItemWeapon_SealedJSword_count_kill_skip_set_flag: - ret -TItemWeapon_SealedJSword_count_kill_end: - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/BlueBurstExclusive/BankSize.5___.patch.s b/system/client-functions/BlueBurstExclusive/BankSize.s similarity index 80% rename from system/client-functions/BlueBurstExclusive/BankSize.5___.patch.s rename to system/client-functions/BlueBurstExclusive/BankSize.s index ff7274bb..a1dec881 100644 --- a/system/client-functions/BlueBurstExclusive/BankSize.5___.patch.s +++ b/system/client-functions/BlueBurstExclusive/BankSize.s @@ -1,18 +1,14 @@ -# This patch changes the amount of items and Meseta that can be stored in the -# bank. If the bank item limit is increased beyond 200, this patch requires -# server support for extended bank data stored outside of the player's data. -# newserv has support for this, but you must set the BBBankItemLimit and -# BBBankMesetaLimit values in config.json to match the values used here. +# This patch changes the amount of items and Meseta that can be stored in the bank. If the bank item limit is increased +# beyond 200, this patch requires server support for extended bank data stored outside of the player's data. newserv +# has support for this, but you must set the BBBankItemLimit and BBBankMesetaLimit values in config.json to match the +# values used here. -# As written, this changes the meseta limit to 2000000000 and the item limit to -# 1000. The meseta limit can be any value up to 2147483647, and the item limit -# can be any value up to 1321. To use different values than the defaults, first -# compute the data size as ((slot count * 0x18) + 8), then replace each value -# below appropriately. +# As written, this changes the meseta limit to 2000000000 and the item limit to 1000. The meseta limit can be any value +# up to 2147483647, and the item limit can be any value up to 1321. To use different values than the defaults, first +# compute the data size as ((slot count * 0x18) + 8), then replace each value below appropriately. .meta name="More bank slots" .meta description="" -.meta hide_from_patches_menu .versions 59NJ 59NL @@ -21,7 +17,7 @@ reloc0: .offsetof start start: - .include WriteCodeBlocksBB + .include WriteCodeBlocks .data .data 4 diff --git a/system/client-functions/BlueBurstExclusive/ClassicMainWarpBehavior.5___.patch.s b/system/client-functions/BlueBurstExclusive/ClassicMainWarpBehavior.s similarity index 66% rename from system/client-functions/BlueBurstExclusive/ClassicMainWarpBehavior.5___.patch.s rename to system/client-functions/BlueBurstExclusive/ClassicMainWarpBehavior.s index 5740cb79..29c19fe5 100644 --- a/system/client-functions/BlueBurstExclusive/ClassicMainWarpBehavior.5___.patch.s +++ b/system/client-functions/BlueBurstExclusive/ClassicMainWarpBehavior.s @@ -1,14 +1,11 @@ -# This patch disables the logic that causes all unlockable areas to be open by -# default for all players, instead restoring the logic that checks quest flags -# to open areas (as previous PSO versions used). +# This patch disables the logic that causes all unlockable areas to be open by default for all players, instead +# restoring the logic that checks quest flags to open areas (as previous PSO versions used). -# This patch is intended to be used in the BBRequiredPatches field in -# config.json if you want the classic behavior, hence the presence of the -# hide_from_patches_menu directive here. +# This patch is intended to be used in the BBRequiredPatches field in config.json if you want the classic behavior, +# hence the visibility setting here. .meta name="Classic main warp behavior" .meta description="" -.meta hide_from_patches_menu .versions 59NJ 59NL @@ -16,7 +13,7 @@ entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksBB + .include WriteCodeBlocks .data # Episode 1 .data 1 .binary 01 diff --git a/system/client-functions/BlueBurstExclusive/ClearUnreleasedItemList.5___.patch.s b/system/client-functions/BlueBurstExclusive/ClearUnreleasedItemList.s similarity index 51% rename from system/client-functions/BlueBurstExclusive/ClearUnreleasedItemList.5___.patch.s rename to system/client-functions/BlueBurstExclusive/ClearUnreleasedItemList.s index e5ebc9fe..e0547247 100644 --- a/system/client-functions/BlueBurstExclusive/ClearUnreleasedItemList.5___.patch.s +++ b/system/client-functions/BlueBurstExclusive/ClearUnreleasedItemList.s @@ -1,14 +1,11 @@ -# It would be a bad idea to remove `.meta hide_from_patches_menu` to make this -# patch an option for players to be able to select; either all players on the -# server should have this patch, or none should have it. +# It would be a bad idea to change this function's visibility; either all players on the server should have this patch, +# or none should have it. -# This patch clears the list of unreleased items on the client, so the client -# never creates buggy items when the server generates an item that wasn't -# released on the official servers. +# This patch clears the list of unreleased items on the client, so the client never creates buggy items when the server +# generates an item that wasn't released on the official servers. .meta name="Clear unreleased item list" .meta description="" -.meta hide_from_patches_menu .versions 59NJ 59NL diff --git a/system/client-functions/BlueBurstExclusive/MomokaItemExchangeFix.5___.patch.s b/system/client-functions/BlueBurstExclusive/MomokaItemExchangeFix.s similarity index 98% rename from system/client-functions/BlueBurstExclusive/MomokaItemExchangeFix.5___.patch.s rename to system/client-functions/BlueBurstExclusive/MomokaItemExchangeFix.s index 324d3825..8c13196f 100644 --- a/system/client-functions/BlueBurstExclusive/MomokaItemExchangeFix.5___.patch.s +++ b/system/client-functions/BlueBurstExclusive/MomokaItemExchangeFix.s @@ -1,3 +1,4 @@ +.meta visibility="all" .meta name="Item exch. fix" .meta description="Fixes some quest item\nexchange opcodes" @@ -7,7 +8,7 @@ entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksBB + .include WriteCodeBlocks diff --git a/system/client-functions/BlueBurstExclusive/MoreSaveSlots.59NJ.patch.s b/system/client-functions/BlueBurstExclusive/MoreSaveSlots.59NJ.patch.s deleted file mode 100644 index 157e92ef..00000000 --- a/system/client-functions/BlueBurstExclusive/MoreSaveSlots.59NJ.patch.s +++ /dev/null @@ -1,624 +0,0 @@ -# This patch changes the number of BB character save slots from 4 to any number -# up to 127. - -# This patch is for documentation purposes only; it works when used as a server -# patch via newserv, but is decidedly inconvenient to use via this method. This -# is because it affects logic that runs before any patches can be sent by the -# server, so the player has to connect once to get the patch, then disconnect -# and connect again to use the additional slots. - -# As written, this patch changes the slot count from 4 to 12. To use a -# different slot count, first compute the following values: -# slot count = your desired number of player slots (must be >= 4, <= 127) -# total file size = (slot count * 0x2EA4) + 0x14 -# bgm_test_songs_unlocked offset = total file size - 0x10 -# save_count offset = total file size - 8 -# round2_seed offset = total file size - 4 -# Then, for each of the above, search for the string to the left of the = sign -# and change the values used in all of the matching lines. - -.meta name="More save slots" -.meta description="" -.meta hide_from_patches_menu - -entry_ptr: -reloc0: - .offsetof start - - # Include a few functions first -write_call_to_code: - .include WriteCallToCode-59NJ -memcpy: - .include CopyData - ret - - - -start: - # Apply all necessary patches - call apply_enable_scroll_patch - call apply_fix_scroll_patch1 - call apply_fix_scroll_patch2 - call apply_fix_file_index - call apply_preview_window_fix - call apply_static_patches - # Rewrite the existing char file regions to have the appropriate size; this - # must be done after the patches are applied because we call the checksum - # function, which is patched by one of the above calls - call update_existing_char_file_list - jmp update_existing_char_file_list_memcard - - - -apply_enable_scroll_patch: - # This patch enables scrolling behavior within the character list - push -5 # Jump size (negative = jmp instead of call) - push 0x00413B77 # Jump address - call get_code_size_for_enable_scroll - .deltaof enable_scroll_start, enable_scroll_end -get_code_size_for_enable_scroll: - pop eax - push dword [eax] - call enable_scroll_end -enable_scroll_start: - mov eax, dword ptr [edi + 0x28] # cursor = char_select_menu->cursor_obj (TAdSelectCurGC*) - or dword [eax + 0x01F8], 3 # cursor->flags |= 3 # Enable scrolling - mov eax, [0x00A38BD0] # scroll_bar = TAdScrollBarXb_objs[0] - mov ecx, [eax + 0xEC] # ecx = scroll_bar->client_id - imul ecx, ecx, 0x24 - # Set up scroll bar graphics (in struct at scroll_bar + 0x1C) - mov dword [eax + ecx + 0x1C], 0x439D0000 - mov dword [eax + ecx + 0x20], 0x43360000 - mov dword [eax + ecx + 0x24], 0x439D0000 - mov dword [eax + ecx + 0x28], 0x4392AB85 - mov dword [eax + ecx + 0x2C], 0x40400000 - mov dword [eax + ecx + 0x30], 0x425EA3D7 - mov dword [eax + ecx + 0x34], 0x00000008 - mov dword [eax + ecx + 0x38], 0x00000000 - mov dword [eax + ecx + 0x3C], 0x00000000 - or dword [eax + 0xF0], 1 # scroll_bar->flags |= 1 - mov ecx, [eax + 0xEC] - shl ecx, 4 - mov dword [eax + ecx + 0xAC], 0 # scroll_bar->selection_state[client_id].scroll_offset = 0 - mov dword [eax + ecx + 0xB0], 0 # scroll_bar->selection_state[client_id].selected_index = 0 - mov dword [eax + ecx + 0xB4], 4 # scroll_bar->selection_state[client_id].num_items_in_view = 4 - mov dword [eax + ecx + 0xB8], 0x0B # scroll_bar->selection_state[client_id].last_item_index = (slot count - 1) - pop edi - ret -enable_scroll_end: - call write_call_to_code - ret - - - -apply_fix_scroll_patch1: - # This patch fixes character selection cursor object so it will take the - # scroll offset into account - push 6 # Call size - push 0x00413C30 # Call address - call get_code_size_for_fix_scroll_patch1 - .deltaof fix_scroll_patch1_start, fix_scroll_patch1_end -get_code_size_for_fix_scroll_patch1: - pop eax - push dword [eax] - call fix_scroll_patch1_end -fix_scroll_patch1_start: - mov edx, [edi + 0x28] # cursor = this->ad_select_cur_obj (TAdSelectCurGC*) - mov ebp, [edx + 0x44] # ebp = cursor->selected_index_within_view - mov eax, [0x00A38BD0] # scroll_bar = TAdScrollBarXb_objs[0] - add ebp, [eax + 0xAC] # ebp += scroll_bar->selection_state[0].scroll_offset - ret -fix_scroll_patch1_end: - call write_call_to_code - ret - - - -apply_fix_scroll_patch2: - # This patch changes the TAdSinglePlyChrSelectGC::selected_index_within_view - # to be the selected character's absolute index (including scroll_offset), - # not the index only within the displayed four characters - push 6 # Call size - push 0x00413CD0 # Call address - call get_code_size_for_fix_scroll_patch2 - .deltaof fix_scroll_patch2_start, fix_scroll_patch2_end -get_code_size_for_fix_scroll_patch2: - pop eax - push dword [eax] - call fix_scroll_patch2_end -fix_scroll_patch2_start: - mov eax, [0x00A38BD0] # scroll_bar = TAdScrollBarXb_objs[0] - mov eax, [eax + 0xAC] # eax = scroll_bar->selection_state[0].scroll_offset - mov edx, [edi + 0x28] # cursor = this->ad_select_cur_obj (TAdSelectCurGC*) - add eax, [edx + 0x44] # eax += cursor->selected_index_within_view - ret -fix_scroll_patch2_end: - call write_call_to_code - ret - - - -apply_fix_file_index: - # This patch fixes the character file indexing so it will account for the - # scroll position - push 5 # Call size - push 0x00413CE8 # Call address - call get_code_size_for_selection_index_fix2 - .deltaof selection_index_fix2_start, selection_index_fix2_end -get_code_size_for_selection_index_fix2: - pop eax - push dword [eax] - call selection_index_fix2_end -selection_index_fix2_start: - mov eax, [0x00A38BD0] - mov eax, [eax + 0xAC] # eax = TAdScrollBarXb_objs[0]->selection_state[0].scroll_offset - add ebp, eax # arg0 += eax - mov [esp + 4], ebp - mov eax, 0x006C1ABC - jmp eax # set_current_char_slot -selection_index_fix2_end: - call write_call_to_code - ret - - - -apply_preview_window_fix: - # This patch fixes the preview display so it will show the correct section - # ID, level, etc. - push 5 # Call size - push 0x0040216C # Call address - call get_code_size_for_preview_window_fix - .deltaof preview_window_fix_start, preview_window_fix_end -get_code_size_for_preview_window_fix: - pop eax - push dword [eax] - call preview_window_fix_end -preview_window_fix_start: - mov eax, [0x00A38BD0] # scroll_bar = TAdScrollBarXb_objs[0] - mov eax, [eax + 0xAC] # eax = scroll_bar->selection_state[0].scroll_offset - add [esp + 4], eax - mov eax, 0x006C4514 # get_player_preview_info - jmp eax -preview_window_fix_end: - # This patch applies in two places, so push the second set of args now, then - # apply it twice - push 5 # Call size - push 0x00401842 # Call address - push dword [esp + 0x10] # Code size - push dword [esp + 0x10] # Code address - call write_call_to_code - call write_call_to_code - ret - - - -apply_static_patches: - .include WriteCodeBlocksBB - # These patches change various places where the character data size and slot - # count are referenced - .data 0x00475294 - .data 0x00000001 - .binary 0C # slot count; TDataProtocol::handle_E5 - .data 0x0047534B - .data 0x00000001 - .binary 0C # slot count; import_player_preview - .data 0x004786D1 - .data 0x00000001 - .binary 0C # slot count; TDataProtocol::handle_E4 - .data 0x00482559 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C17FB - .data 0x00000001 - .binary 0C # slot count - .data 0x006C1D07 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C1D3A - .data 0x00000001 - .binary 0C # slot count - .data 0x006C1D58 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C1E13 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C226A - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C22A9 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C22CA - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C22DA - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C2517 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C267F - .data 0x00000004 - .data 0x00022FBC # save_count offset - .data 0x006C2689 - .data 0x00000004 - .data 0x00022FBC # save_count offset - .data 0x006C272B - .data 0x00000004 - .data 0x00022FBC # save_count offset - .data 0x006C2741 - .data 0x00000004 - .data 0x00022FC0 # round2_seed offset - .data 0x006C27CF - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C28A8 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C314F - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C357B - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C35BA - .data 0x00000001 - .binary 0C # slot count - .data 0x006C35E6 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C35F3 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C360E - .data 0x00000004 - .data 0x00022FBC # save_count offset - .data 0x006C3617 - .data 0x00000004 - .data 0x00022FBC # save_count offset - .data 0x006C371C - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C3B5A - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C424D - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C4833 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C486A - .data 0x00000001 - .binary 0C # slot count - .data 0x006C49A6 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C49DD - .data 0x00000001 - .binary 0C # slot count - .data 0x006C4AC5 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C4AFE - .data 0x00000001 - .binary 0C # slot count - .data 0x006C4CDE - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C4D15 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C4DFD - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C4E36 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C4F9C - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C4FD7 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C51C5 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C5201 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C5376 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C53B0 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C5545 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C5581 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C56F6 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C5730 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C58B6 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C58F0 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C5A85 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C5AC1 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C5BB2 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C5BEC - .data 0x00000001 - .binary 0C # slot count - .data 0x006C5D72 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C5DAC - .data 0x00000001 - .binary 0C # slot count - .data 0x006C5F32 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C5F6C - .data 0x00000001 - .binary 0C # slot count - .data 0x006C60F2 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C612C - .data 0x00000001 - .binary 0C # slot count - .data 0x006C6346 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C6381 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C6505 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C6541 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C6632 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C666C - .data 0x00000001 - .binary 0C # slot count - .data 0x006C67F2 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C682C - .data 0x00000001 - .binary 0C # slot count - .data 0x006C69B2 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C69EC - .data 0x00000001 - .binary 0C # slot count - .data 0x006C6B87 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C6BB8 - .data 0x00000004 - .data 0x0000005D # memcard block count - .data 0x006C6C3A - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C6C74 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C6E82 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C6EBC - .data 0x00000001 - .binary 0C # slot count - .data 0x006C70B9 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C70F3 - .data 0x00000001 - .binary 0C # slot count - .data 0x006C7A46 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C7D66 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x006C7D7C - .data 0x00000001 - .binary 0C # slot count - .data 0x006C7DC0 - .data 0x00000004 - .data 0x00022FC4 # total file size - .data 0x0077CC72 - .data 0x00000004 - .data 0x00022FB4 # bgm_test_songs_unlocked offset - - # Signature check on all save files (rewritten as loop) - .data 0x006C1C69 - .deltaof sig_check_begin, sig_check_end -sig_check_begin: - mov edx, 0xC87ED5B1 # Expected signature value - add eax, 0x04E8 # &char_file_list->chars[0].part2.signature - mov ecx, 0x0C # slot count -again: - cmp dword [eax], 0 # signature == 0 (no char in slot) - je sig_ok - cmp dword [eax], edx # signature == expected value - jne sig_bad -sig_ok: - add eax, 0x2EA4 # Advance to next slot - dec ecx - jnz again - xor eax, eax # All signatures OK (eax = 0) - jmp sig_check_end -sig_bad: - xor eax, eax # Bad signature (eax = 1) - inc eax - jmp sig_check_end - .binary CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC -sig_check_end: # 006C1CB2 - - # Send slot count in E3 command - .data 0x0046EC10 # TDataProtocol::send_E3_for_index - .deltaof send_slot_count_in_E3_begin, send_slot_count_in_E3_end -send_slot_count_in_E3_begin: - # ecx = this (TDataProtocol*) - # [esp + 4] = slot_index - push 0 - push dword [esp + 8] # slot_index - push 0x0C # slot count - push 0x00E30010 - mov eax, esp - push 0x10 - push eax - mov eax, [ecx] - call [eax + 0x20] # this->send_command(&cmd, 0x10) // ret 8 - add esp, 8 - mov eax, 0x006C1ABC - call eax # set_current_char_slot(slot_index) // ret 0 - add esp, 8 - ret 4 -send_slot_count_in_E3_end: - - # Show slot number in each menu item - .data 0x00401D57 - .deltaof show_slot_number_begin, show_slot_number_end -show_slot_number_begin: - # Original call (sprintf(line_buf, "LV%d", preview_info->visual.disp.level + 1)) - lea edx, [esp + 0x02C4] - mov ebx, [ebx + 8] - inc ebx - push ebx - mov ecx, esi - push edx - mov eax, 0x00402604 - call eax - # Find the end of the string - lea eax, [esp + 0x02C4] -show_slot_number_strend_again: - cmp word [eax], 0 - je show_slot_number_strend_done - add eax, 2 - jmp show_slot_number_strend_again -show_slot_number_strend_done: - # Format the slot number and append it to the string - mov ecx, [0x00A38BD0] # scroll_bar = TAdScrollBarXb_objs[0] - mov ecx, [ecx + 0xAC] # ecx = scroll_bar->selection_state[0].scroll_offset - lea ecx, [ecx + ebp + 1] - push ecx # Slot number (scroll_offset + z) - call get_show_slot_number_suffix_fmt - .binary 20002800230025006400290020000000 # L" (#%d) " -get_show_slot_number_suffix_fmt: - push eax # Destination buffer - mov eax, 0x00835578 # _swprintf - call eax - add esp, 0x0C - jmp show_slot_number_end - .zero 0x96 -show_slot_number_end: # 00401E4D - - # End static patches - .data 0x00000000 - .data 0x00000000 - - - -update_existing_char_file_list: - # Replace the existing character list with an appropriately-longer one. This - # part does not need to be done if the patch is applied statically to the - # executable; this is only necessary when used as a server patch because the - # character list is already allocated at the time the patch is applied. - push 0x00022FC4 # total file size - mov eax, 0x00835915 # operator_new - call eax - add esp, 4 - mov edx, [0x00A939C4] # edx = old char_file_list - mov [0x00A939C4], eax - mov ecx, [edx + 0xBA94] # Copy bgm_test_songs_unlocked_high to new file - mov [eax + 0x00022FB4], ecx - mov ecx, [edx + 0xBA98] # Copy bgm_test_songs_unlocked_low to new file - mov [eax + 0x00022FB8], ecx - mov ecx, [edx + 0xBA9C] # Copy save_count to new file - mov [eax + 0x00022FBC], ecx - mov ecx, [edx + 0xBAA0] # Copy round2_seed to new file - mov [eax + 0x00022FC0], ecx - add eax, 4 - add edx, 4 - mov ecx, 0xBA90 - call memcpy # Copy the existing 4 characters over - mov eax, [0x00A939C4] - add eax, 0xBA94 - mov ecx, 4 -clear_next_char: - cmp ecx, 0x0C # slot count - jge clear_next_char_done - lea edx, [eax + 0x2EA4] # edx = ptr to next char (or footer) -clear_next_char_write_again: - mov dword [eax], 0 - add eax, 4 - cmp eax, edx - jl clear_next_char_write_again -clear_next_char_done: - - # Call eh_vector_constructor_iterator( - # &char_file_list.chars[4], - # sizeof(char_file_list.chars[0]), - # countof(char_file_list.chars) - 4, - # PSOCharacterFile::init, - # PSOCharacterFile::destroy) - push 0x006C197C # PSOCharacterFile::destroy - push 0x006C182C # PSOCharacterFile::init - push 0x08 # slot count - 4 - push 0x2EA4 # sizeof(PSOCharacterFile) - mov eax, [0x00A939C4] - add eax, 0xBA94 - push eax - mov eax, 0x00835E86 - call eax - - # Fix the file's checksum - mov eax, [0x00A939C4] - mov ecx, 0x006C2738 - jmp ecx # PSOBBCharacterFileList::checksum(char_file_list) - - - -update_existing_char_file_list_memcard: - # Allocate a new memory card file area and copy the data there too. It seems - # Sega didn't fully strip out the local saving code from PSOBB; instead, they - # just made it write to a heap-allocated buffer. Since the file is much - # bigger now, we also have to make that heap-allocated buffer larger. We add - # a few "blocks" on the end, since the original code in the game does that - # too, but it's probably not strictly necessary. - # Like the above, this part is not necessary if this patch is statically - # applied to the executable. - mov eax, 0x00022FC4 # total file size - add eax, 0x0000FFFF - and eax, 0xFFFFC000 - push eax - mov eax, 0x0084F258 - call eax # malloc10(total file size) - add esp, 4 - mov [0x00A939AC], eax - mov edx, [0x00A939C4] - mov ecx, 0x00022FC4 # total file size - jmp memcpy diff --git a/system/client-functions/BlueBurstExclusive/MoreSaveSlots.59NL.patch.s b/system/client-functions/BlueBurstExclusive/MoreSaveSlots.s similarity index 70% rename from system/client-functions/BlueBurstExclusive/MoreSaveSlots.59NL.patch.s rename to system/client-functions/BlueBurstExclusive/MoreSaveSlots.s index e78f246a..51603de0 100644 --- a/system/client-functions/BlueBurstExclusive/MoreSaveSlots.59NL.patch.s +++ b/system/client-functions/BlueBurstExclusive/MoreSaveSlots.s @@ -1,25 +1,24 @@ -# This patch changes the number of BB character save slots from 4 to any number -# up to 127. +# This patch changes the number of BB character save slots from 4 to any number up to 127. -# This patch is for documentation purposes only; it works when used as a server -# patch via newserv, but is decidedly inconvenient to use via this method. This -# is because it affects logic that runs before any patches can be sent by the -# server, so the player has to connect once to get the patch, then disconnect -# and connect again to use the additional slots. +# This patch is for documentation purposes only; it works when used as a server patch via newserv, but is decidedly +# inconvenient to use via this method. This is because it affects logic that runs before any patches can be sent by the +# server, so the player has to connect once to get the patch, then disconnect and connect again to use the additional +# slots. -# As written, this patch changes the slot count from 4 to 12. To use a -# different slot count, first compute the following values: +# As written, this patch changes the slot count from 4 to 12. To use a different slot count, first compute the +# following values: # slot count = your desired number of player slots (must be >= 4, <= 127) # total file size = (slot count * 0x2EA4) + 0x14 # bgm_test_songs_unlocked offset = total file size - 0x10 # save_count offset = total file size - 8 # round2_seed offset = total file size - 4 -# Then, for each of the above, search for the string to the left of the = sign -# and change the values used in all of the matching lines. +# Then, for each of the above, search for the string to the left of the = sign and change the values used in all of the +# matching lines. .meta name="More save slots" .meta description="" -.meta hide_from_patches_menu + +.versions 59NJ 59NL entry_ptr: reloc0: @@ -27,7 +26,7 @@ reloc0: # Include a few functions first write_call_to_code: - .include WriteCallToCode-59NL + .include WriteCallToCode memcpy: .include CopyData ret @@ -53,7 +52,7 @@ start: apply_enable_scroll_patch: # This patch enables scrolling behavior within the character list push -5 # Jump size (negative = jmp instead of call) - push 0x00413B7F # Jump address + push # Jump address call get_code_size_for_enable_scroll .deltaof enable_scroll_start, enable_scroll_end get_code_size_for_enable_scroll: @@ -63,7 +62,7 @@ get_code_size_for_enable_scroll: enable_scroll_start: mov eax, dword ptr [edi + 0x28] # cursor = char_select_menu->cursor_obj (TAdSelectCurGC*) or dword [eax + 0x01F8], 3 # cursor->flags |= 3 # Enable scrolling - mov eax, [0x00A3B050] # scroll_bar = TAdScrollBarXb_objs[0] + mov eax, [] # scroll_bar = TAdScrollBarXb_objs[0] mov ecx, [eax + 0xEC] # ecx = scroll_bar->client_id imul ecx, ecx, 0x24 # Set up scroll bar graphics (in struct at scroll_bar + 0x1C) @@ -92,10 +91,9 @@ enable_scroll_end: apply_fix_scroll_patch1: - # This patch fixes character selection cursor object so it will take the - # scroll offset into account + # This patch fixes character selection cursor object so it will take the scroll offset into account push 6 # Call size - push 0x00413C38 # Call address + push # Call address call get_code_size_for_fix_scroll_patch1 .deltaof fix_scroll_patch1_start, fix_scroll_patch1_end get_code_size_for_fix_scroll_patch1: @@ -105,7 +103,7 @@ get_code_size_for_fix_scroll_patch1: fix_scroll_patch1_start: mov edx, [edi + 0x28] # cursor = this->ad_select_cur_obj (TAdSelectCurGC*) mov ebp, [edx + 0x44] # ebp = cursor->selected_index_within_view - mov eax, [0x00A3B050] # scroll_bar = TAdScrollBarXb_objs[0] + mov eax, [] # scroll_bar = TAdScrollBarXb_objs[0] add ebp, [eax + 0xAC] # ebp += scroll_bar->selection_state[0].scroll_offset ret fix_scroll_patch1_end: @@ -115,11 +113,10 @@ fix_scroll_patch1_end: apply_fix_scroll_patch2: - # This patch changes the TAdSinglePlyChrSelectGC::selected_index_within_view - # to be the selected character's absolute index (including scroll_offset), - # not the index only within the displayed four characters + # This patch changes the TAdSinglePlyChrSelectGC::selected_index_within_view to be the selected character's absolute + # index (including scroll_offset), not the index only within the displayed four characters push 6 # Call size - push 0x00413CD8 # Call address + push # Call address call get_code_size_for_fix_scroll_patch2 .deltaof fix_scroll_patch2_start, fix_scroll_patch2_end get_code_size_for_fix_scroll_patch2: @@ -127,7 +124,7 @@ get_code_size_for_fix_scroll_patch2: push dword [eax] call fix_scroll_patch2_end fix_scroll_patch2_start: - mov eax, [0x00A3B050] # scroll_bar = TAdScrollBarXb_objs[0] + mov eax, [] # scroll_bar = TAdScrollBarXb_objs[0] mov eax, [eax + 0xAC] # eax = scroll_bar->selection_state[0].scroll_offset mov edx, [edi + 0x28] # cursor = this->ad_select_cur_obj (TAdSelectCurGC*) add eax, [edx + 0x44] # eax += cursor->selected_index_within_view @@ -139,10 +136,9 @@ fix_scroll_patch2_end: apply_fix_file_index: - # This patch fixes the character file indexing so it will account for the - # scroll position + # This patch fixes the character file indexing so it will account for the scroll position push 5 # Call size - push 0x00413CF0 # Call address + push # Call address call get_code_size_for_selection_index_fix2 .deltaof selection_index_fix2_start, selection_index_fix2_end get_code_size_for_selection_index_fix2: @@ -150,11 +146,11 @@ get_code_size_for_selection_index_fix2: push dword [eax] call selection_index_fix2_end selection_index_fix2_start: - mov eax, [0x00A3B050] + mov eax, [] mov eax, [eax + 0xAC] # eax = TAdScrollBarXb_objs[0]->selection_state[0].scroll_offset add ebp, eax # arg0 += eax mov [esp + 4], ebp - mov eax, 0x006C1A80 + mov eax, jmp eax # set_current_char_slot selection_index_fix2_end: call write_call_to_code @@ -163,10 +159,9 @@ selection_index_fix2_end: apply_preview_window_fix: - # This patch fixes the preview display so it will show the correct section - # ID, level, etc. + # This patch fixes the preview display so it will show the correct section ID, level, etc. push 5 # Call size - push 0x0040216C # Call address + push 0x0040216C call get_code_size_for_preview_window_fix .deltaof preview_window_fix_start, preview_window_fix_end get_code_size_for_preview_window_fix: @@ -174,10 +169,10 @@ get_code_size_for_preview_window_fix: push dword [eax] call preview_window_fix_end preview_window_fix_start: - mov eax, [0x00A3B050] # scroll_bar = TAdScrollBarXb_objs[0] + mov eax, [] # scroll_bar = TAdScrollBarXb_objs[0] mov eax, [eax + 0xAC] # eax = scroll_bar->selection_state[0].scroll_offset add [esp + 4], eax - mov eax, 0x006C44D0 # get_player_preview_info + mov eax, # get_player_preview_info jmp eax preview_window_fix_end: # This patch applies in two places, so push the second set of args now, then @@ -193,267 +188,266 @@ preview_window_fix_end: apply_static_patches: - .include WriteCodeBlocksBB - # These patches change various places where the character data size and slot - # count are referenced - .data 0x004751A4 + .include WriteCodeBlocks + # These patches change various places where the character data size and slot count are referenced + .data .data 0x00000001 .binary 0C # slot count; TDataProtocol::handle_E5 - .data 0x0047525B + .data .data 0x00000001 .binary 0C # slot count; import_player_preview - .data 0x004785E1 + .data .data 0x00000001 .binary 0C # slot count; TDataProtocol::handle_E4 - .data 0x0048242D + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C17BF + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C1CCB + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C1CFE + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C1D1C + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C1DD7 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C222E + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C226D + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C228E + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C229E + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C24DB + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C2643 + .data .data 0x00000004 .data 0x00022FBC # save_count offset - .data 0x006C264D + .data .data 0x00000004 .data 0x00022FBC # save_count offset - .data 0x006C26EF + .data .data 0x00000004 .data 0x00022FBC # save_count offset - .data 0x006C2705 + .data .data 0x00000004 .data 0x00022FC0 # round2_seed offset - .data 0x006C2793 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C286C + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C3113 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C353F + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C357E + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C35AA + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C35B7 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C35D2 + .data .data 0x00000004 .data 0x00022FBC # save_count offset - .data 0x006C35DB + .data .data 0x00000004 .data 0x00022FBC # save_count offset - .data 0x006C36E0 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C3B1E + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C4209 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C47EF + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C4826 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C4962 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C4999 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C4A81 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C4ABA + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C4C9A + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C4CD1 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C4DB9 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C4DF2 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C4F58 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C4F94 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C5181 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C51BD + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C5332 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C536C + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C5501 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C553D + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C56B2 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C56EC + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C5872 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C58AC + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C5A41 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C5A7D + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C5B6E + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C5BA8 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C5D2E + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C5D68 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C5EEE + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C5F28 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C60AE + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C60E8 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C6303 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C633D + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C64C1 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C64FD + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C65EE + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C6628 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C67AE + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C67E8 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C696E + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C69A8 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C6B43 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C6B74 + .data .data 0x00000004 .data 0x0000005D # memcard block count - .data 0x006C6BF6 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C6C30 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C6E3E + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C6E78 + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C7075 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C70AF + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C7A02 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C7D22 + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x006C7D5E + .data .data 0x00000001 .binary 0C # slot count - .data 0x006C7D7C + .data .data 0x00000004 .data 0x00022FC4 # total file size - .data 0x0077BE92 + .data .data 0x00000004 .data 0x00022FB4 # bgm_test_songs_unlocked offset # Signature check on all save files (rewritten as loop) - .data 0x006C1C2D + .data .deltaof sig_check_begin, sig_check_end sig_check_begin: mov edx, 0xC87ED5B1 # Expected signature value @@ -475,10 +469,10 @@ sig_bad: inc eax jmp sig_check_end .binary CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC -sig_check_end: # 006C1C76 +sig_check_end: # # Send slot count in E3 command - .data 0x0046EB20 # TDataProtocol::send_E3_for_index + .data # TDataProtocol::send_E3_for_index .deltaof send_slot_count_in_E3_begin, send_slot_count_in_E3_end send_slot_count_in_E3_begin: # ecx = this (TDataProtocol*) @@ -493,7 +487,7 @@ send_slot_count_in_E3_begin: mov eax, [ecx] call [eax + 0x20] # this->send_command(&cmd, 0x10) // ret 8 add esp, 8 - mov eax, 0x006C1A80 + mov eax, call eax # set_current_char_slot(slot_index) // ret 0 add esp, 8 ret 4 @@ -521,7 +515,7 @@ show_slot_number_strend_again: jmp show_slot_number_strend_again show_slot_number_strend_done: # Format the slot number and append it to the string - mov ecx, [0x00A3B050] # scroll_bar = TAdScrollBarXb_objs[0] + mov ecx, [] # scroll_bar = TAdScrollBarXb_objs[0] mov ecx, [ecx + 0xAC] # ecx = scroll_bar->selection_state[0].scroll_offset lea ecx, [ecx + ebp + 1] push ecx # Slot number (scroll_offset + z) @@ -529,7 +523,7 @@ show_slot_number_strend_done: .binary 20002800230025006400290020000000 # L" (#%d) " get_show_slot_number_suffix_fmt: push eax # Destination buffer - mov eax, 0x00857E29 # _swprintf + mov eax, # _swprintf call eax add esp, 0x0C jmp show_slot_number_end @@ -543,16 +537,15 @@ show_slot_number_end: # 00401E4D update_existing_char_file_list: - # Replace the existing character list with an appropriately-longer one. This - # part does not need to be done if the patch is applied statically to the - # executable; this is only necessary when used as a server patch because the + # Replace the existing character list with an appropriately-longer one. This part does not need to be done if the + # patch is applied statically to the executable; this is only necessary when used as a server patch because the # character list is already allocated at the time the patch is applied. push 0x00022FC4 # total file size - mov eax, 0x008581C5 # operator_new + mov eax, # operator_new call eax add esp, 4 - mov edx, [0x00A95E44] # edx = old char_file_list - mov [0x00A95E44], eax + mov edx, [] # edx = old char_file_list + mov [], eax mov ecx, [edx + 0xBA94] # Copy bgm_test_songs_unlocked_high to new file mov [eax + 0x00022FB4], ecx mov ecx, [edx + 0xBA98] # Copy bgm_test_songs_unlocked_low to new file @@ -565,7 +558,7 @@ update_existing_char_file_list: add edx, 4 mov ecx, 0xBA90 call memcpy # Copy the existing 4 characters over - mov eax, [0x00A95E44] + mov eax, [] add eax, 0xBA94 mov ecx, 4 clear_next_char: @@ -585,40 +578,37 @@ clear_next_char_done: # countof(char_file_list.chars) - 4, # PSOCharacterFile::init, # PSOCharacterFile::destroy) - push 0x006C1940 # PSOCharacterFile::destroy - push 0x006C17F0 # PSOCharacterFile::init + push # PSOCharacterFile::destroy + push # PSOCharacterFile::init push 0x08 # slot count - 4 push 0x2EA4 # sizeof(PSOCharacterFile) - mov eax, [0x00A95E44] + mov eax, [] add eax, 0xBA94 push eax - mov eax, 0x00858736 + mov eax, call eax # Fix the file's checksum - mov eax, [0x00A95E44] - mov ecx, 0x006C26FC + mov eax, [] + mov ecx, jmp ecx # PSOBBCharacterFileList::checksum(char_file_list) update_existing_char_file_list_memcard: - # Allocate a new memory card file area and copy the data there too. It seems - # Sega didn't fully strip out the local saving code from PSOBB; instead, they - # just made it write to a heap-allocated buffer. Since the file is much - # bigger now, we also have to make that heap-allocated buffer larger. We add - # a few "blocks" on the end, since the original code in the game does that - # too, but it's probably not strictly necessary. - # Like the above, this part is not necessary if this patch is statically - # applied to the executable. + # Allocate a new memory card file area and copy the data there too. It seems Sega didn't fully strip out the local + # saving code from PSOBB; instead, they just made it write to a heap-allocated buffer. Since the file is much bigger + # now, we also have to make that heap-allocated buffer larger. We add a few "blocks" on the end, since the original + # code in the game does that too, but it's probably not strictly necessary. Like the above, this part is not + # necessary if this patch is statically applied to the executable. mov eax, 0x00022FC4 # total file size add eax, 0x0000FFFF and eax, 0xFFFFC000 push eax - mov eax, 0x0082E940 + mov eax, call eax # malloc10(total file size) add esp, 4 - mov [0x00A95E2C], eax - mov edx, [0x00A95E44] + mov [], eax + mov edx, [] mov ecx, 0x00022FC4 # total file size jmp memcpy diff --git a/system/client-functions/BlueBurstExclusive/ServerEXPDisplay.59NJ.patch.s b/system/client-functions/BlueBurstExclusive/ServerEXPDisplay.59NJ.patch.s deleted file mode 100644 index ef877019..00000000 --- a/system/client-functions/BlueBurstExclusive/ServerEXPDisplay.59NJ.patch.s +++ /dev/null @@ -1,103 +0,0 @@ -# This patch causes the client not to generate its own EXP text and instead use -# the EXP values generated by the server when showing the purple text for enemy -# deaths. This makes EXP gained via EXP share visible, as well as makes -# fractional EXP multiplers (in config.json) display properly. - -.meta name="Server EXP display" -.meta description="" -.meta hide_from_patches_menu - -entry_ptr: -reloc0: - .offsetof start -start: - call install_hook - call apply_static_patches - ret - - - -install_hook: - pop ecx - push 0 # Write address instead of a call/jmp opcode - push 0x00A0DC54 - call get_code_size - .deltaof handle_6xBF_start, handle_6xBF_end -get_code_size: - pop eax - push dword [eax] - call handle_6xBF_end -handle_6xBF_start: # [std](G_6xBF* cmd @ [esp + 4]) -> void - mov edx, [esp + 4] - - mov ecx, [0x00A9A074] # local_client_id - cmp [edx + 2], cx - jne skip_text - - cmp byte [edx + 1], 3 - jl skip_text - movzx eax, word [edx + 8] # cmd.from_enemy_id - cmp eax, 0x1000 - jl skip_text - cmp eax, 0x1B50 - jge skip_text - call get_enemy_entity - - test eax, eax - jnz enemy_entity_ok - - # Use player entity if enemy entity is already gone - mov eax, 0x0068D618 - xchg eax, ecx - call ecx # eax = TObjPlayer::for_client_id(local_client_id); conveniently, this function preserves all regs except eax - -enemy_entity_ok: - push 0x0000FFFF # entity_id; ignored by TFontSmallTask if not a player - push dword [edx + 4] # amount = cmd.amount - push 0x00976380 # prefix = L"EXP" - push 0x14 - push 0x14 - push 0xFFFF00FF # color (ARGB) - add eax, 0x300 - push eax # position - mov eax, 0x0078B8E8 - call eax # TFontSmallTask___new__(...) - add esp, 0x1C - -skip_text: - mov eax, 0x0069292C # Original handle_6xBF - jmp eax # original_handle_6xBF(cmd) - -get_enemy_entity: - .include GetEnemyEntity-59NJ - ret - -handle_6xBF_end: - push ecx - .include WriteCallToCode-59NJ - - - -apply_static_patches: - .include WriteCodeBlocksBB - - .data 0x0078827D - .deltaof disable_kill_enemy_callsite_start, disable_kill_enemy_callsite_end -disable_kill_enemy_callsite_start: - nop - nop - nop - nop - nop -disable_kill_enemy_callsite_end: - - .data 0x00777381 - .deltaof disable_exp_steal_callsite_start, disable_exp_steal_callsite_end -disable_exp_steal_callsite_start: - add esp, 0x0C # Original function has `ret 0x0C` - nop - nop -disable_exp_steal_callsite_end: - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/BlueBurstExclusive/ServerEXPDisplay.59NL.patch.s b/system/client-functions/BlueBurstExclusive/ServerEXPDisplay.s similarity index 75% rename from system/client-functions/BlueBurstExclusive/ServerEXPDisplay.59NL.patch.s rename to system/client-functions/BlueBurstExclusive/ServerEXPDisplay.s index 69f89027..ed28f173 100644 --- a/system/client-functions/BlueBurstExclusive/ServerEXPDisplay.59NL.patch.s +++ b/system/client-functions/BlueBurstExclusive/ServerEXPDisplay.s @@ -1,11 +1,11 @@ -# This patch causes the client not to generate its own EXP text and instead use -# the EXP values generated by the server when showing the purple text for enemy -# deaths. This makes EXP gained via EXP share visible, as well as makes +# This patch causes the client not to generate its own EXP text and instead use the EXP values generated by the server +# when showing the purple text for enemy deaths. This makes EXP gained via EXP share visible, as well as makes # fractional EXP multiplers (in config.json) display properly. .meta name="Server EXP display" .meta description="" -.meta hide_from_patches_menu + +.versions 59NJ 59NL entry_ptr: reloc0: @@ -20,7 +20,7 @@ start: install_hook: pop ecx push 0 # Write address instead of a call/jmp opcode - push 0x00A0FC54 + push call get_code_size .deltaof handle_6xBF_start, handle_6xBF_end get_code_size: @@ -30,7 +30,7 @@ get_code_size: handle_6xBF_start: # [std](G_6xBF* cmd @ [esp + 4]) -> void mov edx, [esp + 4] - mov ecx, [0x00A9C4F4] # local_client_id + mov ecx, [] # local_client_id cmp [edx + 2], cx jne skip_text @@ -47,41 +47,41 @@ handle_6xBF_start: # [std](G_6xBF* cmd @ [esp + 4]) -> void jnz enemy_entity_ok # Use player entity if enemy entity is already gone - mov eax, 0x0068D5AC + mov eax, xchg eax, ecx call ecx # eax = TObjPlayer::for_client_id(local_client_id); conveniently, this function preserves all regs except eax enemy_entity_ok: push 0x0000FFFF # entity_id; ignored by TFontSmallTask if not a player push dword [edx + 4] # amount = cmd.amount - push 0x009783A0 # prefix = L"EXP" + push # prefix = L"EXP" push 0x14 push 0x14 push 0xFFFF00FF # color (ARGB) add eax, 0x300 push eax # position - mov eax, 0x0078AABC + mov eax, call eax # TFontSmallTask___new__(...) add esp, 0x1C skip_text: - mov eax, 0x006928C0 # Original handle_6xBF + mov eax, # Original handle_6xBF jmp eax # original_handle_6xBF(cmd) get_enemy_entity: - .include GetEnemyEntity-59NL + .include GetEnemyEntity ret handle_6xBF_end: push ecx - .include WriteCallToCode-59NL + .include WriteCallToCode apply_static_patches: - .include WriteCodeBlocksBB + .include WriteCodeBlocks - .data 0x0078749D + .data .deltaof disable_kill_enemy_callsite_start, disable_kill_enemy_callsite_end disable_kill_enemy_callsite_start: nop @@ -91,7 +91,7 @@ disable_kill_enemy_callsite_start: nop disable_kill_enemy_callsite_end: - .data 0x007765A5 + .data .deltaof disable_exp_steal_callsite_start, disable_exp_steal_callsite_end disable_exp_steal_callsite_start: add esp, 0x0C # Original function has `ret 0x0C` diff --git a/system/client-functions/BlueBurstExclusive/StackLimits.5___.patch.s b/system/client-functions/BlueBurstExclusive/StackLimits.s similarity index 74% rename from system/client-functions/BlueBurstExclusive/StackLimits.5___.patch.s rename to system/client-functions/BlueBurstExclusive/StackLimits.s index 86d49933..67f7891a 100644 --- a/system/client-functions/BlueBurstExclusive/StackLimits.5___.patch.s +++ b/system/client-functions/BlueBurstExclusive/StackLimits.s @@ -1,14 +1,11 @@ -# It would be a bad idea to remove `.meta hide_from_patches_menu` to make this -# patch an option for players to be able to select; either all players on the -# server should have this patch, or none should have it. +# It would be a bad idea to change this function's visibility; either all players on the server should have this patch, +# or none should have it. -# If you change the stack limits in config.json away from the defaults, you -# should change the limits array below to match config.json and add this patch -# to the BBRequiredPatches list. +# If you change the stack limits in config.json away from the defaults, you should change the limits array below to +# match config.json and add this patch to the BBRequiredPatches list. .meta name="Item stacks" .meta description="" -.meta hide_from_patches_menu .versions 59NJ 59NL @@ -16,7 +13,7 @@ entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksBB + .include WriteCodeBlocks # Patch 1: rewrite item_is_stackable .data @@ -63,10 +60,8 @@ max_stack_size_for_tool_start: # declare return values array call data_end - # This array specifies the stack limits for each tool class. The array index - # is the second byte of the item data (see names-v4.json for the values; for - # e.g. tech disks this would be 02). For classes beyond 15, the value for 15 - # is used. + # This array specifies the stack limits for each tool class. The array index is the second byte of the item data (see + # names-v4.json for the values; for e.g. tech disks this is 02). For classes beyond 15, the value for 15 is used. # Index: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 .binary 0A 0A 01 0A 0A 0A 0A 0A 0A 01 01 01 01 01 01 01 63 01 01 01 01 01 data_end: diff --git a/system/client-functions/BugFixes/BugFixes.3___.patch.s b/system/client-functions/BugFixes.s similarity index 79% rename from system/client-functions/BugFixes/BugFixes.3___.patch.s rename to system/client-functions/BugFixes.s index 62747066..d0ec239d 100644 --- a/system/client-functions/BugFixes/BugFixes.3___.patch.s +++ b/system/client-functions/BugFixes.s @@ -1,21 +1,27 @@ -.meta name="Bug fixes" -.meta description="Fixes many minor\ngameplay, sound,\nand graphical bugs" # Most original codes by Ralf @ GC-Forever and Aleron Ives, except where noted # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 +# Xbox ports by fuzziqersoftware +# TODO: Port the rest of the GC patches to Xbox + +.meta visibility="all" +.meta name="Bug fixes" +.meta description="Fixes many minor\ngameplay, sound,\nand graphical bugs" -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks + # Olga Flow Barta Bug Fix (makes barta work on ice weakness Olga Flow instead of damaging player) + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g1_hook_call, .label g1_hook_loc, 0x8000D980 .data g1_hook_loc @@ -34,10 +40,34 @@ g1_hook_end: .address g1_hook_call bl g1_hook_loc + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .label g1_hook_call, + .label g1_hook_loc, + .data g1_hook_call + .data 6 + .address g1_hook_call + mov eax, esi + cmp al, 19 + jmp g1_hook_loc +g1_hook_call_end: + .data g1_hook_loc + .deltaof g1_hook_start, g1_hook_end + .address g1_hook_loc +g1_hook_start: + jne g1_hook_skip_replace_value + mov al, 2 +g1_hook_skip_replace_value: + cmp eax, [ebx + 0x440] // Original opcode + jmp g1_hook_call_end +g1_hook_end: + # Morfos Frozen Player Bug Fix (stops Morfos Laser multi-hitting when player is frozen) + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g2_hook_call, .label g2_hook_loc, 0x8000D9A0 .data g2_hook_loc @@ -57,18 +87,62 @@ g2_hook_end: .address g2_hook_call bl g2_hook_loc + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .label g2_hook_call, + .label g2_hook_loc1, + .label g2_hook_loc2, + .data g2_hook_call + .data 6 + .address g2_hook_call + call g2_hook_loc1 + nop + .data g2_hook_loc1 + .deltaof g2_hook_start1, g2_hook_end1 + .address g2_hook_loc1 +g2_hook_start1: + fld1 st0 // st = [1.0, speed] + fld1 st0 // st = [1.0, 1.0, speed] + fadd st0, st1 // st = [2.0, 1.0, speed] + fdivp st1, st0 // st = [0.5, speed] + jmp g2_hook_loc2 +g2_hook_end1: + + .data g2_hook_loc2 + .deltaof g2_hook_start2, g2_hook_end2 + .address g2_hook_loc2 +g2_hook_start2: + test byte [esi + 0x30], 0x20 // If not set, use 1.5; if set, use 0.5 + jnz g2_hook_entity_is_frozen + fld1 st0 // st = [1, 0.5, speed] + faddp st1, st0 // st = [1.5, speed] +g2_hook_entity_is_frozen: + fmulp st1, st0 // st = [((game_flags & 0x20) ? 0.5 : 1.5) * speed] + ret +g2_hook_end2: + # Tiny Grass Assassins Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 b +0x10 + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .data + .data 0x00000002 + .binary EB0E + # Bulclaw HP Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 8 bl +0x024C @@ -78,6 +152,8 @@ g2_hook_end: # Control Tower: Delbiter Death SFX Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g3_patch_loc, .data g3_patch_loc .deltaof g3_code_start, g3_code_end @@ -101,6 +177,8 @@ g3_code_end: # Weapon Attributes Patch (allows attributes to work on minibosses and Olga Flow) + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g4_hook_call1, .label g4_hook_call2, .label g4_hook_loc, 0x8000C8C0 @@ -129,6 +207,8 @@ g4_hook_end: # Ruins Laser Fence SFX Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 8 lis r3, 0x4005 @@ -142,6 +222,8 @@ g4_hook_end: # SFX Cancellation Distance Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 .float 22500 @@ -154,6 +236,8 @@ g4_hook_end: # Foie SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -170,6 +254,8 @@ g4_hook_end: # Gifoie SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -186,6 +272,8 @@ g4_hook_end: # Rafoie SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -214,6 +302,8 @@ g4_hook_end: # Barta SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -230,6 +320,8 @@ g4_hook_end: # Gibarta SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -246,6 +338,8 @@ g4_hook_end: # Rabarta SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -262,6 +356,8 @@ g4_hook_end: # Zonde SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -278,6 +374,8 @@ g4_hook_end: # Gizonde SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -294,6 +392,8 @@ g4_hook_end: # Razonde SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -310,6 +410,8 @@ g4_hook_end: # Grants SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -326,6 +428,8 @@ g4_hook_end: # Megid SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r4, 0xFFFFFF00 @@ -342,6 +446,8 @@ g4_hook_end: # Anti SFX Pitch Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 cmpwi r0, 1 @@ -350,26 +456,46 @@ g4_hook_end: # Shield DFP/EVP Bug Fix (allows shields to reach true max DFP/EVP values) + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 lbz r0, [r4 + 0x0016] - .data .data 4 lbz r0, [r4 + 0x0017] + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .data + .data 0x00000001 + .binary 16 + .data + .data 0x00000001 + .binary 17 + # VR Spaceship Item Drop Bug Fix (allows items to drop from enemies above a certain Y position) + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 .float 220 + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .data + .data 0x00000002 + .data 0x435C0000 + # Invalid Items Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 0x0C mr r3, r0 @@ -392,6 +518,8 @@ g4_hook_end: # Item Removal Maxed Stats Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g5_hook1_call, .label g5_hook1_ret, .label g5_hook2_call, @@ -498,6 +626,8 @@ g5_hook4_end: # Unit Present Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g6_hook_loc, 0x8000C640 .label g6_hook_call, .data g6_hook_loc @@ -521,6 +651,8 @@ g6_hook_end: # Bank Item Stacking Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g7_hook1_loc, 0x8000C6D0 .label g7_hook1_call, .label g7_hook2_call, @@ -553,14 +685,27 @@ g7_hooks_end: # Dropped Mag Color Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 li r0, 0x12 + .versions 4OJB + + .data 0x001759E6 + .data 1 + .binary 12 + .data 0x00180898 + .data 1 + .binary 12 + # Meseta Drop System Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 b +0x0C @@ -573,16 +718,18 @@ g7_hooks_end: # Present Color Bug Fix - .only_versions 3OJ2 3OE0 3OE1 + .versions 3OJ2 3OE0 3OE1 + .data .data 4 nop - .all_versions # Offline Quests Drop Table Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 beq +0x0C @@ -591,6 +738,8 @@ g7_hooks_end: # Mag Revival Priority Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g8_hook_loc, 0x8000C8A0 .label g8_hook_call, .data g8_hook_loc @@ -613,6 +762,8 @@ g8_hook_end: # Mag Revival Challenge & Quest Mode Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 b +0x10 @@ -621,6 +772,8 @@ g8_hook_end: # Chat Bubble Window TAB Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 nop @@ -629,6 +782,8 @@ g8_hook_end: # Chat Log Window LF/Tab Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 nop @@ -637,6 +792,8 @@ g8_hook_end: # Dark/Hell Special GFX Bug Fix (makes Dark/Hell display graphic on success like in PSO BB) + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g9_hook_loc, 0x8000E1E0 .label g9_hook_call1, .label g9_hook_call2, @@ -675,14 +832,24 @@ g9_hook_end: # Gol Dragon Camera Bug Fix (makes the camera after Gol Dragon display "normally") + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 cmpwi r3, 1 + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .data + .data 0x00000002 + .binary 01 + # Box/Fence Fadeout Bug Fix (stops boxes and other environmental objects fading in and out as you approach) + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 nop @@ -691,30 +858,62 @@ g9_hook_end: .data 4 nop + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .data + .data 2 + nop + nop + + .data + .data 6 + nop + nop + nop + nop + nop + nop + # TP Bar Color Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 subi r4, r4, 0x5506 - .data .data 4 subi r3, r3, 0x5506 - .data .data 4 subi r4, r3, 0x5506 - .data .data 4 .data 0xFF0074EE + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .data + .data 0x00000004 + .data 0xFF00AAFA + .data + .data 0x00000004 + .data 0xFF00AAFA + .data + .data 0x00000004 + .data 0xFF00AAFA + .data + .data 0x00000004 + .data 0xFF0074EE + # Devil's and Demon's Special Damage Display Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 b -0x0340 @@ -723,6 +922,8 @@ g9_hook_end: # Christmas Trees Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g10_hook_loc, 0x8000B5C8 .label g10_hook_call, .label g10_hook_ret, @@ -750,15 +951,25 @@ g10_hook_end: # Rain Drops Color Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 8 .data 0x70808080 .data 0x60707070 + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .data + .data 0x00000008 + .binary 7080808060707070 + # Reverser Target Lock Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 addi r4, r31, 0x02FC @@ -767,11 +978,13 @@ g10_hook_end: # Deband/Shifta/Resta Target Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 bgt +0x0630 - .only_versions 3OJ2 3OE0 3OE1 + .versions 3OJ2 3OE0 3OE1 .data .data 4 bgt +0x033C @@ -779,12 +992,13 @@ g10_hook_end: .data .data 4 bgt +0x0248 - .all_versions # Tech Auto Targeting Bug Fix + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data .data 4 nop @@ -817,6 +1031,8 @@ g10_hook_end: # Enable Trap Animations + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label g11_hook_loc, 0x8000BBD0 .label g11_hook_call, .data g11_hook_loc @@ -846,7 +1062,8 @@ g11_hook_end: # Belra arm bug fix (this part by fuzziqersoftware) - .only_versions 3OJ2 3OE0 3OE1 + .versions 3OJ2 3OE0 3OE1 + .label g12_hook1_call, .label g12_hook2_call, .label g12_hook_loc, 0x8000B06C @@ -874,12 +1091,12 @@ g12_hook_end: .address g12_hook2_call bl g12_hook2_start - .all_versions - # Tsumikiri J-Sword special attack + rapid weapon switch bug fix (this part by fuzziqersoftware) + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .label tjs_switch_fix_hook_call, .label tjs_switch_fix_hook_loc, 0x8000B050 .data tjs_switch_fix_hook_loc @@ -905,7 +1122,8 @@ tjs_switch_fix_hook_end: # Battle param reload bug fix (this part by fuzziqersoftware) - .only_versions 3OJ2 3OE0 3OE1 + .versions 3OJ2 3OE0 3OE1 + .label end_loading_screen, .label load_battle_params, .label bp_reload_hook_loc, 0x8000E1BC @@ -928,9 +1146,10 @@ bp_reload_hook_end: .data 4 .address bp_reload_hook_call bl bp_reload_hook_start + + + .all_versions - - .data 0 .data 0 diff --git a/system/client-functions/BugFixes/BugFixes.4___.patch.s b/system/client-functions/BugFixes/BugFixes.4___.patch.s deleted file mode 100644 index e0a77ead..00000000 --- a/system/client-functions/BugFixes/BugFixes.4___.patch.s +++ /dev/null @@ -1,213 +0,0 @@ -.meta name="Bug fixes" -.meta description="Fixes many minor\ngameplay, sound,\nand graphical bugs" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 -# Xbox port by fuzziqersoftware - -# This patch is a collection of many smaller patches, most of which are not yet ported. - -.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksXB - - - - # Tiny Grass Assassins Bug Fix - - .data - .data 0x00000002 - .binary EB0E - - - - # Shield DFP/EVP Bug Fix (allows shields to reach true max DFP/EVP values) - - .data - .data 0x00000001 - .binary 16 - .data - .data 0x00000001 - .binary 17 - - - - # VR Spaceship Item Drop Bug Fix (allows items to drop from enemies above a certain Y position) - - .data - .data 0x00000002 - .data 0x435C0000 - - - - # Gol Dragon Camera Bug Fix (makes the camera after Gol Dragon display "normally") - - .data - .data 0x00000002 - .binary 01 - - - - # Rain Drops Color Bug Fix - - .data - .data 0x00000008 - .binary 7080808060707070 - - - - # TP Bar Color Bug Fix - - .data - .data 0x00000004 - .data 0xFF00AAFA - .data - .data 0x00000004 - .data 0xFF00AAFA - .data - .data 0x00000004 - .data 0xFF00AAFA - .data - .data 0x00000004 - .data 0xFF0074EE - - - - # Olga Flow Barta Bug Fix - - .label g1_hook_call, - .label g1_hook_loc, - .data g1_hook_call - .data 6 - .address g1_hook_call - mov eax, esi - cmp al, 19 - jmp g1_hook_loc -g1_hook_call_end: - .data g1_hook_loc - .deltaof g1_hook_start, g1_hook_end - .address g1_hook_loc -g1_hook_start: - jne g1_hook_skip_replace_value - mov al, 2 -g1_hook_skip_replace_value: - cmp eax, [ebx + 0x440] // Original opcode - jmp g1_hook_call_end -g1_hook_end: - - - - # Morfos Frozen Player Bug Fix - - .label g2_hook_call, - .label g2_hook_loc1, - .label g2_hook_loc2, - .data g2_hook_call - .data 6 - .address g2_hook_call - call g2_hook_loc1 - nop - .data g2_hook_loc1 - .deltaof g2_hook_start1, g2_hook_end1 - .address g2_hook_loc1 -g2_hook_start1: - fld1 st0 // st = [1.0, speed] - fld1 st0 // st = [1.0, 1.0, speed] - fadd st0, st1 // st = [2.0, 1.0, speed] - fdivp st1, st0 // st = [0.5, speed] - jmp g2_hook_loc2 -g2_hook_end1: - - .data g2_hook_loc2 - .deltaof g2_hook_start2, g2_hook_end2 - .address g2_hook_loc2 -g2_hook_start2: - test byte [esi + 0x30], 0x20 // If not set, use 1.5; if set, use 0.5 - jnz g2_hook_entity_is_frozen - fld1 st0 // st = [1, 0.5, speed] - faddp st1, st0 // st = [1.5, speed] -g2_hook_entity_is_frozen: - fmulp st1, st0 // st = [((game_flags & 0x20) ? 0.5 : 1.5) * speed] - ret -g2_hook_end2: - - - - # Dropped Mag Color Bug Fix (only needed on beta version) - - .only_versions 4OJB - .data 0x001759E6 - .data 1 - .binary 12 - .data 0x00180898 - .data 1 - .binary 12 - .all_versions - - - - # Box/Fence Fadeout Bug Fix - - .data - .data 2 - nop - nop - - .data - .data 6 - nop - nop - nop - nop - nop - nop - - - - # TODO: Port the rest of the patches in the GC version of BugFixes: - - # Bulclaw HP Bug Fix - # Weapon Attributes Patch - # Invalid Items Bug Fix - # Item Removal Maxed Stats Bug Fix - # Unit Present Bug Fix - # Bank Item Stacking Bug Fix - # Meseta Drop System Bug Fix - # Offline Quests Drop Table Bug Fix - # Mag Revival Priority Bug Fix - # Mag Revival Challenge & Quest Mode Bug Fix - # Reverser Target Lock Bug Fix - # Deband/Shifta/Resta Target Bug Fix - # Tech Auto Targeting Bug Fix - # Enable Trap Animations - # Tsumikiri J-Sword special attack + rapid weapon switch bug fix - - # Control Tower: Delbiter Death SFX Bug Fix - # Ruins Laser Fence SFX Bug Fix - # SFX Cancellation Distance Bug Fix - # Foie SFX Pitch Bug Fix - # Gifoie SFX Pitch Bug Fix - # Rafoie SFX Pitch Bug Fix - # Barta SFX Pitch Bug Fix - # Gibarta SFX Pitch Bug Fix - # Rabarta SFX Pitch Bug Fix - # Zonde SFX Pitch Bug Fix - # Gizonde SFX Pitch Bug Fix - # Razonde SFX Pitch Bug Fix - # Grants SFX Pitch Bug Fix - # Megid SFX Pitch Bug Fix - # Anti SFX Pitch Bug Fix - # Present Color Bug Fix - # Chat Bubble Window TAB Bug Fix - # Chat Log Window LF/Tab Bug Fix - # Dark/Hell Special GFX Bug Fix - # Devil's and Demon's Special Damage Display Bug Fix - # Christmas Trees Bug Fix - - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/CallProtectedHandler.s b/system/client-functions/CallProtectedHandler.s new file mode 100644 index 00000000..e4d479c6 --- /dev/null +++ b/system/client-functions/CallProtectedHandler.s @@ -0,0 +1,115 @@ +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start + + + +.versions 3OJT 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + +start: + stwu [r1 - 0x10], r1 + mflr r0 + stw [r1 + 0x14], r0 + stw [r1 + 0x08], r31 + stw [r1 + 0x0C], r30 + + b get_data_addr +resume: + mflr r31 + + lwz r30, [r31] + li r0, 1 + stw [r30], r0 + + addi r3, r31, 0x0C + lwz r4, [r31 + 8] + lwz r0, [r31 + 4] + mtctr r0 + bctrl + + li r0, 0 + stw [r30], r0 + + lwz r30, [r1 + 0x0C] + lwz r31, [r1 + 0x08] + lwz r0, [r1 + 0x14] + mtlr r0 + addi r1, r1, 0x10 + blr + +get_data_addr: + bl resume + # allow_local_client_commands + .data + # RcvPsoData2 + .data + + + +.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + +start: + jmp get_data_addr +resume: + xchg ebx, [esp] + + mov edx, [ebx] + mov dword [edx], 1 + + mov edx, [ebx + 4] + lea ecx, [ebx + 0x0C] + mov eax, [ebx + 8] + call edx + + mov edx, [ebx] + mov dword [edx], 0 + + pop ebx + ret + +get_data_addr: + call resume + .data + .data + + + +.versions 59NJ 59NL + +start: + jmp get_data_addr +resume: + xchg ebx, [esp] + + mov edx, [ebx] + mov dword [edx], 1 + + mov edx, [ebx + 4] + push dword [ebx + 8] + lea ecx, [ebx + 0x0C] + push ecx + call edx # RcvPsoData2(data, size) + add esp, 8 + + mov edx, [ebx] + mov dword [edx], 0 + + pop ebx + ret + +get_data_addr: + call resume + + .data # should_allow_protected_commands + .data # RcvPsoData2[std](void* data @ [esp + 4], uint32_t size @ [esp + 8]) + + + +.all_versions + +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler/CallProtectedHandler.3___.patch.s b/system/client-functions/CallProtectedHandler/CallProtectedHandler.3___.patch.s deleted file mode 100644 index a7fb3e20..00000000 --- a/system/client-functions/CallProtectedHandler/CallProtectedHandler.3___.patch.s +++ /dev/null @@ -1,50 +0,0 @@ -.meta hide_from_patches_menu -.meta name="CallProtectedHandler" -.meta description="" - -.versions 3OJT 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start -start: - stwu [r1 - 0x10], r1 - mflr r0 - stw [r1 + 0x14], r0 - stw [r1 + 0x08], r31 - stw [r1 + 0x0C], r30 - - b get_data_addr -resume: - mflr r31 - - lwz r30, [r31] - li r0, 1 - stw [r30], r0 - - addi r3, r31, 0x0C - lwz r4, [r31 + 8] - lwz r0, [r31 + 4] - mtctr r0 - bctrl - - li r0, 0 - stw [r30], r0 - - lwz r30, [r1 + 0x0C] - lwz r31, [r1 + 0x08] - lwz r0, [r1 + 0x14] - mtlr r0 - addi r1, r1, 0x10 - blr - -get_data_addr: - bl resume - # allow_local_client_commands - .data - # RcvPsoData2 - .data - -size: - .data 0x00000000 -data: diff --git a/system/client-functions/CallProtectedHandler/CallProtectedHandler.4___.patch.s b/system/client-functions/CallProtectedHandler/CallProtectedHandler.4___.patch.s deleted file mode 100644 index 35ce2cf9..00000000 --- a/system/client-functions/CallProtectedHandler/CallProtectedHandler.4___.patch.s +++ /dev/null @@ -1,36 +0,0 @@ -.meta hide_from_patches_menu -.meta name="CallProtectedHandler" -.meta description="" - -.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start -start: - jmp get_data_addr -resume: - xchg ebx, [esp] - - mov edx, [ebx] - mov dword [edx], 1 - - mov edx, [ebx + 4] - lea ecx, [ebx + 0x0C] - mov eax, [ebx + 8] - call edx - - mov edx, [ebx] - mov dword [edx], 0 - - pop ebx - ret - -get_data_addr: - call resume - .data - .data - -size: - .data 0x00000000 -data: diff --git a/system/client-functions/CallProtectedHandler/CallProtectedHandler.5___.patch.s b/system/client-functions/CallProtectedHandler/CallProtectedHandler.5___.patch.s deleted file mode 100644 index 31c391fc..00000000 --- a/system/client-functions/CallProtectedHandler/CallProtectedHandler.5___.patch.s +++ /dev/null @@ -1,38 +0,0 @@ -.meta hide_from_patches_menu -.meta name="CallProtectedHandler" -.meta description="" - -.versions 59NJ 59NL - -entry_ptr: -reloc0: - .offsetof start -start: - jmp get_data_addr -resume: - xchg ebx, [esp] - - mov edx, [ebx] - mov dword [edx], 1 - - mov edx, [ebx + 4] - push dword [ebx + 8] - lea ecx, [ebx + 0x0C] - push ecx - call edx # RcvPsoData2(data, size) - add esp, 8 - - mov edx, [ebx] - mov dword [edx], 0 - - pop ebx - ret - -get_data_addr: - call resume - - .data # should_allow_protected_commands - .data # RcvPsoData2[std](void* data @ [esp + 4], uint32_t size @ [esp + 8]) -size: - .data 0x00000000 -data: diff --git a/system/client-functions/ChatFeatures/ChatFeatures.3___.patch.s b/system/client-functions/ChatFeatures.s similarity index 97% rename from system/client-functions/ChatFeatures/ChatFeatures.3___.patch.s rename to system/client-functions/ChatFeatures.s index 2481148f..1731944d 100644 --- a/system/client-functions/ChatFeatures/ChatFeatures.3___.patch.s +++ b/system/client-functions/ChatFeatures.s @@ -1,16 +1,18 @@ -.meta name="Chat" -.meta description="Enables extended\nWord Select and\nstops the Log\nWindow from\nscrolling with L+R" # Original codes by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 +.meta visibility="all" +.meta name="Chat" +.meta description="Enables extended\nWord Select and\nstops the Log\nWindow from\nscrolling with L+R" + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 3SJT 3SJ0 3SE0 3SP0 entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks # Extended Word Select Menu (PSO PCv2 Style) .data diff --git a/system/client-functions/CommonBank/CommonBank.3___.patch.s b/system/client-functions/CommonBank.s similarity index 98% rename from system/client-functions/CommonBank/CommonBank.3___.patch.s rename to system/client-functions/CommonBank.s index 6057ac03..e8e96dbd 100644 --- a/system/client-functions/CommonBank/CommonBank.3___.patch.s +++ b/system/client-functions/CommonBank.s @@ -1,16 +1,18 @@ -.meta name="Common bank" -.meta description="Hold L and open\nthe bank to use a\ncommon bank stored\nin temp character\n3's data" # Original code by Ralf @ GC-Forever ("Common Bank (Hold L And Open Bank)") # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 +.meta visibility="all" +.meta name="Common bank" +.meta description="Hold L and open\nthe bank to use a\ncommon bank stored\nin temp character\n3's data" + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks .data 0x8000BAB4 .deltaof hook1, hooks_end diff --git a/system/client-functions/ReticleColors/DCReticleColors.3___.patch.s b/system/client-functions/DCReticleColors.s similarity index 58% rename from system/client-functions/ReticleColors/DCReticleColors.3___.patch.s rename to system/client-functions/DCReticleColors.s index 145a357c..366ca8e1 100644 --- a/system/client-functions/ReticleColors/DCReticleColors.3___.patch.s +++ b/system/client-functions/DCReticleColors.s @@ -1,16 +1,21 @@ -.meta name="DC targets" -.meta description="Changes the target\nreticle colors to\nthose used on the\nDreamcast" # Original code by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 +.meta visibility="all" +.meta name="DC targets" +.meta description="Changes the target\nreticle colors to\nthose used on the\nDreamcast" + entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks + + + + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 .data .data 0x00000004 @@ -54,5 +59,52 @@ start: .float 0.1 .float 0.1 + + + .versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU + + .data + .data 0x00000004 + .data 0x00FF0000 + + .data + .data 0x00000004 + .data 0x000000FF + + .data + .data 0x00000004 + .data 0x00FFFF00 + + .data + .data 0x00000060 + .data 0x3F800000 + .data 0x3F800000 + .data 0x00000000 + .data 0x00000000 + .data 0x3F800000 + .data 0x3F800000 + .data 0x00000000 + .data 0x00000000 + .data 0x3F800000 + .data 0x3F800000 + .data 0x3F800000 + .data 0x00000000 + .data 0x3F800000 + .data 0x00000000 + .data 0x00000000 + .data 0x3F800000 + .data 0x3F800000 + .data 0x3ECCCCCD + .data 0x3DCCCCCD + .data 0x3DCCCCCD + .data 0x3F800000 + .data 0x00000000 + .data 0x00000000 + .data 0x00000000 + + + + .all_versions + .data 0x00000000 .data 0x00000000 diff --git a/system/client-functions/Debug/CreateObject.3OE1.patch.s b/system/client-functions/Debug/CreateObject.3OE1.patch.s deleted file mode 100644 index 430b9190..00000000 --- a/system/client-functions/Debug/CreateObject.3OE1.patch.s +++ /dev/null @@ -1,52 +0,0 @@ -.meta hide_from_patches_menu -.meta name="CreateObject" -.meta description="" - -entry_ptr: -reloc0: - .offsetof start -start: - mflr r0 - b get_data -get_data_ret: - mflr r3 - mtlr r0 - lwz r0, [r3] - mtctr r0 - addi r3, r3, 4 - bctr - -get_data: - bl get_data_ret - .data 0x8020C158 # construct_dat_object_from_args -base_type_high: - .data 0xFFFF0000 # base_type, set_flags -floor_low: - .data 0x0000FFFF # index, floor - .data 0x00000000 # entity_id, group - .data 0x00000000 # room, unknown_a3 -pos_x: - .float 0.0 # pos.x -pos_y: - .float 0.0 # pos.y -pos_z: - .float 0.0 # pos.z -angle_x: - .data 0x00000000 # angle.x -angle_y: - .data 0x00000000 # angle.y -angle_z: - .data 0x00000000 # angle.z -param1: - .float 0.0 # param1 -param2: - .float 0.0 # param2 -param3: - .float 0.0 # param3 -param4: - .data 0 # param4 -param5: - .data 0 # param5 -param6: - .data 0 # param6 - .data 0 # unused_obj_ptr diff --git a/system/client-functions/Debug/CreateObject.3SE0.patch.s b/system/client-functions/Debug/CreateObject.s similarity index 90% rename from system/client-functions/Debug/CreateObject.3SE0.patch.s rename to system/client-functions/Debug/CreateObject.s index 1aba194c..b261a562 100644 --- a/system/client-functions/Debug/CreateObject.3SE0.patch.s +++ b/system/client-functions/Debug/CreateObject.s @@ -1,7 +1,8 @@ -.meta hide_from_patches_menu .meta name="CreateObject" .meta description="" +.versions 3OE1 3SE0 + entry_ptr: reloc0: .offsetof start @@ -18,7 +19,7 @@ get_data_ret: get_data: bl get_data_ret - .data 0x80056D6C # construct_dat_object_from_args + .data # construct_dat_object_from_args base_type_high: .data 0xFFFF0000 # base_type, set_flags floor_low: diff --git a/system/client-functions/Debug/FogDebug.3OE1.patch.s b/system/client-functions/Debug/FogDebug.s similarity index 97% rename from system/client-functions/Debug/FogDebug.3OE1.patch.s rename to system/client-functions/Debug/FogDebug.s index 0b929432..d5c3ee2b 100644 --- a/system/client-functions/Debug/FogDebug.3OE1.patch.s +++ b/system/client-functions/Debug/FogDebug.s @@ -1,12 +1,13 @@ -.meta hide_from_patches_menu .meta name="Player flags" .meta description="" +.versions 3OE1 + entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks .label check_controller_button, 0x801A6C68 # [std](ControllerState* st, uint32_t flags) -> bool .label TFogCtrl_change_fog, 0x800FB10C # [std](TFogCtrl* this, uint32_t fog_num, uint32_t instant_transition) -> void diff --git a/system/client-functions/Debug/MovementDebug.3OE1.patch.s b/system/client-functions/Debug/MovementDebug.s similarity index 98% rename from system/client-functions/Debug/MovementDebug.3OE1.patch.s rename to system/client-functions/Debug/MovementDebug.s index 6d9e801d..e1e685fe 100644 --- a/system/client-functions/Debug/MovementDebug.3OE1.patch.s +++ b/system/client-functions/Debug/MovementDebug.s @@ -1,8 +1,9 @@ -.meta hide_from_patches_menu .meta name="MovementDebug" .meta description="" .meta show_return_value +.versions 3OE1 + # Usage examples: # Read movement data 09 fparam1: # $patch MovementDebug e=0x09 f=1 r=1 diff --git a/system/client-functions/Debug/PlayerFlags.3OE1.patch.s b/system/client-functions/Debug/PlayerFlags.s similarity index 96% rename from system/client-functions/Debug/PlayerFlags.3OE1.patch.s rename to system/client-functions/Debug/PlayerFlags.s index 7d0273a3..6ba555c9 100644 --- a/system/client-functions/Debug/PlayerFlags.3OE1.patch.s +++ b/system/client-functions/Debug/PlayerFlags.s @@ -1,12 +1,13 @@ -.meta hide_from_patches_menu .meta name="Player flags" .meta description="" +.versions 3OE1 + entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks .label TObjPlayer_for_client_id, 0x801BA59C # [std](uint32_t client_id) .label render_debug_printf, 0x803D4E3C # [std](uint32_t coords, const char* fmt, ...); diff --git a/system/client-functions/Decoction/Decoction.4___.patch.s b/system/client-functions/Decoction.s similarity index 61% rename from system/client-functions/Decoction/Decoction.4___.patch.s rename to system/client-functions/Decoction.s index a4adb079..606fcff4 100644 --- a/system/client-functions/Decoction/Decoction.4___.patch.s +++ b/system/client-functions/Decoction.s @@ -1,17 +1,69 @@ -.meta name="Decoction" -.meta description="Makes the Decoction\nitem reset your\nmaterial usage" # Original code by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 # Xbox port by fuzziqersoftware -.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU +.meta visibility="all" +.meta name="Decoction" +.meta description="Makes the Decoction\nitem reset your\nmaterial usage" + entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksXB + .include WriteCodeBlocks + + + + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + + .data + .data 0x00000098 + .address + lbz r0, [r3 + 0xEE] + cmplwi r0, 11 + bne +0x144 + lwz r31, [r3 + 0xF0] + li r0, 0 + nop + li r4, 0x0374 + li r5, 0x0D38 + bl +0x58 + li r5, 0x0D3A + bl +0x50 + li r5, 0x0D3C + bl +0x48 + li r5, 0x0D40 + bl +0x40 + li r5, 0x0D44 + bl +0x38 + mr r3, r31 + .data + lhz r0, [r31 + 0x032C] + lhz r3, [r31 + 0x02B8] + cmpl r0, r3 + ble +0x08 + sth [r31 + 0x032C], r3 + lhz r0, [r31 + 0x032E] + lhz r3, [r31 + 0x02BA] + cmpl r0, r3 + ble +0x08 + sth [r31 + 0x032E], r3 + b +0xD8 + lbzx r6, [r31 + r4] + lhzx r7, [r31 + r5] + rlwinm r6, r6, 1, 0, 30 + subf r7, r6, r7 + sthx [r31 + r5], r7 + stbx [r31 + r4], r0 + addi r4, r4, 0x0001 + blr + + + + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + .data .deltaof code_start, code_end .address @@ -63,7 +115,11 @@ next_stat: pop esi skip_all: ret - code_end: + + + + .all_versions + .data 0x00000000 .data 0x00000000 diff --git a/system/client-functions/Decoction/Decoction.3___.patch.s b/system/client-functions/Decoction/Decoction.3___.patch.s deleted file mode 100644 index 66aacff3..00000000 --- a/system/client-functions/Decoction/Decoction.3___.patch.s +++ /dev/null @@ -1,58 +0,0 @@ -.meta name="Decoction" -.meta description="Makes the Decoction\nitem reset your\nmaterial usage" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - .data - .data 0x00000098 - .address - lbz r0, [r3 + 0xEE] - cmplwi r0, 11 - bne +0x144 - lwz r31, [r3 + 0xF0] - li r0, 0 - nop - li r4, 0x0374 - li r5, 0x0D38 - bl +0x58 - li r5, 0x0D3A - bl +0x50 - li r5, 0x0D3C - bl +0x48 - li r5, 0x0D40 - bl +0x40 - li r5, 0x0D44 - bl +0x38 - mr r3, r31 - .data - lhz r0, [r31 + 0x032C] - lhz r3, [r31 + 0x02B8] - cmpl r0, r3 - ble +0x08 - sth [r31 + 0x032C], r3 - lhz r0, [r31 + 0x032E] - lhz r3, [r31 + 0x02BA] - cmpl r0, r3 - ble +0x08 - sth [r31 + 0x032E], r3 - b +0xD8 - lbzx r6, [r31 + r4] - lhzx r7, [r31 + r5] - rlwinm r6, r6, 1, 0, 30 - subf r7, r6, r7 - sthx [r31 + r5], r7 - stbx [r31 + r4], r0 - addi r4, r4, 0x0001 - blr - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/DisableIdleDisconnect.s b/system/client-functions/DisableIdleDisconnect.s new file mode 100644 index 00000000..84edab39 --- /dev/null +++ b/system/client-functions/DisableIdleDisconnect.s @@ -0,0 +1,47 @@ +.meta visibility="all" +.meta name="Disable idle DC" +.meta description="Disables the idle\ndisconnect timeout" + +entry_ptr: +reloc0: + .offsetof start +start: + .include WriteCodeBlocks + + + + .versions 1OJ3 1OJ4 1OJF 1OEF 1OPF 2OJ4 2OJ5 2OJF 2OEF 2OPF + .align 4 + .data + .data 0x00000002 + mov r0, 0 + .align 4 + + + + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 3SJT 3SJ0 3SE0 3SP0 + .data + .data 0x00000004 + li r3, 0 + + + + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + .data + .data 0x00000004 + xor ecx, ecx + jmp +3 + + + + .versions 59NJ 59NL + .data + .data 0x00000005 + mov eax, 0 + + + + .all_versions + + .data 0x00000000 + .data 0x00000000 diff --git a/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.1___.patch.s b/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.1___.patch.s deleted file mode 100644 index 49370e03..00000000 --- a/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.1___.patch.s +++ /dev/null @@ -1,19 +0,0 @@ -.meta name="Disable idle DC" -.meta description="Disables the idle\ndisconnect timeout" - -.versions 1OJ3 1OJ4 1OJF 1OEF 1OPF 2OJ4 2OJ5 2OJF 2OEF 2OPF - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksDC - - .align 4 - .data - .data 0x00000002 - mov r0, 0 - - .align 4 - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.3___.patch.s b/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.3___.patch.s deleted file mode 100644 index 0cca3715..00000000 --- a/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.3___.patch.s +++ /dev/null @@ -1,17 +0,0 @@ -.meta name="Disable idle DC" -.meta description="Disables the idle\ndisconnect timeout" - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 3SJT 3SJ0 3SE0 3SP0 - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - .data - .data 0x00000004 - li r3, 0 - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.4___.patch.s b/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.4___.patch.s deleted file mode 100644 index de77fa33..00000000 --- a/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.4___.patch.s +++ /dev/null @@ -1,18 +0,0 @@ -.meta name="Disable idle DC" -.meta description="Disables the idle\ndisconnect timeout" - -.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksXB - - .data - .data 0x00000004 - xor ecx, ecx - jmp +3 - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.5___.patch.s b/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.5___.patch.s deleted file mode 100644 index 456c5e47..00000000 --- a/system/client-functions/DisableIdleDisconnect/DisableIdleDisconnect.5___.patch.s +++ /dev/null @@ -1,17 +0,0 @@ -.meta name="Disable idle DC" -.meta description="Disables the idle\ndisconnect timeout" - -.versions 59NJ 59NL - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksBB - - .data - .data 0x00000005 - mov eax, 0 - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/DrawDistance.s b/system/client-functions/DrawDistance.s new file mode 100644 index 00000000..1583b71d --- /dev/null +++ b/system/client-functions/DrawDistance.s @@ -0,0 +1,367 @@ +# Original code by Ralf @ GC-Forever and Aleron Ives +# https://www.gc-forever.com/forums/viewtopic.php?t=2050 +# https://www.gc-forever.com/forums/viewtopic.php?t=2049 +# Xbox port by fuzziqersoftware + +# BB notes: +# Currently beta quality, map objects that fade like boxes, and Pioneer's background billboards and elevators still +# have regular draw distance. +# TODO: 90% of stuff is included, bring home the last 10%. + +.meta visibility="all" +.meta name="Draw Distance" +.meta description="Extends the draw\ndistance of many\nobjects" + +entry_ptr: +reloc0: + .offsetof start + + + +.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + +start: + .include WriteCodeBlocks + + .data 0x8000DFA0 + .deltaof hook_start, hook_end + .address 0x8000DFA0 +hook_start: +hook1: + lfs f30, [r2 - ] + fmuls f30, f30, f1 + blr +hook2: + lfs f2, [r2 - ] + lfs f0, [r30 + 0x001C] + fmuls f0, f0, f2 + blr +hook3: + lfs f28, [r2 - ] + fmuls f28, f28, f2 + blr +hook4: + lfs f0, [r2 - ] + lfs f1, [r3 + 0x000C] + fmuls f0, f0, f1 + stfs [r3 + 0x000C], f0 + lis r3, + blr +hook_end: + + .data + .data 0x00000004 + .address + bl hook1 + + .data + .data 0x00000004 + .address + bl hook2 + + .data + .data 0x00000004 + .address + bl hook3 + + .data + .data 0x00000004 + .address + bl hook1 + + .data + .data 0x00000004 + .address + bl hook4 + + .data + .data 0x00000004 + .address + bl hook4 + + .data + .data 0x00000004 + .float 90000 + + .data + .data 0x00000004 + .float 62500 + + .data + .data 0x00000004 + .float 640000 + + .data + .data 0x00000004 + .float 90000 + + .data + .data 0x00000004 + .float 1400 + + .data 0x00000000 + .data 0x00000000 + + + +.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU + +start: + .include WriteCodeBlocks + + .data # From 3OE1:80100B8C + .deltaof p1_1s, p1_1e + .address # From 3OE1:80100B8C +p1_1s: + call p1_2s + nop +p1_1e: + .data + .deltaof p1_2s, p1_2e + .address +p1_2s: + fld st0, dword [esp + 0x1C] + fadd st0, st0 + fchs st0 + ret +p1_2e: + + .data # From 3OE1:80156AD8 + .deltaof p2_1s, p2_1e + .address # From 3OE1:80156AD8 +p2_1s: + call p2_2s +p2_1e: + .data + .deltaof p2_2s, p2_2e + .address +p2_2s: + fld st0, dword [ecx + 0x1C] + fadd st0, st0 + fld st0, st1 + ret +p2_2e: + + .data # From 3OE1:801A2040 + .deltaof p3_1s, p3_1e + .address # From 3OE1:801A2040 +p3_1s: + call p3_2s + nop +p3_1e: + .data + .deltaof p3_2s, p3_2e + .address +p3_2s: + fld st0, dword [esp + 0x24] + fadd st0, st0 + fchs st0 + ret +p3_2e: + + .data # From 3OE1:801A2240 + .deltaof p4_1s, p4_1e + .address # From 3OE1:801A2240 +p4_1s: + call p4_2s + nop +p4_1e: + .data + .deltaof p4_2s, p4_2e + .address +p4_2s: + fld st0, dword [esp + 0x28] + fadd st0, st0 + fchs st0 + ret +p4_2e: + + .data # From 3OE1:80205840 + .deltaof p5_1s, p5_1e + .address # From 3OE1:80205840 +p5_1s: + call p5_3s +p5_1e: + .data # From 3OE1:80205FE4 + .deltaof p5_2s, p5_2e + .address # From 3OE1:80205FE4 +p5_2s: + call p5_3s +p5_2e: + .data + .deltaof p5_3s, p5_3e + .address +p5_3s: + fld st0, dword [eax + 0x0C] + fadd st0, st0 + fstp dword [eax + 0x0C], st0 + mov eax, [] + ret +p5_3e: + + .data # From 3OE1:805CFCD0 + .data 0x00000004 + .data 0x47AFC800 + + .data # From 3OE1:805D0B7C + .data 0x00000004 + .data 0x437A0000 + + .data # From 3OE1:805D11A4 + .data 0x00000004 + .data 0x491C4000 + + .data # From 3OE1:805D2030 + .data 0x00000004 + .data 0x47AFC800 + + .data # From 3OE1:805D25C0 + .data 0x00000004 + .data 0x44AF0000 + + .data # From 3OE1:805D25C0 + .data 0x00000004 + .data 0x44AF0000 + + .data 0x00000000 + .data 0x00000000 + + + +.versions 59NJ 59NL + +write_call_func: + .include WriteCallToCode + +start: + mov eax, 0x41800000 # Environment clip distance mod 16.0f + mov [], eax # This affects mostly static map objects + mov [], eax + mov [], eax + + mov ax, 0x9090 + mov [], ax # Players draw distance 10000.0f always + mov eax, 0x41000000 # Use newly acquired skipped branch room + mov [], eax # to store our float multiplier + + call patch_func_1 # Floor items + call patch_func_2 # Whole bunch of stuff, including NPCs + call patch_func_3 # Duplicate function from above, reuse same hook + call patch_func_4 # TODO: Which objects this affects? + call patch_func_5 # TODO: This one too? + call patch_func_6 # TODO: And this one? + ret + +# Floor items +patch_func_1: + pop ecx + push 8 + push + call get_code_size1 + .deltaof patch_code1, patch_code_end1 +get_code_size1: + pop eax + push dword [eax] + call patch_code_end1 +patch_code1: + mov edx, [esp + 0x18] + fld st0, dword [] + fld st0, dword [esp + 0x14] + fmulp st1, st0 + ret +patch_code_end1: + push ecx + jmp write_call_func + +# Whole bunch of stuff, including NPCs +patch_func_2: + pop ecx + push 9 + push + call get_code_size2 + .deltaof patch_code2, patch_code_end2 +get_code_size2: + pop eax + push dword [eax] + call patch_code_end2 +patch_code2: + test eax, 0x400 + fld st0, dword [] + fld st0, dword [esp + 0x2C] + fmulp st1, st0 + ret +patch_code_end2: + push ecx + jmp write_call_func + +# Duplicate function from above, reuse same hook +patch_func_3: + mov eax, dword [] + add eax, 0x002A1C74 + mov dword [], eax + mov byte [], 0xE8 + mov dword [], 0x90909090 + ret + +# TOComputerMachine01 +patch_func_4: + pop ecx + push 7 + push + call get_code_size4 + .deltaof patch_code4, patch_code_end4 +get_code_size4: + pop eax + push dword [eax] + call patch_code_end4 +patch_code4: + lea edx, [edi + 0x38] + fld st0, dword [] + fld st0, dword [esp + 0x14] + fmulp st1, st0 + ret +patch_code_end4: + push ecx + jmp write_call_func + +# TObjCamera +patch_func_5: + pop ecx + push 6 + push + call get_code_size5 + .deltaof patch_code5, patch_code_end5 +get_code_size5: + pop eax + push dword [eax] + call patch_code_end5 +patch_code5: + fld st0, dword [] + fld st0, dword [esp + 0x28] + fmulp st1, st0 + fchs st0 + ret +patch_code_end5: + push ecx + jmp write_call_func + +# TODO: And this one? +patch_func_6: + pop ecx + push 6 + push + call get_code_size6 + .deltaof patch_code6, patch_code_end6 +get_code_size6: + pop eax + push dword [eax] + call patch_code_end6 +patch_code6: + mov ebp, ecx + fld st0, dword [] + fld st0, dword [esp + 0x30] + fmulp st1, st0 + ret +patch_code_end6: + push ecx + jmp write_call_func diff --git a/system/client-functions/DrawDistance/DrawDistance.3___.patch.s b/system/client-functions/DrawDistance/DrawDistance.3___.patch.s deleted file mode 100644 index 17d31d56..00000000 --- a/system/client-functions/DrawDistance/DrawDistance.3___.patch.s +++ /dev/null @@ -1,92 +0,0 @@ -.meta name="Draw Distance" -.meta description="Extends the draw\ndistance of many\nobjects" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - .data 0x8000DFA0 - .deltaof hook_start, hook_end - .address 0x8000DFA0 -hook_start: -hook1: - lfs f30, [r2 - ] - fmuls f30, f30, f1 - blr -hook2: - lfs f2, [r2 - ] - lfs f0, [r30 + 0x001C] - fmuls f0, f0, f2 - blr -hook3: - lfs f28, [r2 - ] - fmuls f28, f28, f2 - blr -hook4: - lfs f0, [r2 - ] - lfs f1, [r3 + 0x000C] - fmuls f0, f0, f1 - stfs [r3 + 0x000C], f0 - lis r3, - blr -hook_end: - - .data - .data 0x00000004 - .address - bl hook1 - - .data - .data 0x00000004 - .address - bl hook2 - - .data - .data 0x00000004 - .address - bl hook3 - - .data - .data 0x00000004 - .address - bl hook1 - - .data - .data 0x00000004 - .address - bl hook4 - - .data - .data 0x00000004 - .address - bl hook4 - - .data - .data 0x00000004 - .float 90000 - - .data - .data 0x00000004 - .float 62500 - - .data - .data 0x00000004 - .float 640000 - - .data - .data 0x00000004 - .float 90000 - - .data - .data 0x00000004 - .float 1400 - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/DrawDistance/DrawDistance.4___.patch.s b/system/client-functions/DrawDistance/DrawDistance.4___.patch.s deleted file mode 100644 index 40e25dd0..00000000 --- a/system/client-functions/DrawDistance/DrawDistance.4___.patch.s +++ /dev/null @@ -1,131 +0,0 @@ -.meta name="Draw Distance" -.meta description="Extends the draw\ndistance of many\nobjects" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 -# Xbox port by fuzziqersoftware - -.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksXB - - .data # From 3OE1:80100B8C - .deltaof p1_1s, p1_1e - .address # From 3OE1:80100B8C -p1_1s: - call p1_2s - nop -p1_1e: - .data - .deltaof p1_2s, p1_2e - .address -p1_2s: - fld st0, dword [esp + 0x1C] - fadd st0, st0 - fchs st0 - ret -p1_2e: - - .data # From 3OE1:80156AD8 - .deltaof p2_1s, p2_1e - .address # From 3OE1:80156AD8 -p2_1s: - call p2_2s -p2_1e: - .data - .deltaof p2_2s, p2_2e - .address -p2_2s: - fld st0, dword [ecx + 0x1C] - fadd st0, st0 - fld st0, st1 - ret -p2_2e: - - .data # From 3OE1:801A2040 - .deltaof p3_1s, p3_1e - .address # From 3OE1:801A2040 -p3_1s: - call p3_2s - nop -p3_1e: - .data - .deltaof p3_2s, p3_2e - .address -p3_2s: - fld st0, dword [esp + 0x24] - fadd st0, st0 - fchs st0 - ret -p3_2e: - - .data # From 3OE1:801A2240 - .deltaof p4_1s, p4_1e - .address # From 3OE1:801A2240 -p4_1s: - call p4_2s - nop -p4_1e: - .data - .deltaof p4_2s, p4_2e - .address -p4_2s: - fld st0, dword [esp + 0x28] - fadd st0, st0 - fchs st0 - ret -p4_2e: - - .data # From 3OE1:80205840 - .deltaof p5_1s, p5_1e - .address # From 3OE1:80205840 -p5_1s: - call p5_3s -p5_1e: - .data # From 3OE1:80205FE4 - .deltaof p5_2s, p5_2e - .address # From 3OE1:80205FE4 -p5_2s: - call p5_3s -p5_2e: - .data - .deltaof p5_3s, p5_3e - .address -p5_3s: - fld st0, dword [eax + 0x0C] - fadd st0, st0 - fstp dword [eax + 0x0C], st0 - mov eax, [] - ret -p5_3e: - - .data # From 3OE1:805CFCD0 - .data 0x00000004 - .data 0x47AFC800 - - .data # From 3OE1:805D0B7C - .data 0x00000004 - .data 0x437A0000 - - .data # From 3OE1:805D11A4 - .data 0x00000004 - .data 0x491C4000 - - .data # From 3OE1:805D2030 - .data 0x00000004 - .data 0x47AFC800 - - .data # From 3OE1:805D25C0 - .data 0x00000004 - .data 0x44AF0000 - - .data # From 3OE1:805D25C0 - .data 0x00000004 - .data 0x44AF0000 - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/DrawDistance/DrawDistance.59NJ.patch.s b/system/client-functions/DrawDistance/DrawDistance.59NJ.patch.s deleted file mode 100644 index 02d785f7..00000000 --- a/system/client-functions/DrawDistance/DrawDistance.59NJ.patch.s +++ /dev/null @@ -1,146 +0,0 @@ -# Currently beta quality, map objects that fade like boxes, and Pioneer's -# background billboards and elevators still have regular draw distance. -# TODO: 90% of stuff is included, bring home the last 10%. - -.meta name="Draw Distance" -.meta description="Extends the draw\ndistance of many\nobjects" - -entry_ptr: -reloc0: - .offsetof start - -write_call_func: - .include WriteCallToCode-59NJ - -start: - mov eax, 0x41800000 # Environment clip distance mod 16.0f - mov [0x0097D198], eax # This affects mostly static map objects - mov [0x0097D19C], eax - mov [0x00097D1A0], eax - - mov ax, 0x9090 - mov [0x00689BC7], ax # Players draw distance 10000.0f always - mov eax, 0x41000000 # Use newly acquired skipped branch room - mov [0x00689BD1], eax # to store our float multiplier - - call patch_func_1 # Floor items - call patch_func_2 # Whole bunch of stuff, including NPCs - call patch_func_3 # Duplicate function from above, reuse same hook - call patch_func_4 # TODO: Which objects this affects? - call patch_func_5 # TODO: This one too? - call patch_func_6 # TODO: And this one? - ret - -# Floor items -patch_func_1: - pop ecx - push 8 - push 0x005C525B - call get_code_size1 - .deltaof patch_code1, patch_code_end1 -get_code_size1: - pop eax - push dword [eax] - call patch_code_end1 -patch_code1: - mov edx, [esp + 0x18] - fld st0, dword [0x00689BD1] - fld st0, dword [esp + 0x14] - fmulp st1, st0 - ret -patch_code_end1: - push ecx - jmp write_call_func - -# Whole bunch of stuff, including NPCs -patch_func_2: - pop ecx - push 9 - push 0x007BB21E - call get_code_size2 - .deltaof patch_code2, patch_code_end2 -get_code_size2: - pop eax - push dword [eax] - call patch_code_end2 -patch_code2: - test eax, 0x400 - fld st0, dword [0x00689BD1] - fld st0, dword [esp + 0x2C] - fmulp st1, st0 - ret -patch_code_end2: - push ecx - jmp write_call_func - -# Duplicate function from above, reuse same hook -patch_func_3: - mov eax, dword [0x007BB21F] - add eax, 0x002A1C74 - mov dword [0x00518843], eax - mov byte [0x00518842], 0xE8 - mov dword [0x00518847], 0x90909090 - ret - -# TOComputerMachine01 -patch_func_4: - pop ecx - push 7 - push 0x00616FF4 - call get_code_size4 - .deltaof patch_code4, patch_code_end4 -get_code_size4: - pop eax - push dword [eax] - call patch_code_end4 -patch_code4: - lea edx, [edi + 0x38] - fld st0, dword [0x00689BD1] - fld st0, dword [esp + 0x14] - fmulp st1, st0 - ret -patch_code_end4: - push ecx - jmp write_call_func - -# TObjCamera -patch_func_5: - pop ecx - push 6 - push 0x006439A8 - call get_code_size5 - .deltaof patch_code5, patch_code_end5 -get_code_size5: - pop eax - push dword [eax] - call patch_code_end5 -patch_code5: - fld st0, dword [0x00689BD1] - fld st0, dword [esp + 0x28] - fmulp st1, st0 - fchs st0 - ret -patch_code_end5: - push ecx - jmp write_call_func - -# TODO: And this one? -patch_func_6: - pop ecx - push 6 - push 0x0065B959 - call get_code_size6 - .deltaof patch_code6, patch_code_end6 -get_code_size6: - pop eax - push dword [eax] - call patch_code_end6 -patch_code6: - mov ebp, ecx - fld st0, dword [0x00689BD1] - fld st0, dword [esp + 0x30] - fmulp st1, st0 - ret -patch_code_end6: - push ecx - jmp write_call_func diff --git a/system/client-functions/DrawDistance/DrawDistance.59NL.patch.s b/system/client-functions/DrawDistance/DrawDistance.59NL.patch.s deleted file mode 100644 index c995db3c..00000000 --- a/system/client-functions/DrawDistance/DrawDistance.59NL.patch.s +++ /dev/null @@ -1,146 +0,0 @@ -# Currently beta quality, map objects that fade like boxes, and Pioneer's -# background billboards and elevators still have regular draw distance. -# TODO: 90% of stuff is included, bring home the last 10%. - -.meta name="Draw Distance" -.meta description="Extends the draw\ndistance of many\nobjects" - -entry_ptr: -reloc0: - .offsetof start - -write_call_func: - .include WriteCallToCode-59NL - -start: - mov eax, 0x41800000 # Environment clip distance mod 16.0f - mov [0x0097F1B8], eax # This affects mostly static map objects - mov [0x0097F1BC], eax - mov [0x0097F1C0], eax - - mov ax, 0x9090 - mov [0x00689B5B], ax # Players draw distance 10000.0f always - mov eax, 0x41000000 # Use newly acquired skipped branch room - mov [0x00689B65], eax # to store our float multiplier - - call patch_func_1 # Floor items - call patch_func_2 # Whole bunch of stuff, including NPCs - call patch_func_3 # Duplicate function from above, reuse same hook - call patch_func_4 # TODO: Which objects this affects? - call patch_func_5 # TODO: This one too? - call patch_func_6 # TODO: And this one? - ret - -# Floor items -patch_func_1: - pop ecx - push 8 - push 0x005C5267 - call get_code_size1 - .deltaof patch_code1, patch_code_end1 -get_code_size1: - pop eax - push dword [eax] - call patch_code_end1 -patch_code1: - mov edx, [esp + 0x18] - fld st0, dword [0x00689B65] - fld st0, dword [esp + 0x14] - fmulp st1, st0 - ret -patch_code_end1: - push ecx - jmp write_call_func - -# Whole bunch of stuff, including NPCs -patch_func_2: - pop ecx - push 9 - push 0x007BA472 - call get_code_size2 - .deltaof patch_code2, patch_code_end2 -get_code_size2: - pop eax - push dword [eax] - call patch_code_end2 -patch_code2: - test eax, 0x400 - fld st0, dword [0x00689B65] - fld st0, dword [esp + 0x2C] - fmulp st1, st0 - ret -patch_code_end2: - push ecx - jmp write_call_func - -# Duplicate function from above, reuse same hook -patch_func_3: - mov eax, dword [0x007BA473] - add eax, 0x002A1C74 - mov dword [0x005187FF], eax - mov byte [0x005187FE], 0xE8 - mov dword [0x00518803], 0x90909090 - ret - -# TOComputerMachine01 -patch_func_4: - pop ecx - push 7 - push 0x00616FFC - call get_code_size4 - .deltaof patch_code4, patch_code_end4 -get_code_size4: - pop eax - push dword [eax] - call patch_code_end4 -patch_code4: - lea edx, [edi + 0x38] - fld st0, dword [0x00689B65] - fld st0, dword [esp + 0x14] - fmulp st1, st0 - ret -patch_code_end4: - push ecx - jmp write_call_func - -# TObjCamera -patch_func_5: - pop ecx - push 6 - push 0x0064394C - call get_code_size5 - .deltaof patch_code5, patch_code_end5 -get_code_size5: - pop eax - push dword [eax] - call patch_code_end5 -patch_code5: - fld st0, dword [0x00689B65] - fld st0, dword [esp + 0x28] - fmulp st1, st0 - fchs st0 - ret -patch_code_end5: - push ecx - jmp write_call_func - -# TODO: And this one? -patch_func_6: - pop ecx - push 6 - push 0x0065B985 - call get_code_size6 - .deltaof patch_code6, patch_code_end6 -get_code_size6: - pop eax - push dword [eax] - call patch_code_end6 -patch_code6: - mov ebp, ecx - fld st0, dword [0x00689B65] - fld st0, dword [esp + 0x30] - fmulp st1, st0 - ret -patch_code_end6: - push ecx - jmp write_call_func diff --git a/system/client-functions/EnemyDamageSync/EnemyDamageSync.59NJ.patch.s b/system/client-functions/EnemyDamageSync/EnemyDamageSync.59NJ.patch.s deleted file mode 100644 index 5f9d88ac..00000000 --- a/system/client-functions/EnemyDamageSync/EnemyDamageSync.59NJ.patch.s +++ /dev/null @@ -1,258 +0,0 @@ -.meta name="DMC" -.meta description="Mitigates effects\nof enemy health\ndesync" -.meta client_flag="0x0000001000000000" - -entry_ptr: -reloc0: - .offsetof start - -write_call_to_code_multi: - .include WriteCallToCodeMulti-59NJ -write_address_of_code: - .include WriteAddressOfCode-59NJ - -start: - - # Replace 6x09 with 6xE4 in subcommand handler table - mov dword [0x00A0DC30], 0x000600E4 # subcommand=0xE4, flags=6 - push 0x00A0DC34 - call +4 - .deltaof handle_6xE4_start, handle_6xE4_end - pop eax - push dword [eax] - call handle_6xE4_end - -handle_6xE4_start: # (G_6xE4* cmd @ [esp + 4]) -> void - push ebx - push esi - push edi - - test byte [0x00AA8DFC], 0x80 - jz handle_6xE4_return - mov ebx, [esp + 0x10] # cmd - movzx eax, word [ebx + 2] - cmp eax, 0x1000 - jl handle_6xE4_return - cmp eax, 0x1B50 - jge handle_6xE4_return - - movzx eax, word [ebx + 2] - .include GetEnemyEntity-59NJ # auto* ene = get_enemy_entity(cmd->header.entity_id); - push eax - - movzx eax, word [ebx + 2] - and eax, 0x0FFF - imul eax, eax, 0x0C - add eax, [0x00AADE38] # eax = state_for_enemy(cmd->header.entity_id) - - cmp dword [ebx + 0x0C], 0 - jl handle_6xE4_not_proportional - mov cx, [ebx + 0x0A] # cmd->max_hp - sub cx, [eax + 0x06] # st.total_damage - movzx ecx, cx - xor edx, edx - cmp ecx, edx - cmovl ecx, edx - push ecx - fild st0, dword [esp] # current_hp = static_cast(max(cmd->max_hp - st.total_damage, 0)) - fld st0, dword [ebx + 0x0C] - fmulp st1, st0 - fistp dword [esp], st0 - mov ecx, dword [esp] # adjusted_hit_amount = static_cast(current_hp * cmd->factor) - add esp, 4 - xor edx, edx - inc edx - cmp ecx, edx - cmovl ecx, edx - mov [ebx + 0x04], cx # cmd->hit_amount = min(1, adjusted_hit_amount) -handle_6xE4_not_proportional: - - movzx edx, word [eax + 0x06] # st.total_damage - movsx esi, word [ebx + 0x04] # cmd->hit_amount - movzx edi, word [ebx + 0x0A] # cmd->max_hp - add edx, esi # st.total_damage + cmd->hit_amount - cmp edx, edi - jl handle_6xE4_damage_less_than_max_hp - mov [eax + 0x06], di # st.total_damage = cmd->max_hp; - mov edx, [eax] - test edx, 0x800 - jnz handle_6xE4_return_pop_ene - or edx, 0x800 - mov [eax], edx - cmp dword [esp], 0 - je handle_6xE4_return_pop_ene - push edx # out_cmd.flags - sub esp, 8 - mov word [esp], 0x030A # out_cmd.header.{subcommand,size} - mov si, [ebx + 2] - mov [esp + 2], si # out_cmd.header.entity_id - and si, 0x0FFF - mov [esp + 4], si # out_cmd.entity_index - mov [esp + 6], di # out_cmd.total_damage - mov ecx, esp - mov edx, 0x00801150 - call edx # send_and_handle_60(&out_cmd); - add esp, 0x10 - jmp handle_6xE4_return - -handle_6xE4_damage_less_than_max_hp: - xor edi, edi - cmp edx, edx - cmovl edx, edi - mov [eax + 0x06], dx # st.total_damage = std::max(st.total_damage + cmd->hit_amount, 0); - - mov edx, eax # edx = ene_st - mov eax, [esp] # eax = ene - test eax, eax - jz handle_6xE4_return_pop_ene - mov ecx, eax - push edx - mov edx, [ecx] - call [edx + 0x148] # ene->vtable[0x52](ene, &st); - -handle_6xE4_return_pop_ene: - add esp, 4 -handle_6xE4_return: - pop edi - pop esi - pop ebx - ret - -handle_6xE4_end: - call write_address_of_code - - - - # Write TObjectV00b421c0::incr_hp_with_sync - push 5 - push 0x00775224 # TObjectV00b421c0::v18_accept_hit (presumably Resta) - this is add_hp, not subtract_hp! - push 5 - push 0x00778063 # TObjectV00b421c0::subtract_hp_if_not_in_state_2 - push 5 - push 0x00777AB2 # TObjectV00b421c0::v19_handle_hit_special_effects - push 5 - push 0x00777B2B # TObjectV00b421c0::v19_handle_hit_special_effects - push 5 - push 0x00777BFC # TObjectV00b421c0::v19_handle_hit_special_effects - push 5 - push 0x00777C75 # TObjectV00b421c0::v19_handle_hit_special_effects - push 5 - push 0x00776D2D # TObjectV00b421c0::v19_handle_hit_special_effects - push 5 - push 0x007769C2 # TObjectV00b421c0::v19_handle_hit_special_effects - push 5 - push 0x0077683C # TObjectV00b421c0::v19_handle_hit_special_effects - push 5 - push 0x00776502 # TObjectV00b421c0::v19_handle_hit_special_effects (Devil's/Demon's) - push 5 - push 0x00775B57 # TObjectV00b421c0::v18_accept_hit - push 5 - push 0x00775A23 # TObjectV00b421c0::v18_accept_hit - push 5 - push 0x007757F0 # TObjectV00b421c0::v18_accept_hit - push 5 - push 0x00775606 # TObjectV00b421c0::v18_accept_hit - push 5 - push 0x007754BC # TObjectV00b421c0::v18_accept_hit - push 5 - push 0x00774E3D # TObjectV00b421c0::v18_accept_hit - push 5 - push 0x00774CD6 # TObjectV00b421c0::v18_accept_hit - push 5 - push 0x00774713 # TObjectV00b421c0::v17 - push 18 - call +4 - .deltaof on_add_or_subtract_hp_start, on_add_or_subtract_hp_end - pop eax - push dword [eax] - call on_add_or_subtract_hp_end - -on_add_or_subtract_hp_start: # (TObjectV00b421c0* this @ ecx, int16_t amount @ [esp + 4]) -> bool @ eax - test byte [0x00AA8DFC], 0x80 - jz on_add_or_subtract_hp_skip_send - movzx eax, word [ecx + 0x1C] # ene->entity_id - cmp eax, 0x1000 - jl on_add_or_subtract_hp_skip_send - cmp eax, 0x1B50 - jge on_add_or_subtract_hp_skip_send - - and eax, 0x0FFF - imul eax, eax, 0x0C - add eax, [0x00AADE38] # eax = state_for_enemy(cmd->header.entity_id) - - sub esp, 0x10 - mov word [esp], 0x04E4 - mov dx, [ecx + 0x1C] - mov [esp + 0x02], dx # cmd.entity_id - mov dx, [esp + 0x14] - cmp dword [esp + 0x10], 0x00775229 # Check if callsite is add_hp - jne on_add_or_subtract_hp_skip_negate_amount - neg dx -on_add_or_subtract_hp_skip_negate_amount: - mov [esp + 0x04], dx # cmd.hit_amount - mov dx, [eax + 6] - mov [esp + 0x06], dx # cmd.total_damage_before_hit - mov dx, [ecx + 0x0334] - mov [esp + 0x08], dx # cmd.current_hp - mov dx, [ecx + 0x02BC] - mov [esp + 0x0A], dx # cmd.max_hp - mov dword [esp + 0x0C], 0xBF800000 # cmd.factor - - cmp dword [esp + 0x10], 0x00776507 # Check if callsite is Devil's/Demon's - jne on_add_or_subtract_hp_not_proportional - # esp is 0x18 down from where it is in caller's context - mov edx, 100 - sub edx, [esp + 0x24] # edx = (100 - special_amount) - push edx - fild st0, dword [esp] # current_hp_factor = static_cast(100 - special_amount) - fmul st0, dword [esp + 0x54] # *= weapon_reduction_factor - mov dword [esp], 0x42C80000 # 100.0f - fdiv st0, dword [esp] - add esp, 4 - fstp dword [esp + 0x0C], st0 # cmd.factor = ((100 - special_amount) * weapon_reduction_factor) / 100 -on_add_or_subtract_hp_not_proportional: - - mov edx, esp - push ecx - push 0x10 - push edx - mov ecx, [0x00AA8E04] - mov edx, 0x007D4CBC - call edx # send_60(root_protocol, &cmd, sizeof(cmd)); - pop ecx - add esp, 0x10 - -on_add_or_subtract_hp_skip_send: - mov eax, 0x007781F0 # subtract_hp - mov edx, 0x007781B0 # add_hp - cmp dword [esp], 0x00775229 # Check if callsite is add_hp - cmove eax, edx - jmp eax - -on_add_or_subtract_hp_end: - call write_call_to_code_multi - - - - push 5 - push 0x0078864B - push 1 - call +4 - .deltaof on_6x0A_patch_start, on_6x0A_patch_end - pop eax - push dword [eax] - call on_6x0A_patch_end - -on_6x0A_patch_start: # (TObjectV00b421c0* this @ ecx, int16_t amount @ [esp + 4]) -> bool @ eax - test byte [0x00AA8DFC], 0x80 - jz on_6x0A_patch_skip_write - mov [esp + 0x0A], cx -on_6x0A_patch_skip_write: - ret - -on_6x0A_patch_end: - call write_call_to_code_multi - - - - ret diff --git a/system/client-functions/EnemyDamageSync/EnemyDamageSync.59NL.patch.s b/system/client-functions/EnemyDamageSyncBB.s similarity index 67% rename from system/client-functions/EnemyDamageSync/EnemyDamageSync.59NL.patch.s rename to system/client-functions/EnemyDamageSyncBB.s index 8ad0ee0d..e7128c74 100644 --- a/system/client-functions/EnemyDamageSync/EnemyDamageSync.59NL.patch.s +++ b/system/client-functions/EnemyDamageSyncBB.s @@ -1,21 +1,25 @@ +.meta visibility="all" +.meta key="EnemyDamageSync" .meta name="DMC" .meta description="Mitigates effects\nof enemy health\ndesync" .meta client_flag="0x0000001000000000" +.versions 59NJ 59NL + entry_ptr: reloc0: .offsetof start write_call_to_code_multi: - .include WriteCallToCodeMulti-59NL + .include WriteCallToCodeMulti write_address_of_code: - .include WriteAddressOfCode-59NL + .include WriteAddressOfCode start: # Replace 6x09 with 6xE4 in subcommand handler table - mov dword [0x00A0FC30], 0x000600E4 # subcommand=0xE4, flags=6 - push 0x00A0FC34 + mov dword [], 0x000600E4 # subcommand=0xE4, flags=6 + push call +4 .deltaof handle_6xE4_start, handle_6xE4_end pop eax @@ -27,7 +31,7 @@ handle_6xE4_start: # (G_6xE4* cmd @ [esp + 4]) -> void push esi push edi - test byte [0x00AAB27C], 0x80 + test byte [], 0x80 jz handle_6xE4_return mov ebx, [esp + 0x10] # cmd movzx eax, word [ebx + 2] @@ -37,13 +41,13 @@ handle_6xE4_start: # (G_6xE4* cmd @ [esp + 4]) -> void jge handle_6xE4_return movzx eax, word [ebx + 2] - .include GetEnemyEntity-59NL # auto* ene = get_enemy_entity(cmd->header.entity_id); + .include GetEnemyEntity # auto* ene = get_enemy_entity(cmd->header.entity_id); push eax movzx eax, word [ebx + 2] and eax, 0x0FFF imul eax, eax, 0x0C - add eax, [0x00AB02B8] # eax = state_for_enemy(cmd->header.entity_id) + add eax, [] # eax = state_for_enemy(cmd->header.entity_id) cmp dword [ebx + 0x0C], 0 jl handle_6xE4_not_proportional @@ -90,7 +94,7 @@ handle_6xE4_not_proportional: mov [esp + 4], si # out_cmd.entity_index mov [esp + 6], di # out_cmd.total_damage mov ecx, esp - mov edx, 0x008003E0 + mov edx, call edx # send_and_handle_60(&out_cmd); add esp, 0x10 jmp handle_6xE4_return @@ -123,43 +127,44 @@ handle_6xE4_end: + # Note: in 59NJ this object is TObjectV00b421c0 (it's the same as 3OE1's TObjectV8047c128) # Write TObjectV00b441c0::incr_hp_with_sync push 5 - push 0x00774448 # TObjectV00b441c0::v18_accept_hit (presumably Resta) - this is add_hp, not subtract_hp! + push # TObjectV00b441c0::v18_accept_hit (presumably Resta) - this is add_hp, not subtract_hp! push 5 - push 0x00777287 # TObjectV00b441c0::subtract_hp_if_not_in_state_2 + push # TObjectV00b441c0::subtract_hp_if_not_in_state_2 push 5 - push 0x00776CD6 # TObjectV00b441c0::v19_handle_hit_special_effects + push # TObjectV00b441c0::v19_handle_hit_special_effects push 5 - push 0x00776D4F # TObjectV00b441c0::v19_handle_hit_special_effects + push # TObjectV00b441c0::v19_handle_hit_special_effects push 5 - push 0x00776E20 # TObjectV00b441c0::v19_handle_hit_special_effects + push # TObjectV00b441c0::v19_handle_hit_special_effects push 5 - push 0x00776E99 # TObjectV00b441c0::v19_handle_hit_special_effects + push # TObjectV00b441c0::v19_handle_hit_special_effects push 5 - push 0x00775F51 # TObjectV00b441c0::v19_handle_hit_special_effects + push # TObjectV00b441c0::v19_handle_hit_special_effects push 5 - push 0x00775BE6 # TObjectV00b441c0::v19_handle_hit_special_effects + push # TObjectV00b441c0::v19_handle_hit_special_effects push 5 - push 0x00775A60 # TObjectV00b441c0::v19_handle_hit_special_effects + push # TObjectV00b441c0::v19_handle_hit_special_effects push 5 - push 0x00775726 # TObjectV00b441c0::v19_handle_hit_special_effects (Devil's/Demon's) + push # TObjectV00b441c0::v19_handle_hit_special_effects (Devil's/Demon's) push 5 - push 0x00774D7B # TObjectV00b441c0::v18_accept_hit + push # TObjectV00b441c0::v18_accept_hit push 5 - push 0x00774C47 # TObjectV00b441c0::v18_accept_hit + push # TObjectV00b441c0::v18_accept_hit push 5 - push 0x00774A14 # TObjectV00b441c0::v18_accept_hit + push # TObjectV00b441c0::v18_accept_hit push 5 - push 0x0077482A # TObjectV00b441c0::v18_accept_hit + push # TObjectV00b441c0::v18_accept_hit push 5 - push 0x007746E0 # TObjectV00b441c0::v18_accept_hit + push # TObjectV00b441c0::v18_accept_hit push 5 - push 0x00774061 # TObjectV00b441c0::v18_accept_hit + push # TObjectV00b441c0::v18_accept_hit push 5 - push 0x00773EFA # TObjectV00b441c0::v18_accept_hit + push # TObjectV00b441c0::v18_accept_hit push 5 - push 0x00773937 # TObjectV00b441c0::v17 + push # TObjectV00b441c0::v17 push 18 call +4 .deltaof on_add_or_subtract_hp_start, on_add_or_subtract_hp_end @@ -168,7 +173,7 @@ handle_6xE4_end: call on_add_or_subtract_hp_end on_add_or_subtract_hp_start: # (TObjectV00b441c0* this @ ecx, int16_t amount @ [esp + 4]) -> bool @ eax - test byte [0x00AAB27C], 0x80 + test byte [], 0x80 jz on_add_or_subtract_hp_skip_send movzx eax, word [ecx + 0x1C] # ene->entity_id cmp eax, 0x1000 @@ -178,14 +183,14 @@ on_add_or_subtract_hp_start: # (TObjectV00b441c0* this @ ecx, int16_t amount @ and eax, 0x0FFF imul eax, eax, 0x0C - add eax, [0x00AB02B8] # eax = state_for_enemy(cmd->header.entity_id) + add eax, [] # eax = state_for_enemy(cmd->header.entity_id) sub esp, 0x10 mov word [esp], 0x04E4 mov dx, [ecx + 0x1C] mov [esp + 0x02], dx # cmd.entity_id mov dx, [esp + 0x14] - cmp dword [esp + 0x10], 0x0077444D # Check if callsite is add_hp + cmp dword [esp + 0x10], # Check if callsite is add_hp jne on_add_or_subtract_hp_skip_negate_amount neg dx on_add_or_subtract_hp_skip_negate_amount: @@ -198,7 +203,7 @@ on_add_or_subtract_hp_skip_negate_amount: mov [esp + 0x0A], dx # cmd.max_hp mov dword [esp + 0x0C], 0xBF800000 # cmd.factor - cmp dword [esp + 0x10], 0x0077572B # Check if callsite is Devil's/Demon's + cmp dword [esp + 0x10], # Check if callsite is Devil's/Demon's jne on_add_or_subtract_hp_not_proportional # esp is 0x18 down from where it is in caller's context mov edx, 100 @@ -216,16 +221,16 @@ on_add_or_subtract_hp_not_proportional: push ecx push 0x10 push edx - mov ecx, [0x00AAB284] - mov edx, 0x007D3F38 + mov ecx, [] + mov edx, call edx # send_60(root_protocol, &cmd, sizeof(cmd)); pop ecx add esp, 0x10 on_add_or_subtract_hp_skip_send: - mov eax, 0x00777414 # subtract_hp - mov edx, 0x007773D4 # add_hp - cmp dword [esp], 0x0077444D # Check if callsite is add_hp + mov eax, # subtract_hp + mov edx, # add_hp + cmp dword [esp], # Check if callsite is add_hp cmove eax, edx jmp eax @@ -235,7 +240,7 @@ on_add_or_subtract_hp_end: push 5 - push 0x0078781F + push push 1 call +4 .deltaof on_6x0A_patch_start, on_6x0A_patch_end @@ -244,7 +249,7 @@ on_add_or_subtract_hp_end: call on_6x0A_patch_end on_6x0A_patch_start: # (TObjectV00b441c0* this @ ecx, int16_t amount @ [esp + 4]) -> bool @ eax - test byte [0x00AAB27C], 0x80 + test byte [], 0x80 jz on_6x0A_patch_skip_write mov [esp + 0x0A], cx on_6x0A_patch_skip_write: diff --git a/system/client-functions/EnemyDamageSync/EnemyDamageSync.3___.patch.s b/system/client-functions/EnemyDamageSyncGC.s similarity index 99% rename from system/client-functions/EnemyDamageSync/EnemyDamageSync.3___.patch.s rename to system/client-functions/EnemyDamageSyncGC.s index 686a75d2..9e45a002 100644 --- a/system/client-functions/EnemyDamageSync/EnemyDamageSync.3___.patch.s +++ b/system/client-functions/EnemyDamageSyncGC.s @@ -1,3 +1,5 @@ +.meta visibility="all" +.meta key="EnemyDamageSync" .meta name="DMC" .meta description="Mitigates effects\nof enemy health\ndesync" .meta client_flag="0x0000001000000000" @@ -8,7 +10,7 @@ entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks .label TObjectV8047c128_add_hp, .label TObjectV8047c128_subtract_hp, diff --git a/system/client-functions/EnemyDamageSync/EnemyDamageSync.4___.patch.s b/system/client-functions/EnemyDamageSyncXB.s similarity index 97% rename from system/client-functions/EnemyDamageSync/EnemyDamageSync.4___.patch.s rename to system/client-functions/EnemyDamageSyncXB.s index fab791d3..b3dd3a1f 100644 --- a/system/client-functions/EnemyDamageSync/EnemyDamageSync.4___.patch.s +++ b/system/client-functions/EnemyDamageSyncXB.s @@ -1,3 +1,5 @@ +.meta visibility="all" +.meta key="EnemyDamageSync" .meta name="DMC" .meta description="Mitigates effects\nof enemy health\ndesync" .meta client_flag="0x0000001000000000" @@ -9,7 +11,7 @@ reloc0: .offsetof start write_call_to_code_multi: - .include WriteCallToCodeMultiXB + .include WriteCallToCodeMulti @@ -284,7 +286,7 @@ on_add_or_subtract_hp_end: write_static_patches: - .include WriteCodeBlocksXB + .include WriteCodeBlocks @@ -326,10 +328,9 @@ on_subtract_hp_if_not_in_state_2_end: .deltaof v17_subtract_hp_inlined_callsite_start, v17_subtract_hp_inlined_callsite_end .address v17_subtract_hp_inlined_callsite_start: - # This must assemble to exactly 0x1A bytes. There is a vfn call shortly after - # this, and fortunately it appears eax, ecx, and edx are not used before - # then, so we don't have to save any registers here; we just have to move the - # args into the right places. + # This must assemble to exactly 0x1A bytes. There is a vfn call shortly after this, and fortunately it appears eax, + # ecx, and edx are not used before then, so we don't have to save any registers here; we just have to move the args + # into the right places. mov cx, ax mov eax, edi call -1 # Overwritten by write_call_to_code_multi later diff --git a/system/client-functions/EnemyHPBars/EnemyHPBars.5___.patch.s b/system/client-functions/EnemyHPBarsBB.s similarity index 93% rename from system/client-functions/EnemyHPBars/EnemyHPBars.5___.patch.s rename to system/client-functions/EnemyHPBarsBB.s index 23a6c555..877134fb 100644 --- a/system/client-functions/EnemyHPBars/EnemyHPBars.5___.patch.s +++ b/system/client-functions/EnemyHPBarsBB.s @@ -1,3 +1,5 @@ +.meta visibility="all" +.meta key="EnemyHPBars" .meta name="Enemy HP bars" .meta description="Shows HP bars in\nenemy info windows" @@ -7,7 +9,7 @@ entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksBB + .include WriteCodeBlocks .data .data 6 .binary 81E2FDFFFFFF diff --git a/system/client-functions/EnemyHPBars/EnemyHPBars.3___.patch.s b/system/client-functions/EnemyHPBarsGC.s similarity index 97% rename from system/client-functions/EnemyHPBars/EnemyHPBars.3___.patch.s rename to system/client-functions/EnemyHPBarsGC.s index 931d3f89..1e5bc978 100644 --- a/system/client-functions/EnemyHPBars/EnemyHPBars.3___.patch.s +++ b/system/client-functions/EnemyHPBarsGC.s @@ -1,16 +1,19 @@ -.meta name="Enemy HP bars" -.meta description="Shows HP bars in\nenemy info windows" # Original code by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 +.meta visibility="all" +.meta key="EnemyHPBars" +.meta name="Enemy HP bars" +.meta description="Shows HP bars in\nenemy info windows" + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks .label hook_addr, 0x8000B650 .label sprintf, diff --git a/system/client-functions/EnemyHPBars/EnemyHPBars.4___.patch.s b/system/client-functions/EnemyHPBarsXB.s similarity index 95% rename from system/client-functions/EnemyHPBars/EnemyHPBars.4___.patch.s rename to system/client-functions/EnemyHPBarsXB.s index 7000b726..3a0e09a2 100644 --- a/system/client-functions/EnemyHPBars/EnemyHPBars.4___.patch.s +++ b/system/client-functions/EnemyHPBarsXB.s @@ -1,17 +1,20 @@ -.meta name="Enemy HP bars" -.meta description="Shows HP bars in\nenemy info windows" # Original code by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 # Xbox port by fuzziqersoftware +.meta visibility="all" +.meta key="EnemyHPBars" +.meta name="Enemy HP bars" +.meta description="Shows HP bars in\nenemy info windows" + .versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksXB + .include WriteCodeBlocks .data .data 0x00000001 .binary C0 @@ -51,8 +54,8 @@ str_data_start: .data 0x00000000 str_data_end: - # WARNING: FlickeringStatusIcons patch starts immediately after this segment; - # if the size of this is changed, that patch will have to be changed too + # WARNING: FlickeringStatusIcons patch starts immediately after this segment; if the size of this is changed, that + # patch will have to be changed too .data .deltaof new_code_start, new_code_end new_code_start: diff --git a/system/client-functions/Episode3/AllCards.3S__.patch.s b/system/client-functions/Episode3/AllCards.s similarity index 93% rename from system/client-functions/Episode3/AllCards.3S__.patch.s rename to system/client-functions/Episode3/AllCards.s index ae24f4e7..af9e19f5 100644 --- a/system/client-functions/Episode3/AllCards.3S__.patch.s +++ b/system/client-functions/Episode3/AllCards.s @@ -1,7 +1,7 @@ -# This patch gives you the maximum number of each card. It only works if used -# in-game, which means it must be used by running `$patch AllCards`. +# This patch gives you the maximum number of each card. It only works if used in-game, which means it must be used by +# running `$patch AllCards`. -.meta hide_from_patches_menu +.meta visibility="cheat" .meta name="All cards" .meta description="Gives you the\nmaximum number of\neach card." diff --git a/system/client-functions/Episode3/Editors.3SE0.patch.s b/system/client-functions/Episode3/Editors.s similarity index 52% rename from system/client-functions/Episode3/Editors.3SE0.patch.s rename to system/client-functions/Episode3/Editors.s index c4628d12..56e20959 100644 --- a/system/client-functions/Episode3/Editors.3SE0.patch.s +++ b/system/client-functions/Episode3/Editors.s @@ -1,18 +1,16 @@ -# This patch enables the debug menus in PSO Episode 3 USA. Specifically, it -# causes them all to load, but only activates one (selected by uncommenting a -# line below). See the comments for more information. Most of these editors are -# present in PSO PC and PSO Xbox as well, but not in GC Episodes 1 & 2. There -# are notes in the below comments that may help get these editors working on -# PSO PC. +# This patch enables the debug menus in PSO Episode 3 USA. Specifically, it causes them all to load, but only activates +# one (selected by uncommenting a line below). See the comments for more information. Most of these editors are present +# in PSO PC and PSO Xbox as well, but not in GC Episodes 1 & 2. There are notes in the below comments that may help get +# these editors working on PSO PC. -# This patch must not be run from the Patches menu - it should only be run with -# the $patch command, since the client will likely crash if the player is not -# in a game or lobby when the patch runs. +# This patch must not be run from the Patches menu - it should only be run with the $patch command, since the client +# will likely crash if the player is not in a game or lobby when the patch runs. -.meta hide_from_patches_menu .meta name="Editors" .meta description="Enables the various\ndebug menus" +.versions 3SE0 + entry_ptr: reloc0: .offsetof start @@ -25,8 +23,7 @@ start: stw [r1 + 0x0C], r30 stw [r1 + 0x08], r29 - # Write a short hook that updates our editors table when TEditor_destroy() is - # called + # Write a short hook that updates our editors table when TEditor_destroy() is called bl get_TEditor_destroy_hook_addr mr r4, r3 bl get_TEditor_destroy_hook_end @@ -48,10 +45,9 @@ start: ori r29, r29, 0x17C4 construct_editors: - # Call the constructors for all the editors and save the object pointers. If - # an editor already exists, set its disable flags. (This behavior allows this - # patch to run again to switch to a different editor without changing rooms.) - # Note: In PSO PC (the version I have, at least) this table is at 00691FA8. + # Call the constructors for all the editors and save the object pointers. If an editor already exists, set its + # disable flags. (This behavior allows this patch to run again to switch to a different editor without changing + # rooms.) In PSO PC (2OJW) this table is at 00691FA8. lis r30, 0x8043 ori r30, r30, 0x3760 addi r31, r30, 0xB4 # 15 entries * 12 bytes per entry @@ -76,13 +72,11 @@ editor_construction_failed: blt again activate_chosen_editor: - # All of the editors have flags set at construction time that effectively - # disable them (by disabling both the update and render functions). At the - # time this code is executed, the flags are already set (and we set them again - # in the above loop anyway), so we can unset the flags for whichever editor we - # want to run by uncommenting the appropriate lwz opcode below. - # Most of these tools expect input from the controller in port 3; the comments - # below all refer to inputs from that port. + # All of the editors have flags set at construction time that effectively disable them (by disabling both the update + # and render functions). At the time this code is executed, the flags are already set (and we set them again in the + # above loop anyway), so we can unset the flags for whichever editor we want to run by uncommenting the appropriate + # lwz opcode below. Most of these tools expect input from the controller in port 3; the comments below all refer to + # inputs from that port. li r4, 0 lis r29, 0x8000 @@ -92,91 +86,76 @@ activate_chosen_editor: # This editor is very similar to TGroupEnemySetEditor (see below). # lwz r4, [r29 + 0x04] # TGroupEnemySetEditor - # This editor only works in a game; it crashes if loaded in the lobby. - # Use the D-pad to choose a value; hold X and use the D-pad to modify the - # selected value. Hold R to use the menu on the right. + # This editor only works in a game; it crashes if loaded in the lobby. Use the D-pad to choose a value; hold X and + # use the D-pad to modify the selected value. Hold R to use the menu on the right. # lwz r4, [r29 + 0x08] # TCameraEditor - # This editor displays a floating-point value at the bottom of the screen, - # which you can modify with C-left and C-right. It's not apparent what this - # value represents, though. + # This editor displays a floating-point value at the bottom of the screen, which you can modify with C-left and + # C-right. It's not apparent what this value represents, though. # lwz r4, [r29 + 0x0C] # TParticleEditor - # This editor has two modes. Hold A and press X to switch modes. In "MAIN - # MODE", use D-left/D-right to pick an effect. Hold L to make the effect - # picker manageable (instead of insanely fast). In "ELEMENT MODE", it seems - # that any of the displayed values can be modified, but the selector is very - # hard to see (the selected section is rendered in FFFFFF, while the others - # are rendered in F0F0F0 - very similar colors!). Hold A, Y, or X and use - # the D-pad to change a value in the selected section (each of A/Y/X - # corresponds to a specific field in the current section). + # This editor has two modes. Hold A and press X to switch modes. In "MAIN MODE", use D-left/D-right to pick an + # effect. Hold L to make the effect picker manageable (instead of insanely fast). In "ELEMENT MODE", it seems that + # any of the displayed values can be modified, but the selector is very hard to see (the selected section is + # rendered in FFFFFF, while the others are rendered in F0F0F0 - very similar colors!). Hold A, Y, or X and use the + # D-pad to change a value in the selected section (each of A/Y/X corresponds to a specific field in the current + # section). # lwz r4, [r29 + 0x10] # TFreeCamera - # This editor does nothing. Probably it was never implemented or the code - # was intentionally deleted (though if it was, it's not clear why only this - # editor's code was deleted). + # This editor does nothing. Probably it was never implemented or the code was intentionally deleted (though if it + # was, it's not clear why only this editor's code was deleted). # lwz r4, [r29 + 0x14] # TFogEditor - # Use L/R to pick a line, and the D-pad to modify the values. NO specifies - # which fog entry you're editing (0-127). + # Use L/R to pick a line, and the D-pad to modify the values. NO specifies which fog entry you're editing (0-127). # lwz r4, [r29 + 0x18] # TLightEditor - # Used for testing character lighting. Use L to select a section and the - # D-pad to choose and modify values within that section. COLOR and DIR - # specify the properties of the highlight; AMBIENT specifies the color of - # the non-highlight lighting. It's not clear what the last section does. + # Used for testing character lighting. Use L to select a section and the D-pad to choose and modify values within + # that section. COLOR and DIR specify the properties of the highlight; AMBIENT specifies the color of the non- + # highlight lighting. It's not clear what the last section does. # lwz r4, [r29 + 0x1C] # nothing (type table entry is blank) # lwz r4, [r29 + 0x20] # TSeqVarsEdit - # Use L/R to change pages, use the D-pad to pick a flag, and use A to toggle - # it. There are 8192 flags in total (0x400 bytes). + # Use L/R to change pages, use the D-pad to pick a flag, and use A to toggle it. There are 8192 flags in total + # (0x400 bytes). # lwz r4, [r29 + 0x24] # TSetEvtScriptTest - # Use D-left/D-right to change the label value and D-up/D-down to move the - # menu selection. Two of the menu items appear to do nothing, and the last - # crashes. Maybe it works better on Episodes 1&2. + # Use D-left/D-right to change the label value and D-up/D-down to move the menu selection. Two of the menu items + # appear to do nothing, and the last crashes. Maybe it works better on Episodes 1&2. # lwz r4, [r29 + 0x28] # nothing (type table entry is blank) # lwz r4, [r29 + 0x2C] # TQuestScriptChecker (quest debugger) - # Use L to change functions, and the D-pad to navigate within each function. - # If you set EVENT NO to a very high value, the editor can appear messed up; - # what actually happens is the value is shifted one decimal place to the - # right, but the cursor remains in the same position with incorrect - # highlighting. The value appears to be a signed 32-bit integer. On the - # registers page, use D-left/D-right to see more registers; hold X and use - # the D-pad to modify a register's value. Similarly, hold X and use the - # D-pad on the breakpoints page to change values. + # Use L to change functions, and the D-pad to navigate within each function. If you set EVENT NO to a very high + # value, the editor can appear messed up; what actually happens is the value is shifted one decimal place to the + # right, but the cursor remains in the same position with incorrect highlighting. The value appears to be a signed + # 32-bit integer. On the registers page, use D-left/D-right to see more registers; hold X and use the D-pad to + # modify a register's value. Similarly, hold X and use the D-pad on the breakpoints page to change values. # lwz r4, [r29 + 0x30] # TPlyPKEditor (battle mode options) - # Use the D-pad to move the cursor and set options. In Episode 3, it appears - # this debugger doesn't do anything. It's likely more functional in Episodes - # 1 & 2. + # Use the D-pad to move the cursor and set options. In Episode 3, it appears this debugger doesn't do anything. + # It's likely more functional in Episodes 1 & 2. # lwz r4, [r29 + 0x34] # TEffIndirectEditor # li r0, 1 # stw [r4 + 0x38], r0 - # This editor is missing in PSO PC, but is present in PSOX. It appears to be - # used for testing texture overlay effects, but it doesn't work properly in - # Episode 3 - none of the effects appear to do anything. All three lines + # This editor is missing in PSO PC, but is present in PSOX. It appears to be used for testing texture overlay + # effects, but it doesn't work properly in Episode 3 - none of the effects appear to do anything. All three lines # above must be uncommented for it to load. # lwz r4, [r29 + 0x38] # TCCScenarioDebug (movie/cutscene tests) - # This editor exists only in Episode 3 - it is neither in PSOPC nor PSOX. - # Nothing appears immediately after activating this debugger because the - # default page is blank. Use C-left and C-right to change major pages; use - # L/R to change minor pages (sets of 50 flags within each major page). Use - # the D-pad to pick a flag and A to toggle it. On the "STAFFROLL" page, use - # the D-pad to pick a movie, and R+A to play it. If you watch the movie to - # the end, you'll return to your game and things will work as normal, but - # the textures will likely have been overwritten with garbage data. + # This editor exists only in Episode 3 - it is neither in PSOPC nor PSOX. Nothing appears immediately after + # activating this debugger because the default page is blank. Use C-left and C-right to change major pages; use L/R + # to change minor pages (sets of 50 flags within each major page). Use the D-pad to pick a flag and A to toggle it. + # On the "STAFFROLL" page, use the D-pad to pick a movie, and R+A to play it. If you watch the movie to the end, + # you'll return to your game and things will work as normal, but the textures will likely have been overwritten + # with garbage data. li r3, 0 mr. r0, r4 beq skip_enable_editor - # Note: The PSO PC TObject structure is a bit different; the flags field is at - # +8 instead of +4 (but it is still a 16-bit integer). + # Note: The PSO PC TObject structure is a bit different; the flags field is at +8 instead of +4 (but it is still a + # 16-bit integer). sth [r4 + 4], r3 skip_enable_editor: diff --git a/system/client-functions/Episode3/PlusPatches.3S__.patch.s b/system/client-functions/Episode3/PlusPatches.s similarity index 99% rename from system/client-functions/Episode3/PlusPatches.3S__.patch.s rename to system/client-functions/Episode3/PlusPatches.s index 3ce7cf3a..6590e8d4 100644 --- a/system/client-functions/Episode3/PlusPatches.3S__.patch.s +++ b/system/client-functions/Episode3/PlusPatches.s @@ -1,6 +1,7 @@ # Original patch by Gigobooma # https://docs.google.com/document/d/1zG73l9joEqp_zB-xNgK9g8pXL0RSpmXfxPFQcdAvess/edit +.meta visibility="all" .meta name="Episode 3 Plus" .meta description="Enables Episode 3\nPlus features.\nDoes not include\ntext fixes.\n\nOriginally created\nby Gigobooma" @@ -11,7 +12,7 @@ reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks # Disable curse word filter .data diff --git a/system/client-functions/Episode3/ReplacePinzCards.3S__.patch.s b/system/client-functions/Episode3/ReplacePinzCards.s similarity index 92% rename from system/client-functions/Episode3/ReplacePinzCards.3S__.patch.s rename to system/client-functions/Episode3/ReplacePinzCards.s index bf476806..bf91d3d6 100644 --- a/system/client-functions/Episode3/ReplacePinzCards.3S__.patch.s +++ b/system/client-functions/Episode3/ReplacePinzCards.s @@ -1,11 +1,12 @@ -# This patch replaces the prices and contents of Pinz's Shop. -# Each entry is structured as follows: +# This patch replaces the prices and contents of Pinz's Shop. Each entry is structured as follows: # uint16_t card_id; # int16_t min_clv; // -1 = limit doesn't apply # int16_t max_clv; // -1 = limit doesn't apply # uint16_t relative_chance; # The values in the patch data below are the defaults. +# Uncomment the .meta visibility line to make this appear in the Patches menu. +# .meta visibility="all" .meta name="New Pinz cards" .meta description="Replaces the cards\navailable in Pinz's\nShop" @@ -16,7 +17,7 @@ reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks # Meseta prices .data diff --git a/system/client-functions/Episode3/VIPCard.3S__.patch.s b/system/client-functions/Episode3/VIPCard.s similarity index 91% rename from system/client-functions/Episode3/VIPCard.3S__.patch.s rename to system/client-functions/Episode3/VIPCard.s index e6d2f79b..274e2031 100644 --- a/system/client-functions/Episode3/VIPCard.3S__.patch.s +++ b/system/client-functions/Episode3/VIPCard.s @@ -1,6 +1,6 @@ +.meta visibility="cheat" .meta name="VIP card" .meta description="Gives you a VIP card" -.meta hide_from_patches_menu .versions 3SJT 3SJ0 3SE0 3SP0 diff --git a/system/client-functions/ExitAnywhere.s b/system/client-functions/ExitAnywhere.s new file mode 100644 index 00000000..62b3ee2c --- /dev/null +++ b/system/client-functions/ExitAnywhere.s @@ -0,0 +1,62 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" + + +entry_ptr: +reloc0: + .offsetof start + + + +.versions 2OJ5 2OJF 2OEF 2OPF +start: + mov r2, 0 + mova r0, [addrs] + mov.l r1, [r0] + mov.l [r1], r2 + mov.l r1, [r0 + 4] + mov.l [r1], r2 + mov r2, 1 + mov.l r1, [r0 + 8] + rets + mov.w [r1], r2 + .align 4 +addrs: + .data + .data + .data + + + +.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 +start: + li r0, 0 + stw [r13 - ], r0 + stw [r13 - ], r0 + li r0, 1 + sth [r13 - ], r0 + blr + + + +.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU +start: + xor eax, eax + mov [], eax # is_in_quest = false + mov [], eax # dat_source_type = NONE + inc eax + mov [], ax # should_leave_game = true + ret + + + +.versions 59NJ 59NL +start: + xor eax, eax + mov [], eax # is_in_quest = false + mov [], eax # dat_source_type = NONE + inc eax + mov [], ax # should_leave_game = true + ret diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.2___.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.2___.patch.s deleted file mode 100644 index a04909eb..00000000 --- a/system/client-functions/ExitAnywhere/ExitAnywhere.2___.patch.s +++ /dev/null @@ -1,28 +0,0 @@ -# This function implements $exit in a game when no quest is loaded. - -.meta name="Exit anywhere" -.meta description="" -.meta hide_from_patches_menu - -.versions 2OJ5 2OJF 2OEF 2OPF - -entry_ptr: -reloc0: - .offsetof start - -start: - mov r2, 0 - mova r0, [addrs] - mov.l r1, [r0] - mov.l [r1], r2 - mov.l r1, [r0 + 4] - mov.l [r1], r2 - mov r2, 1 - mov.l r1, [r0 + 8] - rets - mov.w [r1], r2 - .align 4 -addrs: - .data - .data - .data diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.3___.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.3___.patch.s deleted file mode 100644 index 43b0524b..00000000 --- a/system/client-functions/ExitAnywhere/ExitAnywhere.3___.patch.s +++ /dev/null @@ -1,19 +0,0 @@ -# This function implements $exit in a game when no quest is loaded. - -.meta name="Exit anywhere" -.meta description="" -.meta hide_from_patches_menu - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start - -start: - li r0, 0 - stw [r13 - ], r0 - stw [r13 - ], r0 - li r0, 1 - sth [r13 - ], r0 - blr diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.4___.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.4___.patch.s deleted file mode 100644 index 8da8929d..00000000 --- a/system/client-functions/ExitAnywhere/ExitAnywhere.4___.patch.s +++ /dev/null @@ -1,19 +0,0 @@ -# This function implements $exit in a game when no quest is loaded. - -.meta name="Exit anywhere" -.meta description="" -.meta hide_from_patches_menu - -.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start - -start: - xor eax, eax - mov [], eax # is_in_quest = false - mov [], eax # dat_source_type = NONE - inc eax - mov [], ax # should_leave_game = true - ret diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.5___.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.5___.patch.s deleted file mode 100644 index 2ae8c25c..00000000 --- a/system/client-functions/ExitAnywhere/ExitAnywhere.5___.patch.s +++ /dev/null @@ -1,19 +0,0 @@ -# This function implements $exit in a game when no quest is loaded. - -.meta name="Exit anywhere" -.meta description="" -.meta hide_from_patches_menu - -.versions 59NJ 59NL - -entry_ptr: -reloc0: - .offsetof start - -start: - xor eax, eax - mov [], eax # is_in_quest = false - mov [], eax # dat_source_type = NONE - inc eax - mov [], ax # should_leave_game = true - ret diff --git a/system/client-functions/ExtendedItemInfo/ExtendedItemInfo.3___.patch.s b/system/client-functions/ExtendedItemInfo.s similarity index 98% rename from system/client-functions/ExtendedItemInfo/ExtendedItemInfo.3___.patch.s rename to system/client-functions/ExtendedItemInfo.s index 7a392367..cc465616 100644 --- a/system/client-functions/ExtendedItemInfo/ExtendedItemInfo.3___.patch.s +++ b/system/client-functions/ExtendedItemInfo.s @@ -1,3 +1,4 @@ +.meta visibility="all" .meta name="Ext item info" .meta description="Shows more info\nbefore picking up\nan item" @@ -7,7 +8,7 @@ entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks .data .data 0x00000004 @@ -132,8 +133,7 @@ set_window_state: # (TItem* item: r3) -> void cmplwi r3, 0 beq window_should_not_exist - # Only show the window for weapons, armors, shields, and mags (not for - # units, tools, or meseta) + # Only show the window for weapons, armors, shields, and mags (not for units, tools, or meseta) lhz r4, [r3 + 0xEC] # data[0] and data[1] cmplwi r4, 0x0103 beq window_should_not_exist @@ -145,8 +145,7 @@ set_window_state: # (TItem* item: r3) -> void cmplwi r3, 0 bne window_should_not_exist - # If the TWindowMainMenu1P exists and is visible, the TWindowHelpItem should - # not be visible + # If the TWindowMainMenu1P exists and is visible, the TWindowHelpItem should not be visible lis r3, 0x8000 lwz r3, [r3 + 0x4004] cmplwi r3, 0 diff --git a/system/client-functions/FastTekker.s b/system/client-functions/FastTekker.s new file mode 100644 index 00000000..61ff3655 --- /dev/null +++ b/system/client-functions/FastTekker.s @@ -0,0 +1,96 @@ +.meta visibility="all" +.meta name="Fast tekker" +.meta description="Skips wind-up sound\nat tekker window" + + +entry_ptr: +reloc0: + .offsetof start +start: + .include WriteCodeBlocks + + + + .versions 1OJ1 1OJ2 1OJ3 1OJ4 1OJF 1OEF 1OPF 2OJ4 2OJ5 2OJF 2OEF 2OPF + + .align 4 + .data + .data 0x00000002 + mov r1, 1 + + .align 4 + .data + .data 0x00000002 + nop + + .align 4 + + + + .versions 3OJT 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + + .data + .data 4 + li r0, 1 + + .data + .data 4 + nop + + + + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + + .data + .deltaof patch1_start, patch1_end +patch1_start: + mov dword [ebp + 0x14C], 1 +patch1_end: + + .data + .deltaof patch2_start, patch2_end +patch2_start: + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop +patch2_end: + + + + .versions 59NJ 59NL + + .data + .deltaof patch1_start, patch1_end +patch1_start: + mov dword [edi + 0x14C], 1 +patch1_end: + + .data + .deltaof patch2_start, patch2_end +patch2_start: + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop +patch2_end: + + + + .all_versions + + .data 0x00000000 + .data 0x00000000 diff --git a/system/client-functions/FastTekker/FastTekker.1___.patch.s b/system/client-functions/FastTekker/FastTekker.1___.patch.s deleted file mode 100644 index e03d2471..00000000 --- a/system/client-functions/FastTekker/FastTekker.1___.patch.s +++ /dev/null @@ -1,24 +0,0 @@ -.meta name="Fast tekker" -.meta description="Skips wind-up sound\nat tekker window" - -.versions 1OJ1 1OJ2 1OJ3 1OJ4 1OJF 1OEF 1OPF 2OJ4 2OJ5 2OJF 2OEF 2OPF - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksDC - - .align 4 - .data - .data 0x00000002 - mov r1, 1 - - .align 4 - .data - .data 0x00000002 - nop - - .align 4 - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/FastTekker/FastTekker.3___.patch.s b/system/client-functions/FastTekker/FastTekker.3___.patch.s deleted file mode 100644 index 579606b7..00000000 --- a/system/client-functions/FastTekker/FastTekker.3___.patch.s +++ /dev/null @@ -1,21 +0,0 @@ -.meta name="Fast tekker" -.meta description="Skips wind-up sound\nat tekker window" - -.versions 3OJT 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - .data - .data 4 - li r0, 1 - - .data - .data 4 - nop - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/FastTekker/FastTekker.4___.patch.s b/system/client-functions/FastTekker/FastTekker.4___.patch.s deleted file mode 100644 index 3896b214..00000000 --- a/system/client-functions/FastTekker/FastTekker.4___.patch.s +++ /dev/null @@ -1,34 +0,0 @@ -.meta name="Fast tekker" -.meta description="Skips wind-up sound\nat tekker window" - -.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksXB - - .data - .deltaof patch1_start, patch1_end -patch1_start: - mov dword [ebp + 0x14C], 1 -patch1_end: - - .data - .deltaof patch2_start, patch2_end -patch2_start: - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop -patch2_end: - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/FastTekker/FastTekker.5___.patch.s b/system/client-functions/FastTekker/FastTekker.5___.patch.s deleted file mode 100644 index 049a6b0b..00000000 --- a/system/client-functions/FastTekker/FastTekker.5___.patch.s +++ /dev/null @@ -1,35 +0,0 @@ -.meta name="Fast tekker" -.meta description="Skips wind-up sound\nat tekker window" - -.versions 59NJ 59NL - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksBB - - .data - .deltaof patch1_start, patch1_end -patch1_start: - mov dword [edi + 0x14C], 1 -patch1_end: - - .data - .deltaof patch2_start, patch2_end -patch2_start: - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop -patch2_end: - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/FlickeringStatusIcons/FlickeringStatusIcons.4___.patch.s b/system/client-functions/FlickeringStatusIcons.s similarity index 61% rename from system/client-functions/FlickeringStatusIcons/FlickeringStatusIcons.4___.patch.s rename to system/client-functions/FlickeringStatusIcons.s index 7439da75..ca190c6d 100644 --- a/system/client-functions/FlickeringStatusIcons/FlickeringStatusIcons.4___.patch.s +++ b/system/client-functions/FlickeringStatusIcons.s @@ -1,17 +1,59 @@ -.meta name="Blinking SD" -.meta description="Makes the Shifta\nand Deband status\nicons flicker when\nthey are about\nto run out" # Original code by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 -# Xbox port by fuzziqersoftware -.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU +.meta visibility="all" +.meta name="Blinking SD" +.meta description="Makes the Shifta\nand Deband status\nicons flicker when\nthey are about\nto run out" + entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksXB + .include WriteCodeBlocks + + + + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + + .data 0x8000B86C + .data 0x00000054 + .address 0x8000B86C +code_start: + mr r3, r0 + andi. r0, r31, 2 + beqlr + lwz r4, [r3 + 0x0028] + cmplwi r4, 0 + beqlr + lwz r4, [r4] + cmplwi r4, 0 + beqlr + mulli r0, r31, 12 + add r5, r29, r0 + lwz r6, [r5 + 0x025C] + cmplwi r6, 450 + bge full_intensity + lbz r6, [r4 + 0x002C] + subi r6, r6, 0x0008 + cmpwi r6, 0 + bge not_full_intensity +full_intensity: + li r6, 0x00FF +not_full_intensity: + stb [r4 + 0x002C], r6 + blr +code_end: + + .data + .data 4 + .address + bl code_start + + + + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU .data .data 5 @@ -58,5 +100,9 @@ skip: jmp edx code_end: + + + .all_versions + .data 0 .data 0 diff --git a/system/client-functions/FlickeringStatusIcons/FlickeringStatusIcons.3___.patch.s b/system/client-functions/FlickeringStatusIcons/FlickeringStatusIcons.3___.patch.s deleted file mode 100644 index 5e9f6799..00000000 --- a/system/client-functions/FlickeringStatusIcons/FlickeringStatusIcons.3___.patch.s +++ /dev/null @@ -1,50 +0,0 @@ -.meta name="Blinking SD" -.meta description="Makes the Shifta\nand Deband status\nicons flicker when\nthey are about\nto run out" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - .data 0x8000B86C - .data 0x00000054 - .address 0x8000B86C -code_start: - mr r3, r0 - andi. r0, r31, 2 - beqlr - lwz r4, [r3 + 0x0028] - cmplwi r4, 0 - beqlr - lwz r4, [r4] - cmplwi r4, 0 - beqlr - mulli r0, r31, 12 - add r5, r29, r0 - lwz r6, [r5 + 0x025C] - cmplwi r6, 450 - bge full_intensity - lbz r6, [r4 + 0x002C] - subi r6, r6, 0x0008 - cmpwi r6, 0 - bge not_full_intensity -full_intensity: - li r6, 0x00FF -not_full_intensity: - stb [r4 + 0x002C], r6 - blr -code_end: - - .data - .data 4 - .address - bl code_start - - .data 0 - .data 0 diff --git a/system/client-functions/ReticleColors/GCReticleColors.4___.patch.s b/system/client-functions/GCReticleColors.s similarity index 96% rename from system/client-functions/ReticleColors/GCReticleColors.4___.patch.s rename to system/client-functions/GCReticleColors.s index 15b5ce15..52702015 100644 --- a/system/client-functions/ReticleColors/GCReticleColors.4___.patch.s +++ b/system/client-functions/GCReticleColors.s @@ -1,16 +1,18 @@ -.meta name="GC targets" -.meta description="Changes the target\nreticle colors to\nthose used on the\nGameCube" # Original code by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 +.meta visibility="all" +.meta name="GC targets" +.meta description="Changes the target\nreticle colors to\nthose used on the\nGameCube" + .versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksXB + .include WriteCodeBlocks .data .data 0x00000004 diff --git a/system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.2___.patch.s b/system/client-functions/GetExtendedPlayerInfoDC.s similarity index 97% rename from system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.2___.patch.s rename to system/client-functions/GetExtendedPlayerInfoDC.s index b75d1eec..f8e8f057 100644 --- a/system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.2___.patch.s +++ b/system/client-functions/GetExtendedPlayerInfoDC.s @@ -1,5 +1,5 @@ -.meta hide_from_patches_menu -.meta name="GetExtendedPlayerInfo" +.meta key="GetExtendedPlayerInfo" +.meta name="Get extended player info" .meta description="" .versions 2OJ5 2OJF 2OEF 2OPF diff --git a/system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.3___.patch.s b/system/client-functions/GetExtendedPlayerInfoGC.s similarity index 97% rename from system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.3___.patch.s rename to system/client-functions/GetExtendedPlayerInfoGC.s index c80f0c44..2916908c 100644 --- a/system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.3___.patch.s +++ b/system/client-functions/GetExtendedPlayerInfoGC.s @@ -1,5 +1,5 @@ -.meta hide_from_patches_menu -.meta name="GetExtendedPlayerInfo" +.meta key="GetExtendedPlayerInfo" +.meta name="Get extended player info" .meta description="" .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 3SJT 3SJ0 3SE0 3SP0 diff --git a/system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.3OJT.patch.s b/system/client-functions/GetExtendedPlayerInfoGCNTE.s similarity index 95% rename from system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.3OJT.patch.s rename to system/client-functions/GetExtendedPlayerInfoGCNTE.s index 2aad6106..054fd557 100644 --- a/system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.3OJT.patch.s +++ b/system/client-functions/GetExtendedPlayerInfoGCNTE.s @@ -1,7 +1,9 @@ -.meta hide_from_patches_menu -.meta name="GetExtendedPlayerInfo" +.meta key="GetExtendedPlayerInfo" +.meta name="Get extended player info" .meta description="" +.versions 3OJT + entry_ptr: reloc0: .offsetof start diff --git a/system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.4___.patch.s b/system/client-functions/GetExtendedPlayerInfoXB.s similarity index 97% rename from system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.4___.patch.s rename to system/client-functions/GetExtendedPlayerInfoXB.s index 9df7b140..a8d10937 100644 --- a/system/client-functions/ExtendedPlayerInfo/GetExtendedPlayerInfo.4___.patch.s +++ b/system/client-functions/GetExtendedPlayerInfoXB.s @@ -1,5 +1,5 @@ -.meta hide_from_patches_menu -.meta name="GetExtendedPlayerInfo" +.meta key="GetExtendedPlayerInfo" +.meta name="Get extended player info" .meta description="" .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU diff --git a/system/client-functions/HungryMagSound/HungryMagSound.4___.patch.s b/system/client-functions/HungryMagSound.s similarity index 58% rename from system/client-functions/HungryMagSound/HungryMagSound.4___.patch.s rename to system/client-functions/HungryMagSound.s index 01b2ddaa..e505a585 100644 --- a/system/client-functions/HungryMagSound/HungryMagSound.4___.patch.s +++ b/system/client-functions/HungryMagSound.s @@ -1,17 +1,55 @@ -.meta name="MAG alert" -.meta description="Plays a sound when\nyour MAG is hungry" # Original code by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 # Xbox port by fuzziqersoftware -.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU +.meta visibility="all" +.meta name="Mag alert" +.meta description="Plays a sound when\nyour Mag is hungry" + entry_ptr: reloc0: .offsetof start + + + + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + + .label play_sound, + start: - .include WriteCodeBlocksXB + .include WriteCodeBlocks + + .data 0x8000BF30 + .deltaof code_start, code_end + .address 0x8000BF30 +code_start: # [std] (TItemMag* this @ r3) -> void + lwz r4, [r3 + 0xF0] + lhz r4, [r4 + 0x1C] # r4 = this->owner_player->entity_id + lwz r5, [r13 - ] # local_client_id + cmpl r4, r5 + bnelr + lis r3, 0x0002 + ori r3, r3, 0x2825 + li r4, 0 + b play_sound +code_end: + + .data + .data 0x00000004 + .address + b code_start + + .data 0x00000000 + .data 0x00000000 + + + + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + +start: + .include WriteCodeBlocks .data .data 0x0A @@ -81,3 +119,36 @@ hook6_end: .data 0x00000000 .data 0x00000000 + + + +.versions 59NJ 59NL + +start: + pop ecx + push 6 + push + call get_code_size + .deltaof patch_code, patch_code_end +get_code_size: + pop eax + push dword [eax] + call patch_code_end +patch_code: # [eax] (TItemMag* this @ ecx) -> void + mov dword [ecx + 0x01B8], eax + mov eax, [ecx + 0x00F8] + movzx eax, word [eax + 0x001C] # eax = this->owner_player->entity_id + cmp [], eax + jne patch_code_skip_sound + push 0 + push 0 + push 0 + push 0xAC + mov eax, + call eax + add esp, 0x10 +patch_code_skip_sound: + ret +patch_code_end: + push ecx + .include WriteCallToCode diff --git a/system/client-functions/HungryMagSound/HungryMagSound.3___.patch.s b/system/client-functions/HungryMagSound/HungryMagSound.3___.patch.s deleted file mode 100644 index f108e7fc..00000000 --- a/system/client-functions/HungryMagSound/HungryMagSound.3___.patch.s +++ /dev/null @@ -1,38 +0,0 @@ -.meta name="Mag alert" -.meta description="Plays a sound when\nyour Mag is hungry" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - .label play_sound, - - .data 0x8000BF30 - .deltaof code_start, code_end - .address 0x8000BF30 -code_start: # [std] (TItemMag* this @ r3) -> void - lwz r4, [r3 + 0xF0] - lhz r4, [r4 + 0x1C] # r4 = this->owner_player->entity_id - lwz r5, [r13 - ] # local_client_id - cmpl r4, r5 - bnelr - lis r3, 0x0002 - ori r3, r3, 0x2825 - li r4, 0 - b play_sound -code_end: - - .data - .data 0x00000004 - .address - b code_start - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/HungryMagSound/HungryMagSound.59NJ.patch.s b/system/client-functions/HungryMagSound/HungryMagSound.59NJ.patch.s deleted file mode 100644 index 46644072..00000000 --- a/system/client-functions/HungryMagSound/HungryMagSound.59NJ.patch.s +++ /dev/null @@ -1,34 +0,0 @@ -.meta name="MAG alert" -.meta description="Plays a sound when\nyour MAG is hungry" - -entry_ptr: -reloc0: - .offsetof start -start: - pop ecx - push 6 - push 0x005D91BE - call get_code_size - .deltaof patch_code, patch_code_end -get_code_size: - pop eax - push dword [eax] - call patch_code_end -patch_code: # [eax] (TItemMag* this @ ecx) -> void - mov dword [ecx + 0x01B8], eax - mov eax, [ecx + 0x00F8] - movzx eax, word [eax + 0x001C] # eax = this->owner_player->entity_id - cmp [0x00A9A074], eax - jne patch_code_skip_sound - push 0 - push 0 - push 0 - push 0xAC - mov eax, 0x00815020 - call eax - add esp, 0x10 -patch_code_skip_sound: - ret -patch_code_end: - push ecx - .include WriteCallToCode-59NJ diff --git a/system/client-functions/HungryMagSound/HungryMagSound.59NL.patch.s b/system/client-functions/HungryMagSound/HungryMagSound.59NL.patch.s deleted file mode 100644 index 1eb85479..00000000 --- a/system/client-functions/HungryMagSound/HungryMagSound.59NL.patch.s +++ /dev/null @@ -1,34 +0,0 @@ -.meta name="MAG alert" -.meta description="Plays a sound when\nyour MAG is hungry" - -entry_ptr: -reloc0: - .offsetof start -start: - pop ecx - push 6 - push 0x005D91E2 - call get_code_size - .deltaof patch_code, patch_code_end -get_code_size: - pop eax - push dword [eax] - call patch_code_end -patch_code: # [eax] (TItemMag* this @ ecx) -> void - mov dword [ecx + 0x01B8], eax - mov eax, [ecx + 0x00F8] - movzx eax, word [eax + 0x001C] # eax = this->owner_player->entity_id - cmp [0x00A9C4F4], eax - jne patch_code_skip_sound - push 0 - push 0 - push 0 - push 0xAC - mov eax, 0x00814298 - call eax - add esp, 0x10 -patch_code_skip_sound: - ret -patch_code_end: - push ecx - .include WriteCallToCode-59NL diff --git a/system/client-functions/InvisibleMag.s b/system/client-functions/InvisibleMag.s new file mode 100644 index 00000000..e3f04fdf --- /dev/null +++ b/system/client-functions/InvisibleMag.s @@ -0,0 +1,45 @@ +# Original code by Ralf @ GC-Forever and Aleron Ives +# https://www.gc-forever.com/forums/viewtopic.php?t=2050 +# https://www.gc-forever.com/forums/viewtopic.php?t=2049 +# DC and Xbox ports by fuzziqersoftware + +.meta visibility="all" +.meta name="Invisible MAG" +.meta description="Makes MAGs invisible" + + +entry_ptr: +reloc0: + .offsetof start +start: + .include WriteCodeBlocks + + + + .versions 1OJ2 1OJ3 1OJ4 1OJF 1OEF 1OPF 2OJ4 2OJ5 2OJF 2OEF 2OPF + .align 4 + .data + .data 0x00000004 + rets + nop + .align 4 + + + + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + .data + .data 0x00000004 + .data 0x480000D4 + + + + .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + .data + .data 0x00000002 + .binary 90E9 + + + + .all_versions + .data 0x00000000 + .data 0x00000000 diff --git a/system/client-functions/InvisibleMag/InvisibleMag.1___.patch.s b/system/client-functions/InvisibleMag/InvisibleMag.1___.patch.s deleted file mode 100644 index 4197270a..00000000 --- a/system/client-functions/InvisibleMag/InvisibleMag.1___.patch.s +++ /dev/null @@ -1,24 +0,0 @@ -.meta name="Invisible MAG" -.meta description="Makes MAGs invisible" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 -# DC port by fuzziqersoftware - -.versions 1OJ2 1OJ3 1OJ4 1OJF 1OEF 1OPF 2OJ4 2OJ5 2OJF 2OEF 2OPF - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksDC - - .align 4 - .data - .data 0x00000004 - rets - nop - - .align 4 - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/InvisibleMag/InvisibleMag.3___.patch.s b/system/client-functions/InvisibleMag/InvisibleMag.3___.patch.s deleted file mode 100644 index 2ae5874a..00000000 --- a/system/client-functions/InvisibleMag/InvisibleMag.3___.patch.s +++ /dev/null @@ -1,20 +0,0 @@ -.meta name="Invisible MAG" -.meta description="Makes MAGs invisible" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - .data - .data 0x00000004 - .data 0x480000D4 - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/InvisibleMag/InvisibleMag.4___.patch.s b/system/client-functions/InvisibleMag/InvisibleMag.4___.patch.s deleted file mode 100644 index 7d1d34a8..00000000 --- a/system/client-functions/InvisibleMag/InvisibleMag.4___.patch.s +++ /dev/null @@ -1,19 +0,0 @@ -.meta name="Invisible MAG" -.meta description="Makes MAGs invisible" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 -# Xbox port by fuzziqersoftware - -.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksXB - .data - .data 0x00000002 - .binary 90E9 - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/ItemLossPrevention.s b/system/client-functions/ItemLossPrevention.s new file mode 100644 index 00000000..cf277c2d --- /dev/null +++ b/system/client-functions/ItemLossPrevention.s @@ -0,0 +1,240 @@ +.meta visibility="all" +.meta name="No item loss" +.meta description="Disables logic that\ndeletes items if\nyou don't log off\nnormally" + +entry_ptr: +reloc0: + .offsetof start +start: + .include WriteCodeBlocks + + + + .versions 1OJ4 1OEF 1OPF + + .align 4 + .data + .data 4 + bs +0x38 + nop + + .align 4 + .data + .data 2 + sett + + .align 4 + .data + .data 2 + and r0, 0xFE + + .align 4 + .data + .data 2 + and r0, 0xFE + + .align 4 + .data + .data 2 + and r0, 0xFE + + .align 4 + .data + .data 2 + nop + + .align 4 + .data + .data 2 + nop + + .align 4 + .data + .data 2 + nop + + .align 4 + + + + .versions 1OJ2 + + .align 4 + .data 0x8C14C71A + .data 2 + nop + .align 4 + + + + .versions 2OJ4 2OJ5 2OJF 2OEF 2OPF + + .align 4 + .data + .data 6 + nop + bs +0x2C + nop + + .align 4 + .data + .data 2 + sett + + .align 4 + .data + .data 2 + and r0, 0xFE + + .align 4 + .data + .data 2 + nop + + .align 4 + .data + .data 2 + and r0, 0xFE + + .align 4 + .data + .data 2 + nop + + .align 4 + .data + .data 2 + and r0, 0xFE + + .align 4 + .data + .data 2 + nop + + .align 4 + .data + .data 2 + and r0, 0xFE + + .align 4 + .data + .data 2 + nop + + .align 4 + .data + .data 2 + and r0, 0xFE + + .align 4 + + + + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + + .data + .data 0x00000004 + b +0x4C + + .data + .data 0x00000004 + nop + + .data + .data 0x00000004 + nop + + .data + .data 0x00000004 + li r0, 0 + + .data + .data 0x00000004 + b +0x4C + + .data + .data 0x00000004 + b +0x20 + + + + .versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU + + .data + .data 0x00000001 + .binary 00 + + .data + .data 0x00000002 + .binary EB08 + + .data + .data 0x00000001 + .binary EB + + .data + .data 0x00000002 + .binary EB74 + + .data + .data 0x00000002 + .binary 9090 + + .data + .data 0x00000002 + .binary EB08 + + .data + .data 0x00000002 + .binary 9090 + + .data + .data 0x00000002 + .binary EB08 + + .data + .data 0x00000001 + .binary 00 + + .data + .data 0x00000002 + .binary EB08 + + .data + .data 0x00000001 + .binary 00 + + .data + .data 0x00000001 + .binary 00 + + .data + .data 0x00000002 + .binary EB08 + + .data + .data 0x00000001 + .binary 00 + + .data + .data 0x00000002 + .binary EB08 + + .data + .data 0x00000001 + .binary 00 + + .data + .data 0x00000002 + .binary EB08 + + .data + .data 0x00000001 + .binary EB + + + + .all_versions + + .data 0x00000000 + .data 0x00000000 diff --git a/system/client-functions/ItemLossPrevention/ItemLossPrevention.1OJ2.patch.s b/system/client-functions/ItemLossPrevention/ItemLossPrevention.1OJ2.patch.s deleted file mode 100644 index fab3accd..00000000 --- a/system/client-functions/ItemLossPrevention/ItemLossPrevention.1OJ2.patch.s +++ /dev/null @@ -1,17 +0,0 @@ -.meta name="No item loss" -.meta description="Disables logic that\ndeletes items if\nyou don't log off\nnormally" - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksDC - - .align 4 - .data 0x8C14C71A - .data 2 - nop - - .align 4 - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/ItemLossPrevention/ItemLossPrevention.1___.patch.s b/system/client-functions/ItemLossPrevention/ItemLossPrevention.1___.patch.s deleted file mode 100644 index 30d611fb..00000000 --- a/system/client-functions/ItemLossPrevention/ItemLossPrevention.1___.patch.s +++ /dev/null @@ -1,55 +0,0 @@ -.meta name="No item loss" -.meta description="Disables logic that\ndeletes items if\nyou don't log off\nnormally" - -.versions 1OJ4 1OEF 1OPF - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksDC - - .align 4 - .data - .data 4 - bs +0x38 - nop - - .align 4 - .data - .data 2 - sett - - .align 4 - .data - .data 2 - and r0, 0xFE - - .align 4 - .data - .data 2 - and r0, 0xFE - - .align 4 - .data - .data 2 - and r0, 0xFE - - .align 4 - .data - .data 2 - nop - - .align 4 - .data - .data 2 - nop - - .align 4 - .data - .data 2 - nop - - .align 4 - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/ItemLossPrevention/ItemLossPrevention.2___.patch.s b/system/client-functions/ItemLossPrevention/ItemLossPrevention.2___.patch.s deleted file mode 100644 index c8de53e7..00000000 --- a/system/client-functions/ItemLossPrevention/ItemLossPrevention.2___.patch.s +++ /dev/null @@ -1,75 +0,0 @@ -.meta name="No item loss" -.meta description="Disables logic that\ndeletes items if\nyou don't log off\nnormally" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 -# DCv2 port by fuzziqersoftware - -.versions 2OJ4 2OJ5 2OJF 2OEF 2OPF - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksDC - - .align 4 - .data - .data 6 - nop - bs +0x2C - nop - - .align 4 - .data - .data 2 - sett - - .align 4 - .data - .data 2 - and r0, 0xFE - - .align 4 - .data - .data 2 - nop - - .align 4 - .data - .data 2 - and r0, 0xFE - - .align 4 - .data - .data 2 - nop - - .align 4 - .data - .data 2 - and r0, 0xFE - - .align 4 - .data - .data 2 - nop - - .align 4 - .data - .data 2 - and r0, 0xFE - - .align 4 - .data - .data 2 - nop - - .align 4 - .data - .data 2 - and r0, 0xFE - - .align 4 - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/ItemLossPrevention/ItemLossPrevention.3___.patch.s b/system/client-functions/ItemLossPrevention/ItemLossPrevention.3___.patch.s deleted file mode 100644 index 1c5b8034..00000000 --- a/system/client-functions/ItemLossPrevention/ItemLossPrevention.3___.patch.s +++ /dev/null @@ -1,40 +0,0 @@ -.meta name="No item loss" -.meta description="Disables logic that\ndeletes items if\nyou don't log off\nnormally" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - .data - .data 0x00000004 - b +0x4C - - .data - .data 0x00000004 - nop - - .data - .data 0x00000004 - nop - - .data - .data 0x00000004 - li r0, 0 - - .data - .data 0x00000004 - b +0x4C - - .data - .data 0x00000004 - b +0x20 - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/ItemLossPrevention/ItemLossPrevention.4___.patch.s b/system/client-functions/ItemLossPrevention/ItemLossPrevention.4___.patch.s deleted file mode 100644 index 6367b735..00000000 --- a/system/client-functions/ItemLossPrevention/ItemLossPrevention.4___.patch.s +++ /dev/null @@ -1,70 +0,0 @@ -.meta name="No item loss" -.meta description="Disables logic that\ndeletes items if\nyou don't log off\nnormally" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 -# Xbox port by fuzziqersoftware - -.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksXB - .data - .data 0x00000001 - .binary 00 - .data - .data 0x00000002 - .binary EB08 - .data - .data 0x00000001 - .binary EB - .data - .data 0x00000002 - .binary EB74 - .data - .data 0x00000002 - .binary 9090 - .data - .data 0x00000002 - .binary EB08 - .data - .data 0x00000002 - .binary 9090 - .data - .data 0x00000002 - .binary EB08 - .data - .data 0x00000001 - .binary 00 - .data - .data 0x00000002 - .binary EB08 - .data - .data 0x00000001 - .binary 00 - .data - .data 0x00000001 - .binary 00 - .data - .data 0x00000002 - .binary EB08 - .data - .data 0x00000001 - .binary 00 - .data - .data 0x00000002 - .binary EB08 - .data - .data 0x00000001 - .binary 00 - .data - .data 0x00000002 - .binary EB08 - .data - .data 0x00000001 - .binary EB - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/ItemPickup/ItemPickup.3___.patch.s b/system/client-functions/ItemPickup.s similarity index 57% rename from system/client-functions/ItemPickup/ItemPickup.3___.patch.s rename to system/client-functions/ItemPickup.s index 073de352..cdf226f8 100644 --- a/system/client-functions/ItemPickup/ItemPickup.3___.patch.s +++ b/system/client-functions/ItemPickup.s @@ -1,16 +1,22 @@ -.meta name="Item pickup" -.meta description="Prevents picking\nup items unless you\nhold the Z button" # Original code by Ralf @ GC-Forever # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 +# Xbox port by fuzziqersoftware + +.meta visibility="all" +.meta name="Item pickup" +.meta description="Prevents picking\nup items unless you\nhold the Z button" -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks + + + + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 .label hook_loc, 0x8000B938 .data hook_loc @@ -38,5 +44,33 @@ hook_end: .data 0x00000004 li r4, 8 + + + .versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU + + .data + .data 0x07 + .binary E8880100009090 + + .data + .data 0x0A + .binary 8B866C05000085C0EB46 + + .data + .data 0x09 + .binary 74038A40013408EB46 + + .data + .data 0x0A + .binary 7507F68624030000E0C3 + + .data + .data 0x01 + .binary 00 + + + + .all_versions + .data 0x00000000 .data 0x00000000 diff --git a/system/client-functions/ItemPickup/ItemPickup.4___.patch.s b/system/client-functions/ItemPickup/ItemPickup.4___.patch.s deleted file mode 100644 index 904e0ee3..00000000 --- a/system/client-functions/ItemPickup/ItemPickup.4___.patch.s +++ /dev/null @@ -1,31 +0,0 @@ -.meta name="Item pickup" -.meta description="Prevents picking\nup items unless you\nhold the white or\nblack button" -# Original code by Ralf @ GC-Forever -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 -# Xbox port by fuzziqersoftware - -.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksXB - .data - .data 0x07 - .binary E8880100009090 - .data - .data 0x0A - .binary 8B866C05000085C0EB46 - .data - .data 0x09 - .binary 74038A40013408EB46 - .data - .data 0x0A - .binary 7507F68624030000E0C3 - .data - .data 0x01 - .binary 00 - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/Movement/Movement.4___.patch.s b/system/client-functions/Movement.s similarity index 70% rename from system/client-functions/Movement/Movement.4___.patch.s rename to system/client-functions/Movement.s index 4ba39f82..f7921b69 100644 --- a/system/client-functions/Movement/Movement.4___.patch.s +++ b/system/client-functions/Movement.s @@ -1,13 +1,33 @@ -.meta name="Movement" -.meta description="Fixes movement dead\nzone thresholds" +# Original code by Ralf @ GC-Forever and Aleron Ives +# https://www.gc-forever.com/forums/viewtopic.php?t=2050 +# https://www.gc-forever.com/forums/viewtopic.php?t=2049 + +.meta visibility="all" +.meta name="Movement" +.meta description="Allow backsteps and\nmovement when\nenemies are nearby" -.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksXB + .include WriteCodeBlocks + + + + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + + .data + .data 0x00000004 + b +0x0C + + .data + .data 0x00000004 + b +0x14 + + + + .versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU .data .deltaof code_start, code_end @@ -50,5 +70,10 @@ process_stick_value: .zero 0x56 code_end: + + + + .all_versions + .data 0x00000000 .data 0x00000000 diff --git a/system/client-functions/Movement/Movement.3___.patch.s b/system/client-functions/Movement/Movement.3___.patch.s deleted file mode 100644 index e890a6cc..00000000 --- a/system/client-functions/Movement/Movement.3___.patch.s +++ /dev/null @@ -1,24 +0,0 @@ -.meta name="Movement" -.meta description="Allow backsteps and\nmovement when\nenemies are nearby" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - .data - .data 0x00000004 - b +0x0C - - .data - .data 0x00000004 - b +0x14 - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/NoRareSelling.s b/system/client-functions/NoRareSelling.s new file mode 100644 index 00000000..211ceb5c --- /dev/null +++ b/system/client-functions/NoRareSelling.s @@ -0,0 +1,123 @@ +# Original patch by Soly, in Blue Burst Patch Project +# https://github.com/Solybum/Blue-Burst-Patch-Project +# GC and Xbox ports by fuzziqersoftware + +.meta visibility="all" +.meta name="No rare selling" +.meta description="Stops you from\naccidentally\nselling rares\nto shops" + +entry_ptr: +reloc0: + .offsetof start + + + +.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 + +start: + .include WriteCodeBlocks + + # See comments in the 59NL version of this patch for details on how it works. + + .data + .data 0x00000004 + li r29, 0 + + .data + .data 0x00000004 + li r29, 0 + + .data + .data 0x00000004 + li r29, 0 + + .label tool_check_hook_loc, 0x800041A0 + .data tool_check_hook_loc + .deltaof tool_check_hook_start, tool_check_hook_end + .address tool_check_hook_loc +tool_check_hook_start: + lwz r29, [r3 + 0x10] # Flags + xori r29, r29, 0x0080 + andi. r29, r29, 0x0080 + bnelr # Not rare; r29 (returned price) is zero already + lwz r29, [r3 + 0x0C] # Cost + blr +tool_check_hook_end: + + .label tool_check_hook_call, + .data tool_check_hook_call + .data 0x00000004 + .address tool_check_hook_call + bl tool_check_hook_start + + .data 0x00000000 + .data 0x00000000 + + + +.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU + +start: + .include WriteCodeBlocks + + # See comments in the 59NL version of this patch for details on how it works. + + .data + .data 0x00000004 + .data 0x00000000 + + .data + .data 0x00000004 + .data 0x00000000 + + .data + .data 0x00000005 + .binary E98E0C0000 + + .data + .deltaof tool_check_start, tool_check_end +tool_check_start: + xor edi, edi + test byte [eax + 0x10], 0x80 + cmovz edi, [eax + 0x0C] + .binary E995F3FFFF +tool_check_end: + + .data 0x00000000 + .data 0x00000000 + + + +.versions 59NJ 59NL + +start: + # This works by setting the item price to zero if it's rare, which causes the game to prevent you from selling the + # item. For armors and weapons, this is easy because there are easily-patchable opcodes within branches that return a + # constant price for rare items. + xor eax, eax + mov [], eax # Rare armors + mov [], eax # Unidentified weapons + mov [], eax # Rare weapons + + # For tools, it's harder to implement this, because the price comes from the ItemPMT tools table and there is no + # branch for rares. Still, we can add a branch to a stub to handle tools. + pop ecx + push 5 + push + call get_code_size + .deltaof patch_code, patch_code_end +get_code_size: + pop eax + push dword [eax] + call patch_code_end +patch_code: + # TODO: It'd be nice to have something like WriteJumpToAndFromCode, since this hook is supposed to return to a + # different place than where it was called, hence this mov [esp]. + mov dword [esp], + xor edi, edi + test byte [eax + 0x14], 0x80 # flags & 0x80 = is rare + cmovz edi, [eax + 0x10] # Use price from table if not rare + ret +patch_code_end: + push ecx + .include WriteCallToCode diff --git a/system/client-functions/NoRareSelling/NoRareSelling.3___.patch.s b/system/client-functions/NoRareSelling/NoRareSelling.3___.patch.s deleted file mode 100644 index 868a531d..00000000 --- a/system/client-functions/NoRareSelling/NoRareSelling.3___.patch.s +++ /dev/null @@ -1,50 +0,0 @@ -# Original patch by Soly, in Blue Burst Patch Project -# https://github.com/Solybum/Blue-Burst-Patch-Project -# GC port by fuzziqersoftware - -.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 - -.meta name="No rare selling" -.meta description="Stops you from\naccidentally\nselling rares\nto shops" - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksGC - - # See comments in the 59NL version of this patch for details on how it works. - - .data - .data 0x00000004 - li r29, 0 - - .data - .data 0x00000004 - li r29, 0 - - .data - .data 0x00000004 - li r29, 0 - - .label tool_check_hook_loc, 0x800041A0 - .data tool_check_hook_loc - .deltaof tool_check_hook_start, tool_check_hook_end - .address tool_check_hook_loc -tool_check_hook_start: - lwz r29, [r3 + 0x10] # Flags - xori r29, r29, 0x0080 - andi. r29, r29, 0x0080 - bnelr # Not rare; r29 (returned price) is zero already - lwz r29, [r3 + 0x0C] # Cost - blr -tool_check_hook_end: - - .label tool_check_hook_call, - .data tool_check_hook_call - .data 0x00000004 - .address tool_check_hook_call - bl tool_check_hook_start - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/NoRareSelling/NoRareSelling.4___.patch.s b/system/client-functions/NoRareSelling/NoRareSelling.4___.patch.s deleted file mode 100644 index 6fe2727a..00000000 --- a/system/client-functions/NoRareSelling/NoRareSelling.4___.patch.s +++ /dev/null @@ -1,40 +0,0 @@ -# Original patch by Soly, in Blue Burst Patch Project -# https://github.com/Solybum/Blue-Burst-Patch-Project -# Xbox port by fuzziqersoftware - -.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU - -.meta name="No rare selling" -.meta description="Stops you from\naccidentally\nselling rares\nto shops" - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksXB - - # See comments in the 59NL version of this patch for details on how it works. - - .data - .data 0x00000004 - .data 0x00000000 - - .data - .data 0x00000004 - .data 0x00000000 - - .data - .data 0x00000005 - .binary E98E0C0000 - - .data - .deltaof tool_check_start, tool_check_end -tool_check_start: - xor edi, edi - test byte [eax + 0x10], 0x80 - cmovz edi, [eax + 0x0C] - .binary E995F3FFFF -tool_check_end: - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/NoRareSelling/NoRareSelling.59NJ.patch.s b/system/client-functions/NoRareSelling/NoRareSelling.59NJ.patch.s deleted file mode 100644 index 38f19d49..00000000 --- a/system/client-functions/NoRareSelling/NoRareSelling.59NJ.patch.s +++ /dev/null @@ -1,43 +0,0 @@ -# Original patch by Soly, in Blue Burst Patch Project -# https://github.com/Solybum/Blue-Burst-Patch-Project - -.meta name="No rare selling" -.meta description="Stops you from accidentally\nselling rares to vendors" - -entry_ptr: -reloc0: - .offsetof start -start: - # This works by setting the item price to zero if it's rare, which causes - # the game to prevent you from selling the item. For armors and weapons, this - # is easy because there are easily-patchable opcodes within branches that - # return a constant price for rare items. - xor eax, eax - mov [0x005D258F], eax # Rare armors - mov [0x005D26D1], eax # Unidentified weapons - mov [0x005D26E6], eax # Rare weapons - - # For tools, it's harder to implement this, because the price comes from the - # ItemPMT tools table and there is no branch for rares. Still, we can add a - # branch to a stub to handle tools. - pop ecx - push 5 - push 0x005D2508 - call get_code_size - .deltaof patch_code, patch_code_end -get_code_size: - pop eax - push dword [eax] - call patch_code_end -patch_code: - # TODO: It'd be nice to have something like WriteJumpToAndFromCode, since - # this hook is supposed to return to a different place than where it was - # called, hence this mov [esp]. - mov dword [esp], 0x005D2556 - xor edi, edi - test byte [eax + 0x14], 0x80 # flags & 0x80 = is rare - cmovz edi, [eax + 0x10] # Use price from table if not rare - ret -patch_code_end: - push ecx - .include WriteCallToCode-59NJ diff --git a/system/client-functions/NoRareSelling/NoRareSelling.59NL.patch.s b/system/client-functions/NoRareSelling/NoRareSelling.59NL.patch.s deleted file mode 100644 index dd92472c..00000000 --- a/system/client-functions/NoRareSelling/NoRareSelling.59NL.patch.s +++ /dev/null @@ -1,43 +0,0 @@ -# Original patch by Soly, in Blue Burst Patch Project -# https://github.com/Solybum/Blue-Burst-Patch-Project - -.meta name="No rare selling" -.meta description="Stops you from accidentally\nselling rares to vendors" - -entry_ptr: -reloc0: - .offsetof start -start: - # This works by setting the item price to zero if it's rare, which causes - # the game to prevent you from selling the item. For armors and weapons, this - # is easy because there are easily-patchable opcodes within branches that - # return a constant price for rare items. - xor eax, eax - mov [0x005D25AF], eax # Rare armors - mov [0x005D26F1], eax # Unidentified weapons - mov [0x005D2706], eax # Rare weapons - - # For tools, it's harder to implement this, because the price comes from the - # ItemPMT tools table and there is no branch for rares. Still, we can add a - # branch to a stub to handle tools. - pop ecx - push 5 - push 0x005D2528 - call get_code_size - .deltaof patch_code, patch_code_end -get_code_size: - pop eax - push dword [eax] - call patch_code_end -patch_code: - # TODO: It'd be nice to have something like WriteJumpToAndFromCode, since - # this hook is supposed to return to a different place than where it was - # called, hence this mov [esp]. - mov dword [esp], 0x005D2576 - xor edi, edi - test byte [eax + 0x14], 0x80 # flags & 0x80 = is rare - cmovz edi, [eax + 0x10] # Use price from table if not rare - ret -patch_code_end: - push ecx - .include WriteCallToCode-59NL diff --git a/system/client-functions/Palette/Palette.59NL.patch.s b/system/client-functions/Palette/Palette.59NL.patch.s deleted file mode 100644 index 3382cf22..00000000 --- a/system/client-functions/Palette/Palette.59NL.patch.s +++ /dev/null @@ -1,230 +0,0 @@ -# Original patch by Soly, in Blue Burst Patch Project -# https://github.com/Solybum/Blue-Burst-Patch-Project - -.meta name="Palette" -.meta description="Enables the alternate action\npalette for number keys" - -entry_ptr: -reloc0: - .offsetof start - -write_call_func: - .include WriteCallToCode-59NL - -start: - mov al, 0xEB - mov [0x0068A739], al # SecondaryPaletteAttack1 - xor al, al - mov [0x006A114F], al # SecondaryPaletteAttack2 - mov [0x006A0C4F], al # SecondaryPaletteAttack3 - - call patch_func_1 # GetCurrentPalette - call patch_func_2 # CheckHotkey1_1 - call patch_func_3 # CheckHotkey1_2 - call patch_func_4 # CheckHotkey2_1 - call patch_func_5 # CheckHotkey2_2 - call patch_func_6 # CheckHotkey3_1 - call patch_func_7 # CheckHotkey3_2 - jmp write_code_blocks # UnsetHotkey1, UnsetHotkey2, SetHotkey - -# GetCurrentPalette -patch_func_1: - pop ecx - push 8 - push 0x00748944 - call get_code_size1 - .deltaof patch_code1, patch_code_end1 -get_code_size1: - pop eax - push dword [eax] - call patch_code_end1 -patch_code1: - mov edx, [ebp - 0x14] - mov edx, [edx + 0x2C] - movzx edx, byte [edx + 0x62] - test edx, edx - setnz byte [0x00748ACF] - mov edx, edi - and edx, 0xFF - ret -patch_code_end1: - push ecx - jmp write_call_func - -# CheckHotkey1_1 -patch_func_2: - pop ecx - push 5 - push 0x00748992 - call get_code_size2 - .deltaof patch_code2, patch_code_end2 -get_code_size2: - pop eax - push dword [eax] - call patch_code_end2 -patch_code2: - cmp byte [0x00748ACF], 0 - jnz +0x06 - movzx edx, byte [eax + esi * 4 + 0x04] # main palette - ret - movzx edx, byte [eax + esi * 4 + 0x3C] # alt palette - ret -patch_code_end2: - push ecx - jmp write_call_func - -# CheckHotkey1_2 -patch_func_3: - pop ecx - push 5 - push 0x007489A1 - call get_code_size3 - .deltaof patch_code3, patch_code_end3 -get_code_size3: - pop eax - push dword [eax] - call patch_code_end3 -patch_code3: - cmp byte [0x00748ACF], 0 - jnz +0x06 - movzx ecx, byte [eax + ecx * 2 + 0x05] # main palette - ret - movzx ecx, byte [eax + ecx * 2 + 0x3D] # alt palette - ret -patch_code_end3: - push ecx - jmp write_call_func - -# CheckHotkey2_1 -patch_func_4: - pop ecx - push 5 - push 0x00748A3C - call get_code_size4 - .deltaof patch_code4, patch_code_end4 -get_code_size4: - pop eax - push dword [eax] - call patch_code_end4 -patch_code4: - cmp byte [0x00748ACF], 0 - jnz +0x06 - movzx edx, byte [edx + ebx * 4 + 0x04] # main palette - ret - movzx edx, byte [edx + ebx * 4 + 0x3C] # alt palette - ret -patch_code_end4: - push ecx - jmp write_call_func - -# CheckHotkey2_2 -patch_func_5: - pop ecx - push 5 - push 0x00748A4B - call get_code_size5 - .deltaof patch_code5, patch_code_end5 -get_code_size5: - pop eax - push dword [eax] - call patch_code_end5 -patch_code5: - cmp byte [0x00748ACF], 0 - jnz +0x06 - movzx ecx, byte [edx + eax * 2 + 0x05] # main palette - ret - movzx ecx, byte [edx + eax * 2 + 0x3D] # alt palette - ret -patch_code_end5: - push ecx - jmp write_call_func - -# CheckHotkey3_1 -patch_func_6: - pop ecx - push 5 - push 0x007103B7 - call get_code_size6 - .deltaof patch_code6, patch_code_end6 -get_code_size6: - pop eax - push dword [eax] - call patch_code_end6 -patch_code6: - cmp byte [0x00748ACF], 0 - jnz +0x06 - movzx ecx, byte [eax + edx * 4 + 0x04] # main palette - ret - movzx ecx, byte [eax + edx * 4 + 0x3C] # alt palette - ret -patch_code_end6: - push ecx - jmp write_call_func - -# CheckHotkey3_2 -patch_func_7: - pop ecx - push 5 - push 0x007103C0 - call get_code_size7 - .deltaof patch_code7, patch_code_end7 -get_code_size7: - pop eax - push dword [eax] - call patch_code_end7 -patch_code7: - cmp byte [0x00748ACF], 0 - jnz +0x06 - movzx ecx, byte [eax + edx * 4 + 0x05] # main palette - ret - movzx ecx, byte [eax + edx * 4 + 0x3D] # alt palette - ret -patch_code_end7: - push ecx - jmp write_call_func - -write_code_blocks: - .include WriteCodeBlocksBB - - .data 0x007489B9 - .deltaof code_block1_start, code_block1_end - -# UnsetHotkey1 -code_block1_start: - push dword [0x00748ACF] - push eax - mov eax, 0x0068CDE0 # SetPaletteHotkey - call eax - .binary 909090909090909090 -code_block1_end: - .data 0x00748A5F - .deltaof code_block2_start, code_block2_end - -# UnsetHotkey2 -code_block2_start: - push dword [0x00748ACF] - push eax - mov eax, 0x0068CDE0 # SetPaletteHotkey - call eax - .binary 909090909090909090 -code_block2_end: - .data 0x00748ABE - .deltaof code_block3_start, code_block3_end - -# SetHotkey -code_block3_start: - mov eax, [ebp - 0x24] - mov ecx, [ebp - 0x28] - movzx ebx, word [eax] - movzx edx, word [eax + 0x02] - push edx - push ebx - push esi - .binary 6800000000 # tmpCurrentPalette = 0x00748ACF - push 0 - mov eax, 0x0068CDE0 # SetPaletteHotkey - call eax - .binary 90909090909090909090909090909090 -code_block3_end: - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/Palette/Palette.59NJ.patch.s b/system/client-functions/PaletteBB.s similarity index 75% rename from system/client-functions/Palette/Palette.59NJ.patch.s rename to system/client-functions/PaletteBB.s index a1ada3b3..211daabc 100644 --- a/system/client-functions/Palette/Palette.59NJ.patch.s +++ b/system/client-functions/PaletteBB.s @@ -1,22 +1,26 @@ # Original patch by Soly, in Blue Burst Patch Project # https://github.com/Solybum/Blue-Burst-Patch-Project +.meta visibility="all" +.meta key="Palette" .meta name="Palette" .meta description="Enables the alternate action\npalette for number keys" +.versions 59NJ 59NL + entry_ptr: reloc0: .offsetof start write_call_func: - .include WriteCallToCode-59NJ + .include WriteCallToCode start: mov al, 0xEB - mov [0x0068A7A5], al # SecondaryPaletteAttack1 + mov [], al # SecondaryPaletteAttack1 xor al, al - mov [0x006A11B7], al # SecondaryPaletteAttack2 - mov [0x006A0CB7], al # SecondaryPaletteAttack3 + mov [], al # SecondaryPaletteAttack2 + mov [], al # SecondaryPaletteAttack3 call patch_func_1 # GetCurrentPalette call patch_func_2 # CheckHotkey1_1 @@ -31,7 +35,7 @@ start: patch_func_1: pop ecx push 8 - push 0x00748990 + push call get_code_size1 .deltaof patch_code1, patch_code_end1 get_code_size1: @@ -43,7 +47,7 @@ patch_code1: mov edx, [edx + 0x2C] movzx edx, byte [edx + 0x62] test edx, edx - setnz byte [0x00748B1B] + setnz byte [] mov edx, edi and edx, 0xFF ret @@ -55,7 +59,7 @@ patch_code_end1: patch_func_2: pop ecx push 5 - push 0x007489DE + push call get_code_size2 .deltaof patch_code2, patch_code_end2 get_code_size2: @@ -63,7 +67,7 @@ get_code_size2: push dword [eax] call patch_code_end2 patch_code2: - cmp byte [0x00748B1B], 0 + cmp byte [], 0 jnz +0x06 movzx edx, byte [eax + esi * 4 + 0x04] # main palette ret @@ -77,7 +81,7 @@ patch_code_end2: patch_func_3: pop ecx push 5 - push 0x007489ED + push call get_code_size3 .deltaof patch_code3, patch_code_end3 get_code_size3: @@ -85,7 +89,7 @@ get_code_size3: push dword [eax] call patch_code_end3 patch_code3: - cmp byte [0x00748B1B], 0 + cmp byte [], 0 jnz +0x06 movzx ecx, byte [eax + ecx * 2 + 0x05] # main palette ret @@ -99,7 +103,7 @@ patch_code_end3: patch_func_4: pop ecx push 5 - push 0x00748A88 + push call get_code_size4 .deltaof patch_code4, patch_code_end4 get_code_size4: @@ -107,7 +111,7 @@ get_code_size4: push dword [eax] call patch_code_end4 patch_code4: - cmp byte [0x00748B1B], 0 + cmp byte [], 0 jnz +0x06 movzx edx, byte [edx + ebx * 4 + 0x04] # main palette ret @@ -121,7 +125,7 @@ patch_code_end4: patch_func_5: pop ecx push 5 - push 0x00748A97 + push call get_code_size5 .deltaof patch_code5, patch_code_end5 get_code_size5: @@ -129,7 +133,7 @@ get_code_size5: push dword [eax] call patch_code_end5 patch_code5: - cmp byte [0x00748B1B], 0 + cmp byte [], 0 jnz +0x06 movzx ecx, byte [edx + eax * 2 + 0x05] # main palette ret @@ -143,7 +147,7 @@ patch_code_end5: patch_func_6: pop ecx push 5 - push 0x007103D3 + push call get_code_size6 .deltaof patch_code6, patch_code_end6 get_code_size6: @@ -151,7 +155,7 @@ get_code_size6: push dword [eax] call patch_code_end6 patch_code6: - cmp byte [0x00748B1B], 0 + cmp byte [], 0 jnz +0x06 movzx ecx, byte [eax + edx * 4 + 0x04] # main palette ret @@ -165,7 +169,7 @@ patch_code_end6: patch_func_7: pop ecx push 5 - push 0x007103DC + push call get_code_size7 .deltaof patch_code7, patch_code_end7 get_code_size7: @@ -173,7 +177,7 @@ get_code_size7: push dword [eax] call patch_code_end7 patch_code7: - cmp byte [0x00748B1B], 0 + cmp byte [], 0 jnz +0x06 movzx ecx, byte [eax + edx * 4 + 0x05] # main palette ret @@ -184,31 +188,31 @@ patch_code_end7: jmp write_call_func write_code_blocks: - .include WriteCodeBlocksBB + .include WriteCodeBlocks - .data 0x00748A05 + .data .deltaof code_block1_start, code_block1_end # UnsetHotkey1 code_block1_start: - push dword [0x00748B1B] + push dword [] push eax - mov eax, 0x0068CE4C # SetPaletteHotkey + mov eax, # SetPaletteHotkey call eax .binary 909090909090909090 code_block1_end: - .data 0x00748AAB + .data .deltaof code_block2_start, code_block2_end # UnsetHotkey2 code_block2_start: - push dword [0x00748B1B] + push dword [] push eax - mov eax, 0x0068CE4C # SetPaletteHotkey + mov eax, # SetPaletteHotkey call eax .binary 909090909090909090 code_block2_end: - .data 0x00748B0A + .data .deltaof code_block3_start, code_block3_end # SetHotkey @@ -220,9 +224,9 @@ code_block3_start: push edx push ebx push esi - .binary 6800000000 # tmpCurrentPalette = 0x00748B1B + .binary 6800000000 # tmpCurrentPalette = push 0 - mov eax, 0x0068CE4C # SetPaletteHotkey + mov eax, # SetPaletteHotkey call eax .binary 90909090909090909090909090909090 code_block3_end: diff --git a/system/client-functions/Palette/Palette.3___.patch.s b/system/client-functions/PaletteGC.s similarity index 99% rename from system/client-functions/Palette/Palette.3___.patch.s rename to system/client-functions/PaletteGC.s index 9d190151..6721894c 100644 --- a/system/client-functions/Palette/Palette.3___.patch.s +++ b/system/client-functions/PaletteGC.s @@ -1,9 +1,12 @@ -.meta name="Palette" -.meta description="Use C stick to\nuse 4 customize\nconfigurations\ninstead of just one" # Original codes by Ralf @ GC-Forever # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 +.meta visibility="all" +.meta key="Palette" +.meta name="Palette" +.meta description="Use C stick to\nuse 4 customize\nconfigurations\ninstead of just one" + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 # This code will let you have up to four different palettes of action buttons. @@ -27,7 +30,7 @@ entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks .label memcpy, 0x8000E41C .label memset, 0x8000E334 diff --git a/system/client-functions/RareDropNotifications/RareDropNotifications.2___.patch.s b/system/client-functions/RareDropNotificationsDC.s similarity index 83% rename from system/client-functions/RareDropNotifications/RareDropNotifications.2___.patch.s rename to system/client-functions/RareDropNotificationsDC.s index 7ce6505e..ea067486 100644 --- a/system/client-functions/RareDropNotifications/RareDropNotifications.2___.patch.s +++ b/system/client-functions/RareDropNotificationsDC.s @@ -1,31 +1,33 @@ -.meta name="Rare alerts" -.meta description="Show rare items on\nthe map and play a\nsound when a rare\nitem drops" # Inspired by and adapted from the original patch for Ep1&2 made by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 +.meta visibility="all" +.meta key="RareDropNotifications" +.meta name="Rare alerts" +.meta description="Show rare items on\nthe map and play a\nsound when a rare\nitem drops" + .versions 2OJF 2OJ5 2OEF 2OPF entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksDC + .include WriteCodeBlocks - - #replace function call from command 6x5f to call to custom code for sound + # Replace function call from command 6x5F to call to custom code for sound .align 4 .data .data 4 .data - - #replace function call from TItem::update in the case when the item is on the ground + + # Replace function call from TItem::update in the case when the item is on the ground .align 4 .data .data 4 .data - #custom code that goes to check if item is rare and in the area the player is in and plays sound if it is + # Custom code that goes to check if item is rare and in the area the player is in and plays sound if it is .align 4 .data .data 72 @@ -47,11 +49,8 @@ start: .data .data 0x00050013 .data - - - - - #custom code for checking if an item is rare or not (from r5) + + # Custom code for checking if an item is rare or not (from r5) .align 4 .data .data 68 @@ -72,9 +71,8 @@ start: .data 0x89013066 .data 0xE000000B .data 0xE001000B - - - #custom code for checking if item is rare, then showing a dot on the map with color FF0000 if it is + + # Custom code for checking if item is rare, then showing a dot on the map with color FF0000 if it is .align 4 .data .data 88 @@ -101,7 +99,6 @@ start: .data 0xFFFF0000 .data - .align 4 .data 0x00000000 .data 0x00000000 diff --git a/system/client-functions/RareDropNotifications/RareDropNotifications.3___.patch.s b/system/client-functions/RareDropNotificationsGC.s similarity index 96% rename from system/client-functions/RareDropNotifications/RareDropNotifications.3___.patch.s rename to system/client-functions/RareDropNotificationsGC.s index da1e4f58..9a6e9891 100644 --- a/system/client-functions/RareDropNotifications/RareDropNotifications.3___.patch.s +++ b/system/client-functions/RareDropNotificationsGC.s @@ -1,16 +1,19 @@ -.meta name="Rare alerts" -.meta description="Shows rare items on\nthe map and plays a\nsound when a rare\nitem drops" # Original code by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 +.meta visibility="all" +.meta key="RareDropNotifications" +.meta name="Rare alerts" +.meta description="Shows rare items on\nthe map and plays a\nsound when a rare\nitem drops" + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks .label hook_code, 0x8000C660 diff --git a/system/client-functions/RareDropNotifications/RareDropNotifications.4___.patch.s b/system/client-functions/RareDropNotificationsXB.s similarity index 96% rename from system/client-functions/RareDropNotifications/RareDropNotifications.4___.patch.s rename to system/client-functions/RareDropNotificationsXB.s index 0c010798..e28e18dc 100644 --- a/system/client-functions/RareDropNotifications/RareDropNotifications.4___.patch.s +++ b/system/client-functions/RareDropNotificationsXB.s @@ -1,17 +1,20 @@ -.meta name="Rare alerts" -.meta description="Shows rare items on\nthe map and plays a\nsound when a rare\nitem drops" # Original code by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 # Xbox port by fuzziqersoftware +.meta visibility="all" +.meta key="RareDropNotifications" +.meta name="Rare alerts" +.meta description="Shows rare items on\nthe map and plays a\nsound when a rare\nitem drops" + .versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksXB + .include WriteCodeBlocks diff --git a/system/client-functions/RaresInQuests.s b/system/client-functions/RaresInQuests.s new file mode 100644 index 00000000..4550a098 --- /dev/null +++ b/system/client-functions/RaresInQuests.s @@ -0,0 +1,63 @@ +.meta visibility="all" +.meta name="Rares in quests" +.meta description="Disables logic that\nprevents items\nabove 8 stars and\nrares from dropping\nin quests." + + +entry_ptr: +reloc0: + .offsetof start +start: + .include WriteCodeBlocks + + + + .versions 1OEF 1OJ3 1OJ4 1OJF 1OPF + + .align 4 + .data + .data 2 + mov r0, 0 + + .align 4 + .data + .data 2 + mov r0, 0 + + .align 4 + + + + .versions 2OJ4 2OJ5 2OJF 2OEF 2OPF + + .align 4 + .data + .data 2 + nop + + .align 4 + .data + .data 2 + nop + + .align 4 + + + + .versions 2OJW + + .data 0x004DFC9A + .data 2 + nop + nop + + .data 0x004E03F4 + .data 2 + nop + nop + + + + .all_versions + + .data 0x00000000 + .data 0x00000000 diff --git a/system/client-functions/RaresInQuests/RaresInQuests.1___.patch.s b/system/client-functions/RaresInQuests/RaresInQuests.1___.patch.s deleted file mode 100644 index 49d9b9e3..00000000 --- a/system/client-functions/RaresInQuests/RaresInQuests.1___.patch.s +++ /dev/null @@ -1,24 +0,0 @@ -.meta name="Rares in quests" -.meta description="Disables logic that\nprevents items\nabove 8 stars and\nrares from dropping\nin quests." - -.versions 1OEF 1OJ3 1OJ4 1OJF 1OPF - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksDC - - .align 4 - .data - .data 2 - mov r0, 0 - - .align 4 - .data - .data 2 - mov r0, 0 - - .align 4 - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/RaresInQuests/RaresInQuests.2OJW.patch.s b/system/client-functions/RaresInQuests/RaresInQuests.2OJW.patch.s deleted file mode 100644 index 9fedb05c..00000000 --- a/system/client-functions/RaresInQuests/RaresInQuests.2OJW.patch.s +++ /dev/null @@ -1,21 +0,0 @@ -.meta name="Rares in quests" -.meta description="Disables logic that\nprevents items\nabove 8 stars and\nrares from dropping\nin quests." - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksBB - - .data 0x004DFC9A - .data 2 - nop - nop - - .data 0x004E03F4 - .data 2 - nop - nop - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/RaresInQuests/RaresInQuests.2___.patch.s b/system/client-functions/RaresInQuests/RaresInQuests.2___.patch.s deleted file mode 100644 index 3b0f45e1..00000000 --- a/system/client-functions/RaresInQuests/RaresInQuests.2___.patch.s +++ /dev/null @@ -1,24 +0,0 @@ -.meta name="Rares in quests" -.meta description="Disables logic that\nprevents items\nabove 8 stars and\nrares from dropping\nin quests." - -.versions 2OJ4 2OJ5 2OJF 2OEF 2OPF - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksDC - - .align 4 - .data - .data 2 - nop - - .align 4 - .data - .data 2 - nop - - .align 4 - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/ReticleColors/DCReticleColors.4___.patch.s b/system/client-functions/ReticleColors/DCReticleColors.4___.patch.s deleted file mode 100644 index edd56237..00000000 --- a/system/client-functions/ReticleColors/DCReticleColors.4___.patch.s +++ /dev/null @@ -1,55 +0,0 @@ -.meta name="DC targets" -.meta description="Changes the target\nreticle colors to\nthose used on the\nDreamcast" -# Original code by Ralf @ GC-Forever and Aleron Ives -# https://www.gc-forever.com/forums/viewtopic.php?t=2050 -# https://www.gc-forever.com/forums/viewtopic.php?t=2049 - -.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU - -entry_ptr: -reloc0: - .offsetof start -start: - .include WriteCodeBlocksXB - - .data - .data 0x00000004 - .data 0x00FF0000 - - .data - .data 0x00000004 - .data 0x000000FF - - .data - .data 0x00000004 - .data 0x00FFFF00 - - .data - .data 0x00000060 - .data 0x3F800000 - .data 0x3F800000 - .data 0x00000000 - .data 0x00000000 - .data 0x3F800000 - .data 0x3F800000 - .data 0x00000000 - .data 0x00000000 - .data 0x3F800000 - .data 0x3F800000 - .data 0x3F800000 - .data 0x00000000 - .data 0x3F800000 - .data 0x00000000 - .data 0x00000000 - .data 0x3F800000 - .data 0x3F800000 - .data 0x3ECCCCCD - .data 0x3DCCCCCD - .data 0x3DCCCCCD - .data 0x3F800000 - .data 0x00000000 - .data 0x00000000 - .data 0x00000000 - - .data 0x00000000 - .data 0x00000000 diff --git a/system/client-functions/ExtendedPlayerInfo/SetExtendedPlayerInfo.2___.patch.s b/system/client-functions/SetExtendedPlayerInfoDC.s similarity index 89% rename from system/client-functions/ExtendedPlayerInfo/SetExtendedPlayerInfo.2___.patch.s rename to system/client-functions/SetExtendedPlayerInfoDC.s index fc3f2c01..925b8240 100644 --- a/system/client-functions/ExtendedPlayerInfo/SetExtendedPlayerInfo.2___.patch.s +++ b/system/client-functions/SetExtendedPlayerInfoDC.s @@ -1,5 +1,5 @@ -.meta hide_from_patches_menu -.meta name="SetExtendedPlayerInfo" +.meta key="SetExtendedPlayerInfo" +.meta name="Set extended player info" .meta description="" .versions 2OJ5 2OJF 2OEF 2OPF @@ -21,9 +21,8 @@ start: calls memcpy add r5, 8 - # First, copy some important fields out of the saved character so that the - # game won't consider the character corrupt. Note that r5 already points to - # inbound_part2 here since it immediately follows inbound_part1. + # First, copy some important fields out of the saved character so that the game won't consider the character corrupt. + # Note that r5 already points to inbound_part2 here since it immediately follows inbound_part1. mov.l r4, [r7 + 4] mov.l r4, [r4] mov r1, r4 # r1 = character_file_part2 diff --git a/system/client-functions/ExtendedPlayerInfo/SetExtendedPlayerInfo.3___.patch.s b/system/client-functions/SetExtendedPlayerInfoGC.s similarity index 91% rename from system/client-functions/ExtendedPlayerInfo/SetExtendedPlayerInfo.3___.patch.s rename to system/client-functions/SetExtendedPlayerInfoGC.s index 1d7c0aca..5c91cfd5 100644 --- a/system/client-functions/ExtendedPlayerInfo/SetExtendedPlayerInfo.3___.patch.s +++ b/system/client-functions/SetExtendedPlayerInfoGC.s @@ -1,5 +1,5 @@ -.meta hide_from_patches_menu -.meta name="SetExtendedPlayerInfo" +.meta key="SetExtendedPlayerInfo" +.meta name="Set extended player info" .meta description="" .versions 3OJT 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 3SJT 3SJ0 3SE0 3SP0 @@ -21,8 +21,7 @@ get_data_ptr_ret: li r5, 0x41C bl memcpy - # Copy part2 data into place, but retain the values of a few metadata fields - # so the game won't think the file is corrupt + # Copy part2 data into place, but retain the values of a few metadata fields so the game won't think it's corrupt lwz r3, [r10 + 0x0C] lwz r7, [r3 + 0x04] # creation_timestamp lwz r8, [r3 + 0x08] # signature diff --git a/system/client-functions/ExtendedPlayerInfo/SetExtendedPlayerInfo.4___.patch.s b/system/client-functions/SetExtendedPlayerInfoXB.s similarity index 90% rename from system/client-functions/ExtendedPlayerInfo/SetExtendedPlayerInfo.4___.patch.s rename to system/client-functions/SetExtendedPlayerInfoXB.s index 7a3b94ef..7fcb5f20 100644 --- a/system/client-functions/ExtendedPlayerInfo/SetExtendedPlayerInfo.4___.patch.s +++ b/system/client-functions/SetExtendedPlayerInfoXB.s @@ -1,5 +1,5 @@ -.meta hide_from_patches_menu -.meta name="SetExtendedPlayerInfo" +.meta key="SetExtendedPlayerInfo" +.meta name="Set extended player info" .meta description="" .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU @@ -21,8 +21,7 @@ get_data_ptr_ret: mov ecx, 0x041C call memcpy - # Copy part2 data into place, but retain the values of a few metadata fields - # so the game won't think the file is corrupt + # Copy part2 data into place, but retain the values of a few metadata fields so the game won't think it's corrupt mov eax, [ebx + 4] mov eax, [eax] push dword [eax + 0x04] # creation_timestamp diff --git a/system/client-functions/System/CacheClearFix-Orig.ppc.inc.s b/system/client-functions/System/CacheClearFix-Orig.ppc.inc.s deleted file mode 100644 index dcf8ab67..00000000 --- a/system/client-functions/System/CacheClearFix-Orig.ppc.inc.s +++ /dev/null @@ -1,89 +0,0 @@ -start: - mflr r7 - - # If this patch has already been run, then the opcode that led here will - # not be bctrl (4E800421). In that case, do nothing. - lis r3, 0x4E80 - ori r3, r3, 0x0421 - lwz r4, [r7 - 4] - cmp r3, r4 - beq apply_patch - blr -apply_patch: - - bl patch_end - .offsetof patch - .offsetof patch_end -patch: - mfctr r6 - mr r3, r6 - li r4, 0x7C00 - .include FlushCachedCode-GC - mtctr r6 - bctr -patch_end: - mflr r4 - - addi r4, r4, 8 - lwz r3, [r4 - 8] - lwz r5, [r4 - 4] - sub r5, r5, r3 - - # At this point: - # r4 = address of patch label - # r5 = patch size in bytes - # r7 = saved LR - - # Find a spot in the interrupt handlers with enough memory for the patch - lis r3, 0x8000 - ori r3, r3, 0x0200 - sub r3, r3, r5 - -check_location: - rlwinm r0, r5, 30, 2, 31 - mtctr r0 # ctr = patch size in words - subi r8, r3, 4 -check_location_next_word: - lwzu r0, [r8 + 4] - cmpwi r0, 0 - beq check_location_word_ok - addi r3, r3, 0x0100 - rlwinm r0, r3, 0, 16, 31 - cmpwi r0, 0x1800 - blt check_location - # No suitable location was found - return null - li r3, 0 - mtlr r7 - blr - -check_location_word_ok: - bdnz check_location_next_word - -location_ok: - mr r6, r3 - # Now: - # r3 = destination location - # r4 = patch src data - # r5 = patch size in bytes - # r6 = destination location - # r7 = saved LR - .include CopyCode-GC - -setup_branch: - # Replace the bctrl opcode that led to this call with a bl opcode that - # leads to the copied patch code - subi r3, r7, 4 - sub r4, r6, r3 - rlwinm r4, r4, 0, 6, 31 - oris r4, r4, 0x4800 - ori r4, r4, 0x0001 - stw [r3], r4 - dcbst r0, r3 - sync - icbi r0, r3 - isync - - # Return the address that the patch was copied to - mr r3, r6 - mtlr r7 - blr diff --git a/system/client-functions/System/CacheClearFix-Phase1.ppc.s b/system/client-functions/System/CacheClearFix-Phase1.ppc.s deleted file mode 100644 index 412b1db8..00000000 --- a/system/client-functions/System/CacheClearFix-Phase1.ppc.s +++ /dev/null @@ -1,4 +0,0 @@ -entry_ptr: - .data 0x8000C274 -start: - .include CacheClearFix diff --git a/system/client-functions/System/CacheClearFix-Phase1.s b/system/client-functions/System/CacheClearFix-Phase1.s new file mode 100644 index 00000000..cdf0661e --- /dev/null +++ b/system/client-functions/System/CacheClearFix-Phase1.s @@ -0,0 +1,6 @@ +.versions PPC + +entry_ptr: + .data 0x8000C274 +start: + .include CacheClearFix diff --git a/system/client-functions/System/CacheClearFix-Phase2.ppc.s b/system/client-functions/System/CacheClearFix-Phase2.ppc.s deleted file mode 100644 index bcc07bdc..00000000 --- a/system/client-functions/System/CacheClearFix-Phase2.ppc.s +++ /dev/null @@ -1,5 +0,0 @@ -entry_ptr: -reloc0: - .offsetof start -start: - .include CacheClearFix diff --git a/system/client-functions/System/CacheClearFix-Phase2.s b/system/client-functions/System/CacheClearFix-Phase2.s new file mode 100644 index 00000000..fc001c77 --- /dev/null +++ b/system/client-functions/System/CacheClearFix-Phase2.s @@ -0,0 +1,7 @@ +.versions PPC + +entry_ptr: +reloc0: + .offsetof start +start: + .include CacheClearFix diff --git a/system/client-functions/System/CacheClearFix.inc.s b/system/client-functions/System/CacheClearFix.inc.s new file mode 100644 index 00000000..654c5020 --- /dev/null +++ b/system/client-functions/System/CacheClearFix.inc.s @@ -0,0 +1,61 @@ +.versions PPC + +start: + mflr r7 + + # If this patch has already been run, then the opcode that led here will not be bctrl (4E800421). In that case, do + # nothing. + lis r3, 0x4E80 + ori r3, r3, 0x0421 + lwz r4, [r7 - 4] + cmp r3, r4 + beq apply_patch + blr +apply_patch: + + bl patch_end + .offsetof patch + .offsetof patch_end +patch: + mfctr r6 + mr r3, r6 + li r4, 0x7C00 + .include FlushCachedCode + mtctr r6 + bctr +patch_end: + mflr r4 + + addi r4, r4, 8 + lwz r3, [r4 - 8] + lwz r5, [r4 - 4] + sub r5, r5, r3 + + lis r3, 0x8000 + ori r3, r3, 0x01BC + mr r6, r3 + # At this point: + # r3 = destination location (overwritten by CopyCode) + # r4 = patch src data (overwritten by CopyCode) + # r5 = patch size in bytes (overwritten by CopyCode) + # r6 = destination location + # r7 = saved LR + .include CopyCode + +setup_branch: + # Replace the bctrl opcode that led to this call with a bl opcode that leads to the copied patch code + subi r3, r7, 4 + sub r4, r6, r3 + rlwinm r4, r4, 0, 6, 31 + oris r4, r4, 0x4800 + ori r4, r4, 0x0001 + stw [r3], r4 + dcbst r0, r3 + sync + icbi r0, r3 + isync + + # Return the address that the patch was copied to + mr r3, r6 + mtlr r7 + blr diff --git a/system/client-functions/System/CacheClearFix.ppc.inc.s b/system/client-functions/System/CacheClearFix.ppc.inc.s deleted file mode 100644 index c3c31051..00000000 --- a/system/client-functions/System/CacheClearFix.ppc.inc.s +++ /dev/null @@ -1,60 +0,0 @@ -start: - mflr r7 - - # If this patch has already been run, then the opcode that led here will - # not be bctrl (4E800421). In that case, do nothing. - lis r3, 0x4E80 - ori r3, r3, 0x0421 - lwz r4, [r7 - 4] - cmp r3, r4 - beq apply_patch - blr -apply_patch: - - bl patch_end - .offsetof patch - .offsetof patch_end -patch: - mfctr r6 - mr r3, r6 - li r4, 0x7C00 - .include FlushCachedCode - mtctr r6 - bctr -patch_end: - mflr r4 - - addi r4, r4, 8 - lwz r3, [r4 - 8] - lwz r5, [r4 - 4] - sub r5, r5, r3 - - lis r3, 0x8000 - ori r3, r3, 0x01BC - mr r6, r3 - # At this point: - # r3 = destination location (overwritten by CopyCode) - # r4 = patch src data (overwritten by CopyCode) - # r5 = patch size in bytes (overwritten by CopyCode) - # r6 = destination location - # r7 = saved LR - .include CopyCode - -setup_branch: - # Replace the bctrl opcode that led to this call with a bl opcode that - # leads to the copied patch code - subi r3, r7, 4 - sub r4, r6, r3 - rlwinm r4, r4, 0, 6, 31 - oris r4, r4, 0x4800 - ori r4, r4, 0x0001 - stw [r3], r4 - dcbst r0, r3 - sync - icbi r0, r3 - isync - - # Return the address that the patch was copied to - mr r3, r6 - mtlr r7 - blr diff --git a/system/client-functions/System/CallNativeFunctionGC.ppc.s b/system/client-functions/System/CallNativeFunction.s similarity index 64% rename from system/client-functions/System/CallNativeFunctionGC.ppc.s rename to system/client-functions/System/CallNativeFunction.s index 99c14ee2..6d96f281 100644 --- a/system/client-functions/System/CallNativeFunctionGC.ppc.s +++ b/system/client-functions/System/CallNativeFunction.s @@ -1,9 +1,42 @@ -# This function implements the $nativecall chat command on GameCube clients. +# This function implements the $nativecall chat command. entry_ptr: reloc0: .offsetof start + + +.versions SH4 + +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: + .data 0 +arg0: + .data 0 +arg1: + .data 0 +arg2: + .data 0 +arg3: + .data 0 + + + +.versions PPC + start: mflr r0 stw [r1 + 4], r0 diff --git a/system/client-functions/System/CallNativeFunctionDC.sh4.s b/system/client-functions/System/CallNativeFunctionDC.sh4.s deleted file mode 100644 index 22ac2fc2..00000000 --- a/system/client-functions/System/CallNativeFunctionDC.sh4.s +++ /dev/null @@ -1,30 +0,0 @@ -# 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: - .data 0 -arg0: - .data 0 -arg1: - .data 0 -arg2: - .data 0 -arg3: - .data 0 diff --git a/system/client-functions/System/CopyCode.inc.s b/system/client-functions/System/CopyCode.inc.s new file mode 100644 index 00000000..68e677a5 --- /dev/null +++ b/system/client-functions/System/CopyCode.inc.s @@ -0,0 +1,22 @@ +# r3 = dest ptr +# r4 = src ptr +# r5 = size +# Clobbers r0, r3, r4, r5 + +.versions PPC + +copy_code: + addi r5, r5, 3 + rlwinm r5, r5, 30, 2, 31 # r5 = number of words to copy + mtctr r5 + subi r3, r3, 4 # r3 -= 4 (so we can use stwu) + subi r4, r4, 4 # r4 -= 4 (so we can use lwzu) +copy_word_again: + lwzu r0, [r4 + 4] + stwu [r3 + 4], r0 + bdnz copy_word_again + + rlwinm r4, r5, 2, 0, 29 + addi r3, r3, 4 + sub r3, r3, r4 + .include FlushCachedCode diff --git a/system/client-functions/System/CopyCode.ppc.inc.s b/system/client-functions/System/CopyCode.ppc.inc.s deleted file mode 100644 index dcf90b79..00000000 --- a/system/client-functions/System/CopyCode.ppc.inc.s +++ /dev/null @@ -1,18 +0,0 @@ - # r3 = dest ptr - # r4 = src ptr - # r5 = size - # Clobbers r0, r3, r4, r5 - addi r5, r5, 3 - rlwinm r5, r5, 30, 2, 31 # r5 = number of words to copy - mtctr r5 - subi r3, r3, 4 # r3 = r3 - 4 (so we can use stwu) - subi r4, r4, 4 # r4 = r4 - 4 (so we can use lwzu) -copy_word_again: - lwzu r0, [r4 + 4] - stwu [r3 + 4], r0 - bdnz copy_word_again - - rlwinm r4, r5, 2, 0, 29 - addi r3, r3, 4 - sub r3, r3, r4 - .include FlushCachedCode diff --git a/system/client-functions/System/CopyData.x86.inc.s b/system/client-functions/System/CopyData.inc.s similarity index 61% rename from system/client-functions/System/CopyData.x86.inc.s rename to system/client-functions/System/CopyData.inc.s index f87ef1ee..cf437b9a 100644 --- a/system/client-functions/System/CopyData.x86.inc.s +++ b/system/client-functions/System/CopyData.inc.s @@ -1,7 +1,11 @@ - # eax = dest ptr - # edx = src ptr - # ecx = size - # Clobbers eax, ecx, edx +# eax = dest ptr +# edx = src ptr +# ecx = size +# Clobbers eax, ecx, edx + +.versions X86 + +copy_data: push ebx again: test ecx, ecx diff --git a/system/client-functions/System/CopyDataWords.ppc.inc.s b/system/client-functions/System/CopyDataWords.inc.s similarity index 73% rename from system/client-functions/System/CopyDataWords.ppc.inc.s rename to system/client-functions/System/CopyDataWords.inc.s index 886a1a82..63517933 100644 --- a/system/client-functions/System/CopyDataWords.ppc.inc.s +++ b/system/client-functions/System/CopyDataWords.inc.s @@ -1,7 +1,11 @@ - # r3 = dest ptr - # r4 = src ptr - # r5 = size - # Clobbers r3, r4, r5, ctr +# r3 = dest ptr +# r4 = src ptr +# r5 = size +# Clobbers r3, r4, r5, ctr + +.versions PPC + +copy_data_words: addi r5, r5, 3 rlwinm r5, r5, 30, 2, 31 # r5 = number of words to copy mtctr r5 diff --git a/system/client-functions/System/Episode3USAQuestBufferOverflow.ppc.s b/system/client-functions/System/Episode3USAQuestBufferOverflow.s similarity index 57% rename from system/client-functions/System/Episode3USAQuestBufferOverflow.ppc.s rename to system/client-functions/System/Episode3USAQuestBufferOverflow.s index 3fd998c0..ef3083ba 100644 --- a/system/client-functions/System/Episode3USAQuestBufferOverflow.ppc.s +++ b/system/client-functions/System/Episode3USAQuestBufferOverflow.s @@ -1,108 +1,86 @@ -# This program was an early attempt at restoring B2 patching functionality to -# Episode 3. It is no longer used, since the quest loading method is more -# reliable, but this file remains for documentation purposes. +# This program was an early attempt at restoring B2 patching functionality to Episode 3. It is no longer used, since +# the quest loading method is more reliable, but this file remains for documentation purposes. -# There is a buffer overflow bug in PSO Episode 3 that this program uses to -# achieve arbitrary code execution. (This bug is likely present in all versions -# of PSO, but the code here is specific to the USA version of Episode 3.) This -# is only necessary because the non-Japanese versions of Episode 3 lack the B2 -# command, which is used on other console PSO versions to send patches and other -# bits of code. Here, we use a buffer overflow bug to re-implement the B2 -# command, which allows the server to treat PSO Episode 3 like any other version -# of PSO with respect to patching or loading DOL files. +# There is a buffer overflow bug in PSO Episode 3 that this program uses to achieve arbitrary code execution. (This bug +# is likely present in all versions of PSO, but the code here is specific to the USA version of Episode 3.) This is +# only necessary because the non-Japanese versions of Episode 3 lack the B2 command, which is used on other console PSO +# versions to send patches and other bits of code. Here, we use a buffer overflow bug to re-implement the B2 command, +# which allows the server to treat PSO Episode 3 like any other version of PSO with respect to patching or loading DOL +# files. -# For some background, PSO sends download quest files via the A6 and A7 -# commands. The A6 command is used to start sending a download quest file; it -# includes the quest name, file name, and total file size. The A7 command is -# used to send a chunk of 1KB (0x400 bytes) of data, or less if it's the final -# chunk of the file. When the client receives an A6 command for a filename -# ending in .bin, it allocates a buffer of (file size + 0x48) bytes. When it -# later receives an A7 command, it copies (cmd.data_size) bytes from the command -# to position (8 + 0x100 * flag) in the buffer, then if cmd.data_size was less -# than 0x400, it marks the file as done and postprocesses it. +# For some background, PSO sends download quest files via the A6 and A7 commands. The A6 command is used to start +# sending a download quest file; it includes the quest name, file name, and total file size. The A7 command is used to +# send a chunk of 1KB (0x400 bytes) of data, or less if it's the final chunk of the file. When the client receives an +# A6 command for a filename ending in .bin, it allocates a buffer of (file size + 0x48) bytes. When it later receives +# an A7 command, it copies (cmd.data_size) bytes from the command to position (8 + 0x100 * flag) in the buffer, then if +# cmd.data_size was less than 0x400, it marks the file as done and postprocesses it. -# However, the client neglects to check if the last chunk overflows the end of -# the buffer before copying the chunk data. In this function, we send an A6 -# command with an overall file size of only 0x18 bytes, then we send a chunk of -# 0x200 or so bytes (the compiled size of the code in this file), which -# overflows past the end of the allocated buffer and overwrites part of a free -# block after the allocated buffer. The memory allocator library keeps some of -# its bookkeeping structures at the beginning of this free block, which we use -# to cause the next call to malloc() to overwrite its own return address on the -# stack. Conveniently, this call happens soon afterward, during the +# However, the client neglects to check if the last chunk overflows the end of the buffer before copying the chunk +# data. In this function, we send an A6 command with an overall file size of only 0x18 bytes, then we send a chunk of +# 0x200 or so bytes (the compiled size of the code in this file), which overflows past the end of the allocated buffer +# and overwrites part of a free block after the allocated buffer. The memory allocator library keeps some of its +# bookkeeping structures at the beginning of this free block, which we use to cause the next call to malloc() to +# overwrite its own return address on the stack. Conveniently, this call happens soon afterward, during the # postprocessing step. -# The PSO memory allocator is a simple free-list allocator. The allocator -# maintains two linked lists of blocks: one for allocated blocks and one for -# free blocks. The list of free blocks is sorted in order of memory address, but -# the list of allocated blocks is sorted in the order they were allocated. (The -# order of the allocated block list does not matter for the allocator's -# performance or correctness.) +# The PSO memory allocator is a simple free-list allocator. The allocator maintains two linked lists of blocks: one for +# allocated blocks and one for free blocks. The list of free blocks is sorted in order of memory address, but the list +# of allocated blocks is sorted in the order they were allocated. (The order of the allocated block list does not +# matter for the allocator's performance or correctness.) -# Each block begins with two pointers, prev and next, which point to other -# blocks in the allocated or free list. (As with a typical doubly-linked list, -# the first block has prev == nullptr and the last block has next == nullptr; -# there is no sentinel node on either end.) After these two pointers is the -# block's size in bytes, followed by 0x14 unused bytes. The block data -# immediately follows this 0x20-byte header structure. All block sizes are -# rounded up to a multiple of 0x20 bytes. +# Each block begins with two pointers, prev and next, which point to other blocks in the allocated or free list. (As +# with a typical doubly-linked list, the first block has prev == nullptr and the last block has next == nullptr; there +# is no sentinel node on either end.) After these two pointers is the block's size in bytes, followed by 0x14 unused +# bytes. The block data immediately follows this 0x20-byte header structure. All block sizes are rounded up to a +# multiple of 0x20 bytes. -# The malloc() routine simply searches for the first free block that has enough -# space to satisfy the request, and either splits it into an allocated and a -# free block (if the free block's size is at least 0x40 bytes more than the -# requested size), or converts the free block entirely into an allocated block -# and returns it. It is the second case that we take advantage of here. +# The malloc() routine simply searches for the first free block that has enough space to satisfy the request, and +# either splits it into an allocated and a free block (if the free block's size is at least 0x40 bytes more than the +# requested size), or converts the free block entirely into an allocated block and returns it. It is the second case +# that we take advantage of here. -# When we send our A7 command containing this program, the first 0x58 bytes of -# it fill the quest file data buffer. The next 0x0C bytes of it overwrite the -# header fields of the following free block (noted below in the comments), and -# the remainder of the data goes into that block's unused header fields and the -# block's data (which is also otherwise unused, since it is a free block). We -# overwrite the free block's prev and next pointers with specific nonzero values -# and overwrite the size with the exact size that the caller will request, so we -# trigger the malloc() case that does not split the free block. When that code -# attempts to remove the free block from its doubly-linked list, it writes -# block->next to block->prev->next and block->prev to block->next->prev. We set -# block->prev to the address where we want execution to jump to (the start label -# here), and block->next to the address of malloc()'s return address on the -# stack. This overwrites the return address with the start label's address, and -# overwrites the word after the start label with an address within the stack. We -# can't avoid this second write since both pointers must be non-null and the -# values and addresses written are dependent on each other, but we can just use -# a branch opcode to ignore the value that gets written into our code. +# When we send our A7 command containing this program, the first 0x58 bytes of it fill the quest file data buffer. The +# next 0x0C bytes of it overwrite the header fields of the following free block (noted below in the comments), and the +# remainder of the data goes into that block's unused header fields and the block's data (which is also otherwise +# unused, since it is a free block). We overwrite the free block's prev and next pointers with specific nonzero values +# and overwrite the size with the exact size that the caller will request, so we trigger the malloc() case that does +# not split the free block. When that code attempts to remove the free block from its doubly-linked list, it writes +# block->next to block->prev->next and block->prev to block->next->prev. We set block->prev to the address where we +# want execution to jump to (the start label here), and block->next to the address of malloc()'s return address on the +# stack. This overwrites the return address with the start label's address, and overwrites the word after the start +# label with an address within the stack. We can't avoid this second write since both pointers must be non-null and the +# values and addresses written are dependent on each other, but we can just use a branch opcode to ignore the value +# that gets written into our code. -# Once we have control, we clean up the allocator state (restoring the free -# block as it was before we overwrote its header), then copy our implementation -# of the B2 command to an otherwise-unused area of memory and apply a few more +# Once we have control, we clean up the allocator state (restoring the free block as it was before we overwrote its +# header), then copy our implementation of the B2 command to an otherwise-unused area of memory and apply a few more # patches. See the comments within the code below for more details. +.versions 3SE0 - -# This entry_ptr label isn't used since this code isn't sent with the B2 -# command; it just needs to be present for newserv to compile the code properly +# This entry_ptr label isn't used since this code isn't sent with the B2 command; it just needs to be present for +# newserv to compile the code properly entry_ptr: start: b resume1 - # This is the value overwritten by malloc() when it attempts to remove the - # free block from its linked list + # This is the value overwritten by malloc() when it attempts to remove the free block from its linked list .data 0xAAAAAAAA resume1: # We can use any of the caller-save registers (r0, r3-r12) here. # At entry time, some registers contain useful values: - # r5: Address of the allocator instance ("lists"). This structure includes the - # allocated and free list head pointers, one of which we have to update. - # r12: Address of the malloc() function that was called. Conveniently, the - # address that we should return to is very near this location in memory. + # r5: Address of the allocator instance ("lists"). This structure includes the allocated and free list head pointers, + # one of which we have to update. + # r12: Address of the malloc() function that was called. Conveniently, the address that we should return to is very + # near this location in memory. - # Compute the LR we should use to return from this function, but don't put it - # in the LR just yet - we're still going to need the LR for other shenanigans + # Compute the LR we should use to return from this function, but don't put it in the LR just yet - we're still going + # to need the LR for other shenanigans subi r11, r12, 0xB0 # 8038C1B8 - B0 = 8038C108 - # Restore the free block whose header we had destroyed with the A7 command - # buffer overflow + # Restore the free block whose header we had destroyed with the A7 command buffer overflow lis r7, 0x815F ori r7, r7, 0xF440 li r0, 0 @@ -121,8 +99,8 @@ resume1: b resume2 - # TODO: We can probably use this space for something useful. There must be - # exactly 20 opcodes (0x50 bytes) between resume1 and opaque2. + # TODO: We can probably use this space for something useful. There must be exactly 20 opcodes (0x50 bytes) between + # resume1 and opaque2. .zero .zero .zero @@ -130,10 +108,9 @@ resume1: .zero opaque2: - # This block must be exactly here (the number of opcodes above is exactly how - # many will fit in the original buffer), and the 3 words here must have - # exactly these values. This is what causes malloc to overwrite the return - # address on the stack to call this code in the first place. + # This block must be exactly here (the number of opcodes above is exactly how many will fit in the original buffer), + # and the 3 words here must have exactly these values. This is what causes malloc to overwrite the return address on + # the stack to call this code in the first place. .data 0x815FF3E8 # free_head->prev .data 0x80592AC4 # free_head->next .data 0x00000160 # free_head->size @@ -141,11 +118,10 @@ opaque2: resume2: bl get_handle_B2_ptr - # This is the code we're going to use for the B2 command handler, which we - # will copy into an unused area of memory. It's convenient to put it here and - # use a bl opcode to get its address, so this code can be minimally position- - # dependent. Note that this part of the code does not run at the time the A7 - # command is received; it will run later if the client receives a B2 command. + # This is the code we're going to use for the B2 command handler, which we will copy into an unused area of memory. + # It's convenient to put it here and use a bl opcode to get its address, so this code can be minimally position- + # dependent. Note that this part of the code does not run at the time the A7 command is received; it will run later + # if the client receives a B2 command. handle_B2: mflr r0 stwu [r1 - 0x40], r1 @@ -173,10 +149,9 @@ handle_B2: ori r5, r5, 0x0C00 stw [r1 + 0x08], r5 - # If there's no code section, skip it. We also write the code section size to - # the return value field (which will be overwritten later if the size is not - # zero). This is because I'm lazy and this gives the behavior we want: the - # code return value is always zero if the code section size is zero. + # If there's no code section, skip it. We also write the code section size to the return value field (which will be + # overwritten later if the size is not zero). This is because I'm lazy and this gives the behavior we want: the code + # return value is always zero if the code section size is zero. li r6, 4 lwbrx r5, [r4 + r6] # r5 = code_size stw [r1 + 0x0C], r5 # response.code_return_value = code_size @@ -218,8 +193,7 @@ handle_B2_skip_relocations: bctrl # flush_code(code_base_addr, code_section_size) # Call the code section and put the return value (byteswapped) on the stack - # Note: flush_code only uses r3, r4, and r5, so we don't need to reload r7 - # after the above call + # Note: flush_code only uses r3, r4, and r5, so we don't need to reload r7 after the above call lwz r8, [r7 + 0x10] lwzx r8, [r8 + r6] mtctr r8 @@ -284,17 +258,16 @@ copy_handle_B2_word_again: rlwinm r4, r7, 2, 0, 29 bctrl # flush_code(copied_B2_handler, copied_B2_handler_bytes) - # Replace the command handler table entry for command 0E (which appears to be - # a legacy command and has very broken behavior) with our B2 implementation + # Replace the command handler table entry for command 0E (which appears to be a legacy command and has very broken + # behavior) with our B2 implementation lis r5, 0x8044 ori r5, r5, 0xF684 li r0, 0x00B2 stw [r5], r0 stw [r5 + 0x0C], r12 - # Patch both places in the code where command 9E is sent to make them include - # a sentinel value that newserv can use to determine if the client has already - # run the code in this file + # Patch both places in the code where command 9E is sent to make them include a sentinel value that newserv can use + # to determine if the client has already run the code in this file bl get_patch_9E_1_ptr patch_9E_1: lis r4, 0x5F5C @@ -333,12 +306,10 @@ get_patch_9E_2_ptr: mtctr r9 bctrl # flush_code(patch_9E_2_dest, 0x20) - # Finally, patch the A7 handler function (which is on the current callstack) - # so that it does nothing else if this function returns null, which prevents - # further memory corruption. This changes a beq opcode (which never triggers - # under normal circumstances) to skip a couple more function calls, one of - # which would cause memory corruption if executed because the original buffer - # is smaller than 0x100 bytes. + # Finally, patch the A7 handler function (which is on the current callstack) so that it does nothing else if this + # function returns null, which prevents further memory corruption. This changes a beq opcode (which never triggers + # under normal circumstances) to skip a couple more function calls, one of which would cause memory corruption if + # executed because the original buffer is smaller than 0x100 bytes. lis r3, 0x8010 ori r3, r3, 0xFD8A li r4, 0x0064 @@ -348,8 +319,7 @@ get_patch_9E_2_ptr: mtctr r9 bctrl # flush_code(patched_opcode_address & 0xFFFFFFF0, 0x20) - # Return null instead of a malloc'ed block, which triggers the conditional - # branch we just patched above + # Return null instead of a malloc'ed block, which triggers the conditional branch we just patched above li r3, 0 mtlr r11 blr diff --git a/system/client-functions/System/FlushCachedCode.ppc.inc.s b/system/client-functions/System/FlushCachedCode.inc.s similarity index 71% rename from system/client-functions/System/FlushCachedCode.ppc.inc.s rename to system/client-functions/System/FlushCachedCode.inc.s index da0b85f5..2f7f67b2 100644 --- a/system/client-functions/System/FlushCachedCode.ppc.inc.s +++ b/system/client-functions/System/FlushCachedCode.inc.s @@ -1,20 +1,23 @@ -# This code flushes the data cache and invalidates the instruction cache for a -# block of newly-written code in memory. +# This code flushes the data cache and invalidates the instruction cache for a block of newly-written code in memory. # Arguments: # r3 = address of written code # r4 = number of bytes # Returns: nothing # Overwrites: r3, r4, r5 + +.versions PPC + +flush_cached_code: lis r5, 0xFFFF ori r5, r5, 0xFFF1 and r5, r5, r3 subf r3, r5, r3 add r4, r4, r3 -flush_cached_code_writes__again: +flush_cached_code_again: dcbst r0, r5 sync icbi r0, r5 addic r5, r5, 8 subic. r4, r4, 8 - bge flush_cached_code_writes__again + bge flush_cached_code_again isync diff --git a/system/client-functions/System/GetEnemyEntity-59NJ.x86.inc.s b/system/client-functions/System/GetEnemyEntity-59NJ.x86.inc.s deleted file mode 100644 index a082ff82..00000000 --- a/system/client-functions/System/GetEnemyEntity-59NJ.x86.inc.s +++ /dev/null @@ -1,44 +0,0 @@ -# (uint16_t entity_id @ eax) -> TObjectV00b421c0* @ eax -# Preserves all registers except eax -get_enemy_entity: - push esi - push edi - push edx - push ecx - xor edx, edx - xchg edx, eax - cmp edx, 0x1000 - jl done - cmp edx, 0x4000 - jge done - - mov esi, [0x00AABCE8] # bs_low = next_player_entity_index - mov edi, [0x00AABCE4] - lea edi, [edi + esi - 1] # bs_high = next_player_entity_index + next_enemy_entity_index - 1 -bs_again: - cmp esi, edi - jge bs_done - lea ecx, [esi + edi] - shr ecx, 1 - mov eax, [ecx * 4 + 0x00AAB2A0] # all_entities[ecx] - cmp [eax + 0x1C], dx - jge bs_not_less - lea esi, [ecx + 1] - jmp bs_again -bs_not_less: - mov edi, ecx - jmp bs_again -bs_done: - - mov eax, [esi * 4 + 0x00AAB2A0] # all_entities[bs_low] - test eax, eax - je done - xor ecx, ecx - cmp [eax + 0x1C], dx - cmovne eax, ecx - -done: - pop ecx - pop edx - pop edi - pop esi diff --git a/system/client-functions/System/GetEnemyEntity-59NL.x86.inc.s b/system/client-functions/System/GetEnemyEntity.inc.s similarity index 72% rename from system/client-functions/System/GetEnemyEntity-59NL.x86.inc.s rename to system/client-functions/System/GetEnemyEntity.inc.s index aefa712f..2fec4466 100644 --- a/system/client-functions/System/GetEnemyEntity-59NL.x86.inc.s +++ b/system/client-functions/System/GetEnemyEntity.inc.s @@ -1,5 +1,8 @@ # (uint16_t entity_id @ eax) -> TObjectV00b441c0* @ eax # Preserves all registers except eax + +.versions 59NJ 59NL + get_enemy_entity: push esi push edi @@ -12,15 +15,15 @@ get_enemy_entity: cmp edx, 0x4000 jge done - mov esi, [0x00AAE168] # bs_low = next_player_entity_index - mov edi, [0x00AAE164] + mov esi, [] # bs_low = next_player_entity_index + mov edi, [] lea edi, [edi + esi - 1] # bs_high = next_player_entity_index + next_enemy_entity_index - 1 bs_again: cmp esi, edi jge bs_done lea ecx, [esi + edi] shr ecx, 1 - mov eax, [ecx * 4 + 0x00AAD720] # all_entities[ecx] + mov eax, [ecx * 4 + ] # all_entities[ecx] cmp [eax + 0x1C], dx jge bs_not_less lea esi, [ecx + 1] @@ -30,7 +33,7 @@ bs_not_less: jmp bs_again bs_done: - mov eax, [esi * 4 + 0x00AAD720] # all_entities[bs_low] + mov eax, [esi * 4 + ] # all_entities[bs_low] test eax, eax je done xor ecx, ecx diff --git a/system/client-functions/System/GetVersionInfoXB.x86.inc.s b/system/client-functions/System/GetVersionInfoXB.inc.s similarity index 99% rename from system/client-functions/System/GetVersionInfoXB.x86.inc.s rename to system/client-functions/System/GetVersionInfoXB.inc.s index 51ea773a..44d2476f 100644 --- a/system/client-functions/System/GetVersionInfoXB.x86.inc.s +++ b/system/client-functions/System/GetVersionInfoXB.inc.s @@ -25,6 +25,8 @@ # XBOX_PAGE_NOCACHE = 0x00000200 # XBOX_PAGE_WRITECOMBINE = 0x00000400 +.versions X86 + start: push ecx push edx diff --git a/system/client-functions/System/PRSDecompress.ppc.inc.s b/system/client-functions/System/PRSDecompress.inc.s similarity index 99% rename from system/client-functions/System/PRSDecompress.ppc.inc.s rename to system/client-functions/System/PRSDecompress.inc.s index 7f8bfb69..48937ed8 100644 --- a/system/client-functions/System/PRSDecompress.ppc.inc.s +++ b/system/client-functions/System/PRSDecompress.inc.s @@ -6,6 +6,9 @@ # r6 = source data size # Returns: number of bytes written to output buffer, or -1 on error # Overwrites: r3, r4, r5, r6, r7, r8, r9, r10, r11, r12 + +.versions PPC + prs_decompress__start: # r3 = dest ptr (used as write ptr) subi r3, r3, 1 diff --git a/system/client-functions/System/ReadMemoryWord.s b/system/client-functions/System/ReadMemoryWord.s new file mode 100644 index 00000000..5ee87faf --- /dev/null +++ b/system/client-functions/System/ReadMemoryWord.s @@ -0,0 +1,49 @@ +# This function is required for loading DOLs. If it's not present, newserv can't serve DOL files to GameCube clients. + +entry_ptr: +reloc0: + .offsetof start + + + +.versions SH4 + +start: + mova r0, [address] + mov.l r0, [r0] + rets + mov.l r0, [r0] + + .align 4 +address: + .data 0 + + + +.versions PPC + +start: + mflr r12 + bl read +address: + .zero +read: + mflr r3 + lwz r3, [r3] + lwz r3, [r3] + mtlr r12 + blr + + + +.versions X86 + +start: + call resume +address: + .data 0 +resume: + pop eax + mov eax, [eax] + mov eax, [eax] + ret diff --git a/system/client-functions/System/ReadMemoryWordDC.sh4.s b/system/client-functions/System/ReadMemoryWordDC.sh4.s deleted file mode 100644 index 30b034f2..00000000 --- a/system/client-functions/System/ReadMemoryWordDC.sh4.s +++ /dev/null @@ -1,13 +0,0 @@ -entry_ptr: -reloc0: - .offsetof start - -start: - mova r0, [address] - mov.l r0, [r0] - rets - mov.l r0, [r0] - - .align 4 -address: - .data 0 diff --git a/system/client-functions/System/ReadMemoryWordGC.ppc.s b/system/client-functions/System/ReadMemoryWordGC.ppc.s deleted file mode 100644 index e456aed3..00000000 --- a/system/client-functions/System/ReadMemoryWordGC.ppc.s +++ /dev/null @@ -1,18 +0,0 @@ -# This function is required for loading DOLs. If it's not present, newserv can't -# serve DOL files to GameCube clients. - -entry_ptr: -reloc0: - .offsetof start - -start: - mflr r12 - bl read -address: - .zero -read: - mflr r3 - lwz r3, [r3] - lwz r3, [r3] - mtlr r12 - blr diff --git a/system/client-functions/System/ReadMemoryWordX86.x86.s b/system/client-functions/System/ReadMemoryWordX86.x86.s deleted file mode 100644 index e61fe905..00000000 --- a/system/client-functions/System/ReadMemoryWordX86.x86.s +++ /dev/null @@ -1,13 +0,0 @@ -entry_ptr: -reloc0: - .offsetof start - -start: - call resume -address: - .data 0 -resume: - pop eax - mov eax, [eax] - mov eax, [eax] - ret diff --git a/system/client-functions/System/ReturnTokenX86.x86.s b/system/client-functions/System/ReturnToken.s similarity index 90% rename from system/client-functions/System/ReturnTokenX86.x86.s rename to system/client-functions/System/ReturnToken.s index c424bc53..a92e39d2 100644 --- a/system/client-functions/System/ReturnTokenX86.x86.s +++ b/system/client-functions/System/ReturnToken.s @@ -1,3 +1,5 @@ +.versions X86 + entry_ptr: reloc0: .offsetof start diff --git a/system/client-functions/System/RunDOL.ppc.s b/system/client-functions/System/RunDOL.s similarity index 83% rename from system/client-functions/System/RunDOL.ppc.s rename to system/client-functions/System/RunDOL.s index 82445bda..8bc4314e 100644 --- a/system/client-functions/System/RunDOL.ppc.s +++ b/system/client-functions/System/RunDOL.s @@ -1,5 +1,6 @@ -# This function is required for loading DOLs. If it's not present, newserv can't -# serve DOL files to GameCube clients. +# This function is required for loading DOLs. If it's not present, newserv can't serve DOL files to GameCube clients. + +.versions PPC entry_ptr: reloc0: @@ -16,12 +17,11 @@ dol_base_ptr: .zero get_current_addr: mflr r31 - # TODO: It'd be nice to be able to use an expression for the immediate value - # here - something like (dol_base_ptr - start), for example + # TODO: It'd be nice to be able to use an expression for the immediate value here - something like (dol_base_ptr - + # start), for example subi r31, r31, 0x10 # r31 = base of data to copy to low memory (start label) - # If this code is not running from low memory (80001800-80003000), then copy - # it there and branch to it + # If this code is not running from low memory (80001800-80003000), then copy it there and branch to it lis r3, 0x8000 ori r3, r3, 0x3000 cmp r31, r3 @@ -53,9 +53,8 @@ copy_code_to_low_memory__again: run_dol: lwz r30, [r31 + 0x10] # r30 = data base ptr - # Decompress the file first. If the compressed size is zero, then skip this - # step (the file is not compressed). The header consists of two fields: - # compressed size followed by decompressed size. + # Decompress the file first. If the compressed size is zero, then skip this step (the file is not compressed). The + # header consists of two fields: compressed size followed by decompressed size. lwz r6, [r30] cmplwi r6, 0 beq run_dol__not_compressed @@ -70,9 +69,8 @@ run_dol__not_compressed: addi r30, r30, 8 run_dol__decompressed: - # DOL files are very simple: they have up to 7 text sections, up to 11 data - # sections, and a BSS section and an entrypoint. No imports or other fancy - # things to do - we just have to move a bunch of bytes around. + # DOL files are very simple: they have up to 7 text sections, up to 11 data sections, and a BSS section and an + # entrypoint. No imports or other fancy things to do - we just have to move a bunch of bytes around. mr r29, r30 # r29 = DOL header iterator addi r28, r29, 0x48 # r28 = DOL header iterator end value @@ -87,16 +85,15 @@ run_dol__move_section: subi r4, r4, 1 add r5, r4, r5 # r5 = source end pointer run_dol__move_section_data__again: - # TODO: We probably should implement memmove-like semantics here, in case the - # DOL loads at an unusually late address. This is probably very rare. + # TODO: We probably should implement memmove-like semantics here, in case the DOL loads at an unusually late address. + # This is probably very rare. lbzu r0, [r4 + 1] stbu [r3 + 1], r0 cmp r4, r5 bne run_dol__move_section_data__again - # Flush the data cache and invalidate the instruction cache after copying the - # section data. Technically we don't have to do this for data sections, but - # I'm lazy and it doesn't take too long. + # Flush the data cache and invalidate the instruction cache after copying the section data. Technically we don't have + # to do this for data sections, but I'm lazy and it doesn't take too long. lwz r3, [r29 + 0x48] # r3 = dest address of section data lwz r4, [r29 + 0x90] # r4 = size of section data bl flush_cached_code_writes diff --git a/system/client-functions/System/VersionDetectDC.sh4.s b/system/client-functions/System/VersionDetect.s similarity index 51% rename from system/client-functions/System/VersionDetectDC.sh4.s rename to system/client-functions/System/VersionDetect.s index 9f465304..a112ea63 100644 --- a/system/client-functions/System/VersionDetectDC.sh4.s +++ b/system/client-functions/System/VersionDetect.s @@ -1,20 +1,21 @@ -# This function returns the game version, with values more specific than can be -# detected by the sub_version field in the various login commands (e.g. 93/9D). +# This function returns the game version, with values more specific than can be detected by the sub_version field in +# the various login commands (e.g. 9D/9E). We call this value specific_version in the codebase. -# The returned value has the format SSPPRRVV, where: -# S = version (31 = PSOv1, 32 = PSOv2) -# G = game (4F = PSO) -# R = region (45 = E, 4A = J, 50 = P) -# V = minor version (31 = NTE, 32 = 11/2000, 33 = 12/2000, 24 = 01/2001, -# 35 = 08/2001, 46 = not a prototype) -# This results in a 4-character ASCII-printable version code which encodes all -# of the above information. This value is called specific_version in the places -# where it's used by the server. +# The returned value has the format SSGGRRVV, where: +# S = 31 = PSOv1, 32 = PSOv2, 33 = PSOv3, 34 = Xbox, 35 = BB +# G = game (4F (O) = non-Ep3, 53 (S) = Ep3) +# R = region (45 (E), 4A (J), or 50 (P)) +# V = minor version (meaning varies by major version) +# This results in a 4-character ASCII-printable version code which encodes all of the above information. entry_ptr: reloc0: .offsetof start + + +.versions SH4 + start: mova r0, [data_start] mov r1, r0 @@ -59,3 +60,46 @@ data_start: .data 0x8C2E7CE0 # v2 EU .data 0x324F5046 # 2OPF .data 0x00000000 # end sentinel + + + +.versions PPC + +start: + lis r3, 0x8000 + lwz r4, [r3] + + # For Trial Editions, set the V field to 54; for other versions, set it to 0x30 | disc_version + rlwinm r0, r4, 8, 24, 31 + cmplwi r0, 0x47 # Check if high byte of game ID is 'G' + beq not_trial + cmplwi r0, 0x44 # Check if high byte of game ID is 'D' + beq is_nte + li r3, 0 + blr +is_nte: + li r3, 0x0054 + b end_trial_check +not_trial: + lbz r3, [r3 + 7] + ori r3, r3, 0x0030 +end_trial_check: + oris r3, r3, 0x3300 # Set high byte ('3') + rlwimi r3, r4, 8, 8, 23 # Set middle two bytes to last two bytes of game ID + blr + + + +.versions X86 + +start: + .include GetVersionInfoXB + + test eax, eax + jz version_not_found + mov eax, [eax] + ret + +version_not_found: + mov eax, 0x344F0000 + ret diff --git a/system/client-functions/System/VersionDetectGC.ppc.s b/system/client-functions/System/VersionDetectGC.ppc.s deleted file mode 100644 index c4a046f6..00000000 --- a/system/client-functions/System/VersionDetectGC.ppc.s +++ /dev/null @@ -1,39 +0,0 @@ -# This function returns the game version, with values more specific than can be -# detected by the sub_version field in the various login commands (e.g. 9D/9E). - -# The returned value has the format SSGGRRVV, where: -# S = 33 (which represents PSO GC) -# G = game (4F (O) = Ep1&2, 53 (S) = Ep3) -# R = region (45 (E), 4A (J), or 50 (P)) -# V = minor version | 30 (30 = 1.0, 31 = 1.1, 32 = 1.2, etc.), or 54 for NTE -# This results in a 4-character ASCII-printable version code which encodes all -# of the above information. This value is called specific_version in the places -# where it's used by the server. - -entry_ptr: -reloc0: - .offsetof start - -start: - lis r3, 0x8000 - lwz r4, [r3] - - # For Trial Editions, set the V field to 54; for other versions, set it to - # 0x30 | disc_version - rlwinm r0, r4, 8, 24, 31 - cmplwi r0, 0x47 # Check if high byte of game ID is 'G' - beq not_trial - cmplwi r0, 0x44 # Check if high byte of game ID is 'D' - beq is_nte - li r3, 0 - blr -is_nte: - li r3, 0x0054 - b end_trial_check -not_trial: - lbz r3, [r3 + 7] - ori r3, r3, 0x0030 -end_trial_check: - oris r3, r3, 0x3300 # Set high byte ('3') - rlwimi r3, r4, 8, 8, 23 # Set middle two bytes to last two bytes of game ID - blr diff --git a/system/client-functions/System/VersionDetectXB.x86.s b/system/client-functions/System/VersionDetectXB.x86.s deleted file mode 100644 index 7608f4bb..00000000 --- a/system/client-functions/System/VersionDetectXB.x86.s +++ /dev/null @@ -1,26 +0,0 @@ -# This function returns the game version, with values more specific than can be -# detected by the sub_version field in the various login commands (e.g. 9D/9E). - -# The returned value has the format SSSSRRVV, where: -# S = 344F (which represents PSO Xbox) -# R = region (45 (E), 4A (J), or 50 (P)) -# V = version (42 (B) for beta, 44 (D) for disc, 55 (U) for title update) -# This results in a 4-character ASCII-printable version code which encodes all -# of the above information. This value is called specific_version in the places -# where it's used by the server. - -entry_ptr: -reloc0: - .offsetof start - -start: - .include GetVersionInfoXB - - test eax, eax - jz version_not_found - mov eax, [eax] - ret - -version_not_found: - mov eax, 0x344F0000 - ret diff --git a/system/client-functions/System/WriteAddressOfCode-59NJ.x86.inc.s b/system/client-functions/System/WriteAddressOfCode-59NJ.x86.inc.s deleted file mode 100644 index 78adec59..00000000 --- a/system/client-functions/System/WriteAddressOfCode-59NJ.x86.inc.s +++ /dev/null @@ -1,42 +0,0 @@ -# This file defines the following function: -# write_address_of_code( -# const void* patch_code, -# size_t patch_code_size, -# void** ptr_addr); -# This function allocates memory for patch_code, copies patch_code to that -# memory, then writes the address of the allocated code at the specified -# pointer. The allocated memory is never freed. -# This function pops its arguments off the stack before returning. - -write_call_to_code: - # [esp + 0x04] = code ptr - # [esp + 0x08] = code size - # [esp + 0x0C] = ptr addr - - # Allocate memory for the copied code - mov ecx, [0x00AA8F84] - push dword [esp + 0x08] - mov eax, 0x007A984C - call eax # malloc7 - test eax, eax - je done - - # Copy the code to the newly-allocated memory - # eax = dest pointer (from malloc7 call above) - mov edx, [esp + 0x04] # edx = source pointer - mov ecx, [esp + 0x08] # ecx = source size - push ebx -memcpy_again: - dec ecx - mov bl, [edx + ecx] # Copy one byte from source to dest - mov [eax + ecx], bl - test ecx, ecx - jne memcpy_again - pop ebx - - # Write the address - mov ecx, [esp + 0x0C] - mov [ecx], eax - -done: - ret 0x0C diff --git a/system/client-functions/System/WriteAddressOfCode-59NL.x86.inc.s b/system/client-functions/System/WriteAddressOfCode.inc.s similarity index 75% rename from system/client-functions/System/WriteAddressOfCode-59NL.x86.inc.s rename to system/client-functions/System/WriteAddressOfCode.inc.s index 82ae6909..6e19a405 100644 --- a/system/client-functions/System/WriteAddressOfCode-59NL.x86.inc.s +++ b/system/client-functions/System/WriteAddressOfCode.inc.s @@ -3,10 +3,11 @@ # const void* patch_code, # size_t patch_code_size, # void** ptr_addr); -# This function allocates memory for patch_code, copies patch_code to that -# memory, then writes the address of the allocated code at the specified -# pointer. The allocated memory is never freed. -# This function pops its arguments off the stack before returning. +# This function allocates memory for patch_code, copies patch_code to that memory, then writes the address of the +# allocated code at the specified pointer. The allocated memory is never freed. This function pops its arguments off +# the stack before returning. + +.versions 59NJ 59NL write_call_to_code: # [esp + 0x04] = code ptr @@ -14,9 +15,9 @@ write_call_to_code: # [esp + 0x0C] = ptr addr # Allocate memory for the copied code - mov ecx, [0x00AAB404] + mov ecx, [] push dword [esp + 0x08] - mov eax, 0x007A8A38 + mov eax, call eax # malloc7 test eax, eax je done diff --git a/system/client-functions/System/WriteCallToCode-59NL.x86.inc.s b/system/client-functions/System/WriteCallToCode-59NL.x86.inc.s deleted file mode 100644 index 8db81055..00000000 --- a/system/client-functions/System/WriteCallToCode-59NL.x86.inc.s +++ /dev/null @@ -1,76 +0,0 @@ -# This file defines the following function: -# write_call_to_code( -# const void* patch_code, -# size_t patch_code_size, -# void* call_opcode_address, -# ssize_t call_opcode_bytes); -# This function allocates memory for patch_code, copies patch_code to that -# memory, then writes a call or jmp opcode to call_opcode_address that calls -# the code in the allocated memory region. The allocated memory is never freed. -# call_opcode_bytes specifies how many bytes at the callsite should be -# overwritten. This value must be at least 5; the first 5 bytes are overwritten -# with the call/jmp opcode itself; the rest are overwritten with nop opcodes. -# If call_opcode_bytes is positive, a call opcode is written; if it's negative, -# a jmp opcode is written. -# This function pops its arguments off the stack before returning. - -write_call_to_code: - # [esp + 0x04] = code ptr - # [esp + 0x08] = code size - # [esp + 0x0C] = jump callsite - # [esp + 0x10] = callsite size (if zero, write the address instead of a call) - - # Allocate memory for the copied code - mov ecx, [0x00AAB404] - push dword [esp + 0x08] - mov eax, 0x007A8A38 - call eax # malloc7 - test eax, eax - je done - - # Copy the code to the newly-allocated memory - # eax = dest pointer (from malloc7 call above) - mov edx, [esp + 0x04] # edx = source pointer - mov ecx, [esp + 0x08] # ecx = source size - push ebx -memcpy_again: - dec ecx - mov bl, [edx + ecx] # Copy one byte from source to dest - mov [eax + ecx], bl - test ecx, ecx - jne memcpy_again - pop ebx - - mov edx, [esp + 0x0C] # edx = jump callsite - - # If the callsite size is zero, just write the address directly - cmp dword [esp + 0x10], 0 - jne write_call_or_jmp - mov [edx], eax - jmp done - - # Write the call or jmp opcode -write_call_or_jmp: - lea ecx, [eax - 5] - sub ecx, edx # ecx = (dest code addr) - (jump callsite) - 5 - cmp dword [esp + 0x10], 0 - setl al - or al, 0xE8 - mov [edx], al # Write E8 (call), or E9 (jmp) if size was negative - mov [edx + 1], ecx # Write delta - - # Write as many nops after the call opcode as necessary - mov ecx, 5 - mov eax, [esp + 0x10] - cmp eax, 0 - jge write_nop_again - neg eax -write_nop_again: - cmp ecx, eax - jge done - mov byte [edx + ecx], 0x90 - inc ecx - jmp write_nop_again - -done: - ret 0x10 diff --git a/system/client-functions/System/WriteCallToCode-59NJ.x86.inc.s b/system/client-functions/System/WriteCallToCode.inc.s similarity index 78% rename from system/client-functions/System/WriteCallToCode-59NJ.x86.inc.s rename to system/client-functions/System/WriteCallToCode.inc.s index 1dc8975d..1b6747ae 100644 --- a/system/client-functions/System/WriteCallToCode-59NJ.x86.inc.s +++ b/system/client-functions/System/WriteCallToCode.inc.s @@ -1,17 +1,16 @@ +.versions 59NJ 59NL + # This file defines the following function: # write_call_to_code( # const void* patch_code, # size_t patch_code_size, # void* call_opcode_address, # ssize_t call_opcode_bytes); -# This function allocates memory for patch_code, copies patch_code to that -# memory, then writes a call or jmp opcode to call_opcode_address that calls -# the code in the allocated memory region. The allocated memory is never freed. -# call_opcode_bytes specifies how many bytes at the callsite should be -# overwritten. This value must be at least 5; the first 5 bytes are overwritten -# with the call/jmp opcode itself; the rest are overwritten with nop opcodes. -# If call_opcode_bytes is positive, a call opcode is written; if it's negative, -# a jmp opcode is written. +# This function allocates memory for patch_code, copies patch_code to that memory, then writes a call or jmp opcode to +# call_opcode_address that calls the code in the allocated memory region. The allocated memory is never freed. +# call_opcode_bytes specifies how many bytes at the callsite should be overwritten. This value must be at least 5; the +# first 5 bytes are overwritten with the call/jmp opcode itself; the rest are overwritten with nop opcodes. If +# call_opcode_bytes is positive, a call opcode is written; if it's negative, a jmp opcode is written. # This function pops its arguments off the stack before returning. write_call_to_code: @@ -21,9 +20,9 @@ write_call_to_code: # [esp + 0x10] = callsite size (if zero, write the address instead of a call) # Allocate memory for the copied code - mov ecx, [0x00AA8F84] + mov ecx, [] push dword [esp + 0x08] - mov eax, 0x007A984C + mov eax, call eax # malloc7 test eax, eax je done diff --git a/system/client-functions/System/WriteCallToCodeMulti-59NJ.x86.inc.s b/system/client-functions/System/WriteCallToCodeMulti-59NJ.x86.inc.s deleted file mode 100644 index 1bcb838d..00000000 --- a/system/client-functions/System/WriteCallToCodeMulti-59NJ.x86.inc.s +++ /dev/null @@ -1,83 +0,0 @@ -# This file defines the following function: -# void [/std] write_call_to_code( -# const void* patch_code @ [esp + 0x04], -# size_t patch_code_size @ [esp + 0x08], -# size_t call_count @ [esp + 0x0C], -# void* call_opcode_address @ [esp + 0x10], -# ssize_t call_opcode_bytes @ [esp + 0x14], -# ...); -# This function allocates memory for patch_code, copies patch_code to that -# memory, then writes a call or jmp opcode to call_opcode_address that calls -# the code in the allocated memory region. The allocated memory is never freed. -# call_opcode_bytes specifies how many bytes at the callsite should be -# overwritten. This value must be at least 5; the first 5 bytes are overwritten -# with the call/jmp opcode itself; the rest are overwritten with nop opcodes. -# This function pops its arguments off the stack before returning (including -# all the varargs). - -write_call_to_code: - # [esp + 0x04] = code ptr - # [esp + 0x08] = code size - # [esp + 0x0C] = callsite count - # [esp + 0x10] = callsite address - # [esp + 0x14] = callsite size - # ... (further callsite address/size pairs) - - # Allocate memory for the copied code - mov ecx, [0x00AA8F84] - push dword [esp + 0x08] - mov eax, 0x007A984C - call eax # malloc7 - test eax, eax - je done - - # Copy the code to the newly-allocated memory - # eax = dest pointer (from malloc7 call above) - mov edx, [esp + 0x04] # edx = source pointer - mov ecx, [esp + 0x08] # ecx = source size - push ebx -memcpy_again: - dec ecx - mov bl, [edx + ecx] # Copy one byte from source to dest - mov [eax + ecx], bl - test ecx, ecx - jne memcpy_again - pop ebx - - # Write the call opcodes - xchg ebx, [esp + 0x0C] # Save ebx; get callsite count - mov [esp - 0x08], esi - mov [esp - 0x0C], eax - mov esi, 0x10 # Stack offset of first callsite pair - -next_callsite: - mov edx, [esp + esi] # edx = jump callsite - lea ecx, [eax - 5] - sub ecx, edx # ecx = (dest code addr) - (jump callsite) - 5 - mov byte [edx], 0xE8 - mov [edx + 1], ecx # Write E8 (call) followed by delta - - # Write as many nops after the call opcode as necessary - mov ecx, 5 - mov eax, [esp + esi + 4] -write_nop_again: - cmp ecx, eax - jge this_callsite_done - mov byte [edx + ecx], 0x90 - inc ecx - jmp write_nop_again - -this_callsite_done: - mov eax, [esp - 0x0C] - add esi, 8 - dec ebx - jnz next_callsite - - mov ecx, esi - mov ebx, [esp + 0x0C] - mov esi, [esp - 0x08] - -done: - mov eax, [esp] - add esp, ecx - jmp eax diff --git a/system/client-functions/System/WriteCallToCodeMulti-59NL.x86.inc.s b/system/client-functions/System/WriteCallToCodeMulti-59NL.x86.inc.s deleted file mode 100644 index fc785016..00000000 --- a/system/client-functions/System/WriteCallToCodeMulti-59NL.x86.inc.s +++ /dev/null @@ -1,83 +0,0 @@ -# This file defines the following function: -# void [/std] write_call_to_code( -# const void* patch_code @ [esp + 0x04], -# size_t patch_code_size @ [esp + 0x08], -# size_t call_count @ [esp + 0x0C], -# void* call_opcode_address @ [esp + 0x10], -# ssize_t call_opcode_bytes @ [esp + 0x14], -# ...); -# This function allocates memory for patch_code, copies patch_code to that -# memory, then writes a call or jmp opcode to call_opcode_address that calls -# the code in the allocated memory region. The allocated memory is never freed. -# call_opcode_bytes specifies how many bytes at the callsite should be -# overwritten. This value must be at least 5; the first 5 bytes are overwritten -# with the call/jmp opcode itself; the rest are overwritten with nop opcodes. -# This function pops its arguments off the stack before returning (including -# all the varargs). - -write_call_to_code: - # [esp + 0x04] = code ptr - # [esp + 0x08] = code size - # [esp + 0x0C] = callsite count - # [esp + 0x10] = callsite address - # [esp + 0x14] = callsite size - # ... (further callsite address/size pairs) - - # Allocate memory for the copied code - mov ecx, [0x00AAB404] - push dword [esp + 0x08] - mov eax, 0x007A8A38 - call eax # malloc7 - test eax, eax - je done - - # Copy the code to the newly-allocated memory - # eax = dest pointer (from malloc7 call above) - mov edx, [esp + 0x04] # edx = source pointer - mov ecx, [esp + 0x08] # ecx = source size - push ebx -memcpy_again: - dec ecx - mov bl, [edx + ecx] # Copy one byte from source to dest - mov [eax + ecx], bl - test ecx, ecx - jne memcpy_again - pop ebx - - # Write the call opcodes - xchg ebx, [esp + 0x0C] # Save ebx; get callsite count - mov [esp - 0x08], esi - mov [esp - 0x0C], eax - mov esi, 0x10 # Stack offset of first callsite pair - -next_callsite: - mov edx, [esp + esi] # edx = jump callsite - lea ecx, [eax - 5] - sub ecx, edx # ecx = (dest code addr) - (jump callsite) - 5 - mov byte [edx], 0xE8 - mov [edx + 1], ecx # Write E8 (call) followed by delta - - # Write as many nops after the call opcode as necessary - mov ecx, 5 - mov eax, [esp + esi + 4] -write_nop_again: - cmp ecx, eax - jge this_callsite_done - mov byte [edx + ecx], 0x90 - inc ecx - jmp write_nop_again - -this_callsite_done: - mov eax, [esp - 0x0C] - add esi, 8 - dec ebx - jnz next_callsite - - mov ecx, esi - mov ebx, [esp + 0x0C] - mov esi, [esp - 0x08] - -done: - mov eax, [esp] - add esp, ecx - jmp eax diff --git a/system/client-functions/System/WriteCallToCodeMultiXB.x86.inc.s b/system/client-functions/System/WriteCallToCodeMulti.inc.s similarity index 54% rename from system/client-functions/System/WriteCallToCodeMultiXB.x86.inc.s rename to system/client-functions/System/WriteCallToCodeMulti.inc.s index c115cb60..1e357395 100644 --- a/system/client-functions/System/WriteCallToCodeMultiXB.x86.inc.s +++ b/system/client-functions/System/WriteCallToCodeMulti.inc.s @@ -1,4 +1,91 @@ -# This function has the same signature as WriteCallToCodeMulti-59NL. +# This file defines the following function: +# void [/std] write_call_to_code( +# const void* patch_code @ [esp + 0x04], +# size_t patch_code_size @ [esp + 0x08], +# size_t call_count @ [esp + 0x0C], +# void* call_opcode_address @ [esp + 0x10], +# ssize_t call_opcode_bytes @ [esp + 0x14], +# ...); +# This function allocates memory for patch_code, copies patch_code to that memory, then writes a call or jmp opcode to +# call_opcode_address that calls the code in the allocated memory region. The allocated memory is never freed. +# call_opcode_bytes specifies how many bytes at the callsite should be overwritten. This value must be at least 5; the +# first 5 bytes are overwritten with the call/jmp opcode itself; the rest are overwritten with nop opcodes. This +# function pops its arguments off the stack before returning (including all the varargs). + + + +.versions 59NJ 59NL + +write_call_to_code: + # [esp + 0x04] = code ptr + # [esp + 0x08] = code size + # [esp + 0x0C] = callsite count + # [esp + 0x10] = callsite address + # [esp + 0x14] = callsite size + # ... (further callsite address/size pairs) + + # Allocate memory for the copied code + mov ecx, [] + push dword [esp + 0x08] + mov eax, + call eax # malloc7 + test eax, eax + je done + + # Copy the code to the newly-allocated memory + # eax = dest pointer (from malloc7 call above) + mov edx, [esp + 0x04] # edx = source pointer + mov ecx, [esp + 0x08] # ecx = source size + push ebx +memcpy_again: + dec ecx + mov bl, [edx + ecx] # Copy one byte from source to dest + mov [eax + ecx], bl + test ecx, ecx + jne memcpy_again + pop ebx + + # Write the call opcodes + xchg ebx, [esp + 0x0C] # Save ebx; get callsite count + mov [esp - 0x08], esi + mov [esp - 0x0C], eax + mov esi, 0x10 # Stack offset of first callsite pair + +next_callsite: + mov edx, [esp + esi] # edx = jump callsite + lea ecx, [eax - 5] + sub ecx, edx # ecx = (dest code addr) - (jump callsite) - 5 + mov byte [edx], 0xE8 + mov [edx + 1], ecx # Write E8 (call) followed by delta + + # Write as many nops after the call opcode as necessary + mov ecx, 5 + mov eax, [esp + esi + 4] +write_nop_again: + cmp ecx, eax + jge this_callsite_done + mov byte [edx + ecx], 0x90 + inc ecx + jmp write_nop_again + +this_callsite_done: + mov eax, [esp - 0x0C] + add esi, 8 + dec ebx + jnz next_callsite + + mov ecx, esi + mov ebx, [esp + 0x0C] + mov esi, [esp - 0x08] + +done: + mov eax, [esp] + add esp, ecx + jmp eax + + + +.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU write_call_to_code: .include GetVersionInfoXB diff --git a/system/client-functions/System/WriteCodeBlocks.inc.s b/system/client-functions/System/WriteCodeBlocks.inc.s new file mode 100644 index 00000000..9d1f17fe --- /dev/null +++ b/system/client-functions/System/WriteCodeBlocks.inc.s @@ -0,0 +1,151 @@ +.versions SH4 + + mova r0, [first_patch_header] + mov r7, r0 # r7 = read ptr + xor r3, r3 + dec r3 + shl r3, 2 # r3 = 0xFFFFFFFC (mask for aligning r7) +apply_patch: + add r7, 3 + and r7, r3 # r7 = (r7 + 3) & (~3) (align to 4-byte boundary) + mov.l r4, [r7]+ # r4 = dest addr + mov.l r5, [r7]+ + add r5, r4 # r5 = dest end ptr (dest addr + size) + cmpeq r4, r5 # if (size == 0) return + bt done + +again: + cmpeq r4, r5 + bt apply_patch # if (r4 == r5) done with the patch; go to next header + mov.b r0, [r7]+ + mov.b [r4], r0 # *(r4) = *(r7++); + bs again # r4++; continue + add r4, 1 + +done: + rets + nop + + .align 4 +first_patch_header: + + + +.versions PPC + + mflr r8 + b get_patch_data_ptr +get_patch_data_ptr_ret: + mflr r7 # r7 = patch header +apply_patch: + addi r4, r7, 8 # r4 = start of patch data + lwz r3, [r4 - 8] # r3 = patch dest address + lwz r5, [r4 - 4] # r5 = patch data size + or r0, r3, r5 + cmplwi r0, 0 + mtlr r8 + beqlr + add r7, r4, r5 # r7 = next patch header + .include CopyCode + b apply_patch + +get_patch_data_ptr: + bl get_patch_data_ptr_ret + +first_patch_header: + + + +.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU + +start: + .include GetVersionInfoXB + test eax, eax + jnz can_patch + ret + +can_patch: + push esi + push edi + push ebx + mov edi, eax # edi = ptr to version info struct + jmp get_patch_data_ptr +get_patch_data_ptr_ret: + pop ebx # ebx = patch header + +apply_next_patch: + cmp dword [ebx + 4], 0 + jne copy_code_and_apply_again + pop ebx + pop edi + pop esi + mov eax, 1 + ret + +copy_code_and_apply_again: + push dword [ebx] # dest addr + mov ecx, [edi + 0x0C] + call [ecx] # MmQueryAddressProtect + mov esi, eax # esi = prev protection flags + + push 4 # new protection flags + push dword [ebx + 4] # size + push dword [ebx] # base address + mov ecx, [edi + 0x08] + call [ecx] # MmSetAddressProtect + + xor ecx, ecx # ecx = offset + mov edx, [ebx] # edx = dest addr +copy_next_byte: + mov al, [ebx + ecx + 8] # copy one byte to dest + mov [edx + ecx], al + inc ecx # offset++ + cmp [ebx + 4], ecx # check if all bytes have been copied + jne copy_next_byte + + push esi # new protection flags + push dword [ebx + 4] # size + push dword [ebx] # base address + lea ebx, [ebx + ecx + 8] # advance to next block + mov ecx, [edi + 0x08] + call [ecx] # MmSetAddressProtect + jmp apply_next_patch + +get_patch_data_ptr: + call get_patch_data_ptr_ret + +first_patch_header: + + + +.versions 2OJW 2OJZ 59NJ 59NL + +start: + push ebx + jmp get_patch_data_ptr +get_patch_data_ptr_ret: + pop ebx # ebx = patch header + +apply_next_patch: + cmp dword [ebx + 4], 0 + jne copy_code_and_apply_again + pop ebx + mov eax, 1 + ret + +copy_code_and_apply_again: + xor ecx, ecx # ecx = offset + mov edx, [ebx] # edx = dest addr +copy_next_byte: + mov al, [ebx + ecx + 8] # copy one byte to dest + mov [edx + ecx], al + inc ecx # offset++ + cmp [ebx + 4], ecx # check if all bytes have been copied + jne copy_next_byte + + lea ebx, [ebx + ecx + 8] # advance to next block + jmp apply_next_patch + +get_patch_data_ptr: + call get_patch_data_ptr_ret +first_patch_header: diff --git a/system/client-functions/System/WriteCodeBlocksBB.x86.inc.s b/system/client-functions/System/WriteCodeBlocksBB.x86.inc.s deleted file mode 100644 index c7285f48..00000000 --- a/system/client-functions/System/WriteCodeBlocksBB.x86.inc.s +++ /dev/null @@ -1,29 +0,0 @@ -start: - push ebx - jmp get_patch_data_ptr -get_patch_data_ptr_ret: - pop ebx # ebx = patch header - -apply_next_patch: - cmp dword [ebx + 4], 0 - jne copy_code_and_apply_again - pop ebx - mov eax, 1 - ret - -copy_code_and_apply_again: - xor ecx, ecx # ecx = offset - mov edx, [ebx] # edx = dest addr -copy_next_byte: - mov al, [ebx + ecx + 8] # copy one byte to dest - mov [edx + ecx], al - inc ecx # offset++ - cmp [ebx + 4], ecx # check if all bytes have been copied - jne copy_next_byte - - lea ebx, [ebx + ecx + 8] # advance to next block - jmp apply_next_patch - -get_patch_data_ptr: - call get_patch_data_ptr_ret -first_patch_header: diff --git a/system/client-functions/System/WriteCodeBlocksDC.sh4.inc.s b/system/client-functions/System/WriteCodeBlocksDC.sh4.inc.s deleted file mode 100644 index b0ac2dea..00000000 --- a/system/client-functions/System/WriteCodeBlocksDC.sh4.inc.s +++ /dev/null @@ -1,28 +0,0 @@ - mova r0, [first_patch_header] - mov r7, r0 # r7 = read ptr - xor r3, r3 - dec r3 - shl r3, 2 # r3 = 0xFFFFFFFC (mask for aligning r7) -apply_patch: - add r7, 3 - and r7, r3 # r7 = (r7 + 3) & (~3) (align to 4-byte boundary) - mov.l r4, [r7]+ # r4 = dest addr - mov.l r5, [r7]+ - add r5, r4 # r5 = dest end ptr (dest addr + size) - cmpeq r4, r5 # if (size == 0) return - bt done - -again: - cmpeq r4, r5 - bt apply_patch # if (r4 == r5) done with the patch; go to next header - mov.b r0, [r7]+ - mov.b [r4], r0 # *(r4) = *(r7++); - bs again # r4++; continue - add r4, 1 - -done: - rets - nop - - .align 4 -first_patch_header: diff --git a/system/client-functions/System/WriteCodeBlocksGC.ppc.inc.s b/system/client-functions/System/WriteCodeBlocksGC.ppc.inc.s deleted file mode 100644 index b12a852a..00000000 --- a/system/client-functions/System/WriteCodeBlocksGC.ppc.inc.s +++ /dev/null @@ -1,20 +0,0 @@ - mflr r8 - b get_patch_data_ptr -get_patch_data_ptr_ret: - mflr r7 # r7 = patch header -apply_patch: - addi r4, r7, 8 # r4 = start of patch data - lwz r3, [r4 - 8] # r3 = patch dest address - lwz r5, [r4 - 4] # r5 = patch data size - or r0, r3, r5 - cmplwi r0, 0 - mtlr r8 - beqlr - add r7, r4, r5 # r7 = next patch header - .include CopyCode - b apply_patch - -get_patch_data_ptr: - bl get_patch_data_ptr_ret - -first_patch_header: diff --git a/system/client-functions/System/WriteCodeBlocksXB.x86.inc.s b/system/client-functions/System/WriteCodeBlocksXB.x86.inc.s deleted file mode 100644 index f20b1987..00000000 --- a/system/client-functions/System/WriteCodeBlocksXB.x86.inc.s +++ /dev/null @@ -1,57 +0,0 @@ -start: - .include GetVersionInfoXB - test eax, eax - jnz can_patch - ret - -can_patch: - push esi - push edi - push ebx - mov edi, eax # edi = ptr to version info struct - jmp get_patch_data_ptr -get_patch_data_ptr_ret: - pop ebx # ebx = patch header - -apply_next_patch: - cmp dword [ebx + 4], 0 - jne copy_code_and_apply_again - pop ebx - pop edi - pop esi - mov eax, 1 - ret - -copy_code_and_apply_again: - push dword [ebx] # dest addr - mov ecx, [edi + 0x0C] - call [ecx] # MmQueryAddressProtect - mov esi, eax # esi = prev protection flags - - push 4 # new protection flags - push dword [ebx + 4] # size - push dword [ebx] # base address - mov ecx, [edi + 0x08] - call [ecx] # MmSetAddressProtect - - xor ecx, ecx # ecx = offset - mov edx, [ebx] # edx = dest addr -copy_next_byte: - mov al, [ebx + ecx + 8] # copy one byte to dest - mov [edx + ecx], al - inc ecx # offset++ - cmp [ebx + 4], ecx # check if all bytes have been copied - jne copy_next_byte - - push esi # new protection flags - push dword [ebx + 4] # size - push dword [ebx] # base address - lea ebx, [ebx + ecx + 8] # advance to next block - mov ecx, [edi + 0x08] - call [ecx] # MmSetAddressProtect - jmp apply_next_patch - -get_patch_data_ptr: - call get_patch_data_ptr_ret - -first_patch_header: diff --git a/system/client-functions/System/WriteMemory.s b/system/client-functions/System/WriteMemory.s new file mode 100644 index 00000000..e75546dc --- /dev/null +++ b/system/client-functions/System/WriteMemory.s @@ -0,0 +1,193 @@ +# This function is required for loading DOLs. If it's not present, newserv can't serve DOL files to GameCube clients. + +# This is also the file I've chosen to document how to write code for newserv's functions subsystem. Client functions +# are assembly snippets written in the native language of the client, which can be sent to the client with the B2 +# command. This is done at login time if the server administrator has enabled automatic patches in config.json or if +# the client has enabled certain patches in the Patches menu. Client functions can also be sent at any time with the +# $patch chat command, if they include .meta visibility (see below). + +# This file is a general function (it does not appear in the Patches menu). General functions are used to implement +# various server operations; this one is used to write arbitrary data to the client's memory space. For example, to use +# this function to write the bytes 38 00 00 05 to the address 8010521C, send_function_call could be called like this: +# auto fn = s->client_functions->name_to_function.at("WriteMemoryGC"); +# unordered_map label_writes({{"dest_addr", 0x8010521C}, {"size", 4}}); +# string suffix("\x38\x00\x00\x05", 4); +# send_function_call( +# c, // Client to send function call to +# fn, // The function's code +# label_writes, // Variables to pass in to the function's code +# suffix); // Data to append after the code (not all functions use this) +# The meanings of label_writes and suffix are described in the comments below. + +# The .versions directive is required for all client functions that can be called by the server or the player. This +# directive specifies which architectures or specific versions of the game the client function is compatible with. The +# version tokens may be specific game versions (e.g. 3OE1, 59NL) or architectures (PPC, X86, or SH4); in the latter +# case, the source applies to all versions which use that architecture. All lines after a .versions directive apply +# only to the specified versions; this set of "active" versions can be changed with another .versions +# directive later in the file, thereby splitting the file into different sections that apply to different sets of +# versions. Any lines in the file the appear before the first .versions directive apply to all versions. After a +# .versions directive, expressions like "VERS value1 value2 ..." (but with <> instead of "") can be used to specialize +# the patch for each version. In a VERS expression, the number of values must match the number of versions given in the +# .versions directive, and the values must appear in the same order. This function is implemented on all versions and +# all architecture, so we specify all architectures here. Later on, the implementations for each architecture are +# segregated via further .versions directives. +.versions SH4 PPC X86 + +# This directive controls where the function appears. The values are (note that the quotes are required): +# visibility="hidden" (default): this function does not appear in the Patches menu and cannot be used via $patch +# visibility="cheat": this function doesn't appear in the Patches menu but can be used via $patch if cheat mode is on +# visibility="chat": this function doesn't appear in the Patches menu but can be used via $patch +# visibility="menu": this function appears in the Patches menu but can't be used via $patch +# visibility="all": this function appears in the Patches menu and can be used via $patch +# Note that if the client has $debug enabled, then all functions can be run via $patch regardless if this setting. +# .meta visibility="menu" + +# This directive specifies what the function's internal name is. This is the name that can be used in config.json to +# require the patch for all clients, and is also the name used with the $patch command. If not specified, the +# function's internal name is the same as its filename without the .s extension. +# .meta key="WriteMemory" + +# These directives tell newserv what to show to the player in the Patches menu. Neither of them is required; if the +# name is omitted, the filename is used instead. These have no real effect for this function (since .meta visibility is +# not used), so this is primarily for documentation purposes. +.meta name="Write memory" +.meta description="Writes data to any location in memory" + +# When used for debugging purposes, it may be useful to see the value returned by the client function when run via the +# $patch chat command. This directive causes the server to tell you the return value in-game after running it. +# .meta show_return_value + +# The entry_ptr label is required for all functions. It should generally point to a .offsetof directive that itself +# points to the actual entrypoint. +entry_ptr: +# All labels starting with reloc signify that the following PPC word (big-endian 32-bit value) is to be relocated at +# runtime. That is, when the code runs on the client, the PPC word will contain the actual memory address relative to +# the running code instead of the offset that it holds at assembly time. The entry_ptr label should almost always have +# a reloc label next to it. +reloc0: + .offsetof start + + + +# Everything following this directive (until the next .versions directive) applies only to PowerPC architectures. When +# this function is compiled for other architectures, this section will be ignored. +.versions PPC + +start: + mflr r12 + bl get_block_ptr + mr r6, r3 # r6 = address of dest_addr label + +copy_block: + lwz r3, [r6] # r3 = dest ptr + subi r3, r3, 1 # subtract 1 so we can use stbu + lwz r5, [r6 + 4] # r5 = size (bytes remaining) + add r5, r5, r3 # r5 = dest end ptr (last byte to be written) + addi r4, r6, 7 # r4 = src ptr (starting at -1 so we can use lbzu) +copy_block__again: + lbzu r0, [r4 + 1] + stbu [r3 + 1], r0 + cmp r3, r5 + bne copy_block__again + + # Flush the data cache and clear the instruction cache at the written region + lwz r3, [r6] # r3 = dest ptr + lwz r4, [r6 + 4] # r4 = size + # A .include directive essentially pastes in the code from the referenced file. Here, we use the code from the file + # FlushCachedCode.inc.s. When compiling includes, newserv first looks in the same directory as the function's source, + # then looks in system/client-functions/System. + .include FlushCachedCode + + # Return the address after the last byte written. The value returned in r3 from the function is sent back to the + # server in a B3 command. newserv uses the return value during DOL loading to know which section of the DOL file to + # send next, or to send the RunDOL function if all sections have been loaded. + lwz r3, [r6] # r3 = dest ptr + lwz r4, [r6 + 4] # r4 = size + add r3, r3, r4 + mtlr r12 + blr + +get_block_ptr__ret: + mflr r3 + mtlr r10 + blr +get_block_ptr: + # We use a trick here to get the address of the dest_addr label: since bl puts the immediately-following address into + # the link register, we "call" get_block_ptr__ret and get the dest_addr pointer out of the LR. We then put r10 back + # into the LR so get_block_ptr__ret returns to the caller. + mflr r10 + bl get_block_ptr__ret + + + +.versions SH4 + +start: + mova r0, [dest_addr] + mov r4, r0 + mov.l r0, [r4] + mov.l r5, [r4 + 4] + add r4, 8 +again: + test r5, r5 + bt done + mov.b r6, [r4] + mov.b [r0], r6 + add r4, 1 + add r0, 1 + bs again + add r5, -1 +done: + rets + nop + + .align 4 + + + +.versions X86 + +start: + jmp get_block_ptr +get_block_ptr_ret: + xchg ebx, [esp] + mov eax, [ebx] + mov ecx, [ebx + 4] + add ebx, 8 + +again: + test ecx, ecx + jz done + mov dl, [ebx] + mov [eax], dl + inc ebx + inc eax + dec ecx + jmp again + +done: + pop ebx + ret + +get_block_ptr: + call get_block_ptr_ret + + + +# This last section applies to all architectures, so we re-enable all versions again. This directive also disables the +# use of VERS tokens. +.all_versions + +# These fields are filled in right before the command is sent to the client. Specifically, the label_writes argument to +# send_function_call is responsible for this. The label_writes argument is a map of label name to value, and +# send_function_call simply writes the given values after the given labels. This is a way to pass arbitrary arguments +# to a function at call time. +dest_addr: + .data 0 +size: + .data 0 + +# Finally, we use the suffix argument to instruct send_function_call to append the data we want to write to memory +# immediately after the assembled code. (The data_to_write label here is for documentation purposes only; the suffix +# argument always appends data after the end of all the assembled code.) +data_to_write: diff --git a/system/client-functions/System/WriteMemoryDC.sh4.s b/system/client-functions/System/WriteMemoryDC.sh4.s deleted file mode 100644 index 6c46dd2f..00000000 --- a/system/client-functions/System/WriteMemoryDC.sh4.s +++ /dev/null @@ -1,33 +0,0 @@ -.meta name="Write memory" -.meta description="Writes data to any location in memory" - -entry_ptr: -reloc0: - .offsetof start - -start: - mova r0, [dest_addr] - mov r4, r0 - mov.l r0, [r4] - mov.l r5, [r4 + 4] - add r4, 8 -again: - test r5, r5 - bt done - mov.b r6, [r4] - mov.b [r0], r6 - add r4, 1 - add r0, 1 - bs again - add r5, -1 -done: - rets - nop - - .align 4 -dest_addr: - .data 0 -size: - .data 0 - -data_to_write: diff --git a/system/client-functions/System/WriteMemoryGC.ppc.s b/system/client-functions/System/WriteMemoryGC.ppc.s deleted file mode 100644 index 6455033c..00000000 --- a/system/client-functions/System/WriteMemoryGC.ppc.s +++ /dev/null @@ -1,117 +0,0 @@ -# This function is required for loading DOLs. If it's not present, newserv can't serve DOL files to GameCube clients. - -# This is also the file I've chosen to document how to write code for newserv's functions subsystem. There are three -# kinds of functions: includes, patches, and general functions. - -# - General functions are not version-specific (usually) but are architecture-specific. This file, WriteMemoryGC, is a -# general function for all PowerPC versions of PSO, which means all GameCube versions. General functions are named -# like NAME.ARCH.s, where ARCH is sh4, ppc, or x86. - -# - Includes are snippets of code that are intended to be used as part of other general functions and patches. Includes -# are named like NAME.ARCH.inc.s, where ARCH has the same meaning as above. These can be used with the .include -# directive; there is an example of this in the code below. - -# - Patches are functions that are available to run upon client request. They can be made available in the Patches menu -# or via the $patch command. Patches should be named like PATCHNAME.VERS.patch.s, where VERS denotes which specific -# game version the patch is for. These version codes are listed in README.md, and directly correspond to values -# returned by the VersionDetect functions, also in this directory. - -# For example, to use this function to write the bytes 38 00 00 05 to the address 8010521C, send_function_call could be -# called like this: -# auto fn = s->function_code_index->name_to_function.at("WriteMemoryGC"); -# unordered_map label_writes({{"dest_addr", 0x8010521C}, {"size", 4}}); -# string suffix("\x38\x00\x00\x05", 4); -# send_function_call( -# c, // Client to send function call to -# fn, // The function's code -# label_writes, // Variables to pass in to the function's code -# suffix); // Data to append after the code (not all functions use this) -# The meanings of label_writes and suffix are described in the comments below. - -# The .versions directive may be used in patches (but not in includes or general functions) and enables -# parameterization. If .version is used, then the patch may later use expressions like to -# generate the same patch with different values for different game versions. In each expression, the number of -# values must match the number of versions given in the .versions directive. -# .versions VRS1 VRS2 VRS3 ... - -# These directives tell newserv what to show to the player in the Patches menu. Neither of them is required; if the -# name is omitted, the filename is used instead. -.meta name="Write memory" -.meta description="Writes data to any location in memory" - -# To hide a patch from the Patches menu (so it can only be used with the $patch command), this directive can be used. -# This has no effect if used in includes or general functions. -# .meta hide_from_patches_menu - -# When used for debugging purposes, it may be useful to see the value returned by the client function when run via the -# $patch chat command. This directive causes the server to tell you the return value in-game after running it. -# .meta show_return_value - -# The entry_ptr label is required for all functions. It should generally point to a .offsetof directive that itself -# points to the actual entrypoint. -entry_ptr: -# All labels starting with reloc signify that the following PPC word (big-endian 32-bit value) is to be relocated at -# runtime. That is, when the code runs on the client, the PPC word will contain the actual memory address relative to -# the running code instead of the offset that it holds at assembly time. The entry_ptr label should almost always have -# a reloc label next to it. -reloc0: - .offsetof start - -start: - mflr r12 - bl get_block_ptr - mr r6, r3 # r6 = address of dest_addr label - -copy_block: - lwz r3, [r6] # r3 = dest ptr - subi r3, r3, 1 # subtract 1 so we can use stbu - lwz r5, [r6 + 4] # r5 = size (bytes remaining) - add r5, r5, r3 # r5 = dest end ptr (last byte to be written) - addi r4, r6, 7 # r4 = src ptr (starting at -1 so we can use lbzu) -copy_block__again: - lbzu r0, [r4 + 1] - stbu [r3 + 1], r0 - cmp r3, r5 - bne copy_block__again - - # Flush the data cache and clear the instruction cache at the written region - lwz r3, [r6] # r3 = dest ptr - lwz r4, [r6 + 4] # r4 = size - # A .include directive essentially pastes in the code from the referenced file. Here, we use the code from the file - # FlushCachedCode.inc.s. When compiling includes, newserv first looks in the same directory as the function's source, - # then looks in system/client-functions/System. - .include FlushCachedCode - - # Return the address after the last byte written. The value returned in r3 from the function is sent back to the - # server in a B3 command. newserv uses the return value during DOL loading to know which section of the DOL file to - # send next, or to send the RunDOL function if all sections have been loaded. - lwz r3, [r6] # r3 = dest ptr - lwz r4, [r6 + 4] # r4 = size - add r3, r3, r4 - mtlr r12 - blr - -get_block_ptr__ret: - mflr r3 - mtlr r10 - blr -get_block_ptr: - # We use a trick here to get the address of the dest_addr label: since bl puts the immediately-following address into - # the link register, we "call" get_block_ptr__ret and get the dest_addr pointer out of the LR. We then put r10 back - # into the LR so get_block_ptr__ret returns to the caller. - mflr r10 - bl get_block_ptr__ret - -# These fields are filled in right before the command is sent to the client. Specifically, the label_writes argument to -# send_function_call is responsible for this. The label_writes argument is a map of label name to value, and -# send_function_call simply writes the given values after the given labels. This is a way to pass arbitrary arguments -# to a function at call time. -dest_addr: - .zero -size: - .zero - -# Finally, we use the suffix argument to instruct send_function_call to append the data we want to write to memory -# immediately after the assembled code. (The data_to_write label here is for documentation purposes only; the suffix -# argument always appends data after the end of all the assembled code.) -data_to_write: diff --git a/system/client-functions/System/WriteMemoryX86.x86.s b/system/client-functions/System/WriteMemoryX86.x86.s deleted file mode 100644 index f1ace410..00000000 --- a/system/client-functions/System/WriteMemoryX86.x86.s +++ /dev/null @@ -1,37 +0,0 @@ -.meta name="Write memory" -.meta description="Writes data to any location in memory" - -entry_ptr: -reloc0: - .offsetof start - -start: - jmp get_block_ptr -get_block_ptr_ret: - xchg ebx, [esp] - mov eax, [ebx] - mov ecx, [ebx + 4] - add ebx, 8 - -again: - test ecx, ecx - jz done - mov dl, [ebx] - mov [eax], dl - inc ebx - inc eax - dec ecx - jmp again - -done: - pop ebx - ret - -get_block_ptr: - call get_block_ptr_ret -dest_addr: - .data 0 -size: - .data 0 - -data_to_write: diff --git a/system/client-functions/UltimateMapFix/UltimateMapFix.2___.patch.s b/system/client-functions/UltimateMapFix.s similarity index 98% rename from system/client-functions/UltimateMapFix/UltimateMapFix.2___.patch.s rename to system/client-functions/UltimateMapFix.s index 9c7ffeaa..ec7cfd1e 100644 --- a/system/client-functions/UltimateMapFix/UltimateMapFix.2___.patch.s +++ b/system/client-functions/UltimateMapFix.s @@ -1,6 +1,8 @@ +# Patch by Matt Swift + +.meta visibility="all" .meta name="Ultimate map fix" .meta description="Adds missing maps\nto Ultimate for\ncertain quests" -# Patch by Matt Swift .versions 2OJ5 2OJF 2OEF 2OPF @@ -8,7 +10,7 @@ entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksDC + .include WriteCodeBlocks # Modify location of Ultimate Cave 1 map pointer table to 8C008100 and make it have 6 entries .align 4 diff --git a/system/client-functions/ReticleColors/XBReticleColors.3___.patch.s b/system/client-functions/XBReticleColors.s similarity index 93% rename from system/client-functions/ReticleColors/XBReticleColors.3___.patch.s rename to system/client-functions/XBReticleColors.s index b15f65f3..8aec2617 100644 --- a/system/client-functions/ReticleColors/XBReticleColors.3___.patch.s +++ b/system/client-functions/XBReticleColors.s @@ -1,16 +1,18 @@ -.meta name="Xbox/BB targets" -.meta description="Changes the target\nreticle colors to\nthose used on the\nXbox and Blue Burst" # Original code by Ralf @ GC-Forever and Aleron Ives # https://www.gc-forever.com/forums/viewtopic.php?t=2050 # https://www.gc-forever.com/forums/viewtopic.php?t=2049 +.meta visibility="all" +.meta name="Xbox/BB targets" +.meta description="Changes the target\nreticle colors to\nthose used on the\nXbox and Blue Burst" + .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 entry_ptr: reloc0: .offsetof start start: - .include WriteCodeBlocksGC + .include WriteCodeBlocks .data .data 0x00000004 diff --git a/system/config.example.json b/system/config.example.json index 625f66eb..5eacfc13 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -177,9 +177,9 @@ // DNS server messages describe erroneous queries that the DNS server does not respond to. Normal DNS queries do // not generate any log messages. "DNSServer": "INFO", - // Function compiler messages are generated when building client functions, which only happens at startup time or - // when `reload functions` is used in the shell. - "FunctionCompiler": "INFO", + // Client function index messages are generated when building client functions, which only happens at startup time + // or when `reload functions` is used in the shell. + "ClientFunctionIndex": "INFO", // IP stack simulator messages describe clients connecting and disconnecting via the IP stack interface, and errors // that occur at the simulated network level within the simulator. "IPStackSimulator": "INFO", diff --git a/tests/config.json b/tests/config.json index 33befbf8..84f7cecf 100644 --- a/tests/config.json +++ b/tests/config.json @@ -175,7 +175,7 @@ "CommandData": "ERROR", "Config": "WARNING", "DNSServer": "WARNING", - "FunctionCompiler": "WARNING", + "ClientFunctionIndex": "WARNING", "IPStackSimulator": "WARNING", "Lobbies": "WARNING", "Replay": "INFO",