diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 63bc266ad..bc5ffe84a 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1925,25 +1925,28 @@ void patch_olmec_kill_crash() 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 memory = Memory::get(); 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"); + // 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"); 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 + + // could probably just offset 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 } 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] + "\x48\x8B\x7D\x18"sv}; // mov rdi,qword ptr ss:[rbp+18] // TODO: test if needed still auto new_code_addr = patch_and_redirect(offset, code_to_move, new_code, false, return_addr); if (new_code_addr == 0) @@ -1955,3 +1958,48 @@ void patch_olmec_kill_crash() write_mem_prot(new_code_addr + 2, rel, true); once = true; } + +void patch_liquid_OOB() +{ + /* + * The idea: there is a loop thru all liquid entities + * if liquid is out of bounds (coordinate below 0) we essentially simulate `continue;` behavior + */ + + static bool once = false; + if (once) + return; + + const auto offset = get_address("liquid_OOB_crash"); + size_t continue_addr; + { + // find address to continue the loop + auto memory = Memory::get(); + auto rva = offset - memory.exe_ptr; + // first `ja` loop + auto jump_out_lookup = find_inst(memory.exe(), "\x0F\x87"sv, rva, rva + 0x7D, "patch_liquid_OOB"); + if (jump_out_lookup == 0) + return; + + auto jump_out_addr = memory.at_exe(jump_out_lookup); + auto jump_offset = memory_read(jump_out_addr + 2); // should be negative + continue_addr = jump_out_addr + 6 + jump_offset; // 6 size of the jump instruction + } + + constexpr auto code_to_move = 5; + + std::string_view new_code{ + "\x48\x83\xFD\x00"sv // cmp ebp,0x0 (ebp = y) + "\x0f\x8C\x00\x00\x00\x00"sv // jl (offset needs to be updated after we know the address) + "\x48\x83\xfa\x00" // cmp rdx,0x0 (rdx = x) + "\x0f\x8C\x00\x00\x00\x00"sv}; // jl (same offset as before) + + auto new_code_addr = patch_and_redirect(offset, code_to_move, new_code); + new_code_addr += code_to_move; + + int32_t rel = static_cast(continue_addr - (new_code_addr + 10)); // +10 after the jump + write_mem_prot(new_code_addr + 6, rel, true); + write_mem_prot(new_code_addr + 16, rel - 10, true); + + once = true; +} diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index da869ede1..64cba7ca6 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -139,3 +139,4 @@ void set_level_string(std::u16string_view text); void set_ending_unlock(ENT_TYPE type); void patch_orbs_limit(); void patch_olmec_kill_crash(); +void patch_liquid_OOB(); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 5d16d53c0..2029aa8bf 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2018,6 +2018,8 @@ end lua["patch_olmec_kill_crash"] = patch_olmec_kill_crash; + lua["patch_liquid_OOB"] = patch_liquid_OOB; + 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 61ca6b275..e0de73baa 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -1975,7 +1975,16 @@ std::unordered_map g_address_rules{ PatternCommandBuffer{} .find_after_inst("8B 59 3C 48 C1 E3 03"_gh) .at_exe(), - }}; + }, + { + // spawn liquid so it falls off the map to crash the game + // above the code that crash, look for float to int conversion (cvttss2si) + "liquid_OOB_crash"sv, + PatternCommandBuffer{} + .find_after_inst("F3 41 0F 5E F1 F3 48 0F 2C EE"_gh) + .at_exe(), + }, +}; std::unordered_map g_cached_addresses; void preload_addresses()