From 70413668d8c767f0db61c7d36ede2a400dd42521 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sun, 5 May 2024 10:51:13 -0700 Subject: [PATCH] support B2 patches on BB --- src/FunctionCompiler.cc | 3 +- src/ReceiveCommands.cc | 57 ++++++++++++++++--- src/Version.cc | 6 +- .../System/WriteCodeBlocksBB.x86.inc.s | 29 ++++++++++ 4 files changed, 83 insertions(+), 12 deletions(-) create mode 100644 system/client-functions/System/WriteCodeBlocksBB.x86.inc.s diff --git a/src/FunctionCompiler.cc b/src/FunctionCompiler.cc index a385a47f..8bfa2188 100644 --- a/src/FunctionCompiler.cc +++ b/src/FunctionCompiler.cc @@ -392,8 +392,9 @@ shared_ptr FunctionCodeIndex::patch_switches_menu( } 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 & 0xFF000000) == (specific_version & 0xFF000000)) { + if ((it.first & mask) == (specific_version & mask)) { return false; } } diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index f4680726..0ba977dc 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1071,13 +1071,56 @@ static void on_93_BB(shared_ptr c, uint16_t, uint32_t, string& data) { } else { string version_string = config_data.as_string(); strip_trailing_zeroes(version_string); - // Note: Tethealla PSOBB is actually Japanese PSOBB, but with most of the - // files replaced with English text/graphics/etc. For this reason, it still - // reports its language as Japanese, so we have to account for that - // manually here. - if (starts_with(version_string, "TethVer")) { - c->log.info("Client is TethVer subtype; forcing English language"); - c->config.set_flag(Client::Flag::FORCE_ENGLISH_LANGUAGE_BB); + // If the version string starts with "Ver.", assume it's Sega and apply the + // normal version encoding logic. Otherwise, assume it's a community mod, + // almost all of which are based on TethVer12513, so assume that version + // otherwise. + if (true || starts_with(version_string, "Ver.")) { + // Basic algorithm: take all numeric characters from the version string + // and ignore everything else. Treat that as a decimal integer, then + // base36-encode it into the low 3 bytes of specific_version. + uint64_t version = 0; + for (char ch : version_string) { + if (isdigit(ch)) { + version = (version * 10) + (ch - '0'); + } + } + uint8_t shift = 0; + uint32_t specific_version = 0; + while (version) { + if (shift > 16) { + throw runtime_error("invalid version string"); + } + uint8_t ch = (version % 36) + '0'; + version /= 36; + if (ch > '9') { + ch += 7; + } + specific_version |= (ch << shift); + shift += 8; + } + if (!(specific_version & 0x00FF0000)) { + specific_version |= 0x00300000; + } + if (!(specific_version & 0x0000FF00)) { + specific_version |= 0x00003000; + } + if (!(specific_version & 0x000000FF)) { + specific_version |= 0x00000030; + } + c->config.specific_version = 0x35000000 | specific_version; + + } else { + c->config.specific_version = 0x35394E4C; // 59NL + + // Note: Tethealla PSOBB is actually Japanese PSOBB, but with most of the + // files replaced with English text/graphics/etc. For this reason, it still + // reports its language as Japanese, so we have to account for that + // manually here. + if (starts_with(version_string, "TethVer")) { + c->log.info("Client is TethVer subtype; forcing English language"); + c->config.set_flag(Client::Flag::FORCE_ENGLISH_LANGUAGE_BB); + } } } c->channel.language = c->config.check_flag(Client::Flag::FORCE_ENGLISH_LANGUAGE_BB) ? 1 : base_cmd.language; diff --git a/src/Version.cc b/src/Version.cc index b2ae240f..1cb9d0bb 100644 --- a/src/Version.cc +++ b/src/Version.cc @@ -305,10 +305,8 @@ bool specific_version_is_xb(uint32_t specific_version) { } bool specific_version_is_bb(uint32_t specific_version) { - // TODO: We should actually find a way to determine BB specific_versions, but - // there are so many mods out there, and there's a patch server anyway, so it - // seems not worth the effort - return specific_version == 0x35303030; + // BB specific_versions are 5XXX, where X is an encoding of the revision number + return (specific_version & 0xFF000000) == 0x35000000; } const char* file_path_token_for_version(Version version) { diff --git a/system/client-functions/System/WriteCodeBlocksBB.x86.inc.s b/system/client-functions/System/WriteCodeBlocksBB.x86.inc.s new file mode 100644 index 00000000..c7285f48 --- /dev/null +++ b/system/client-functions/System/WriteCodeBlocksBB.x86.inc.s @@ -0,0 +1,29 @@ +start: + push ebx + jmp get_patch_data_ptr +get_patch_data_ptr_ret: + pop ebx # ebx = patch header + +apply_next_patch: + cmp dword [ebx + 4], 0 + jne copy_code_and_apply_again + pop ebx + mov eax, 1 + ret + +copy_code_and_apply_again: + xor ecx, ecx # ecx = offset + mov edx, [ebx] # edx = dest addr +copy_next_byte: + mov al, [ebx + ecx + 8] # copy one byte to dest + mov [edx + ecx], al + inc ecx # offset++ + cmp [ebx + 4], ecx # check if all bytes have been copied + jne copy_next_byte + + lea ebx, [ebx + ecx + 8] # advance to next block + jmp apply_next_patch + +get_patch_data_ptr: + call get_patch_data_ptr_ret +first_patch_header: