set up framework for DC patching

This commit is contained in:
Martin Michelsen
2024-04-14 21:00:32 -07:00
parent 8ecbe6798d
commit d6edf1b24d
12 changed files with 132 additions and 34 deletions
+3
View File
@@ -44,6 +44,9 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case Version::DC_NTE:
case Version::DC_V1_11_2000_PROTOTYPE:
case Version::DC_V1:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case Version::DC_V2:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
+9 -1
View File
@@ -10,6 +10,7 @@
#ifdef HAVE_RESOURCE_FILE
#include <resource_file/Emulators/PPC32Emulator.hh>
#include <resource_file/Emulators/SH4Emulator.hh>
#include <resource_file/Emulators/X86Emulator.hh>
#endif
@@ -39,6 +40,8 @@ const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
return "PowerPC";
case CompiledFunctionCode::Architecture::X86:
return "x86";
case CompiledFunctionCode::Architecture::SH4:
return "SH-4";
default:
throw logic_error("invalid architecture");
}
@@ -165,7 +168,8 @@ shared_ptr<CompiledFunctionCode> compile_function_code(
ret = X86Emulator::assemble(load_file(asm_filename), get_include);
break;
case CompiledFunctionCode::Architecture::SH4:
throw runtime_error("cannot compuile SH-4 assembly");
ret = SH4Emulator::assemble(load_file(asm_filename), get_include);
break;
default:
throw runtime_error("unknown architecture");
}
@@ -184,6 +188,10 @@ shared_ptr<CompiledFunctionCode> compile_function_code(
assembled = PPC32Emulator::assemble(text, get_include);
} else if (arch == CompiledFunctionCode::Architecture::X86) {
assembled = X86Emulator::assemble(text, get_include);
} else if (arch == CompiledFunctionCode::Architecture::SH4) {
assembled = SH4Emulator::assemble(text, get_include);
} else {
throw runtime_error("invalid architecture");
}
ret->code = std::move(assembled.code);
ret->label_offsets = std::move(assembled.label_offsets);
+11 -9
View File
@@ -1196,10 +1196,8 @@ Action a_assemble_all_patches(
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",
+[](Arguments& args) {
string config_filename = args.get<string>("config");
auto s = make_shared<ServerState>(config_filename);
s->compile_functions(false);
+[](Arguments&) {
auto fci = make_shared<FunctionCodeIndex>("system/client-functions");
auto process_code = +[](shared_ptr<const CompiledFunctionCode> code,
uint32_t checksum_addr,
@@ -1216,23 +1214,27 @@ Action a_assemble_all_patches(
}
};
for (const auto& it : s->function_code_index->name_and_specific_version_to_patch_function) {
for (const auto& it : fci->name_and_specific_version_to_patch_function) {
process_code(it.second, 0, 0, 0);
}
try {
process_code(s->function_code_index->name_to_function.at("VersionDetectGC"), 0, 0, 0);
process_code(fci->name_to_function.at("VersionDetectDC"), 0, 0, 0);
} catch (const out_of_range&) {
}
try {
process_code(s->function_code_index->name_to_function.at("VersionDetectXB"), 0, 0, 0);
process_code(fci->name_to_function.at("VersionDetectGC"), 0, 0, 0);
} catch (const out_of_range&) {
}
try {
process_code(s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), 0x80000000, 8, 0x7F2734EC);
process_code(fci->name_to_function.at("VersionDetectXB"), 0, 0, 0);
} catch (const out_of_range&) {
}
try {
process_code(s->function_code_index->name_to_function.at("CacheClearFix-Phase2"), 0, 0, 0);
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);
} catch (const out_of_range&) {
}
});
+9 -1
View File
@@ -24,6 +24,7 @@
#include <phosg/Time.hh>
#ifdef HAVE_RESOURCE_FILE
#include <resource_file/Emulators/PPC32Emulator.hh>
#include <resource_file/Emulators/SH4Emulator.hh>
#include <resource_file/Emulators/X86Emulator.hh>
#endif
@@ -728,7 +729,8 @@ static HandlerResult S_B2(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
// TODO: Support SH-4 disassembly too
bool is_ppc = ::is_ppc(ses->version());
bool is_x86 = ::is_x86(ses->version());
if (is_ppc || is_x86) {
bool is_sh4 = ::is_sh4(ses->version());
if (is_ppc || is_x86 || is_sh4) {
try {
if (code.size() < sizeof(FooterT)) {
throw runtime_error("code section is too small");
@@ -763,6 +765,12 @@ static HandlerResult S_B2(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
code.size(),
0,
&labels);
} else if (is_sh4) {
disassembly = SH4Emulator::disassemble(
&r.pget<uint8_t>(0, code.size()),
code.size(),
0,
&labels);
} else {
// We shouldn't have entered the outer if statement if this happens
throw logic_error("unsupported architecture");
+6 -2
View File
@@ -122,6 +122,10 @@ void send_first_pre_lobby_commands(shared_ptr<Client> c, std::function<void()> o
// TODO: This function is bad. Ideally we would use coroutines and clean up
// all these terrible callbacks.
if (c->login->account->auto_patches_enabled.empty()) {
c->config.set_flag(Client::Flag::HAS_AUTO_PATCHES);
}
if (function_compiler_available() &&
!c->config.check_flag(Client::Flag::HAS_AUTO_PATCHES) &&
!c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
@@ -3002,7 +3006,7 @@ static void on_61_98(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
const auto* cmd3 = &check_size_t<C_CharacterData_Ep3_61_98>(data);
c->ep3_config = make_shared<Episode3::PlayerConfig>(cmd3->ep3_config);
cmd = reinterpret_cast<const C_CharacterData_V3_61_98*>(cmd3);
if (c->config.specific_version == 0x33000000) {
if (c->config.specific_version == 0x00000000) {
c->config.specific_version = 0x33534A30; // 3SJ0
}
} else {
@@ -3010,7 +3014,7 @@ static void on_61_98(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
c->channel.version = Version::GC_EP3_NTE;
c->log.info("Game version changed to GC_EP3_NTE");
c->config.clear_flag(Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL);
if (c->config.specific_version == 0x33000000) {
if (c->config.specific_version == default_specific_version_for_version(Version::GC_EP3, -1)) {
c->config.specific_version = 0x33534A54; // 3SJT
}
c->convert_account_to_temporary_if_nte();
+10 -4
View File
@@ -338,11 +338,17 @@ void prepare_client_for_patches(shared_ptr<Client> c, function<void()> on_comple
if (!c) {
return;
}
bool is_gc = ::is_gc(c->version());
bool is_xb = (c->version() == Version::XB_V3);
if ((is_gc || is_xb) &&
const char* version_detect_name = nullptr;
if (c->version() == Version::DC_V2) {
version_detect_name = "VersionDetectDC";
} else if (is_gc(c->version())) {
version_detect_name = "VersionDetectGC";
} else if (c->version() == Version::XB_V3) {
version_detect_name = "VersionDetectXB";
}
if (version_detect_name &&
c->config.specific_version == default_specific_version_for_version(c->version(), -1)) {
send_function_call(c, s->function_code_index->name_to_function.at(is_xb ? "VersionDetectXB" : "VersionDetectGC"));
send_function_call(c, s->function_code_index->name_to_function.at(version_detect_name));
c->function_call_response_queue.emplace_back([wc = weak_ptr<Client>(c), on_complete](uint32_t specific_version, uint32_t) -> void {
auto c = wc.lock();
if (!c) {
+9 -3
View File
@@ -204,8 +204,14 @@ uint32_t default_specific_version_for_version(Version version, int64_t sub_versi
// 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 the
// VersionDetectGC or VersionDetectXB call.
// VersionDetectDC, VersionDetectGC, or VersionDetectXB call.
switch (version) {
case Version::DC_NTE:
return 0x314F4A31;
case Version::DC_V1_11_2000_PROTOTYPE:
return 0x314F4A32;
case Version::DC_V1:
return 0x00000000; // Need to send VersionDetectDC (but can't on V1; rip)
case Version::GC_NTE:
return 0x334F4A54; // 3OJT
case Version::GC_V3:
@@ -225,7 +231,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 0x33000000;
return 0x00000000; // Need to send VersionDetectGC
}
throw logic_error("this should be impossible");
case Version::GC_EP3_NTE:
@@ -240,7 +246,7 @@ 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 0x33000000;
return 0x00000000; // Need to send VersionDetectGC
}
case Version::XB_V3:
return 0x344F0000;