diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 5fb660225..63bc266ad 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1883,17 +1883,23 @@ void set_ending_unlock(ENT_TYPE type) void patch_orbs_limit() { + /* + * The idea: we nuke jump instruction and some nop's after it + * in new code we check if the number of orbs is greater than 3 + * if yes we set it as 3 (2 becouse of 0 - 2 range) + * we jump back to the place of oryginal jump + */ static bool once = false; if (once) return; auto memory = Memory::get(); const auto function_offset = get_virtual_function_address(VTABLE_OFFSET::ITEM_FLOATING_ORB, (uint32_t)VIRT_FUNC::ENTITY_KILL); - // finding exit of the loop that counts orbs, after jump there is unused code so we have enogh space for a jump - auto instance = find_inst(memory.exe(), "\xF3\x75"sv, function_offset, function_offset + 0x622, "Orbs patch in kill virtual"); + // finding exit of the loop that counts orbs, after jump there is unused code so we have enogh space for a our patch + auto instance = find_inst(memory.exe(), "\xF3\x75"sv, function_offset, function_offset + 0x622, "patch_orbs_limit"); auto offset = memory.at_exe(instance) + 3; - if (instance == 0) // not found, don't crash the game + if (instance == 0) // not found, don't crash the game (ex. PL + OV calling this) return; auto loop_exit_jump = memory_read(offset + 1); @@ -1907,3 +1913,45 @@ void patch_orbs_limit() patch_and_redirect(offset, 8, new_code, true, offset + 2 + loop_exit_jump); once = true; } + +void patch_olmec_kill_crash() +{ + /* + * The idea: if the loop continues, jump back to the original code + * if not, we have to set rdi to correct value and jump out + * we jump just like jump that is used during olmec cutscene and when using shortcut to olmec + */ + static bool once = false; + if (once) + return; + + auto memory = Memory::get(); + const auto offset = get_address("olmec_lookup_crash"); + constexpr auto code_to_move = 7; + 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]' + auto jump_out_lookup = find_inst(memory.exe(), "\x80\x79\x5C\x03"sv, rva, rva + 0x83, "patch_olmec_kill_crash"); + if (jump_out_lookup == 0) + return; + auto jump_offset_offset = memory.at_exe(jump_out_lookup + 6); // 4 (lookup instruction size) + 2 (jump instruction) + auto jump_offset = memory_read(jump_offset_offset); + return_addr = jump_offset_offset + 4 + jump_offset; // +4 to get address after the jump + } + + std::string_view new_code{ + "\x0f\x85\x00\x00\x00\x00"sv // jne (offset needs to be updated after we know the address) + "\x48\x8B\x7D\x18"}; // mov rdi,qword ptr ss:[rbp+18] + + auto new_code_addr = patch_and_redirect(offset, code_to_move, new_code, false, return_addr); + if (new_code_addr == 0) + return; + + new_code_addr += code_to_move; // position after the copied over code + + 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); + once = true; +} diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index baaedd1d6..da869ede1 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -138,3 +138,4 @@ void save_progress(); void set_level_string(std::u16string_view text); void set_ending_unlock(ENT_TYPE type); void patch_orbs_limit(); +void patch_olmec_kill_crash(); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 97920e393..5d16d53c0 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2016,6 +2016,8 @@ end lua["patch_orbs_limit"] = patch_orbs_limit; + lua["patch_olmec_kill_crash"] = patch_olmec_kill_crash; + lua.create_named_table("INPUTS", "NONE", 0, "JUMP", 1, "WHIP", 2, "BOMB", 4, "ROPE", 8, "RUN", 16, "DOOR", 32, "MENU", 64, "JOURNAL", 128, "LEFT", 256, "RIGHT", 512, "UP", 1024, "DOWN", 2048); lua.create_named_table( diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index 17d40c853..61ca6b275 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -1967,7 +1967,15 @@ std::unordered_map g_address_rules{ .decode_call() .at_exe(), }, -}; + { + // warp to olmec, kill/destroy it to crash the game, the code it crashes at should look like this: + // movsx rax,byte ptr ds:[r8+13C] + // scrolling up you should see access to the state, and above that two jump instructions and above those we need at least 5 bytes for patch + "olmec_lookup_crash"sv, + PatternCommandBuffer{} + .find_after_inst("8B 59 3C 48 C1 E3 03"_gh) + .at_exe(), + }}; std::unordered_map g_cached_addresses; void preload_addresses()