diff --git a/src/game_api/game_patches.cpp b/src/game_api/game_patches.cpp index 6533221f2..f71b096fd 100644 --- a/src/game_api/game_patches.cpp +++ b/src/game_api/game_patches.cpp @@ -69,73 +69,41 @@ bool check_if_ent_type_exists(ENT_TYPE type, int mask) return false; } +size_t g_olmec_patch_size; +size_t g_olmec_patch_addr; void patch_olmec_kill_crash() { - /* - * The idea: do what's necessary xd - */ static bool once = false; if (once) return; + // [patch ... whatever this code does] + const auto offset = get_address("olmec_lookup_crash"); constexpr auto code_to_move = 7; auto memory = Memory::get(); size_t return_addr; { // find address to escape to - - auto rva = offset - memory.exe_ptr; - // there are two jump that performe long jump, at the end, of it, the is 'mov rax,qword ptr ds:[rdi]', then find jump that's jumps over that code and create sound meta call - // this is actually unique pattern - auto jump_out_lookup = find_inst(memory.exe(), "\x48\x8B\x45\x50\x48\x83\x78\x60\x00"sv, rva, std::nullopt, "patch_olmec_kill_crash"); + size_t rva = offset - memory.exe_ptr; + // below the patched code there are two jumps that performe long jump, at the end of it there is 'mov rax,qword ptr ds:[rdi]', + // from this point find jump that's jumps over sond meta and fmod stuff, the jump ends up on code `mov eax,dword ptr ss:[rbp+10]`, that's our target for return_addr + auto jump_out_lookup = find_inst(memory.exe(), "\x48\xC7\x40\x60\x00\x00\x00\x00"sv, rva, rva + 0x69D, "patch_olmec_kill_crash"); if (jump_out_lookup == 0) return; - // could probably just make static offset from this stuff - auto jump_offset_offset = memory.at_exe(jump_out_lookup + 10); // 4 (lookup instruction size) + 1 (jump instruction) - auto jump_offset = memory_read(jump_offset_offset); - return_addr = jump_offset_offset + 1 + jump_offset; // +1 to get address after the jump - } - { - // patch the cutscene - - const auto function_offset = get_virtual_function_address(VTABLE_OFFSET::THEME_OLMEC, 24); // spawn_effects - const auto jump_out_lookup = get_address("olmec_lookup_in_theme"); - if (jump_out_lookup == 0) - return; - - // find first jump (skips the whole funciton) - auto end_function_jump = find_inst(memory.exe(), "\x0F\x84"sv, function_offset, function_offset + 0xC3, "patch_olmec_kill_crash"); - if (end_function_jump == 0) - return; - - auto jump_addr = memory.at_exe(end_function_jump); - auto addr_to_jump_to = jump_addr + 6 + memory_read(jump_addr + 2); - std::string clear_ic8_code = fmt::format( - "\x48\xb8{}" // movabs RAX, &clear_cutscene_behavior - "\xff\xd0"sv // call RAX - ); - - /* The idea: - * replace end of the loop jump with jump to the new code - * call our own function to clear all the cutscene behaviors - * jump to the end of the function - * hopefully there isn't something important that we're skipping - */ - - patch_and_redirect(jump_out_lookup, 5, clear_ic8_code, true, addr_to_jump_to); + return_addr = memory.at_exe(jump_out_lookup + 8); // 8 - pattern size } /* The idea: * if it's not the end of the array it's looking for olmec, jump back to the oryginal code via jump in `new_code` * if it's end of the array (no olmec found) jump to return_addr * the place for return_addr was kind of choosen by feel, as the code is complicated - * the whole point of the patched code is to find olmec and check it's faze, maybe for the music? no idea + * the whole point of the patched code is to find olmec and check it's faze, maybe for the music? no idea, it's a big function */ std::string_view new_code{ - "\x0f\x85\x00\x00\x00\x00"sv}; // jne (offset needs to be updated after we know the address) + "\x0f\x85\x00\x00\x00\x00"sv}; // jne (offset needs to be updated after we know the address) auto new_code_addr = patch_and_redirect(offset, code_to_move, new_code, false, return_addr); if (new_code_addr == 0) @@ -144,12 +112,53 @@ void patch_olmec_kill_crash() new_code_addr += code_to_move; int32_t rel = static_cast(offset + code_to_move - (new_code_addr + 6)); // +6 after the jump write_mem_prot(new_code_addr + 2, rel, true); + + // [patch the cutscene] + + const auto patch_addr = get_address("olmec_lookup_in_theme"); + if (patch_addr == 0) + return; + + size_t addr_to_jump_to; + { + // find end of the function that sets the camera and stuff + size_t rva = patch_addr - memory.exe_ptr; + size_t rva_jumpout_to = find_inst(memory.exe(), "\x48\x8B\x06"sv, rva, rva + 0xA50, "patch_olmec_kill_crash"); + if (rva_jumpout_to == 0) + return; + + addr_to_jump_to = memory.at_exe(rva_jumpout_to + 14); // +14 just offset to get what we want + } + + std::string cutscene_new_code = fmt::format( + "\xb9{}"sv // mov ecx, ENT_TYPE + "\xba\x80\x00\x00\x00"sv // mov edx, 0x80 (MASK::ACTIVEFLOOR) + "\x48\xb8{}"sv // movabs RAX, &check_if_ent_type_exists + "\xff\xd0"sv // call RAX + "\x84\xc0"sv // test al, al + "\x0f\x85\x00\x00\x00\x00"sv, // jnz (offset needs to be updated after we know the address) + to_le_bytes(to_id("ENT_TYPE_ACTIVEFLOOR_OLMEC")), + to_le_bytes((size_t)&check_if_ent_type_exists)); + + /* The idea: + * same as tiamat + */ + constexpr auto cutscene_code_to_move = 7; + g_olmec_patch_size = cutscene_new_code.size() + cutscene_code_to_move; // without the final jump added by patch_and_redirect + + g_olmec_patch_addr = patch_and_redirect(patch_addr, cutscene_code_to_move, cutscene_new_code, false, addr_to_jump_to); + if (g_olmec_patch_addr == 0) + return; + + auto jump_addr = g_olmec_patch_addr + g_olmec_patch_size; + int32_t rel_off = static_cast(patch_addr + cutscene_code_to_move - jump_addr); + write_mem_prot(jump_addr - 4, rel_off, true); // update the jump offset + once = true; } size_t g_tiamat_patch_size; size_t g_tiamat_patch_addr; - void patch_tiamat_kill_crash() { static bool once = false; @@ -193,6 +202,8 @@ void patch_tiamat_kill_crash() g_tiamat_patch_size = new_code.size() + copy_over_code_size; // without the final jump added by patch_and_redirect g_tiamat_patch_addr = patch_and_redirect(patch_addr, copy_over_code_size, new_code, false, return_to_addr); + if (g_tiamat_patch_addr == 0) + return; auto jump_addr = g_tiamat_patch_addr + g_tiamat_patch_size; int32_t rel = static_cast(patch_addr + copy_over_code_size - jump_addr); @@ -238,6 +249,9 @@ void patch_liquid_OOB() "\x0f\x8C\x00\x00\x00\x00"sv}; // jl (same jump as before) auto new_code_addr = patch_and_redirect(offset, code_to_move, new_code); + if (new_code_addr == 0) + return; + new_code_addr += code_to_move; int32_t rel = static_cast(continue_addr - (new_code_addr + 10)); // +10 after the jump @@ -249,12 +263,12 @@ void patch_liquid_OOB() void set_skip_olmec_cutscene(bool skip) { - static const auto jump_out_lookup = get_address("olmec_lookup_in_theme"); - if (jump_out_lookup == 0) - return; + patch_olmec_kill_crash(); // just in case + // simple jump over the tiamat check, nop here just so there is no funny business + static const std::string code = fmt::format("\xEB{}\x90"sv, to_le_bytes(static_cast(g_olmec_patch_size - 2))); if (skip) - write_mem_recoverable("set_skip_olmec_cutscene", jump_out_lookup - 2, "\x90\x90"sv, true); + write_mem_recoverable("set_skip_olmec_cutscene", g_olmec_patch_addr, code, true); else recover_mem("set_skip_olmec_cutscene"); } diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index 4721e8a4b..56ff2bdec 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -1986,11 +1986,11 @@ std::unordered_map g_address_rules{ }, { "olmec_lookup_in_theme"sv, - // find the jump out of olmec lookup loop + // find the first jump in the virtual that skips the whole function PatternCommandBuffer{} .get_virtual_function_address(VTABLE_OFFSET::THEME_OLMEC, (VIRT_FUNC)24) // spawn_effects - .find_after_inst("\x48\x03\x58\x28"sv) - .offset(0x5) + .find_after_inst("83 78 0C 0D"_gh) + .offset(0x6) // after the jump instruction .at_exe(), }, { diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index b4b90066a..acdf83648 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -289,7 +289,7 @@ State& State::get() // game patches patch_tiamat_kill_crash(); patch_orbs_limit(); - //patch_olmec_kill_crash(); + patch_olmec_kill_crash(); patch_liquid_OOB(); }