From 1c425b4575d2006a3616c1ab143de304841b075b Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sun, 29 Oct 2023 19:08:16 +0100 Subject: [PATCH] game api stuff --- src/game_api/game_api.cpp | 32 ++++++++++ src/game_api/game_api.hpp | 72 +++++++++++++++++++++ src/game_api/render_api.cpp | 34 +++------- src/game_api/render_api.hpp | 7 +-- src/game_api/script/lua_vm.cpp | 17 ++--- src/game_api/search.cpp | 111 ++++++++++++++++++--------------- src/game_api/settings_api.cpp | 11 ++-- src/game_api/state.cpp | 48 +++----------- src/game_api/state.hpp | 5 +- src/game_api/texture.cpp | 4 +- src/injected/ui_util.cpp | 4 +- 11 files changed, 205 insertions(+), 140 deletions(-) create mode 100644 src/game_api/game_api.cpp create mode 100644 src/game_api/game_api.hpp diff --git a/src/game_api/game_api.cpp b/src/game_api/game_api.cpp new file mode 100644 index 000000000..7b333c631 --- /dev/null +++ b/src/game_api/game_api.cpp @@ -0,0 +1,32 @@ +#include "game_api.hpp" + +#include "search.hpp" + +GameAPI* GameAPI::get() +{ + using GetGameAPI = GameAPI*(); + static auto addr = (GetGameAPI*)get_address("get_game_api"); + return addr(); +} + +float GameAPI::get_current_zoom() +{ + return renderer->current_zoom + renderer->current_zoom_offset; +} + +float GameAPI::get_target_zoom() +{ + return renderer->target_zoom + renderer->target_zoom_offset; +} + +void GameAPI::set_zoom(std::optional current, std::optional target) +{ + if (current.has_value()) + { + renderer->current_zoom = current.value(); // - renderer->current_zoom_offset; + } + if (target.has_value()) + { + renderer->target_zoom = target.value(); // - renderer->target_zoom_offset; + } +} diff --git a/src/game_api/game_api.hpp b/src/game_api/game_api.hpp new file mode 100644 index 000000000..221c479ef --- /dev/null +++ b/src/game_api/game_api.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +struct Renderer +{ + uint32_t render_width; // sam as window size unless resolution scale is set + uint32_t render_height; + + uint32_t fps; // changing it doesn't seam to do anything + uint32_t fps_denominator; + + uint32_t render_width2; // repeat + uint32_t render_height2; + + uint8_t flags1; + uint8_t flags2; + uint8_t padding[6]; + + uint8_t skip[0x1228]; // tons of pointers + + uint8_t skip2[0x7F284]; // a lot of nothing + + float current_zoom; + float target_zoom; + float target_zoom_offset; + float current_zoom_offset; + float backlayer_light_level; // constantly overwritten by theme virtual get_backlayer_light_level + uint8_t unknown2; + uint8_t unknown3; + uint16_t unknown4; + + uint8_t skip3[0xAE4]; + + size_t swap_chain; + + // somewhere there should be shareds stored + + // added just to have the vtable + virtual ~Renderer() = 0; + virtual void some_dx_stuff() = 0; // it actually has a ton of parameters +}; + +struct GameAPI // size 0x60 +{ + static GameAPI* get(); + + float get_current_zoom(); + float get_target_zoom(); + + void set_zoom(std::optional current, std::optional target); + + bool unknown1; + size_t unknown2; // pointer + Renderer* renderer; + uint32_t window_width; + uint32_t window_height; + + size_t unknown5; // garbage? + size_t unknown6; // exe start + size_t unknown7; // some offset + size_t unknown8; // garbage? + size_t SteamAPI_Callback; // just vtable? don't know much about steam stuff + + uint8_t unknown10a; // bool ? + uint32_t unknown10b; + + size_t unknown11; // garbage? + size_t unknown12; // garbage? +}; diff --git a/src/game_api/render_api.cpp b/src/game_api/render_api.cpp index 999632af5..a85f35c5a 100644 --- a/src/game_api/render_api.cpp +++ b/src/game_api/render_api.cpp @@ -12,6 +12,7 @@ #include // for vector #include "entity.hpp" // for Entity, EntityDB +#include "game_api.hpp" // #include "level_api.hpp" // for ThemeInfo #include "logger.h" // for DEBUG #include "memory.hpp" // for memory_read, to_le_bytes, write_mem_prot @@ -31,21 +32,20 @@ struct Layer; RenderAPI& RenderAPI::get() { - static RenderAPI render_api = []() - { - return RenderAPI{(size_t*)get_address("render_api_callback"sv), get_address("render_api_offset"sv)}; - }(); + static RenderAPI render_api; return render_api; } -size_t RenderAPI::renderer() const +Renderer* RenderAPI::renderer() const { - return memory_read(*api + 0x10); + auto game_api = GameAPI::get(); + return game_api->renderer; } size_t RenderAPI::swap_chain() const { - return memory_read(renderer() + swap_chain_off); + return renderer()->swap_chain; + // return memory_read(renderer() + swap_chain_off); // swap_chain_off from pattern: render_api_offset } void (*g_post_render_game)(){nullptr}; @@ -64,7 +64,6 @@ void render_loading(size_t param_1) } std::optional g_forced_lut_textures[2]{}; -float g_layer_zoom_offset[2]{0}; using RenderLayer = void(const std::vector&, uint8_t, const Camera&, const char**, const char**); RenderLayer* g_render_layer_trampoline{nullptr}; @@ -73,16 +72,6 @@ void render_layer(const std::vector& lightsources, uint8_t layer, if (trigger_vanilla_render_layer_callbacks(ON::RENDER_PRE_LAYER, layer)) return; - static size_t offset = 0; - if (offset == 0) - { - auto addr = State::get_zoom_level_address(); - offset = addr + 8; - } - if (offset != 0) - { - g_layer_zoom_offset[layer] = memory_read(offset); - } // The lhs and rhs LUTs are blended in the shader, but we don't know where that value is CPU side so we can only override // with a single LUT for now if (g_forced_lut_textures[layer]) @@ -117,11 +106,6 @@ void render_game(StateMemory* state) trigger_vanilla_render_callbacks(ON::RENDER_POST_GAME); } -float get_layer_zoom_offset(uint8_t layer) -{ - return g_layer_zoom_offset[layer]; -} - void RenderAPI::set_lut(TEXTURE texture_id, uint8_t layer) { g_forced_lut_textures[layer] = texture_id; @@ -482,7 +466,7 @@ void RenderAPI::draw_world_texture(Texture* texture, Quad source, Quad dest, Col dest.top_left_y, unknown}; - typedef void render_func(size_t, WorldShader, const char*** texture_name, uint32_t render_as_non_liquid, float* destination, Quad* source, void*, Color*, float*); + typedef void render_func(Renderer*, WorldShader, const char*** texture_name, uint32_t render_as_non_liquid, float* destination, Quad* source, void*, Color*, float*); static render_func* rf = (render_func*)(func_offset); auto texture_name = texture->name; rf(renderer(), shader, &texture_name, 1, destination, &source, (void*)param_7, &color, nullptr); @@ -500,7 +484,7 @@ void RenderAPI::set_advanced_hud() void RenderAPI::reload_shaders() { - using ReloadShadersFun = void(size_t); + using ReloadShadersFun = void(Renderer*); static ReloadShadersFun* reload_shaders_impl = (ReloadShadersFun*)get_address("reload_shaders"sv); reload_shaders_impl(renderer()); } diff --git a/src/game_api/render_api.hpp b/src/game_api/render_api.hpp index 47da31719..352f167fe 100644 --- a/src/game_api/render_api.hpp +++ b/src/game_api/render_api.hpp @@ -18,6 +18,7 @@ struct JournalUI; struct Layer; class Entity; +struct Renderer; using VANILLA_TEXT_ALIGNMENT = uint32_t; using VANILLA_FONT_STYLE = uint32_t; @@ -225,9 +226,6 @@ struct TextureRenderingInfo struct RenderAPI { - const size_t* api; - size_t swap_chain_off; - mutable std::mutex custom_textures_lock; std::unordered_map custom_textures; std::unordered_map original_textures; @@ -236,7 +234,7 @@ struct RenderAPI static RenderAPI& get(); - size_t renderer() const; + Renderer* renderer() const; size_t swap_chain() const; void set_lut(TEXTURE texture_id, uint8_t layer); @@ -366,7 +364,6 @@ struct RenderInfo void init_render_api_hooks(); bool& get_journal_enabled(); void on_open_journal_chapter(JournalUI* journal_ui, uint8_t chapter, bool instant, bool play_sound); -float get_layer_zoom_offset(uint8_t layer); void render_draw_depth(Layer* layer, uint8_t draw_depth, float bbox_left, float bbox_bottom, float bbox_right, float bbox_top); struct HudInventory diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 39cb801e7..6303f0e27 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -36,6 +36,7 @@ #include "entities_items.hpp" // for Container, Player... #include "entity.hpp" // for get_entity_ptr #include "entity_lookup.hpp" // +#include "game_api.hpp" // #include "game_manager.hpp" // for get_game_manager #include "handle_lua_function.hpp" // for handle_function #include "illumination.hpp" // @@ -671,27 +672,26 @@ end lua["read_prng"] = []() -> std::vector { return read_prng(); }; - using Toast = void(const char16_t*); - using Say = void(size_t, Entity*, const char16_t*, int, bool); - /// Show a message that looks like a level feeling. lua["toast"] = [](std::u16string message) { + using Toast = void(const char16_t*); static Toast* toast_fun = (Toast*)get_address("toast"); toast_fun(message.c_str()); }; /// Show a message coming from an entity lua["say"] = [](uint32_t entity_uid, std::u16string message, int sound_type, bool top) { + using Say = void(HudData*, Entity*, const char16_t*, int, bool); static auto say = (Say*)get_address("speech_bubble_fun"); - static const auto say_context = get_address("say_context"); + const auto hud = get_hud(); auto entity = get_entity_ptr(entity_uid); if (entity == nullptr) return; - say(say_context, entity, message.c_str(), sound_type, top); + say(hud, entity, message.c_str(), sound_type, top); }; /// Add an integer option that the user can change in the UI. Read with `options.name`, `value` is the default. Keep in mind these are just soft /// limits, you can override them in the UI with double click. @@ -1108,7 +1108,7 @@ end /// Set the `more_flags` field from entity by uid lua["set_entity_flags2"] = set_entity_flags2; /// Deprecated - /// As the name is misleading. use entity `move_state` field instead + /// As the name is misleading. use Movable.`move_state` field instead lua["get_entity_ai_state"] = get_entity_ai_state; /// Get `state.level_flags` lua["get_level_flags"] = get_level_flags; @@ -1118,7 +1118,10 @@ end lua["get_entity_type"] = get_entity_type; /// Get the current set zoom level lua["get_zoom_level"] = []() -> float - { return State::get_zoom_level(); }; + { + auto game_api = GameAPI::get(); + return game_api->get_current_zoom(); + }; /// Get the game coordinates at the screen position (`x`, `y`) lua["game_position"] = [](float x, float y) -> std::pair { return State::click_position(x, y); }; diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index e9b59b411..46881a4a5 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -572,25 +572,25 @@ std::unordered_map g_address_rules{ .decode_pc(4) .at_exe(), }, - { - "render_api_callback"sv, - // Break at startup on SteamAPI_RegisterCallback, it gets called twice, second time - // to hook the Steam overlay, at the beginning of that function is the pointer we need - PatternCommandBuffer{} - .find_inst("\x70\x08\x00\x00\xFE\xFF\xFF\xFF\x48\x8B\x05"sv) - .offset(0x8) - .decode_pc() - .at_exe(), - }, - { - // Find reference to SetWindowPos or SetWindowLongA, below one of the references you should see a few instructions like: - // mov rcx,qword ptr ds:[rsi+80FD0] - "render_api_offset"sv, - PatternCommandBuffer{} - .find_inst("\xBA\xF0\xFF\xFF\xFF\x41\xB8\x00\x00\x00\x90"sv) - .offset(0x11) - .decode_imm(), - }, + //{ + // "render_api_callback"sv, + // // Break at startup on SteamAPI_RegisterCallback, it gets called twice, second time + // // to hook the Steam overlay, at the beginning of that function is the pointer we need + // PatternCommandBuffer{} + // .find_inst("\x70\x08\x00\x00\xFE\xFF\xFF\xFF\x48\x8B\x05"sv) + // .offset(0x8) + // .decode_pc() + // .at_exe(), + //}, + //{ + // // Find reference to SetWindowPos or SetWindowLongA, below one of the references you should see a few instructions like: + // // mov rcx,qword ptr ds:[rsi+80FD0] + // "render_api_offset"sv, + // PatternCommandBuffer{} + // .find_inst("\xBA\xF0\xFF\xFF\xFF\x41\xB8\x00\x00\x00\x90"sv) + // .offset(0x11) + // .decode_imm(), + //}, { // in load_item it's written to RCX and then calls spawn_entity "entity_factory"sv, @@ -1044,30 +1044,30 @@ std::unordered_map g_address_rules{ .decode_pc(2) .at_exe(), }, - { - "zoom_level"sv, - // Go stand in a level next to a shop. In Cheat Engine, search for float 13.5 - // Go into the shop, search for 12.5, put a write bp on that address. - // In 1.23.3 the default shop and level zoom levels aren't hardcoded in the exe, they are - // in variables (loaded into xmm6) - // Note that xmm6 (the new zoom level) gets written at a huge offset of rax. Rax is the - // return value of the call just above, so look in that function at the bottom. There will - // be a hardcoded value loaded in rax. At offset 0x10 in rax is another pointer that is the - // base for the big offset. - PatternCommandBuffer{} - .find_inst("\x48\x8B\x05****\x48\x81\xC4\xF8\x08\x00\x00"sv) - .decode_pc() - .at_exe(), - }, - { - "zoom_level_offset"sv, - // Follow the same logic as in `zoom_level` to get to the point where the zoom level is written. - // That instruction contains the offset, the memory is: {current_zoom, target_zoom} and both offset will be present - // current solution uses the target_zoom offset - PatternCommandBuffer{} - .find_inst("\xF3\x0F\x11\xB0****\x49"sv) - .decode_imm(4), - }, + //{ + // "zoom_level"sv, + // // Go stand in a level next to a shop. In Cheat Engine, search for float 13.5 + // // Go into the shop, search for 12.5, put a write bp on that address. + // // In 1.23.3 the default shop and level zoom levels aren't hardcoded in the exe, they are + // // in variables (loaded into xmm6) + // // Note that xmm6 (the new zoom level) gets written at a huge offset of rax. Rax is the + // // return value of the call just above, so look in that function at the bottom. There will + // // be a hardcoded value loaded in rax. At offset 0x10 in rax is another pointer that is the + // // base for the big offset. + // PatternCommandBuffer{} + // .find_inst("\x48\x8B\x05****\x48\x81\xC4\xF8\x08\x00\x00"sv) + // .decode_pc() + // .at_exe(), + //}, + //{ + // "zoom_level_offset"sv, + // // Follow the same logic as in `zoom_level` to get to the point where the zoom level is written. + // // That instruction contains the offset, the memory is: {current_zoom, target_zoom} and both offset will be present + // // current solution uses the target_zoom offset + // PatternCommandBuffer{} + // .find_inst("\xF3\x0F\x11\xB0****\x49"sv) + // .decode_imm(4), + //}, { "default_zoom_level"sv, // Follow the same logic as in `zoom_level` to get to the point where the zoom level is written. @@ -1534,15 +1534,6 @@ std::unordered_map g_address_rules{ .at_exe() .function_start(), }, - { - "say_context"sv, - // Find the pattern for `say`, go one up higher in the callstack and look what writes to rcx - PatternCommandBuffer{} - .find_after_inst("\xC6\x44\x24\x20\x01\x48\x8D\x0D"sv) - .offset(-0x3) - .decode_pc() - .at_exe(), - }, { "force_dark_level"sv, // Put a write bp on State.level_flags (3rd byte, containing dark level flag) @@ -2070,6 +2061,15 @@ std::unordered_map g_address_rules{ .offset(0x5) .at_exe(), }, + { + "get_game_api"sv, + // can be found together with get_feat function + PatternCommandBuffer{} + .find_after_inst("49 89 CE 4C 8B 79 08"_gh) + .find_inst("\xE8"sv) + .decode_call() + .at_exe(), + }, }; std::unordered_map g_cached_addresses; @@ -2081,6 +2081,13 @@ void preload_addresses() { if (auto address = rule(mem, exe, address_name)) { + for (auto& [k, v] : g_cached_addresses) + { + if (v == address.value() && k != address_name) + { + DEBUG("Two patterns refer to the same address: {} & {}", k, address_name); + } + } g_cached_addresses[address_name] = address.value(); } } diff --git a/src/game_api/settings_api.cpp b/src/game_api/settings_api.cpp index 1a1eba15c..604c70ddf 100644 --- a/src/game_api/settings_api.cpp +++ b/src/game_api/settings_api.cpp @@ -8,8 +8,8 @@ #include // for hash, move, conditional_t #include // for unordered_set, _Uset_traits<>::allocator_type -#include "render_api.hpp" // for RenderAPI -#include "search.hpp" // for get_address +#include "game_api.hpp" // +#include "search.hpp" // for get_address union SettingValue { @@ -102,8 +102,11 @@ std::optional get_setting(GAME_SETTING setting) { if ((setting == GAME_SETTING::FREQUENCY_NUMERATOR || setting == GAME_SETTING::FREQUENCY_DENOMINATOR) && get_setting(GAME_SETTING::WINDOW_MODE) == 1u) { - size_t renderer = RenderAPI::get().renderer(); - return *reinterpret_cast(renderer + (setting == GAME_SETTING::FREQUENCY_NUMERATOR ? 0x10 : 0x14)); + auto game_api = GameAPI::get(); + if (setting == GAME_SETTING::FREQUENCY_NUMERATOR) + return game_api->renderer->fps; + else + return game_api->renderer->fps_denominator; } if (SettingData* data = get_setting_data(setting)) diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index 7ef2c9319..5873fedc1 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -13,6 +13,7 @@ #include "entities_chars.hpp" // for Player #include "entity.hpp" // for to_id, Entity, HookWithId, EntityDB #include "entity_hooks_info.hpp" // for Player +#include "game_api.hpp" // #include "game_manager.hpp" // for get_game_manager, GameManager, SaveR... #include "game_patches.hpp" // #include "items.hpp" // for Items, SelectPlayerSlot @@ -332,6 +333,12 @@ StateMemory* State::ptr_local() const return p.decode_local(); } +float get_zoom_level() +{ + auto game_api = GameAPI::get(); + return game_api->get_current_zoom(); +} + std::pair State::click_position(float x, float y) { float cz = get_zoom_level(); @@ -350,40 +357,6 @@ std::pair State::screen_position(float x, float y) return {rx, ry}; } -size_t State::get_zoom_level_address() -{ - static const size_t obj1 = get_address("zoom_level"); - static const size_t zoom_level_offset = get_address("zoom_level_offset"); - size_t obj2 = memory_read(obj1); - if (obj2 == 0) - { - return 0; - } - - size_t obj3 = memory_read(obj2 + 0x10); - if (obj3 == 0) - { - return 0; - } - return obj3 + zoom_level_offset; -} - -float State::get_zoom_level() -{ - static size_t offset = 0; - if (offset == 0) - { - auto addr = get_zoom_level_address(); - if (addr == 0) - { - return 13.5; - } - offset = addr - 4; - } - auto state = State::get().ptr(); - return memory_read(offset) + get_layer_zoom_offset(state->camera_layer); -} - void State::zoom(float level) { auto roomx = ptr()->w; @@ -434,11 +407,8 @@ void State::zoom(float level) write_mem_prot(zoom_telescope, level_str, true); // overwrite the current value - auto zla = get_zoom_level_address(); - if (zla != 0) - { - write_mem_prot(zla, level_str, true); - } + auto game_api = GameAPI::get(); + game_api->set_zoom(std::nullopt, level); } void StateMemory::force_current_theme(uint32_t t) diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index f394f7fa9..7cf1b152c 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -293,8 +293,7 @@ struct StateMemory int8_t unknown41; // other character related (hired hand, basecamp characters) uint8_t unknown42; uint8_t unknown43; - uint32_t unknown44; - uint64_t unknown45; + uint32_t unknown44; // probably padding /// This function should only be used in a very specific circumstance (forcing the exiting theme when manually transitioning). Will crash the game if used inappropriately! void force_current_theme(uint32_t t); @@ -336,8 +335,6 @@ struct State void godmode_companions(bool g); void darkmode(bool g); - static size_t get_zoom_level_address(); - static float get_zoom_level(); void zoom(float level); static std::pair click_position(float x, float y); diff --git a/src/game_api/texture.cpp b/src/game_api/texture.cpp index bb495b842..3cbd0a5b1 100644 --- a/src/game_api/texture.cpp +++ b/src/game_api/texture.cpp @@ -261,8 +261,6 @@ void reload_texture(const char* texture_name) } void reload_texture(const char** texture_name) { - class Renderer; - using LoadTextureFunT = void(Renderer*, const char**); auto& render = RenderAPI::get(); @@ -272,7 +270,7 @@ void reload_texture(const char** texture_name) // to the wanted function static constexpr size_t c_LoadTextureVirtualIndex = 0x2E; - auto renderer_ptr = (Renderer*)render.renderer(); + auto renderer_ptr = render.renderer(); auto load_texture = *vtable_find(renderer_ptr, c_LoadTextureVirtualIndex); load_texture(renderer_ptr, texture_name); } diff --git a/src/injected/ui_util.cpp b/src/injected/ui_util.cpp index aee03ee37..8a31c699c 100644 --- a/src/injected/ui_util.cpp +++ b/src/injected/ui_util.cpp @@ -14,6 +14,7 @@ #include "entities_mounts.hpp" // for Mount #include "entity.hpp" // for to_id, Entity, get_entity_ptr #include "entity_lookup.hpp" // +#include "game_api.hpp" // #include "game_manager.hpp" // for get_game_manager, GameManager #include "illumination.hpp" // #include "items.hpp" // for Items @@ -84,7 +85,8 @@ void UI::transition(uint8_t world, uint8_t level, uint8_t theme) } float UI::get_zoom_level() { - return State::get_zoom_level(); + auto game_api = GameAPI::get(); + return game_api->get_current_zoom(); } void UI::teleport(float x, float y, bool s, float vx, float vy, bool snap) {