rewrite client function compiler

This commit is contained in:
Martin Michelsen
2026-05-11 07:29:25 -07:00
parent 2f2a0bcf2b
commit e78e2ba887
174 changed files with 3931 additions and 5807 deletions
+2 -1
View File
@@ -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
+2 -1
View File
@@ -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 <address>`: Read 4 bytes from the given address and show you the values.
* `$writemem <address> <data>`: 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 <name>`: Run a patch on your client. `<name>` must exactly match the name of a patch on the server.
* `$patch <name>`: Run a client function. `<name>` must exactly match the name of a client function on the server.
* Character data commands (non-proxy only)
* `$switchchar <slot>` (BB only): Switch to a different character from your account without logging out.
+1 -2
View File
@@ -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"
+27 -25
View File
@@ -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<const CompiledFunctionCode> fn;
shared_ptr<const ClientFunctionIndex::Function> 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]<typename CharT>(const CharT& char_file) -> asio::awaitable<void> {
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<float>(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<const CompiledFunctionCode> fn;
shared_ptr<const ClientFunctionIndex::Function> 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<string, uint32_t> 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");
+1 -1
View File
@@ -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"
+558
View File
@@ -0,0 +1,558 @@
#include "ClientFunctionIndex.hh"
#include <stdio.h>
#include <string.h>
#include <filesystem>
#include <phosg/Filesystem.hh>
#include <phosg/Hash.hh>
#include <phosg/Time.hh>
#include <stdexcept>
#include <resource_file/Emulators/PPC32Emulator.hh>
#include <resource_file/Emulators/SH4Emulator.hh>
#include <resource_file/Emulators/X86Emulator.hh>
#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 <typename T>
const T& get_with_sv_fallback(
const std::unordered_map<std::string, T>& 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 <bool BE>
string ClientFunctionIndex::Function::generate_client_command_t(
const unordered_map<string, uint32_t>& label_writes,
const void* suffix_data,
size_t suffix_size,
uint32_t override_relocations_offset) const {
using FooterT = RELFileFooterT<BE>;
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<U32T<FooterT::IsBE>*>(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<U16T<FooterT::IsBE>>(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<string, uint32_t>& 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<true>(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<false>(label_writes, suffix_data, suffix_size, override_relocations_offset);
} else {
throw logic_error("invalid architecture");
}
}
static unordered_map<uint32_t, std::string> preprocess_function_code(const std::string& text) {
std::unordered_set<uint32_t> all_specific_versions;
struct Line {
std::string text;
std::unordered_map<uint32_t, size_t> new_specific_versions; // Nonempty iff line is a .versions directive
bool enable_all_versions = false;
};
std::vector<Line> 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<uint32_t, std::string> ret;
for (uint32_t specific_version : all_specific_versions) {
std::deque<std::string> 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 ");
while (vers_offset != string::npos) {
size_t end_offset = line_text.find('>', vers_offset + 6);
if (end_offset == string::npos) {
throw runtime_error(std::format("(version {}) (line {}) unterminated <VERS> 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 <VERS> 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("<VERS ");
}
version_lines.emplace_back(std::move(line_text));
}
}
ret.emplace(specific_version, phosg::join(version_lines, "\n"));
}
return ret;
}
ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_any_failure) {
map<string, string> source_files;
std::function<void(const std::string&)> 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<string, string> 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<uint32_t, std::string> 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<Function> fn = make_shared<Function>();
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<string> get_include_stack;
function<string(const string&, uint32_t)> 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<uint32_t> 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<const Menu> ClientFunctionIndex::patch_switches_menu(
uint32_t specific_version,
const std::unordered_set<std::string>& server_auto_patches_enabled,
const std::unordered_set<std::string>& client_auto_patches_enabled) const {
auto ret = make_shared<Menu>(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<const ClientFunctionIndex::Function> 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<const ClientFunctionIndex::Function> 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<const ClientFunctionIndex::Function> ClientFunctionIndex::get(const std::string& name) const {
return this->all_functions.at(name);
}
std::shared_ptr<const ClientFunctionIndex::Function> 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<uint32_t, uint32_t> 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>(
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");
}
}
+96
View File
@@ -0,0 +1,96 @@
#pragma once
#include <inttypes.h>
#include <map>
#include <memory>
#include <phosg/Strings.hh>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#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<uint16_t> relocation_deltas;
std::unordered_map<std::string, uint32_t> 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 <bool BE>
std::string generate_client_command_t(
const std::unordered_map<std::string, uint32_t>& 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<std::string, uint32_t>& 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<std::string, std::shared_ptr<Function>> all_functions; // Key is "PatchName-SpecificVersion"
std::map<uint32_t, std::map<std::string, std::shared_ptr<Function>>> functions_by_specific_version;
std::map<uint32_t, std::shared_ptr<Function>> functions_by_menu_item_id;
std::shared_ptr<const Menu> patch_switches_menu(
uint32_t specific_version,
const std::unordered_set<std::string>& server_auto_patches_enabled,
const std::unordered_set<std::string>& client_auto_patches_enabled) const;
bool patch_menu_empty(uint32_t specific_version) const;
std::shared_ptr<const Function> get(const std::string& name, uint32_t specific_version) const;
std::shared_ptr<const Function> get(const std::string& name, Function::Architecture arch) const;
std::shared_ptr<const Function> get(const std::string& name) const;
std::shared_ptr<const Function> 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>(
ClientFunctionIndex::Function::Visibility vis);
+94
View File
@@ -0,0 +1,94 @@
#include "DOLFileIndex.hh"
#include <stdio.h>
#include <string.h>
#include <filesystem>
#include <phosg/Filesystem.hh>
#include <phosg/Hash.hh>
#include <phosg/Time.hh>
#include <stdexcept>
#include <resource_file/Emulators/PPC32Emulator.hh>
#include <resource_file/Emulators/SH4Emulator.hh>
#include <resource_file/Emulators/X86Emulator.hh>
#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<Menu>(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<File>();
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());
}
}
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <inttypes.h>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "Menu.hh"
struct DOLFileIndex {
struct File {
uint32_t menu_item_id;
std::string name;
std::string data;
bool is_compressed;
};
std::vector<std::shared_ptr<File>> item_id_to_file;
std::unordered_map<std::string, std::shared_ptr<File>> name_to_file;
std::shared_ptr<const Menu> 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();
}
};
-612
View File
@@ -1,612 +0,0 @@
#include "FunctionCompiler.hh"
#include <stdio.h>
#include <string.h>
#include <filesystem>
#include <phosg/Filesystem.hh>
#include <phosg/Hash.hh>
#include <phosg/Time.hh>
#include <stdexcept>
#include <resource_file/Emulators/PPC32Emulator.hh>
#include <resource_file/Emulators/SH4Emulator.hh>
#include <resource_file/Emulators/X86Emulator.hh>
#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 <bool BE>
string CompiledFunctionCode::generate_client_command_t(
const unordered_map<string, uint32_t>& label_writes,
const void* suffix_data,
size_t suffix_size,
uint32_t override_relocations_offset) const {
using FooterT = RELFileFooterT<BE>;
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<U32T<FooterT::IsBE>*>(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<U16T<FooterT::IsBE>>(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<string, uint32_t>& 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<true>(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<false>(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<uint32_t, std::string> preprocess_function_code(const std::string& text) {
auto parse_specific_version_list = +[](std::string&& text) -> vector<uint32_t> {
phosg::strip_whitespace(text);
vector<uint32_t> 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<const be_uint32_t*>(vers_token.data()));
}
return ret;
};
// Find a .versions directive and populate specific_versions
vector<uint32_t> 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<deque<string>> version_lines;
version_lines.resize(specific_versions.size());
size_t line_num = 1;
vector<uint32_t> current_only_versions;
unordered_set<uint32_t> 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 ");
if (vers_offset == string::npos) {
for (size_t vers_index = 0; vers_index < specific_versions.size(); vers_index++) {
if (current_only_versions.empty() || current_only_versions_set.count(specific_versions[vers_index])) {
version_lines[vers_index].emplace_back(line);
} else {
version_lines[vers_index].emplace_back("");
}
}
} else {
size_t token_index = 0;
for (size_t vers_index = 0; vers_index < specific_versions.size(); vers_index++) {
if (current_only_versions.empty() || current_only_versions_set.count(specific_versions[vers_index])) {
string version_line = line;
size_t vers_offset = line.find("<VERS ");
while (vers_offset != string::npos) {
size_t end_offset = version_line.find('>', vers_offset + 6);
if (end_offset == string::npos) {
throw runtime_error(std::format("(line {}) unterminated <VERS> 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 <VERS> 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("<VERS ");
}
version_lines[vers_index].emplace_back(version_line);
token_index++;
} else {
version_lines[vers_index].emplace_back("");
}
}
}
}
line_num++;
}
unordered_map<uint32_t, string> 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<shared_ptr<CompiledFunctionCode>> 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<string> get_include_stack;
function<string(const string&)> 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<shared_ptr<CompiledFunctionCode>> 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<CompiledFunctionCode>());
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<uint32_t> 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<uint64_t>(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<const Menu> FunctionCodeIndex::patch_switches_menu(
uint32_t specific_version,
const std::unordered_set<std::string>& server_auto_patches_enabled,
const std::unordered_set<std::string>& client_auto_patches_enabled) const {
auto suffix = std::format("-{:08X}", specific_version);
auto ret = make_shared<Menu>(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<const CompiledFunctionCode> 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<Menu>(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<File>();
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<uint32_t, uint32_t> 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);
}
-95
View File
@@ -1,95 +0,0 @@
#pragma once
#include <inttypes.h>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#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<uint16_t> relocation_deltas;
std::unordered_map<std::string, uint32_t> 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 <bool BE>
std::string generate_client_command_t(
const std::unordered_map<std::string, uint32_t>& 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<std::string, uint32_t>& 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<std::string, std::shared_ptr<CompiledFunctionCode>> name_to_function;
std::unordered_map<uint8_t, std::shared_ptr<CompiledFunctionCode>> index_to_function;
std::unordered_map<uint64_t, std::shared_ptr<CompiledFunctionCode>> menu_item_id_and_specific_version_to_patch_function;
// Key here is e.g. "PATCHNAME-SPECIFICVERSION", with the latter in hex
std::map<std::string, std::shared_ptr<CompiledFunctionCode>> name_and_specific_version_to_patch_function;
std::shared_ptr<const Menu> patch_switches_menu(
uint32_t specific_version,
const std::unordered_set<std::string>& server_auto_patches_enabled,
const std::unordered_set<std::string>& client_auto_patches_enabled) const;
bool patch_menu_empty(uint32_t specific_version) const;
std::shared_ptr<const CompiledFunctionCode> 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<std::shared_ptr<File>> item_id_to_file;
std::unordered_map<std::string, std::shared_ptr<File>> name_to_file;
std::shared_ptr<const Menu> 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);
+3 -3
View File
@@ -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");
+1 -1
View File
@@ -4,11 +4,11 @@
#include <phosg/Strings.hh>
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;
+18 -29
View File
@@ -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<FunctionCodeIndex>("system/client-functions", false);
auto fci = make_shared<ClientFunctionIndex>("system/client-functions", false);
const std::string& output_dir = args.get<string>(1);
std::filesystem::create_directories(output_dir);
bool skip_encrypted = args.get<bool>("skip-encrypted");
auto process_code = [&](shared_ptr<const CompiledFunctionCode> code,
auto process_code = [&](shared_ptr<const ClientFunctionIndex::Function> 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");
});
+16 -16
View File
@@ -132,7 +132,7 @@ static void send_main_menu(shared_ptr<Client> 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<void> send_auto_patches_if_needed(shared_ptr<Client> c) {
c->set_flag(Client::Flag::HAS_AUTO_PATCHES);
co_await prepare_client_for_patches(c);
unordered_set<shared_ptr<const CompiledFunctionCode>> functions_to_send;
unordered_set<shared_ptr<const ClientFunctionIndex::Function>> 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<void> send_auto_patches_if_needed(shared_ptr<Client> 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<void> on_9D_9E(shared_ptr<Client> 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<string, uint32_t> 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<void> on_9D_9E(shared_ptr<Client> 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<void> on_10_main_menu(shared_ptr<Client> 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<Client> c, uint32_t item_id) {
}
auto s = c->require_server_state();
uint64_t key = (static_cast<uint64_t>(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));
}
}
+18 -18
View File
@@ -335,7 +335,7 @@ asio::awaitable<void> prepare_client_for_patches(shared_ptr<Client> 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<string, uint32_t> 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<void> prepare_client_for_patches(shared_ptr<Client> 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<const CompiledFunctionCode> code,
shared_ptr<const ClientFunctionIndex::Function> code,
const unordered_map<string, uint32_t>& label_writes,
const void* suffix_data,
size_t suffix_size,
@@ -416,7 +416,7 @@ string prepare_send_function_call_data(
asio::awaitable<C_ExecuteCodeResult_B3> send_function_call(
shared_ptr<Client> c,
shared_ptr<const CompiledFunctionCode> code,
shared_ptr<const ClientFunctionIndex::Function> code,
const unordered_map<string, uint32_t>& label_writes,
const void* suffix_data,
size_t suffix_size,
@@ -445,7 +445,7 @@ asio::awaitable<C_ExecuteCodeResult_B3> send_function_call(
}
asio::awaitable<void> send_function_call_multi(
shared_ptr<Client> c, unordered_set<shared_ptr<const CompiledFunctionCode>> codes) {
shared_ptr<Client> c, unordered_set<shared_ptr<const ClientFunctionIndex::Function>> codes) {
if (codes.empty()) {
co_return;
}
@@ -468,7 +468,7 @@ asio::awaitable<void> send_function_call_multi(
void send_function_call(
shared_ptr<Channel> ch,
uint64_t client_enabled_flags,
shared_ptr<const CompiledFunctionCode> code,
shared_ptr<const ClientFunctionIndex::Function> code,
const unordered_map<string, uint32_t>& label_writes,
const void* suffix_data,
size_t suffix_size,
@@ -525,7 +525,7 @@ asio::awaitable<bool> send_protected_command(std::shared_ptr<Client> 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<string, uint32_t> 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<void> send_dol_file(shared_ptr<Client> c, shared_ptr<DOLFileInde
// Determine the necessary start address for the data
unordered_map<string, uint32_t> 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<void> send_dol_file(shared_ptr<Client> c, shared_ptr<DOLFileInde
string data_to_send = dol->data.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<void> send_dol_file(shared_ptr<Client> c, shared_ptr<DOLFileInde
}
// Send the final function, which moves the DOL's sections into place and calls the entrypoint
auto fn = s->function_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<GetPlayerInfoResult> send_get_player_info(shared_ptr<Client> 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<AsyncPromise<C_ExecuteCodeResult_B3>>());
full_req_sent = true;
+5 -5
View File
@@ -9,8 +9,8 @@
#include <unordered_set>
#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<void> prepare_client_for_patches(std::shared_ptr<Client> c);
std::string prepare_send_function_call_data(
std::shared_ptr<const CompiledFunctionCode> code,
std::shared_ptr<const ClientFunctionIndex::Function> code,
const std::unordered_map<std::string, uint32_t>& 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<Channel> ch,
uint64_t client_enabled_flags,
std::shared_ptr<const CompiledFunctionCode> code,
std::shared_ptr<const ClientFunctionIndex::Function> code,
const std::unordered_map<std::string, uint32_t>& 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<C_ExecuteCodeResult_B3> send_function_call(
std::shared_ptr<Client> c,
std::shared_ptr<const CompiledFunctionCode> code,
std::shared_ptr<const ClientFunctionIndex::Function> code,
const std::unordered_map<std::string, uint32_t>& label_writes = {},
const void* suffix_data = nullptr,
size_t suffix_size = 0,
@@ -182,7 +182,7 @@ asio::awaitable<C_ExecuteCodeResult_B3> send_function_call(
uint32_t override_relocations_offset = 0,
bool ignore_actually_runs_code_flag = false);
asio::awaitable<void> send_function_call_multi(
std::shared_ptr<Client> c, std::unordered_set<std::shared_ptr<const CompiledFunctionCode>> codes);
std::shared_ptr<Client> c, std::unordered_set<std::shared_ptr<const ClientFunctionIndex::Function>> codes);
asio::awaitable<bool> send_protected_command(std::shared_ptr<Client> c, const void* data, size_t size, bool echo_to_lobby);
asio::awaitable<void> send_dol_file(std::shared_ptr<Client> c, std::shared_ptr<DOLFileIndex::File> dol);
+1 -1
View File
@@ -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<FunctionCodeIndex>("system/client-functions", raise_on_any_failure);
this->client_functions = make_shared<ClientFunctionIndex>("system/client-functions", raise_on_any_failure);
}
void ServerState::load_dol_files() {
+3 -2
View File
@@ -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<ServerState> {
std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>> bb_private_keys;
std::shared_ptr<const parray<uint8_t, 0x16C>> bb_default_keyboard_config;
std::shared_ptr<const parray<uint8_t, 0x38>> bb_default_joystick_config;
std::shared_ptr<const FunctionCodeIndex> function_code_index;
std::shared_ptr<const ClientFunctionIndex> client_functions;
std::shared_ptr<const PatchFileIndex> pc_patch_file_index;
std::shared_ptr<const PatchFileIndex> bb_patch_file_index;
std::unordered_map<uint64_t, std::shared_ptr<const MapFile>> map_file_for_source_hash;
+22 -5
View File
@@ -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++) {
+5
View File
@@ -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 {
@@ -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, <VERS 0x8012D2D4 0x8012D518 0x8012D550 0x8012D4B0 0x8012D578 0x8012D578 0x8012D4C0 0x8012D698>
.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 <VERS 0x00197610 0x001977A0 0x00197920 0x00197880 0x00197810 0x001978A0 0x00197840>
.deltaof TItemWeapon_SealedJSword_count_kill, TItemWeapon_SealedJSword_count_kill_end
.address <VERS 0x00197610 0x001977A0 0x00197920 0x00197880 0x00197810 0x001978A0 0x00197840>
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 <VERS 0x005E32A4 0x005E32C8>
.deltaof TItemUnitUnsealable_count_kill, TItemUnitUnsealable_count_kill_end
.address <VERS 0x005E32A4 0x005E32C8>
@@ -34,8 +94,6 @@ TItemUnitUnsealable_count_kill_skip_update:
jmp <VERS 0x005E2C10 0x005E2C34>
TItemUnitUnsealable_count_kill_end:
.data <VERS 0x005F3E94 0x005F3EFC>
.deltaof TItemWeapon_LameDArgent_count_kill, TItemWeapon_LameDArgent_count_kill_end
.address <VERS 0x005F3E94 0x005F3EFC>
@@ -59,8 +117,6 @@ TItemWeapon_LameDArgent_count_kill_skip_update:
ret
TItemWeapon_LameDArgent_count_kill_end:
.data <VERS 0x005FC95C 0x005FCA74>
.deltaof TItemWeapon_SealedJSword_count_kill, TItemWeapon_SealedJSword_count_kill_end
.address <VERS 0x005FC95C 0x005FCA74>
@@ -86,5 +142,7 @@ TItemWeapon_SealedJSword_count_kill_end:
.all_versions
.data 0x00000000
.data 0x00000000
@@ -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, <VERS 0x8012D2D4 0x8012D518 0x8012D550 0x8012D4B0 0x8012D578 0x8012D578 0x8012D4C0 0x8012D698>
.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
@@ -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 <VERS 0x00197610 0x001977A0 0x00197920 0x00197880 0x00197810 0x001978A0 0x00197840>
.deltaof TItemWeapon_SealedJSword_count_kill, TItemWeapon_SealedJSword_count_kill_end
.address <VERS 0x00197610 0x001977A0 0x00197920 0x00197880 0x00197810 0x001978A0 0x00197840>
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
@@ -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 <VERS 0x006C8C53 0x006C8C0F>
.data 4
@@ -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 <VERS 0x0064A642 0x0064A5DE> # Episode 1
.data 1
.binary 01
@@ -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
@@ -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
@@ -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
@@ -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 <VERS 0x00413B77 0x00413B7F> # 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, [<VERS 0x00A38BD0 0x00A3B050>] # 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 <VERS 0x00413C30 0x00413C38> # 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, [<VERS 0x00A38BD0 0x00A3B050>] # 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 <VERS 0x00413CD0 0x00413CD8> # 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, [<VERS 0x00A38BD0 0x00A3B050>] # 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 <VERS 0x00413CE8 0x00413CF0> # 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, [<VERS 0x00A38BD0 0x00A3B050>]
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, <VERS 0x006C1ABC 0x006C1A80>
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, [<VERS 0x00A38BD0 0x00A3B050>] # 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, <VERS 0x006C4514 0x006C44D0> # 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 <VERS 0x00475294 0x004751A4>
.data 0x00000001
.binary 0C # slot count; TDataProtocol::handle_E5
.data 0x0047525B
.data <VERS 0x0047534B 0x0047525B>
.data 0x00000001
.binary 0C # slot count; import_player_preview
.data 0x004785E1
.data <VERS 0x004786D1 0x004785E1>
.data 0x00000001
.binary 0C # slot count; TDataProtocol::handle_E4
.data 0x0048242D
.data <VERS 0x00482559 0x0048242D>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C17BF
.data <VERS 0x006C17FB 0x006C17BF>
.data 0x00000001
.binary 0C # slot count
.data 0x006C1CCB
.data <VERS 0x006C1D07 0x006C1CCB>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C1CFE
.data <VERS 0x006C1D3A 0x006C1CFE>
.data 0x00000001
.binary 0C # slot count
.data 0x006C1D1C
.data <VERS 0x006C1D58 0x006C1D1C>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C1DD7
.data <VERS 0x006C1E13 0x006C1DD7>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C222E
.data <VERS 0x006C226A 0x006C222E>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C226D
.data <VERS 0x006C22A9 0x006C226D>
.data 0x00000001
.binary 0C # slot count
.data 0x006C228E
.data <VERS 0x006C22CA 0x006C228E>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C229E
.data <VERS 0x006C22DA 0x006C229E>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C24DB
.data <VERS 0x006C2517 0x006C24DB>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C2643
.data <VERS 0x006C267F 0x006C2643>
.data 0x00000004
.data 0x00022FBC # save_count offset
.data 0x006C264D
.data <VERS 0x006C2689 0x006C264D>
.data 0x00000004
.data 0x00022FBC # save_count offset
.data 0x006C26EF
.data <VERS 0x006C272B 0x006C26EF>
.data 0x00000004
.data 0x00022FBC # save_count offset
.data 0x006C2705
.data <VERS 0x006C2741 0x006C2705>
.data 0x00000004
.data 0x00022FC0 # round2_seed offset
.data 0x006C2793
.data <VERS 0x006C27CF 0x006C2793>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C286C
.data <VERS 0x006C28A8 0x006C286C>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C3113
.data <VERS 0x006C314F 0x006C3113>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C353F
.data <VERS 0x006C357B 0x006C353F>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C357E
.data <VERS 0x006C35BA 0x006C357E>
.data 0x00000001
.binary 0C # slot count
.data 0x006C35AA
.data <VERS 0x006C35E6 0x006C35AA>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C35B7
.data <VERS 0x006C35F3 0x006C35B7>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C35D2
.data <VERS 0x006C360E 0x006C35D2>
.data 0x00000004
.data 0x00022FBC # save_count offset
.data 0x006C35DB
.data <VERS 0x006C3617 0x006C35DB>
.data 0x00000004
.data 0x00022FBC # save_count offset
.data 0x006C36E0
.data <VERS 0x006C371C 0x006C36E0>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C3B1E
.data <VERS 0x006C3B5A 0x006C3B1E>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C4209
.data <VERS 0x006C424D 0x006C4209>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C47EF
.data <VERS 0x006C4833 0x006C47EF>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C4826
.data <VERS 0x006C486A 0x006C4826>
.data 0x00000001
.binary 0C # slot count
.data 0x006C4962
.data <VERS 0x006C49A6 0x006C4962>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C4999
.data <VERS 0x006C49DD 0x006C4999>
.data 0x00000001
.binary 0C # slot count
.data 0x006C4A81
.data <VERS 0x006C4AC5 0x006C4A81>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C4ABA
.data <VERS 0x006C4AFE 0x006C4ABA>
.data 0x00000001
.binary 0C # slot count
.data 0x006C4C9A
.data <VERS 0x006C4CDE 0x006C4C9A>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C4CD1
.data <VERS 0x006C4D15 0x006C4CD1>
.data 0x00000001
.binary 0C # slot count
.data 0x006C4DB9
.data <VERS 0x006C4DFD 0x006C4DB9>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C4DF2
.data <VERS 0x006C4E36 0x006C4DF2>
.data 0x00000001
.binary 0C # slot count
.data 0x006C4F58
.data <VERS 0x006C4F9C 0x006C4F58>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C4F94
.data <VERS 0x006C4FD7 0x006C4F94>
.data 0x00000001
.binary 0C # slot count
.data 0x006C5181
.data <VERS 0x006C51C5 0x006C5181>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C51BD
.data <VERS 0x006C5201 0x006C51BD>
.data 0x00000001
.binary 0C # slot count
.data 0x006C5332
.data <VERS 0x006C5376 0x006C5332>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C536C
.data <VERS 0x006C53B0 0x006C536C>
.data 0x00000001
.binary 0C # slot count
.data 0x006C5501
.data <VERS 0x006C5545 0x006C5501>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C553D
.data <VERS 0x006C5581 0x006C553D>
.data 0x00000001
.binary 0C # slot count
.data 0x006C56B2
.data <VERS 0x006C56F6 0x006C56B2>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C56EC
.data <VERS 0x006C5730 0x006C56EC>
.data 0x00000001
.binary 0C # slot count
.data 0x006C5872
.data <VERS 0x006C58B6 0x006C5872>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C58AC
.data <VERS 0x006C58F0 0x006C58AC>
.data 0x00000001
.binary 0C # slot count
.data 0x006C5A41
.data <VERS 0x006C5A85 0x006C5A41>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C5A7D
.data <VERS 0x006C5AC1 0x006C5A7D>
.data 0x00000001
.binary 0C # slot count
.data 0x006C5B6E
.data <VERS 0x006C5BB2 0x006C5B6E>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C5BA8
.data <VERS 0x006C5BEC 0x006C5BA8>
.data 0x00000001
.binary 0C # slot count
.data 0x006C5D2E
.data <VERS 0x006C5D72 0x006C5D2E>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C5D68
.data <VERS 0x006C5DAC 0x006C5D68>
.data 0x00000001
.binary 0C # slot count
.data 0x006C5EEE
.data <VERS 0x006C5F32 0x006C5EEE>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C5F28
.data <VERS 0x006C5F6C 0x006C5F28>
.data 0x00000001
.binary 0C # slot count
.data 0x006C60AE
.data <VERS 0x006C60F2 0x006C60AE>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C60E8
.data <VERS 0x006C612C 0x006C60E8>
.data 0x00000001
.binary 0C # slot count
.data 0x006C6303
.data <VERS 0x006C6346 0x006C6303>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C633D
.data <VERS 0x006C6381 0x006C633D>
.data 0x00000001
.binary 0C # slot count
.data 0x006C64C1
.data <VERS 0x006C6505 0x006C64C1>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C64FD
.data <VERS 0x006C6541 0x006C64FD>
.data 0x00000001
.binary 0C # slot count
.data 0x006C65EE
.data <VERS 0x006C6632 0x006C65EE>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C6628
.data <VERS 0x006C666C 0x006C6628>
.data 0x00000001
.binary 0C # slot count
.data 0x006C67AE
.data <VERS 0x006C67F2 0x006C67AE>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C67E8
.data <VERS 0x006C682C 0x006C67E8>
.data 0x00000001
.binary 0C # slot count
.data 0x006C696E
.data <VERS 0x006C69B2 0x006C696E>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C69A8
.data <VERS 0x006C69EC 0x006C69A8>
.data 0x00000001
.binary 0C # slot count
.data 0x006C6B43
.data <VERS 0x006C6B87 0x006C6B43>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C6B74
.data <VERS 0x006C6BB8 0x006C6B74>
.data 0x00000004
.data 0x0000005D # memcard block count
.data 0x006C6BF6
.data <VERS 0x006C6C3A 0x006C6BF6>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C6C30
.data <VERS 0x006C6C74 0x006C6C30>
.data 0x00000001
.binary 0C # slot count
.data 0x006C6E3E
.data <VERS 0x006C6E82 0x006C6E3E>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C6E78
.data <VERS 0x006C6EBC 0x006C6E78>
.data 0x00000001
.binary 0C # slot count
.data 0x006C7075
.data <VERS 0x006C70B9 0x006C7075>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C70AF
.data <VERS 0x006C70F3 0x006C70AF>
.data 0x00000001
.binary 0C # slot count
.data 0x006C7A02
.data <VERS 0x006C7A46 0x006C7A02>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C7D22
.data <VERS 0x006C7D66 0x006C7D22>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x006C7D5E
.data <VERS 0x006C7D7C 0x006C7D5E>
.data 0x00000001
.binary 0C # slot count
.data 0x006C7D7C
.data <VERS 0x006C7DC0 0x006C7D7C>
.data 0x00000004
.data 0x00022FC4 # total file size
.data 0x0077BE92
.data <VERS 0x0077CC72 0x0077BE92>
.data 0x00000004
.data 0x00022FB4 # bgm_test_songs_unlocked offset
# Signature check on all save files (rewritten as loop)
.data 0x006C1C2D
.data <VERS 0x006C1C69 0x006C1C2D>
.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: # <VERS 006C1CB2 006C1C76>
# Send slot count in E3 command
.data 0x0046EB20 # TDataProtocol::send_E3_for_index
.data <VERS 0x0046EC10 0x0046EB20> # 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, <VERS 0x006C1ABC 0x006C1A80>
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, [<VERS 0x00A38BD0 0x00A3B050>] # 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, <VERS 0x00835578 0x00857E29> # _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, <VERS 0x00835915 0x008581C5> # operator_new
call eax
add esp, 4
mov edx, [0x00A95E44] # edx = old char_file_list
mov [0x00A95E44], eax
mov edx, [<VERS 0x00A939C4 0x00A95E44>] # edx = old char_file_list
mov [<VERS 0x00A939C4 0x00A95E44>], 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, [<VERS 0x00A939C4 0x00A95E44>]
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 <VERS 0x006C197C 0x006C1940> # PSOCharacterFile::destroy
push <VERS 0x006C182C 0x006C17F0> # PSOCharacterFile::init
push 0x08 # slot count - 4
push 0x2EA4 # sizeof(PSOCharacterFile)
mov eax, [0x00A95E44]
mov eax, [<VERS 0x00A939C4 0x00A95E44>]
add eax, 0xBA94
push eax
mov eax, 0x00858736
mov eax, <VERS 0x00835E86 0x00858736>
call eax
# Fix the file's checksum
mov eax, [0x00A95E44]
mov ecx, 0x006C26FC
mov eax, [<VERS 0x00A939C4 0x00A95E44>]
mov ecx, <VERS 0x006C2738 0x006C26FC>
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, <VERS 0x0084F258 0x0082E940>
call eax # malloc10(total file size)
add esp, 4
mov [0x00A95E2C], eax
mov edx, [0x00A95E44]
mov [<VERS 0x00A939AC 0x00A95E2C>], eax
mov edx, [<VERS 0x00A939C4 0x00A95E44>]
mov ecx, 0x00022FC4 # total file size
jmp memcpy
@@ -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
@@ -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 <VERS 0x00A0DC54 0x00A0FC54>
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, [<VERS 0x00A9A074 0x00A9C4F4>] # 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, <VERS 0x0068D618 0x0068D5AC>
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 <VERS 0x00976380 0x009783A0> # prefix = L"EXP"
push 0x14
push 0x14
push 0xFFFF00FF # color (ARGB)
add eax, 0x300
push eax # position
mov eax, 0x0078AABC
mov eax, <VERS 0x0078B8E8 0x0078AABC>
call eax # TFontSmallTask___new__(...)
add esp, 0x1C
skip_text:
mov eax, 0x006928C0 # Original handle_6xBF
mov eax, <VERS 0x0069292C 0x006928C0> # 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 <VERS 0x0078827D 0x0078749D>
.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 <VERS 0x00777381 0x007765A5>
.deltaof disable_exp_steal_callsite_start, disable_exp_steal_callsite_end
disable_exp_steal_callsite_start:
add esp, 0x0C # Original function has `ret 0x0C`
@@ -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 <VERS 0x005C5020 0x005C502C>
@@ -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:
@@ -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, <VERS 0x802BB4B0 0x802BC3E0 0x802BD528 0x802BD2C0 0x802BBEF4 0x802BBF38 0x802BD474 0x802BCC08>
.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, <VERS 0x000970E0 0x000973F0 0x00097460 0x00097140 0x000970E0 0x00097160 0x00096FE0>
.label g1_hook_loc, <VERS 0x00097124 0x00097434 0x000974A4 0x00097184 0x00097124 0x000971A4 0x00097024>
.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, <VERS 0x80335060 0x803360CC 0x803375E8 0x8033739C 0x80335A50 0x80335A94 0x80337570 0x803369B4>
.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, <VERS 0x0012E257 0x0012E387 0x0012E4E7 0x0012E537 0x0012E567 0x0012E557 0x0012E5A7>
.label g2_hook_loc1, <VERS 0x0012E5F4 0x0012E724 0x0012E884 0x0012E8D4 0x0012E904 0x0012E8F4 0x0012E944>
.label g2_hook_loc2, <VERS 0x0012E622 0x0012E752 0x0012E8B2 0x0012E902 0x0012E932 0x0012E922 0x0012E972>
.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 <VERS 0x800BC750 0x800BCA58 0x800BCBD0 0x800BCB80 0x800BC9E8 0x800BC9E8 0x800BCB90 0x800BCB58>
.data 4
b +0x10
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x0016227A 0x0016238A 0x0016232A 0x0016240A 0x0016229A 0x0016242A 0x0016225A>
.data 0x00000002
.binary EB0E
# Bulclaw HP Bug Fix
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x80091528 0x80091814 0x8009198C 0x8009193C 0x800917B4 0x800917B4 0x8009194C 0x80091914>
.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, <VERS 0x80301600 0x803025CC 0x80303A1C 0x803037D0 0x80301F58 0x80301F9C 0x8030398C 0x80302D64>
.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, <VERS 0x800142DC 0x8001430C 0x800146A4 0x800142BC 0x800142F4 0x800142F4 0x800142BC 0x80014334>
.label g4_hook_call2, <VERS 0x80015D04 0x80015D34 0x80016174 0x80015CE4 0x80015D1C 0x80015D1C 0x80015CE4 0x80015D5C>
.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 <VERS 0x80166324 0x801666D8 0x80166848 0x8016679C 0x801666E0 0x801666E0 0x80166800 0x80166CC4>
.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 <VERS 0x805CB608 0x805D5C08 0x805DD0A8 0x805DCE48 0x805CBF10 0x805D2F30 0x805DC750 0x805D8990>
.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 <VERS 0x8022E2A8 0x8022EC44 0x8022FB30 0x8022F8E4 0x8022EB64 0x8022EB64 0x8022FC18 0x8022F4B0>
.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 <VERS 0x802300B8 0x80230A54 0x80231940 0x802316F4 0x80230974 0x80230974 0x80231A28 0x802312C0>
.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 <VERS 0x802365AC 0x80236F68 0x80237E54 0x80237C08 0x80236E88 0x80236E88 0x80237F3C 0x802377D4>
.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 <VERS 0x80229B54 0x8022A4F0 0x8022B3E0 0x8022B190 0x8022A410 0x8022A410 0x8022B4C4 0x8022AD5C>
.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 <VERS 0x8022EAB4 0x8022F450 0x80230340 0x802300F0 0x8022F370 0x8022F370 0x80230424 0x8022FCBC>
.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 <VERS 0x80235DD4 0x80236790 0x8023767C 0x80237430 0x802366B0 0x802366B0 0x80237764 0x80236FFC>
.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 <VERS 0x8023B2C8 0x8023BC84 0x8023CB70 0x8023C924 0x8023BBA4 0x8023BBA4 0x8023CC58 0x8023C4F0>
.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 <VERS 0x80230E08 0x802317C4 0x802326B0 0x80232464 0x802316E4 0x802316E4 0x80232798 0x80232030>
.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 <VERS 0x80237998 0x80238354 0x80239240 0x80238FF4 0x80238274 0x80238274 0x80239328 0x80238BC0>
.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 <VERS 0x802316FC 0x802320B8 0x80232FA4 0x80232D58 0x80231FD8 0x80231FD8 0x8023308C 0x80232924>
.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 <VERS 0x802337A8 0x80234164 0x80235050 0x80234E04 0x80234084 0x80234084 0x80235138 0x802349D0>
.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 <VERS 0x80229354 0x80229CF0 0x8022ABDC 0x8022A990 0x80229C10 0x80229C10 0x8022ACC4 0x8022A55C>
.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 <VERS 0x801185B0 0x801187CC 0x8011885C 0x80118764 0x80118854 0x80118854 0x80118774 0x8011894C>
.data 4
lbz r0, [r4 + 0x0016]
.data <VERS 0x801185BC 0x801187D8 0x80118868 0x80118770 0x80118860 0x80118860 0x80118780 0x80118958>
.data 4
lbz r0, [r4 + 0x0017]
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x00185D8E 0x00185F4E 0x0018600E 0x00185F0E 0x00185F6E 0x00185F2E 0x00185F2E>
.data 0x00000001
.binary 16
.data <VERS 0x00185D97 0x00185F57 0x00186017 0x00185F17 0x00185F77 0x00185F37 0x00185F37>
.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 <VERS 0x805C996C 0x805D3F6C 0x805DB40C 0x805DB1AC 0x805CA274 0x805D1294 0x805DAAB4 0x805D6CF4>
.data 4
.float 220
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x00175D75 0x00175E55 0x00175F35 0x00175EC5 0x00175ED5 0x00175EE5 0x00175E95>
.data 0x00000002
.data 0x435C0000
# Invalid Items Bug Fix
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x8011CA90 0x8011CCD4 0x8011CD0C 0x8011CC6C 0x8011CD34 0x8011CD34 0x8011CC7C 0x8011CE54>
.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, <VERS 0x801B9A88 0x801B9EF4 0x801BCF6C 0x801B9FC0 0x801B9E74 0x801B9E74 0x801BA024 0x801BA4E0>
.label g5_hook1_ret, <VERS 0x801B9A8C 0x801B9EF8 0x801BCF70 0x801B9FC4 0x801B9E78 0x801B9E78 0x801BA028 0x801BA4E4>
.label g5_hook2_call, <VERS 0x8010B970 0x8010BB70 0x8010BC04 0x8010BAF0 0x8010BC14 0x8010BC14 0x8010BB00 0x8010BCF0>
@@ -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, <VERS 0x80118CE0 0x80118EFC 0x80118FD8 0x80118E94 0x80118F84 0x80118F84 0x80118EA4 0x8011907C>
.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, <VERS 0x8021D098 0x8021D9FC 0x8021E8E8 0x8021E69C 0x8021D91C 0x8021D91C 0x8021E9D0 0x8021E268>
.label g7_hook2_call, <VERS 0x80220528 0x80220EBC 0x80221DA8 0x80221B5C 0x80220DDC 0x80220DDC 0x80221E90 0x80221728>
@@ -553,14 +685,27 @@ g7_hooks_end:
# Dropped Mag Color Bug Fix
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x80114378 0x8011458C 0x80114634 0x80114524 0x8011461C 0x8011461C 0x80114534 0x8011470C>
.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 <VERS 0x80107478 0x80107654 0x80107708 0x801075D4 0x8010771C 0x8010771C 0x801075E4 0x801077D4>
.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 <VERS 0x80101C14 0x80101EB8 0x80101EB8>
.data 4
nop
.all_versions
# Offline Quests Drop Table Bug Fix
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x80104B48 0x80104D24 0x80104DE0 0x80104CA4 0x80104DEC 0x80104DEC 0x80104CB4 0x80104EA4>
.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, <VERS 0x80112664 0x80112864 0x80112A3C 0x801127F0 0x80112908 0x80112908 0x80112800 0x801129E4>
.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 <VERS 0x801CA1F4 0x801CA6E0 0x801CB5EC 0x801CA7AC 0x801CA610 0x801CA610 0x801CA810 0x801CACCC>
.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 <VERS 0x80250264 0x80250CB0 0x80251CA4 0x802519A4 0x80250AEC 0x80250AEC 0x80251C68 0x802514B0>
.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 <VERS 0x80267DDC 0x80268A88 0x80269AE4 0x80269898 0x80268788 0x80268788 0x80269B5C 0x802693A4>
.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, <VERS 0x80355984 0x80356D88 0x803582E4 0x80358098 0x80356838 0x8035687C 0x80358464 0x80357858>
.label g9_hook_call2, <VERS 0x80355A04 0x80356E08 0x80358364 0x80358118 0x803568B8 0x803568FC 0x803584E4 0x803578D8>
@@ -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 <VERS 0x802FB99C 0x802FC968 0x802FDE60 0x802FDB6C 0x802FC2F4 0x802FC338 0x802FDD28 0x802FD100>
.data 4
cmpwi r3, 1
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x000A8AE1 0x000A8C51 0x000A8BD1 0x000A89C1 0x000A8961 0x000A89E1 0x000A8921>
.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 <VERS 0x80189A54 0x80189E2C 0x80189F90 0x80189EF0 0x80189E20 0x80189E20 0x80189F54 0x8018A418>
.data 4
nop
@@ -691,30 +858,62 @@ g9_hook_end:
.data 4
nop
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x001D229B 0x001D244B 0x001D295B 0x001D241B 0x001D26AB 0x001D243B 0x001D26DB>
.data 2
nop
nop
.data <VERS 0x001DF7C4 0x001DF924 0x001DFD94 0x001DF964 0x001DFB04 0x001DF984 0x001DFA74>
.data 6
nop
nop
nop
nop
nop
nop
# TP Bar Color Bug Fix
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x8026DA74 0x8026E738 0x8026F794 0x8026F548 0x8026E2D4 0x8026E2D4 0x8026F6FC 0x8026EF44>
.data 4
subi r4, r4, 0x5506
.data <VERS 0x8026DB88 0x8026E84C 0x8026F8A8 0x8026F65C 0x8026E3E8 0x8026E3E8 0x8026F810 0x8026F058>
.data 4
subi r3, r3, 0x5506
.data <VERS 0x8026DC10 0x8026E8D4 0x8026F930 0x8026F6E4 0x8026E470 0x8026E470 0x8026F898 0x8026F0E0>
.data 4
subi r4, r3, 0x5506
.data <VERS 0x804CBB40 0x804CF290 0x804D17E0 0x804D1580 0x804CC310 0x804CC7F0 0x804D0E58 0x804D1248>
.data 4
.data 0xFF0074EE
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x002779CE 0x00277C7E 0x0027808E 0x00277DAE 0x00277ECE 0x00277DCE 0x00277F9E>
.data 0x00000004
.data 0xFF00AAFA
.data <VERS 0x002779DE 0x00277C8E 0x0027809E 0x00277DBE 0x00277EDE 0x00277DDE 0x00277FAE>
.data 0x00000004
.data 0xFF00AAFA
.data <VERS 0x00277A24 0x00277CD4 0x002780E4 0x00277E04 0x00277F24 0x00277E24 0x00277FF4>
.data 0x00000004
.data 0xFF00AAFA
.data <VERS 0x0054543C 0x00545ACC 0x0054D5B4 0x0054AA34 0x0054A2D4 0x0054AA34 0x0054ADD4>
.data 0x00000004
.data 0xFF0074EE
# Devil's and Demon's Special Damage Display Bug Fix
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x8001306C 0x8001309C 0x80013364 0x8001304C 0x80013084 0x80013084 0x8001304C 0x800130C4>
.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, <VERS 0x80183E94 0x8018425C 0x801843C0 0x80184320 0x80184250 0x80184250 0x80184384 0x80184848>
.label g10_hook_ret, <VERS 0x80183E98 0x80184260 0x801843C4 0x80184324 0x80184254 0x80184254 0x80184388 0x8018484C>
@@ -750,15 +951,25 @@ g10_hook_end:
# Rain Drops Color Bug Fix
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x804B3738 0x804B6E58 0x804B92F8 0x804B90B8 0x804B3EF0 0x804B43D0 0x804B8990 0x804B8E10>
.data 8
.data 0x70808080
.data 0x60707070
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x0054D670 0x0054DD00 0x005557E8 0x00552C68 0x00552508 0x00552C68 0x00553008>
.data 0x00000008
.binary 7080808060707070
# Reverser Target Lock Bug Fix
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x801C5EA4 0x801C6360 0x801C6604 0x801C642C 0x801C62C0 0x801C62C0 0x801C6490 0x801C694C>
.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 <VERS 0x8022CF84 0x8022D920 0x8022E85C 0x8022E5C0 0x8022D840 0x8022D840 0x8022E8F4 0x8022E18C>
.data 4
bgt +0x0630
.only_versions 3OJ2 3OE0 3OE1
.versions 3OJ2 3OE0 3OE1
.data <VERS 0x8022D278 0x8022DB34 0x8022DB34>
.data 4
bgt +0x033C
@@ -779,12 +992,13 @@ g10_hook_end:
.data <VERS 0x8022D36C 0x8022DC28 0x8022DC28>
.data 4
bgt +0x0248
.all_versions
# Tech Auto Targeting Bug Fix
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x8022C850 0x8022D1EC 0x8022E128 0x8022DE8C 0x8022D10C 0x8022D10C 0x8022E1C0 0x8022DA58>
.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, <VERS 0x80170C54 0x80171008 0x80171260 0x801710CC 0x80171010 0x80171010 0x80171130 0x801715F4>
.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, <VERS 0x80095724 0x800959B0 0x800959B0>
.label g12_hook2_call, <VERS 0x80095734 0x800959C0 0x800959C0>
.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, <VERS 0x8034CFA8 0x8034E3AC 0x8034F908 0x8034F6BC 0x8034DE5C 0x8034DEA0 0x8034FA88 0x8034EE7C>
.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, <VERS 0x8001C6D0 0x8001C8F0 0x8001C8F0>
.label load_battle_params, <VERS 0x8001DA48 0x8001DC68 0x8001DC68>
.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
@@ -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 <VERS 0x0016227A 0x0016238A 0x0016232A 0x0016240A 0x0016229A 0x0016242A 0x0016225A>
.data 0x00000002
.binary EB0E
# Shield DFP/EVP Bug Fix (allows shields to reach true max DFP/EVP values)
.data <VERS 0x00185D8E 0x00185F4E 0x0018600E 0x00185F0E 0x00185F6E 0x00185F2E 0x00185F2E>
.data 0x00000001
.binary 16
.data <VERS 0x00185D97 0x00185F57 0x00186017 0x00185F17 0x00185F77 0x00185F37 0x00185F37>
.data 0x00000001
.binary 17
# VR Spaceship Item Drop Bug Fix (allows items to drop from enemies above a certain Y position)
.data <VERS 0x00175D75 0x00175E55 0x00175F35 0x00175EC5 0x00175ED5 0x00175EE5 0x00175E95>
.data 0x00000002
.data 0x435C0000
# Gol Dragon Camera Bug Fix (makes the camera after Gol Dragon display "normally")
.data <VERS 0x000A8AE1 0x000A8C51 0x000A8BD1 0x000A89C1 0x000A8961 0x000A89E1 0x000A8921>
.data 0x00000002
.binary 01
# Rain Drops Color Bug Fix
.data <VERS 0x0054D670 0x0054DD00 0x005557E8 0x00552C68 0x00552508 0x00552C68 0x00553008>
.data 0x00000008
.binary 7080808060707070
# TP Bar Color Bug Fix
.data <VERS 0x002779CE 0x00277C7E 0x0027808E 0x00277DAE 0x00277ECE 0x00277DCE 0x00277F9E>
.data 0x00000004
.data 0xFF00AAFA
.data <VERS 0x002779DE 0x00277C8E 0x0027809E 0x00277DBE 0x00277EDE 0x00277DDE 0x00277FAE>
.data 0x00000004
.data 0xFF00AAFA
.data <VERS 0x00277A24 0x00277CD4 0x002780E4 0x00277E04 0x00277F24 0x00277E24 0x00277FF4>
.data 0x00000004
.data 0xFF00AAFA
.data <VERS 0x0054543C 0x00545ACC 0x0054D5B4 0x0054AA34 0x0054A2D4 0x0054AA34 0x0054ADD4>
.data 0x00000004
.data 0xFF0074EE
# Olga Flow Barta Bug Fix
.label g1_hook_call, <VERS 0x000970E0 0x000973F0 0x00097460 0x00097140 0x000970E0 0x00097160 0x00096FE0>
.label g1_hook_loc, <VERS 0x00097124 0x00097434 0x000974A4 0x00097184 0x00097124 0x000971A4 0x00097024>
.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, <VERS 0x0012E257 0x0012E387 0x0012E4E7 0x0012E537 0x0012E567 0x0012E557 0x0012E5A7>
.label g2_hook_loc1, <VERS 0x0012E5F4 0x0012E724 0x0012E884 0x0012E8D4 0x0012E904 0x0012E8F4 0x0012E944>
.label g2_hook_loc2, <VERS 0x0012E622 0x0012E752 0x0012E8B2 0x0012E902 0x0012E932 0x0012E922 0x0012E972>
.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 <VERS 0x001D229B 0x001D244B 0x001D295B 0x001D241B 0x001D26AB 0x001D243B 0x001D26DB>
.data 2
nop
nop
.data <VERS 0x001DF7C4 0x001DF924 0x001DFD94 0x001DF964 0x001DFB04 0x001DF984 0x001DFA74>
.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
@@ -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 <VERS 0x8065F458 0x805C4D58 0x805CF320 0x805D67A0 0x805D6540 0x805C5650 0x805CC630 0x805D5E50 0x805D2090>
# RcvPsoData2
.data <VERS 0x80236F24 0x801E3B38 0x801E40BC 0x801E4290 0x801E4008 0x801E3F9C 0x801E3F9C 0x801E405C 0x801E4698>
.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 <VERS 0x0071E8C8 0x0071EF28 0x00726A68 0x00723F68 0x007237E8 0x00723F68 0x007242E8>
.data <VERS 0x002DBBA0 0x002DC720 0x002DDFE0 0x002DDB00 0x002DE000 0x002DDB30 0x002DE030>
.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 <VERS 0x00AAC870 0x00AAECF0> # should_allow_protected_commands
.data <VERS 0x008015D0 0x00800860> # RcvPsoData2[std](void* data @ [esp + 4], uint32_t size @ [esp + 8])
.all_versions
size:
.data 0x00000000
data:
@@ -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 <VERS 0x8065F458 0x805C4D58 0x805CF320 0x805D67A0 0x805D6540 0x805C5650 0x805CC630 0x805D5E50 0x805D2090>
# RcvPsoData2
.data <VERS 0x80236F24 0x801E3B38 0x801E40BC 0x801E4290 0x801E4008 0x801E3F9C 0x801E3F9C 0x801E405C 0x801E4698>
size:
.data 0x00000000
data:
@@ -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 <VERS 0x0071E8C8 0x0071EF28 0x00726A68 0x00723F68 0x007237E8 0x00723F68 0x007242E8>
.data <VERS 0x002DBBA0 0x002DC720 0x002DDFE0 0x002DDB00 0x002DE000 0x002DDB30 0x002DE030>
size:
.data 0x00000000
data:
@@ -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 <VERS 0x00AAC870 0x00AAECF0> # should_allow_protected_commands
.data <VERS 0x008015D0 0x00800860> # RcvPsoData2[std](void* data @ [esp + 4], uint32_t size @ [esp + 8])
size:
.data 0x00000000
data:
@@ -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 <VERS 0x8034445C 0x803457AC 0x80346CCC 0x80346A80 0x8034525C 0x803452A0 0x80346E4C 0x8034627C 0x801D9B30 0x801C7CFC 0x801C7D88 0x801C83FC>
@@ -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
@@ -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 <VERS 0x802AB3FC 0x802AC2A4 0x802AD3D0 0x802AD184 0x802ABDB8 0x802ABDFC 0x802AD338 0x802ACACC>
.data 0x00000004
@@ -54,5 +59,52 @@ start:
.float 0.1
.float 0.1
.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU
.data <VERS 0x0025BD09 0x0025BE29 0x0025B889 0x0025BC39 0x0025BFB9 0x0025BD29 0x0025BE59>
.data 0x00000004
.data 0x00FF0000
.data <VERS 0x0025BD17 0x0025BE37 0x0025B897 0x0025BC47 0x0025BFC7 0x0025BD37 0x0025BE67>
.data 0x00000004
.data 0x000000FF
.data <VERS 0x0025BD25 0x0025BE45 0x0025B8A5 0x0025BC55 0x0025BFD5 0x0025BD45 0x0025BE75>
.data 0x00000004
.data 0x00FFFF00
.data <VERS 0x005427A0 0x00542040 0x0053D788 0x0053DE00 0x00545320 0x005427A0 0x00542B40>
.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
@@ -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
@@ -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 <VERS 0x8020C158 0x80056D6C> # construct_dat_object_from_args
base_type_high:
.data 0xFFFF0000 # base_type, set_flags
floor_low:
@@ -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
@@ -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
@@ -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, ...);
@@ -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 <VERS 0x80350740 0x80351B44 0x803530A0 0x80352E54 0x803515F4 0x80351638 0x80353220 0x80352614>
.data 0x00000098
.address <VERS 0x80350740 0x80351B44 0x803530A0 0x80352E54 0x803515F4 0x80351638 0x80353220 0x80352614>
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 <VERS 0x4BE656A1 0x4BE646F1 0x4BE654CD 0x4BE634AD 0x4BE64BD9 0x4BE64B95 0x4BE63145 0x4BE6420D>
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 <VERS 0x00184160 0x00184350 0x00184400 0x00184340 0x00184310 0x00184360 0x001842D0>
.deltaof code_start, code_end
.address <VERS 0x00184160 0x00184350 0x00184400 0x00184340 0x00184310 0x00184360 0x001842D0>
@@ -63,7 +115,11 @@ next_stat:
pop esi
skip_all:
ret
code_end:
.all_versions
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x80350740 0x80351B44 0x803530A0 0x80352E54 0x803515F4 0x80351638 0x80353220 0x80352614>
.data 0x00000098
.address <VERS 0x80350740 0x80351B44 0x803530A0 0x80352E54 0x803515F4 0x80351638 0x80353220 0x80352614>
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 <VERS 0x4BE656A1 0x4BE646F1 0x4BE654CD 0x4BE634AD 0x4BE64BD9 0x4BE64B95 0x4BE63145 0x4BE6420D>
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
@@ -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 <VERS 0x8C01A454 0x8C01A6D0 0x8C01A414 0x8C01A6C8 0x8C01A6DC 0x8C01B6A4 0x8C01B6A4 0x8C01B684 0x8C01B6A4 0x8C01B6A8>
.data 0x00000002
mov r0, 0
.align 4
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 3SJT 3SJ0 3SE0 3SP0
.data <VERS 0x80134D3C 0x80134FA0 0x80135108 0x80135040 0x80134FE0 0x80134FE0 0x80135050 0x801352D0 0x80092C78 0x8009242C 0x80092380 0x80092588>
.data 0x00000004
li r3, 0
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x002C0AEE 0x002C167E 0x002C2BEE 0x002C272E 0x002C291E 0x002C275E 0x002C2A7E>
.data 0x00000004
xor ecx, ecx
jmp +3
.versions 59NJ 59NL
.data <VERS 0x007A1233 0x007A03F7>
.data 0x00000005
mov eax, 0
.all_versions
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x8C01A454 0x8C01A6D0 0x8C01A414 0x8C01A6C8 0x8C01A6DC 0x8C01B6A4 0x8C01B6A4 0x8C01B684 0x8C01B6A4 0x8C01B6A8>
.data 0x00000002
mov r0, 0
.align 4
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x80134D3C 0x80134FA0 0x80135108 0x80135040 0x80134FE0 0x80134FE0 0x80135050 0x801352D0 0x80092C78 0x8009242C 0x80092380 0x80092588>
.data 0x00000004
li r3, 0
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x002C0AEE 0x002C167E 0x002C2BEE 0x002C272E 0x002C291E 0x002C275E 0x002C2A7E>
.data 0x00000004
xor ecx, ecx
jmp +3
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x007A1233 0x007A03F7>
.data 0x00000005
mov eax, 0
.data 0x00000000
.data 0x00000000
+367
View File
@@ -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 - <VERS 0x3E08 0x3E08 0x3E08 0x3E08 0x3E00 0x3E00 0x3E00 0x3E00>]
fmuls f30, f30, f1
blr
hook2:
lfs f2, [r2 - <VERS 0x3E08 0x3E08 0x3E08 0x3E08 0x3E00 0x3E00 0x3E00 0x3E00>]
lfs f0, [r30 + 0x001C]
fmuls f0, f0, f2
blr
hook3:
lfs f28, [r2 - <VERS 0x3E08 0x3E08 0x3E08 0x3E08 0x3E00 0x3E00 0x3E00 0x3E00>]
fmuls f28, f28, f2
blr
hook4:
lfs f0, [r2 - <VERS 0x3E08 0x3E08 0x3E08 0x3E08 0x3E00 0x3E00 0x3E00 0x3E00>]
lfs f1, [r3 + 0x000C]
fmuls f0, f0, f1
stfs [r3 + 0x000C], f0
lis r3, <VERS 0x804C 0x804C 0x804D 0x804D 0x804C 0x804C 0x804D 0x804D>
blr
hook_end:
.data <VERS 0x801008E8 0x80100AD0 0x80100B74 0x80100A50 0x80100B8C 0x80100B8C 0x80100A60 0x80100C50>
.data 0x00000004
.address <VERS 0x801008E8 0x80100AD0 0x80100B74 0x80100A50 0x80100B8C 0x80100B8C 0x80100A60 0x80100C50>
bl hook1
.data <VERS 0x8015671C 0x80156AD0 0x80156C34 0x80156B94 0x80156AD8 0x80156AD8 0x80156BF8 0x801570BC>
.data 0x00000004
.address <VERS 0x8015671C 0x80156AD0 0x80156C34 0x80156B94 0x80156AD8 0x80156AD8 0x80156BF8 0x801570BC>
bl hook2
.data <VERS 0x801A1C64 0x801A203C 0x801A21A0 0x801A2100 0x801A2040 0x801A2040 0x801A2164 0x801A2628>
.data 0x00000004
.address <VERS 0x801A1C64 0x801A203C 0x801A21A0 0x801A2100 0x801A2040 0x801A2040 0x801A2164 0x801A2628>
bl hook3
.data <VERS 0x801A1E64 0x801A223C 0x801A23A0 0x801A2300 0x801A2240 0x801A2240 0x801A2364 0x801A2828>
.data 0x00000004
.address <VERS 0x801A1E64 0x801A223C 0x801A23A0 0x801A2300 0x801A2240 0x801A2240 0x801A2364 0x801A2828>
bl hook1
.data <VERS 0x80205044 0x802058B8 0x80206640 0x802063F4 0x80205840 0x80205840 0x80206728 0x80206124>
.data 0x00000004
.address <VERS 0x80205044 0x802058B8 0x80206640 0x802063F4 0x80205840 0x80205840 0x80206728 0x80206124>
bl hook4
.data <VERS 0x802057E8 0x8020605C 0x80206DE4 0x80206B98 0x80205FE4 0x80205FE4 0x80206ECC 0x802068C8>
.data 0x00000004
.address <VERS 0x802057E8 0x8020605C 0x80206DE4 0x80206B98 0x80205FE4 0x80205FE4 0x80206ECC 0x802068C8>
bl hook4
.data <VERS 0x805C83A8 0x805D29A8 0x805D9E48 0x805D9BE8 0x805C8CB0 0x805CFCD0 0x805D94F0 0x805D5730>
.data 0x00000004
.float 90000
.data <VERS 0x805C9254 0x805D3854 0x805DACF4 0x805DAA94 0x805C9B5C 0x805D0B7C 0x805DA39C 0x805D65DC>
.data 0x00000004
.float 62500
.data <VERS 0x805C987C 0x805D3E7C 0x805DB31C 0x805DB0BC 0x805CA184 0x805D11A4 0x805DA9C4 0x805D6C04>
.data 0x00000004
.float 640000
.data <VERS 0x805CA708 0x805D4D08 0x805DC1A8 0x805DBF48 0x805CB010 0x805D2030 0x805DB850 0x805D7A90>
.data 0x00000004
.float 90000
.data <VERS 0x805CAC98 0x805D5298 0x805DC738 0x805DC4D8 0x805CB5A0 0x805D25C0 0x805DBDE0 0x805D8020>
.data 0x00000004
.float 1400
.data 0x00000000
.data 0x00000000
.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU
start:
.include WriteCodeBlocks
.data <VERS 0x001737C2 0x001737D2 0x00173692 0x00173782 0x00173862 0x001737E2 0x00173792> # From 3OE1:80100B8C
.deltaof p1_1s, p1_1e
.address <VERS 0x001737C2 0x001737D2 0x00173692 0x00173782 0x00173862 0x001737E2 0x00173792> # From 3OE1:80100B8C
p1_1s:
call p1_2s
nop
p1_1e:
.data <VERS 0x00173A42 0x00173A52 0x00173912 0x00173A02 0x00173AE2 0x00173A62 0x00173A12>
.deltaof p1_2s, p1_2e
.address <VERS 0x00173A42 0x00173A52 0x00173912 0x00173A02 0x00173AE2 0x00173A62 0x00173A12>
p1_2s:
fld st0, dword [esp + 0x1C]
fadd st0, st0
fchs st0
ret
p1_2e:
.data <VERS 0x001A3DEF 0x001A3EEF 0x001A3BBF 0x001A3DBF 0x001A3FDF 0x001A3E0F 0x001A3ECF> # From 3OE1:80156AD8
.deltaof p2_1s, p2_1e
.address <VERS 0x001A3DEF 0x001A3EEF 0x001A3BBF 0x001A3DBF 0x001A3FDF 0x001A3E0F 0x001A3ECF> # From 3OE1:80156AD8
p2_1s:
call p2_2s
p2_1e:
.data <VERS 0x001A3E38 0x001A3F38 0x001A3C08 0x001A3E08 0x001A4028 0x001A3E58 0x001A3F18>
.deltaof p2_2s, p2_2e
.address <VERS 0x001A3E38 0x001A3F38 0x001A3C08 0x001A3E08 0x001A4028 0x001A3E58 0x001A3F18>
p2_2s:
fld st0, dword [ecx + 0x1C]
fadd st0, st0
fld st0, st1
ret
p2_2e:
.data <VERS 0x002D2DC8 0x002D3148 0x002D0E68 0x002D1A28 0x002D32F8 0x002D2DF8 0x002D31C8> # From 3OE1:801A2040
.deltaof p3_1s, p3_1e
.address <VERS 0x002D2DC8 0x002D3148 0x002D0E68 0x002D1A28 0x002D32F8 0x002D2DF8 0x002D31C8> # From 3OE1:801A2040
p3_1s:
call p3_2s
nop
p3_1e:
.data <VERS 0x002D2EA7 0x002D3227 0x002D0F47 0x002D1B07 0x002D33D7 0x002D2ED7 0x002D32A7>
.deltaof p3_2s, p3_2e
.address <VERS 0x002D2EA7 0x002D3227 0x002D0F47 0x002D1B07 0x002D33D7 0x002D2ED7 0x002D32A7>
p3_2s:
fld st0, dword [esp + 0x24]
fadd st0, st0
fchs st0
ret
p3_2e:
.data <VERS 0x00156AC8 0x002D32A8 0x001569E8 0x00156A78 0x00156AB8 0x00156AE8 0x002D3328> # From 3OE1:801A2240
.deltaof p4_1s, p4_1e
.address <VERS 0x00156AC8 0x002D32A8 0x001569E8 0x00156A78 0x00156AB8 0x00156AE8 0x002D3328> # From 3OE1:801A2240
p4_1s:
call p4_2s
nop
p4_1e:
.data <VERS 0x00156C44 0x002D33B4 0x00156B64 0x00156BF4 0x00156C34 0x00156C64 0x002D3434>
.deltaof p4_2s, p4_2e
.address <VERS 0x00156C44 0x002D33B4 0x00156B64 0x00156BF4 0x00156C34 0x00156C64 0x002D3434>
p4_2s:
fld st0, dword [esp + 0x28]
fadd st0, st0
fchs st0
ret
p4_2e:
.data <VERS 0x002E2B93 0x002E2E8C 0x002E0C33 0x002E17B3 0x002E2E6C 0x002E2BC3 0x002E2EBC> # From 3OE1:80205840
.deltaof p5_1s, p5_1e
.address <VERS 0x002E2B93 0x002E2E8C 0x002E0C33 0x002E17B3 0x002E2E6C 0x002E2BC3 0x002E2EBC> # From 3OE1:80205840
p5_1s:
call p5_3s
p5_1e:
.data <VERS 0x002E1FD1 0x002E2404 0x002E0071 0x002E0BF1 0x002E23E4 0x002E2001 0x002E2434> # From 3OE1:80205FE4
.deltaof p5_2s, p5_2e
.address <VERS 0x002E1FD1 0x002E2404 0x002E0071 0x002E0BF1 0x002E23E4 0x002E2001 0x002E2434> # From 3OE1:80205FE4
p5_2s:
call p5_3s
p5_2e:
.data <VERS 0x002E2C82 0x002E2FD1 0x002E0D22 0x002E18A2 0x002E2FB1 0x002E2CB2 0x002E3001>
.deltaof p5_3s, p5_3e
.address <VERS 0x002E2C82 0x002E2FD1 0x002E0D22 0x002E18A2 0x002E2FB1 0x002E2CB2 0x002E3001>
p5_3s:
fld st0, dword [eax + 0x0C]
fadd st0, st0
fstp dword [eax + 0x0C], st0
mov eax, [<VERS 0x0053A9CC 0x0053A26C 0x00535BAC 0x0053622C 0x0053D54C 0x0053A9CC 0x0053AD6C>]
ret
p5_3e:
.data <VERS 0x004920A0 0x00491940 0x0048D4F0 0x0048DC88 0x00494C30 0x004920A8 0x00492440> # From 3OE1:805CFCD0
.data 0x00000004
.data 0x47AFC800
.data <VERS 0x0042D0A0 0x0042C940 0x00428DC0 0x00429130 0x0042C940 0x0042D0C0 0x0042D450> # From 3OE1:805D0B7C
.data 0x00000004
.data 0x437A0000
.data <VERS 0x0049222C 0x00491ACC 0x0048D67C 0x0048DE14 0x00494DBC 0x00492234 0x004925CC> # From 3OE1:805D11A4
.data 0x00000004
.data 0x491C4000
.data <VERS 0x0042B838 0x0042B0D8 0x00427558 0x004278C8 0x0042B0D8 0x0042B858 0x0042BBE8> # From 3OE1:805D2030
.data 0x00000004
.data 0x47AFC800
.data <VERS 0x001D9736 0x001D9936 0x001D95F6 0x001D9746 0x001D9BC6 0x001D9756 0x001D98A6> # From 3OE1:805D25C0
.data 0x00000004
.data 0x44AF0000
.data <VERS 0x001D9748 0x001D9948 0x001D9608 0x001D9758 0x001D9BD8 0x001D9768 0x001D98B8> # 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 [<VERS 0x0097D198 0x0097F1B8>], eax # This affects mostly static map objects
mov [<VERS 0x0097D19C 0x0097F1BC>], eax
mov [<VERS 0x0097D1A0 0x0097F1C0>], eax
mov ax, 0x9090
mov [<VERS 0x00689BC7 0x00689B5B>], ax # Players draw distance 10000.0f always
mov eax, 0x41000000 # Use newly acquired skipped branch room
mov [<VERS 0x00689BD1 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 <VERS 0x005C525B 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 [<VERS 0x00689BD1 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 <VERS 0x007BB21E 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 [<VERS 0x00689BD1 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 [<VERS 0x007BB21F 0x007BA473>]
add eax, 0x002A1C74
mov dword [<VERS 0x00518843 0x005187FF>], eax
mov byte [<VERS 0x00518842 0x005187FE>], 0xE8
mov dword [<VERS 0x00518847 0x00518803>], 0x90909090
ret
# TOComputerMachine01
patch_func_4:
pop ecx
push 7
push <VERS 0x00616FF4 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 [<VERS 0x00689BD1 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 <VERS 0x006439A8 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 [<VERS 0x00689BD1 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 <VERS 0x0065B959 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 [<VERS 0x00689BD1 0x00689B65>]
fld st0, dword [esp + 0x30]
fmulp st1, st0
ret
patch_code_end6:
push ecx
jmp write_call_func
@@ -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 - <VERS 0x3E08 0x3E08 0x3E08 0x3E08 0x3E00 0x3E00 0x3E00 0x3E00>]
fmuls f30, f30, f1
blr
hook2:
lfs f2, [r2 - <VERS 0x3E08 0x3E08 0x3E08 0x3E08 0x3E00 0x3E00 0x3E00 0x3E00>]
lfs f0, [r30 + 0x001C]
fmuls f0, f0, f2
blr
hook3:
lfs f28, [r2 - <VERS 0x3E08 0x3E08 0x3E08 0x3E08 0x3E00 0x3E00 0x3E00 0x3E00>]
fmuls f28, f28, f2
blr
hook4:
lfs f0, [r2 - <VERS 0x3E08 0x3E08 0x3E08 0x3E08 0x3E00 0x3E00 0x3E00 0x3E00>]
lfs f1, [r3 + 0x000C]
fmuls f0, f0, f1
stfs [r3 + 0x000C], f0
lis r3, <VERS 0x804C 0x804C 0x804D 0x804D 0x804C 0x804C 0x804D 0x804D>
blr
hook_end:
.data <VERS 0x801008E8 0x80100AD0 0x80100B74 0x80100A50 0x80100B8C 0x80100B8C 0x80100A60 0x80100C50>
.data 0x00000004
.address <VERS 0x801008E8 0x80100AD0 0x80100B74 0x80100A50 0x80100B8C 0x80100B8C 0x80100A60 0x80100C50>
bl hook1
.data <VERS 0x8015671C 0x80156AD0 0x80156C34 0x80156B94 0x80156AD8 0x80156AD8 0x80156BF8 0x801570BC>
.data 0x00000004
.address <VERS 0x8015671C 0x80156AD0 0x80156C34 0x80156B94 0x80156AD8 0x80156AD8 0x80156BF8 0x801570BC>
bl hook2
.data <VERS 0x801A1C64 0x801A203C 0x801A21A0 0x801A2100 0x801A2040 0x801A2040 0x801A2164 0x801A2628>
.data 0x00000004
.address <VERS 0x801A1C64 0x801A203C 0x801A21A0 0x801A2100 0x801A2040 0x801A2040 0x801A2164 0x801A2628>
bl hook3
.data <VERS 0x801A1E64 0x801A223C 0x801A23A0 0x801A2300 0x801A2240 0x801A2240 0x801A2364 0x801A2828>
.data 0x00000004
.address <VERS 0x801A1E64 0x801A223C 0x801A23A0 0x801A2300 0x801A2240 0x801A2240 0x801A2364 0x801A2828>
bl hook1
.data <VERS 0x80205044 0x802058B8 0x80206640 0x802063F4 0x80205840 0x80205840 0x80206728 0x80206124>
.data 0x00000004
.address <VERS 0x80205044 0x802058B8 0x80206640 0x802063F4 0x80205840 0x80205840 0x80206728 0x80206124>
bl hook4
.data <VERS 0x802057E8 0x8020605C 0x80206DE4 0x80206B98 0x80205FE4 0x80205FE4 0x80206ECC 0x802068C8>
.data 0x00000004
.address <VERS 0x802057E8 0x8020605C 0x80206DE4 0x80206B98 0x80205FE4 0x80205FE4 0x80206ECC 0x802068C8>
bl hook4
.data <VERS 0x805C83A8 0x805D29A8 0x805D9E48 0x805D9BE8 0x805C8CB0 0x805CFCD0 0x805D94F0 0x805D5730>
.data 0x00000004
.float 90000
.data <VERS 0x805C9254 0x805D3854 0x805DACF4 0x805DAA94 0x805C9B5C 0x805D0B7C 0x805DA39C 0x805D65DC>
.data 0x00000004
.float 62500
.data <VERS 0x805C987C 0x805D3E7C 0x805DB31C 0x805DB0BC 0x805CA184 0x805D11A4 0x805DA9C4 0x805D6C04>
.data 0x00000004
.float 640000
.data <VERS 0x805CA708 0x805D4D08 0x805DC1A8 0x805DBF48 0x805CB010 0x805D2030 0x805DB850 0x805D7A90>
.data 0x00000004
.float 90000
.data <VERS 0x805CAC98 0x805D5298 0x805DC738 0x805DC4D8 0x805CB5A0 0x805D25C0 0x805DBDE0 0x805D8020>
.data 0x00000004
.float 1400
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x001737C2 0x001737D2 0x00173692 0x00173782 0x00173862 0x001737E2 0x00173792> # From 3OE1:80100B8C
.deltaof p1_1s, p1_1e
.address <VERS 0x001737C2 0x001737D2 0x00173692 0x00173782 0x00173862 0x001737E2 0x00173792> # From 3OE1:80100B8C
p1_1s:
call p1_2s
nop
p1_1e:
.data <VERS 0x00173A42 0x00173A52 0x00173912 0x00173A02 0x00173AE2 0x00173A62 0x00173A12>
.deltaof p1_2s, p1_2e
.address <VERS 0x00173A42 0x00173A52 0x00173912 0x00173A02 0x00173AE2 0x00173A62 0x00173A12>
p1_2s:
fld st0, dword [esp + 0x1C]
fadd st0, st0
fchs st0
ret
p1_2e:
.data <VERS 0x001A3DEF 0x001A3EEF 0x001A3BBF 0x001A3DBF 0x001A3FDF 0x001A3E0F 0x001A3ECF> # From 3OE1:80156AD8
.deltaof p2_1s, p2_1e
.address <VERS 0x001A3DEF 0x001A3EEF 0x001A3BBF 0x001A3DBF 0x001A3FDF 0x001A3E0F 0x001A3ECF> # From 3OE1:80156AD8
p2_1s:
call p2_2s
p2_1e:
.data <VERS 0x001A3E38 0x001A3F38 0x001A3C08 0x001A3E08 0x001A4028 0x001A3E58 0x001A3F18>
.deltaof p2_2s, p2_2e
.address <VERS 0x001A3E38 0x001A3F38 0x001A3C08 0x001A3E08 0x001A4028 0x001A3E58 0x001A3F18>
p2_2s:
fld st0, dword [ecx + 0x1C]
fadd st0, st0
fld st0, st1
ret
p2_2e:
.data <VERS 0x002D2DC8 0x002D3148 0x002D0E68 0x002D1A28 0x002D32F8 0x002D2DF8 0x002D31C8> # From 3OE1:801A2040
.deltaof p3_1s, p3_1e
.address <VERS 0x002D2DC8 0x002D3148 0x002D0E68 0x002D1A28 0x002D32F8 0x002D2DF8 0x002D31C8> # From 3OE1:801A2040
p3_1s:
call p3_2s
nop
p3_1e:
.data <VERS 0x002D2EA7 0x002D3227 0x002D0F47 0x002D1B07 0x002D33D7 0x002D2ED7 0x002D32A7>
.deltaof p3_2s, p3_2e
.address <VERS 0x002D2EA7 0x002D3227 0x002D0F47 0x002D1B07 0x002D33D7 0x002D2ED7 0x002D32A7>
p3_2s:
fld st0, dword [esp + 0x24]
fadd st0, st0
fchs st0
ret
p3_2e:
.data <VERS 0x00156AC8 0x002D32A8 0x001569E8 0x00156A78 0x00156AB8 0x00156AE8 0x002D3328> # From 3OE1:801A2240
.deltaof p4_1s, p4_1e
.address <VERS 0x00156AC8 0x002D32A8 0x001569E8 0x00156A78 0x00156AB8 0x00156AE8 0x002D3328> # From 3OE1:801A2240
p4_1s:
call p4_2s
nop
p4_1e:
.data <VERS 0x00156C44 0x002D33B4 0x00156B64 0x00156BF4 0x00156C34 0x00156C64 0x002D3434>
.deltaof p4_2s, p4_2e
.address <VERS 0x00156C44 0x002D33B4 0x00156B64 0x00156BF4 0x00156C34 0x00156C64 0x002D3434>
p4_2s:
fld st0, dword [esp + 0x28]
fadd st0, st0
fchs st0
ret
p4_2e:
.data <VERS 0x002E2B93 0x002E2E8C 0x002E0C33 0x002E17B3 0x002E2E6C 0x002E2BC3 0x002E2EBC> # From 3OE1:80205840
.deltaof p5_1s, p5_1e
.address <VERS 0x002E2B93 0x002E2E8C 0x002E0C33 0x002E17B3 0x002E2E6C 0x002E2BC3 0x002E2EBC> # From 3OE1:80205840
p5_1s:
call p5_3s
p5_1e:
.data <VERS 0x002E1FD1 0x002E2404 0x002E0071 0x002E0BF1 0x002E23E4 0x002E2001 0x002E2434> # From 3OE1:80205FE4
.deltaof p5_2s, p5_2e
.address <VERS 0x002E1FD1 0x002E2404 0x002E0071 0x002E0BF1 0x002E23E4 0x002E2001 0x002E2434> # From 3OE1:80205FE4
p5_2s:
call p5_3s
p5_2e:
.data <VERS 0x002E2C82 0x002E2FD1 0x002E0D22 0x002E18A2 0x002E2FB1 0x002E2CB2 0x002E3001>
.deltaof p5_3s, p5_3e
.address <VERS 0x002E2C82 0x002E2FD1 0x002E0D22 0x002E18A2 0x002E2FB1 0x002E2CB2 0x002E3001>
p5_3s:
fld st0, dword [eax + 0x0C]
fadd st0, st0
fstp dword [eax + 0x0C], st0
mov eax, [<VERS 0x0053A9CC 0x0053A26C 0x00535BAC 0x0053622C 0x0053D54C 0x0053A9CC 0x0053AD6C>]
ret
p5_3e:
.data <VERS 0x004920A0 0x00491940 0x0048D4F0 0x0048DC88 0x00494C30 0x004920A8 0x00492440> # From 3OE1:805CFCD0
.data 0x00000004
.data 0x47AFC800
.data <VERS 0x0042D0A0 0x0042C940 0x00428DC0 0x00429130 0x0042C940 0x0042D0C0 0x0042D450> # From 3OE1:805D0B7C
.data 0x00000004
.data 0x437A0000
.data <VERS 0x0049222C 0x00491ACC 0x0048D67C 0x0048DE14 0x00494DBC 0x00492234 0x004925CC> # From 3OE1:805D11A4
.data 0x00000004
.data 0x491C4000
.data <VERS 0x0042B838 0x0042B0D8 0x00427558 0x004278C8 0x0042B0D8 0x0042B858 0x0042BBE8> # From 3OE1:805D2030
.data 0x00000004
.data 0x47AFC800
.data <VERS 0x001D9736 0x001D9936 0x001D95F6 0x001D9746 0x001D9BC6 0x001D9756 0x001D98A6> # From 3OE1:805D25C0
.data 0x00000004
.data 0x44AF0000
.data <VERS 0x001D9748 0x001D9948 0x001D9608 0x001D9758 0x001D9BD8 0x001D9768 0x001D98B8> # From 3OE1:805D25C0
.data 0x00000004
.data 0x44AF0000
.data 0x00000000
.data 0x00000000
@@ -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
@@ -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
@@ -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<float>(max<int32_t>(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<int16_t>(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<int32_t>(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<int16_t>(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<float>(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
@@ -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 [<VERS 0x00A0DC30 0x00A0FC30>], 0x000600E4 # subcommand=0xE4, flags=6
push <VERS 0x00A0DC34 0x00A0FC34>
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 [<VERS 0x00AA8DFC 0x00AAB27C>], 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, [<VERS 0x00AADE38 0x00AB02B8>] # 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, <VERS 0x00801150 0x008003E0>
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 <VERS 0x00775224 0x00774448> # 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 <VERS 0x00778063 0x00777287> # TObjectV00b441c0::subtract_hp_if_not_in_state_2
push 5
push 0x00776CD6 # TObjectV00b441c0::v19_handle_hit_special_effects
push <VERS 0x00777AB2 0x00776CD6> # TObjectV00b441c0::v19_handle_hit_special_effects
push 5
push 0x00776D4F # TObjectV00b441c0::v19_handle_hit_special_effects
push <VERS 0x00777B2B 0x00776D4F> # TObjectV00b441c0::v19_handle_hit_special_effects
push 5
push 0x00776E20 # TObjectV00b441c0::v19_handle_hit_special_effects
push <VERS 0x00777BFC 0x00776E20> # TObjectV00b441c0::v19_handle_hit_special_effects
push 5
push 0x00776E99 # TObjectV00b441c0::v19_handle_hit_special_effects
push <VERS 0x00777C75 0x00776E99> # TObjectV00b441c0::v19_handle_hit_special_effects
push 5
push 0x00775F51 # TObjectV00b441c0::v19_handle_hit_special_effects
push <VERS 0x00776D2D 0x00775F51> # TObjectV00b441c0::v19_handle_hit_special_effects
push 5
push 0x00775BE6 # TObjectV00b441c0::v19_handle_hit_special_effects
push <VERS 0x007769C2 0x00775BE6> # TObjectV00b441c0::v19_handle_hit_special_effects
push 5
push 0x00775A60 # TObjectV00b441c0::v19_handle_hit_special_effects
push <VERS 0x0077683C 0x00775A60> # TObjectV00b441c0::v19_handle_hit_special_effects
push 5
push 0x00775726 # TObjectV00b441c0::v19_handle_hit_special_effects (Devil's/Demon's)
push <VERS 0x00776502 0x00775726> # TObjectV00b441c0::v19_handle_hit_special_effects (Devil's/Demon's)
push 5
push 0x00774D7B # TObjectV00b441c0::v18_accept_hit
push <VERS 0x00775B57 0x00774D7B> # TObjectV00b441c0::v18_accept_hit
push 5
push 0x00774C47 # TObjectV00b441c0::v18_accept_hit
push <VERS 0x00775A23 0x00774C47> # TObjectV00b441c0::v18_accept_hit
push 5
push 0x00774A14 # TObjectV00b441c0::v18_accept_hit
push <VERS 0x007757F0 0x00774A14> # TObjectV00b441c0::v18_accept_hit
push 5
push 0x0077482A # TObjectV00b441c0::v18_accept_hit
push <VERS 0x00775606 0x0077482A> # TObjectV00b441c0::v18_accept_hit
push 5
push 0x007746E0 # TObjectV00b441c0::v18_accept_hit
push <VERS 0x007754BC 0x007746E0> # TObjectV00b441c0::v18_accept_hit
push 5
push 0x00774061 # TObjectV00b441c0::v18_accept_hit
push <VERS 0x00774E3D 0x00774061> # TObjectV00b441c0::v18_accept_hit
push 5
push 0x00773EFA # TObjectV00b441c0::v18_accept_hit
push <VERS 0x00774CD6 0x00773EFA> # TObjectV00b441c0::v18_accept_hit
push 5
push 0x00773937 # TObjectV00b441c0::v17
push <VERS 0x00774713 0x00773937> # 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 [<VERS 0x00AA8DFC 0x00AAB27C>], 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, [<VERS 0x00AADE38 0x00AB02B8>] # 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], <VERS 0x00775229 0x0077444D> # 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], <VERS 0x00776507 0x0077572B> # 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, [<VERS 0x00AA8E04 0x00AAB284>]
mov edx, <VERS 0x007D4CBC 0x007D3F38>
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, <VERS 0x007781F0 0x00777414> # subtract_hp
mov edx, <VERS 0x007781B0 0x007773D4> # add_hp
cmp dword [esp], <VERS 0x00775229 0x0077444D> # 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 <VERS 0x0078864B 0x0078781F>
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 [<VERS 0x00AA8DFC 0x00AAB27C>], 0x80
jz on_6x0A_patch_skip_write
mov [esp + 0x0A], cx
on_6x0A_patch_skip_write:
@@ -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, <VERS 0x8001143C 0x8001145C 0x800116EC 0x8001140C 0x80011454 0x80011454 0x8001140C 0x80011484>
.label TObjectV8047c128_subtract_hp, <VERS 0x8001147C 0x8001149C 0x8001172C 0x8001144C 0x80011494 0x80011494 0x8001144C 0x800114C4>
@@ -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 <VERS 0x002A60CA 0x002A6BAA 0x002A807A 0x002A7B0A 0x002A7CEA 0x002A7B2A 0x002A7DAA>
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
@@ -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 <VERS 0x0073197D 0x007318DD>
.data 6
.binary 81E2FDFFFFFF
@@ -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, <VERS 0x80395EFC 0x80398904 0x8039A7A4 0x8039A554 0x803971CC 0x80397224 0x8039A924 0x80399414>
@@ -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 <VERS 0x0026B063 0x0026B193 0x0026ABA3 0x0026AF13 0x0026B2F3 0x0026B083 0x0026B193>
.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 <VERS 0x002DB050 0x002DB550 0x002D90E0 0x002D9CB0 0x002DB580 0x002DB080 0x002DB5D0>
.deltaof new_code_start, new_code_end
new_code_start:
@@ -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."
@@ -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:
@@ -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 <VERS 0x8012FAD0 0x8012FA4C 0x8012FED8>
@@ -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 <VERS 0x80487140 0x80487E80 0x8048A260>
@@ -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
+62
View File
@@ -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 <VERS 0x8C4ED300 0x8C4E6DA0 0x8C4ED300 0x8C4DC800>
.data <VERS 0x8C4ED344 0x8C4E6DE4 0x8C4ED344 0x8C4DC844>
.data <VERS 0x8C4E8D88 0x8C4E2828 0x8C4E8D88 0x8C4D8288>
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
start:
li r0, 0
stw [r13 - <VERS 0x4760 0x4758 0x4738 0x4738 0x4748 0x4748 0x4728 0x46E8>], r0
stw [r13 - <VERS 0x475C 0x4754 0x4734 0x4734 0x4744 0x4744 0x4724 0x46E4>], r0
li r0, 1
sth [r13 - <VERS 0x4950 0x4948 0x4928 0x4928 0x4938 0x4938 0x4918 0x48D8>], r0
blr
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
start:
xor eax, eax
mov [<VERS 0x0062D374 0x0062D914 0x0063544C 0x00632934 0x006321CC 0x00632934 0x00632CCC>], eax # is_in_quest = false
mov [<VERS 0x0062D370 0x0062D910 0x00635448 0x00632930 0x006321C8 0x00632930 0x00632CC8>], eax # dat_source_type = NONE
inc eax
mov [<VERS 0x0071E8E8 0x0071EF48 0x00726A88 0x00723F88 0x00723808 0x00723F88 0x00724308>], ax # should_leave_game = true
ret
.versions 59NJ 59NL
start:
xor eax, eax
mov [<VERS 0x00A931A4 0x00A95624>], eax # is_in_quest = false
mov [<VERS 0x00A93160 0x00A955E0>], eax # dat_source_type = NONE
inc eax
mov [<VERS 0x00AAC254 0x00AAE6D4>], ax # should_leave_game = true
ret
@@ -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 <VERS 0x8C4ED300 0x8C4E6DA0 0x8C4ED300 0x8C4DC800>
.data <VERS 0x8C4ED344 0x8C4E6DE4 0x8C4ED344 0x8C4DC844>
.data <VERS 0x8C4E8D88 0x8C4E2828 0x8C4E8D88 0x8C4D8288>
@@ -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 - <VERS 0x4760 0x4758 0x4738 0x4738 0x4748 0x4748 0x4728 0x46E8>], r0
stw [r13 - <VERS 0x475C 0x4754 0x4734 0x4734 0x4744 0x4744 0x4724 0x46E4>], r0
li r0, 1
sth [r13 - <VERS 0x4950 0x4948 0x4928 0x4928 0x4938 0x4938 0x4918 0x48D8>], r0
blr
@@ -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 [<VERS 0x0062D374 0x0062D914 0x0063544C 0x00632934 0x006321CC 0x00632934 0x00632CCC>], eax # is_in_quest = false
mov [<VERS 0x0062D370 0x0062D910 0x00635448 0x00632930 0x006321C8 0x00632930 0x00632CC8>], eax # dat_source_type = NONE
inc eax
mov [<VERS 0x0071E8E8 0x0071EF48 0x00726A88 0x00723F88 0x00723808 0x00723F88 0x00724308>], ax # should_leave_game = true
ret
@@ -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 [<VERS 0x00A931A4 0x00A95624>], eax # is_in_quest = false
mov [<VERS 0x00A93160 0x00A955E0>], eax # dat_source_type = NONE
inc eax
mov [<VERS 0x00AAC254 0x00AAE6D4>], ax # should_leave_game = true
ret
@@ -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 <VERS 0x804CA3D8 0x804CDB28 0x804D0078 0x804CFE18 0x804CABA8 0x804CB088 0x804CF6F0 0x804CFAE0>
.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
+96
View File
@@ -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 <VERS 0x8C15B0CA 0x8C162302 0x8C175E66 0x8C1780AE 0x8C17600E 0x8C17863E 0x8C1783FA 0x8C19BD4A 0x8C19BD4A 0x8C19ADB6 0x8C19BD4A 0x8C19B7E2>
.data 0x00000002
mov r1, 1
.align 4
.data <VERS 0x8C15B0E6 0x8C16231E 0x8C175E82 0x8C1780CA 0x8C17602A 0x8C17865A 0x8C178416 0x8C19BD66 0x8C19BD66 0x8C19ADD2 0x8C19BD66 0x8C19B7FE>
.data 0x00000002
nop
.align 4
.versions 3OJT 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x8026FAE8 0x8021F8CC 0x80220250 0x80221154 0x80220EF0 0x80220170 0x80220170 0x80221224 0x80220ABC>
.data 4
li r0, 1
.data <VERS 0x8026FB10 0x8021F8F4 0x80220278 0x8022117C 0x80220F18 0x80220198 0x80220198 0x8022124C 0x80220AE4>
.data 4
nop
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x0023EC5C 0x0023EEAC 0x0023F21C 0x0023EF3C 0x0023F0BC 0x0023EF5C 0x0023F14C>
.deltaof patch1_start, patch1_end
patch1_start:
mov dword [ebp + 0x14C], 1
patch1_end:
.data <VERS 0x0023EC77 0x0023EEC7 0x0023F237 0x0023EF57 0x0023F0D7 0x0023EF77 0x0023F167>
.deltaof patch2_start, patch2_end
patch2_start:
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
patch2_end:
.versions 59NJ 59NL
.data <VERS 0x006DA14B 0x006DA113>
.deltaof patch1_start, patch1_end
patch1_start:
mov dword [edi + 0x14C], 1
patch1_end:
.data <VERS 0x006DA168 0x006DA130>
.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
@@ -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 <VERS 0x8C15B0CA 0x8C162302 0x8C175E66 0x8C1780AE 0x8C17600E 0x8C17863E 0x8C1783FA 0x8C19BD4A 0x8C19BD4A 0x8C19ADB6 0x8C19BD4A 0x8C19B7E2>
.data 0x00000002
mov r1, 1
.align 4
.data <VERS 0x8C15B0E6 0x8C16231E 0x8C175E82 0x8C1780CA 0x8C17602A 0x8C17865A 0x8C178416 0x8C19BD66 0x8C19BD66 0x8C19ADD2 0x8C19BD66 0x8C19B7FE>
.data 0x00000002
nop
.align 4
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x8026FAE8 0x8021F8CC 0x80220250 0x80221154 0x80220EF0 0x80220170 0x80220170 0x80221224 0x80220ABC>
.data 4
li r0, 1
.data <VERS 0x8026FB10 0x8021F8F4 0x80220278 0x8022117C 0x80220F18 0x80220198 0x80220198 0x8022124C 0x80220AE4>
.data 4
nop
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x0023EC5C 0x0023EEAC 0x0023F21C 0x0023EF3C 0x0023F0BC 0x0023EF5C 0x0023F14C>
.deltaof patch1_start, patch1_end
patch1_start:
mov dword [ebp + 0x14C], 1
patch1_end:
.data <VERS 0x0023EC77 0x0023EEC7 0x0023F237 0x0023EF57 0x0023F0D7 0x0023EF77 0x0023F167>
.deltaof patch2_start, patch2_end
patch2_start:
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
patch2_end:
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x006DA14B 0x006DA113>
.deltaof patch1_start, patch1_end
patch1_start:
mov dword [edi + 0x14C], 1
patch1_end:
.data <VERS 0x006DA168 0x006DA130>
.deltaof patch2_start, patch2_end
patch2_start:
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
patch2_end:
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x8026DF94 0x8026EC58 0x8026FCB4 0x8026FA68 0x8026E7F4 0x8026E7F4 0x8026FC1C 0x8026F464>
.data 4
.address <VERS 0x8026DF94 0x8026EC58 0x8026FCB4 0x8026FA68 0x8026E7F4 0x8026E7F4 0x8026FC1C 0x8026F464>
bl code_start
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x0027782A 0x00277ADA 0x00277EEA 0x00277C0A 0x00277D2A 0x00277C2A 0x00277DFA>
.data 5
@@ -58,5 +100,9 @@ skip:
jmp edx
code_end:
.all_versions
.data 0
.data 0
@@ -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 <VERS 0x8026DF94 0x8026EC58 0x8026FCB4 0x8026FA68 0x8026E7F4 0x8026E7F4 0x8026FC1C 0x8026F464>
.data 4
.address <VERS 0x8026DF94 0x8026EC58 0x8026FCB4 0x8026FA68 0x8026E7F4 0x8026E7F4 0x8026FC1C 0x8026F464>
bl code_start
.data 0
.data 0
@@ -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 <VERS 0x0025BD09 0x0025BE29 0x0025B889 0x0025BC39 0x0025BFB9 0x0025BD29 0x0025BE59>
.data 0x00000004
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, <VERS 0x800336AC 0x800336DC 0x800336F8 0x8003368C 0x800338CC 0x800338CC 0x80033894 0x8003390C>
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 - <VERS 0x5298 0x5290 0x5270 0x5270 0x5280 0x5280 0x5260 0x5220>] # local_client_id
cmpl r4, r5
bnelr
lis r3, 0x0002
ori r3, r3, 0x2825
li r4, 0
b play_sound
code_end:
.data <VERS 0x80110D94 0x80110F94 0x80111080 0x80110F20 0x80111038 0x80111038 0x80110F30 0x80111114>
.data 0x00000004
.address <VERS 0x80110D94 0x80110F94 0x80111080 0x80110F20 0x80111038 0x80111038 0x80110F30 0x80111114>
b code_start
.data 0x00000000
.data 0x00000000
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
start:
.include WriteCodeBlocks
.data <VERS 0x00180EF5 0x00181075 0x00181125 0x00181065 0x00181095 0x00181085 0x00181055>
.data 0x0A
@@ -81,3 +119,36 @@ hook6_end:
.data 0x00000000
.data 0x00000000
.versions 59NJ 59NL
start:
pop ecx
push 6
push <VERS 0x005D91BE 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 [<VERS 0x00A9A074 0x00A9C4F4>], eax
jne patch_code_skip_sound
push 0
push 0
push 0
push 0xAC
mov eax, <VERS 0x00815020 0x00814298>
call eax
add esp, 0x10
patch_code_skip_sound:
ret
patch_code_end:
push ecx
.include WriteCallToCode
@@ -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, <VERS 0x800336AC 0x800336DC 0x800336F8 0x8003368C 0x800338CC 0x800338CC 0x80033894 0x8003390C>
.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 - <VERS 0x5298 0x5290 0x5270 0x5270 0x5280 0x5280 0x5260 0x5220>] # local_client_id
cmpl r4, r5
bnelr
lis r3, 0x0002
ori r3, r3, 0x2825
li r4, 0
b play_sound
code_end:
.data <VERS 0x80110D94 0x80110F94 0x80111080 0x80110F20 0x80111038 0x80111038 0x80110F30 0x80111114>
.data 0x00000004
.address <VERS 0x80110D94 0x80110F94 0x80111080 0x80110F20 0x80111038 0x80111038 0x80110F30 0x80111114>
b code_start
.data 0x00000000
.data 0x00000000
@@ -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
@@ -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
+45
View File
@@ -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 <VERS 0x8C1AADD8 0x8C1C7408 0x8C1C9E9C 0x8C1C75B4 0x8C1CA49C 0x8C1CA240 0x8C1F27E8 0x8C1F27E8 0x8C1F17F0 0x8C1F27E8 0x8C1F2354>
.data 0x00000004
rets
nop
.align 4
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x80114F04 0x80115118 0x8011521C 0x801150B0 0x801151A8 0x801151A8 0x801150C0 0x80115298>
.data 0x00000004
.data 0x480000D4
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
.data <VERS 0x001837C1 0x00183951 0x00183A01 0x00183941 0x00183971 0x00183961 0x00183931>
.data 0x00000002
.binary 90E9
.all_versions
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x8C1AADD8 0x8C1C7408 0x8C1C9E9C 0x8C1C75B4 0x8C1CA49C 0x8C1CA240 0x8C1F27E8 0x8C1F27E8 0x8C1F17F0 0x8C1F27E8 0x8C1F2354>
.data 0x00000004
rets
nop
.align 4
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x80114F04 0x80115118 0x8011521C 0x801150B0 0x801151A8 0x801151A8 0x801150C0 0x80115298>
.data 0x00000004
.data 0x480000D4
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x001837C1 0x00183951 0x00183A01 0x00183941 0x00183971 0x00183961 0x00183931>
.data 0x00000002
.binary 90E9
.data 0x00000000
.data 0x00000000
@@ -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 <VERS 0x8C0254B2 0x8C0254BE 0x8C0254D2>
.data 4
bs +0x38
nop
.align 4
.data <VERS 0x8C150B2C 0x8C150F9C 0x8C150D58>
.data 2
sett
.align 4
.data <VERS 0x8C15F346 0x8C15F856 0x8C15F612>
.data 2
and r0, 0xFE
.align 4
.data <VERS 0x8C16053A 0x8C160A4A 0x8C160806>
.data 2
and r0, 0xFE
.align 4
.data <VERS 0x8C1617DA 0x8C161D6A 0x8C161B26>
.data 2
and r0, 0xFE
.align 4
.data <VERS 0x8C15F3BA 0x8C15F8CA 0x8C15F686>
.data 2
nop
.align 4
.data <VERS 0x8C1605A6 0x8C160AB6 0x8C160872>
.data 2
nop
.align 4
.data <VERS 0x8C161808 0x8C161D98 0x8C161B54>
.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 <VERS 0x8C0280AA 0x8C0280AA 0x8C028276 0x8C0280AA 0x8C0280AA>
.data 6
nop
bs +0x2C
nop
.align 4
.data <VERS 0x8C16BDFE 0x8C16BDFE 0x8C16B50A 0x8C16BDFE 0x8C16BA22>
.data 2
sett
.align 4
.data <VERS 0x8C17F1DC 0x8C17F1DC 0x8C17E738 0x8C17F1DC 0x8C17EC74>
.data 2
and r0, 0xFE
.align 4
.data <VERS 0x8C17F2BA 0x8C17F2BA 0x8C17E816 0x8C17F2BA 0x8C17ED52>
.data 2
nop
.align 4
.data <VERS 0x8C180D0A 0x8C180D0A 0x8C18005A 0x8C180D0A 0x8C1807A2>
.data 2
and r0, 0xFE
.align 4
.data <VERS 0x8C180DB0 0x8C180DB0 0x8C180100 0x8C180DB0 0x8C180848>
.data 2
nop
.align 4
.data <VERS 0x8C181BC4 0x8C181BC4 0x8C180EC8 0x8C181BC4 0x8C18165C>
.data 2
and r0, 0xFE
.align 4
.data <VERS 0x8C181C92 0x8C181C92 0x8C180F96 0x8C181C92 0x8C18172A>
.data 2
nop
.align 4
.data <VERS 0x8C182BC6 0x8C182BC6 0x8C181DBE 0x8C182BC6 0x8C18265E>
.data 2
and r0, 0xFE
.align 4
.data <VERS 0x8C182BF4 0x8C182BF4 0x8C181DEC 0x8C182BF4 0x8C18268C>
.data 2
nop
.align 4
.data <VERS 0x8C1834D0 0x8C1834D0 0x8C1825F0 0x8C1834D0 0x8C182F68>
.data 2
and r0, 0xFE
.align 4
.versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0
.data <VERS 0x801D33E4 0x801D38EC 0x801D3CC4 0x801D39B8 0x801D381C 0x801D381C 0x801D3A1C 0x801D3ED8>
.data 0x00000004
b +0x4C
.data <VERS 0x801FE900 0x801FF174 0x8020010C 0x801FF710 0x801FF0FC 0x801FF0FC 0x801FFA44 0x801FF9E0>
.data 0x00000004
nop
.data <VERS 0x801FFE5C 0x802006D0 0x802016CC 0x80200C9C 0x80200658 0x80200658 0x80200FD0 0x80200F3C>
.data 0x00000004
nop
.data <VERS 0x802019C8 0x8020223C 0x801FD944 0x80202860 0x802021C4 0x802021C4 0x80202B94 0x80202AA8>
.data 0x00000004
li r0, 0
.data <VERS 0x802C2060 0x802C2F98 0x802C42E4 0x802C3E78 0x802C2A40 0x802C2A84 0x802C402C 0x802C37C0>
.data 0x00000004
b +0x4C
.data <VERS 0x802D0AA0 0x802D1A58 0x802D2C10 0x802D2938 0x802D1480 0x802D14C4 0x802D2AEC 0x802D2280>
.data 0x00000004
b +0x20
.versions 4OED 4OEU 4OJB 4OJD 4OJU 4OPD 4OPU
.data <VERS 0x000D1B85 0x000D1BD5 0x000D1A35 0x000D1B05 0x000D1AF5 0x000D1BA5 0x000D1BD5>
.data 0x00000001
.binary 00
.data <VERS 0x000D1BFC 0x000D1C4C 0x000D1AAC 0x000D1B7C 0x000D1B6C 0x000D1C1C 0x000D1C4C>
.data 0x00000002
.binary EB08
.data <VERS 0x0020E805 0x0020E805 0x0020E5D5 0x0020E755 0x0020E9D5 0x0020E825 0x0020E895>
.data 0x00000001
.binary EB
.data <VERS 0x002119CA 0x00211A2A 0x0021170A 0x0021197A 0x00211BFA 0x002119EA 0x00211ABA>
.data 0x00000002
.binary EB74
.data <VERS 0x002291B5 0x00229255 0x00228F15 0x00229125 0x00229415 0x002291D5 0x002292E5>
.data 0x00000002
.binary 9090
.data <VERS 0x00229237 0x002292D7 0x00228F97 0x002291A7 0x00229497 0x00229257 0x00229367>
.data 0x00000002
.binary EB08
.data <VERS 0x0022A222 0x0022A2C2 0x00229F82 0x0022A192 0x0022A482 0x0022A242 0x0022A352>
.data 0x00000002
.binary 9090
.data <VERS 0x0022A29B 0x0022A33B 0x00229FFB 0x0022A20B 0x0022A4FB 0x0022A2BB 0x0022A3CB>
.data 0x00000002
.binary EB08
.data <VERS 0x0022BF35 0x0022BFD5 0x0022BC95 0x0022BEA5 0x0022C195 0x0022BF55 0x0022C065>
.data 0x00000001
.binary 00
.data <VERS 0x0022BF6E 0x0022C00E 0x0022BCCE 0x0022BEDE 0x0022C1CE 0x0022BF8E 0x0022C09E>
.data 0x00000002
.binary EB08
.data <VERS 0x0022C2E6 0x0022C386 0x0022C046 0x0022C256 0x0022C546 0x0022C306 0x0022C416>
.data 0x00000001
.binary 00
.data <VERS 0x002418E8 0x00241A78 0x00241608 0x00241858 0x00241BD8 0x00241908 0x00241B08>
.data 0x00000001
.binary 00
.data <VERS 0x0024195C 0x00241AEC 0x0024167C 0x002418CC 0x00241C4C 0x0024197C 0x00241B7C>
.data 0x00000002
.binary EB08
.data <VERS 0x002A2904 0x002A2B34 0x002A0FA4 0x002A19F4 0x002A2EC4 0x002A2924 0x002A2BF4>
.data 0x00000001
.binary 00
.data <VERS 0x002A297C 0x002A2BAC 0x002A101C 0x002A1A6C 0x002A2F3C 0x002A299C 0x002A2C6C>
.data 0x00000002
.binary EB08
.data <VERS 0x002D677A 0x002D6C8A 0x002D481A 0x002D53DA 0x002D6CBA 0x002D67AA 0x002D6D0A>
.data 0x00000001
.binary 00
.data <VERS 0x002D67ED 0x002D6CFD 0x002D488D 0x002D544D 0x002D6D2D 0x002D681D 0x002D6D7D>
.data 0x00000002
.binary EB08
.data <VERS 0x002F0E1E 0x002F0FCE 0x002EEEBE 0x002EF9CE 0x002F0FCE 0x002F0E4E 0x002F103E>
.data 0x00000001
.binary EB
.all_versions
.data 0x00000000
.data 0x00000000
@@ -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
@@ -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 <VERS 0x8C0254B2 0x8C0254BE 0x8C0254D2>
.data 4
bs +0x38
nop
.align 4
.data <VERS 0x8C150B2C 0x8C150F9C 0x8C150D58>
.data 2
sett
.align 4
.data <VERS 0x8C15F346 0x8C15F856 0x8C15F612>
.data 2
and r0, 0xFE
.align 4
.data <VERS 0x8C16053A 0x8C160A4A 0x8C160806>
.data 2
and r0, 0xFE
.align 4
.data <VERS 0x8C1617DA 0x8C161D6A 0x8C161B26>
.data 2
and r0, 0xFE
.align 4
.data <VERS 0x8C15F3BA 0x8C15F8CA 0x8C15F686>
.data 2
nop
.align 4
.data <VERS 0x8C1605A6 0x8C160AB6 0x8C160872>
.data 2
nop
.align 4
.data <VERS 0x8C161808 0x8C161D98 0x8C161B54>
.data 2
nop
.align 4
.data 0x00000000
.data 0x00000000

Some files were not shown because too many files have changed in this diff Show More