diff --git a/CMakeLists.txt b/CMakeLists.txt index 351bbba..09e1630 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,6 @@ set(SMOKE_API_SOURCES src/steam_api/virtuals/isteamhttp.cpp src/steam_api/virtuals/isteaminventory.cpp src/steam_api/virtuals/isteamuser.cpp - src/steam_api/virtuals/isteamutils.cpp src/steam_api/virtuals/steam_api_virtuals.hpp src/steam_api/steam_client.hpp src/steam_api/steam_client.cpp diff --git a/KoalaBox b/KoalaBox index 017dfda..d6afc17 160000 --- a/KoalaBox +++ b/KoalaBox @@ -1 +1 @@ -Subproject commit 017dfda8db8e10b7d9ec2f88c7770a1cb438fbb7 +Subproject commit d6afc170cc7be89e51b7246497033e667acdd227 diff --git a/src/smoke_api/smoke_api.cpp b/src/smoke_api/smoke_api.cpp index 24c6183..2b5f407 100644 --- a/src/smoke_api/smoke_api.cpp +++ b/src/smoke_api/smoke_api.cpp @@ -114,6 +114,50 @@ namespace { void init_lib_monitor() { kb::lib_monitor::init_listener({{STEAMCLIENT_DLL, on_steamclient_loaded}}); } + + std::optional get_app_id_from_env() noexcept { + if(const auto app_id_str = kb::util::get_env("SteamAppId")) { + try { + const auto app_id = std::stoi(*app_id_str); + + LOG_DEBUG("Found AppID from environment: {}", app_id); + return app_id; + } catch(const std::exception& e) { + LOG_ERROR("Failed to parse AppID '{}' from environment: {}", *app_id_str, e.what()); + } + } + + return std::nullopt; + } + + std::optional get_app_id_from_steam_client() noexcept { + try { + DECLARE_ARGS(); + + const auto& version_map = steam_interfaces::get_interface_name_to_version_map(); + if((THIS = CreateInterface(version_map.at("ISteamClient").c_str(), nullptr))) { + if(const auto get_steam_utils = SMK_FIND_INTERFACE_FUNC(THIS, ISteamClient, GetISteamUtils)) { + constexpr auto steam_pipe = 1; + const auto& utils_version = version_map.at("ISteamUtils"); + if((THIS = get_steam_utils(ARGS(steam_pipe, utils_version.c_str())))) { + if(const auto get_app_id = SMK_FIND_INTERFACE_FUNC(THIS, ISteamUtils, GetAppID)) { + if(const auto app_id = get_app_id(ARGS())) { + LOG_DEBUG("Found AppID from ISteamUtils: {}", app_id); + return app_id; + } + LOG_ERROR("ISteamUtils::GetAppID returned 0"); + } + } + } + } else { + LOG_ERROR("Failed to create interface '{}'", version_map.at("ISteamClient")) + } + } catch(const std::exception& e) { + LOG_ERROR("Failed to get app id. Unhandled exception: {}", e.what()); + } + + return std::nullopt; + } } namespace smoke_api { @@ -184,50 +228,28 @@ namespace smoke_api { } AppId_t get_app_id() { - static AppId_t app_id = 0; - if(app_id) { - return app_id; // cached value + static AppId_t cached_app_id = 0; + if(cached_app_id) { + return cached_app_id; } - try { - if(const auto app_id_str = kb::util::get_env("SteamAppId")) { - app_id = std::stoi(*app_id_str); - LOG_DEBUG("Found AppID from environment: {}", app_id); + LOG_DEBUG("No cached App ID found. Searching in environment variables."); - return app_id; - } - } catch(std::exception&) { - LOG_WARN("No SteamAppId in environment. Falling back to ISteamUtils::GetAppID."); + if(const auto opt_app_id = get_app_id_from_env()) { + return cached_app_id = *opt_app_id; } - // TODO: Then try to read steam_appid.txt here. SteamAppId env var is not available when it's present. + LOG_WARN("Failed to find App ID in environment variables. Falling back to ISteamUtils::GetAppID."); - try { - DECLARE_ARGS(); + // IDEA: Try to read steam_appid.txt here. SteamAppId env var is not available when it's present. + // But what if the ID specified in steam_appid.txt is invalid? - THIS = CreateInterface("SteamClient007", nullptr); - if(!THIS) { - LOG_ERROR("Failed to create SteamClient interface"); - return 0; - } - - THIS = ISteamClient_GetISteamGenericInterface(ARGS(1, 1, "SteamUtils002")); - if(!THIS) { - LOG_ERROR("Failed to get SteamUtils interface"); - return 0; - } - - app_id = ISteamUtils_GetAppID(ARGS()); - if(!app_id) { - LOG_ERROR("ISteamUtils::GetAppID returned 0"); - return 0; - } - - LOG_DEBUG("Found AppID from ISteamUtils: {}", app_id); - return app_id; - } catch(const std::exception& e) { - LOG_ERROR("Failed to get app id: {}", e.what()); - return 0; + if(const auto opt_app_id = get_app_id_from_steam_client()) { + return cached_app_id = *opt_app_id; } + + LOG_ERROR("Failed to find App ID"); + + return 0; } } diff --git a/src/smoke_api/smoke_api.hpp b/src/smoke_api/smoke_api.hpp index c52d416..510ec08 100644 --- a/src/smoke_api/smoke_api.hpp +++ b/src/smoke_api/smoke_api.hpp @@ -6,7 +6,6 @@ constexpr auto STEAM_APPS = "STEAMAPPS_INTERFACE_VERSION"; constexpr auto STEAM_CLIENT = "SteamClient"; constexpr auto STEAM_HTTP = "STEAMHTTP_INTERFACE_VERSION"; constexpr auto STEAM_USER = "SteamUser"; -constexpr auto STEAM_UTILS = "SteamUtils"; constexpr auto STEAM_INVENTORY = "STEAMINVENTORY_INTERFACE_V"; constexpr auto STEAM_GAME_SERVER = "SteamGameServer"; diff --git a/src/steam_api/steam_interfaces.cpp b/src/steam_api/steam_interfaces.cpp index 806b3ce..dd44dad 100644 --- a/src/steam_api/steam_interfaces.cpp +++ b/src/steam_api/steam_interfaces.cpp @@ -104,15 +104,8 @@ namespace { } } }, - { - STEAM_UTILS, - interface_data_t{ - .fallback_version = "SteamUtils009", - .entry_map = { - ENTRY(ISteamUtils, GetAppID), - } - } - }, + // Hooking SteamUtils for GetAppID should be avoided, since it leads to crashes in TW:WH3. + // No idea why... }; } @@ -257,4 +250,46 @@ namespace steam_interfaces { LOG_ERROR("{} -> Unhandled exception: {}", __func__, e.what()); } } + + void* find_function( + const void* instance_ptr, + const std::string& interface_name, + const std::string& function_name + ) { + if(!get_interface_name_to_version_map().contains(interface_name)) { + LOG_ERROR("Unsupported interface name: '{}'", interface_name); + return nullptr; + } + const auto& interface_version = get_interface_name_to_version_map().at(interface_name); + + static const auto lookup = read_interface_lookup(); + + if(!lookup.contains(interface_version)) { + LOG_ERROR("Interface '{}' not found in lookup map", interface_version); + return nullptr; + } + + const auto interface_lookup = lookup.at(interface_version); + + if(!interface_lookup.contains(function_name)) { + LOG_ERROR("Function '{}' not found in the map of '{}'", function_name, interface_version); + return nullptr; + } + + const auto ordinal = interface_lookup.at(function_name); + + const auto virtual_class = static_cast(instance_ptr); + return virtual_class->vtable[ordinal]; + } + + const std::map& get_interface_name_to_version_map() { + // Choose minimal supported versions for maximum compatibility + // Is it better to get the interface version found in steam_api library? + static const std::map map = { + {"ISteamClient", "SteamClient007"}, + {"ISteamUtils", "SteamUtils002"}, + }; + + return map; + } } diff --git a/src/steam_api/steam_interfaces.hpp b/src/steam_api/steam_interfaces.hpp index 0c483bf..cfa37a4 100644 --- a/src/steam_api/steam_interfaces.hpp +++ b/src/steam_api/steam_interfaces.hpp @@ -1,7 +1,13 @@ #pragma once +#include #include +#define SMK_FIND_INTERFACE_FUNC(INTERFACE_PTR, INTERFACE_NAME, FUNCTION_NAME) \ + reinterpret_cast( \ + steam_interfaces::find_function(INTERFACE_PTR, #INTERFACE_NAME, #FUNCTION_NAME) \ + ) + namespace steam_interfaces { void hook_virtuals(const void* interface_ptr, const std::string& version_string); @@ -15,4 +21,12 @@ namespace steam_interfaces { void* steamclient_handle, const std::string& steam_client_interface_version ) noexcept; + + void* find_function( + const void* instance_ptr, + const std::string& interface_name, + const std::string& function_name + ); + + const std::map& get_interface_name_to_version_map(); } diff --git a/src/steam_api/virtuals/isteaminventory.cpp b/src/steam_api/virtuals/isteaminventory.cpp index 498c7a3..38145e6 100644 --- a/src/steam_api/virtuals/isteaminventory.cpp +++ b/src/steam_api/virtuals/isteaminventory.cpp @@ -17,7 +17,7 @@ VIRTUAL(bool) ISteamInventory_GetResultItems( SteamItemDetails_t* pOutItemsArray, uint32_t* punOutItemsArraySize ) -) noexcept { +) noexcept { return smoke_api::steam_inventory::GetResultItems( __func__, resultHandle, diff --git a/src/steam_api/virtuals/isteamutils.cpp b/src/steam_api/virtuals/isteamutils.cpp deleted file mode 100644 index cab05bb..0000000 --- a/src/steam_api/virtuals/isteamutils.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -#include "smoke_api/smoke_api.hpp" -#include "smoke_api/interfaces/steam_user.hpp" -#include "steam_api/virtuals/steam_api_virtuals.hpp" - -VIRTUAL(AppId_t) ISteamUtils_GetAppID(PARAMS()) noexcept { - SWAPPED_CALL(THIS, ISteamUtils_GetAppID, ARGS()); -} diff --git a/src/steam_api/virtuals/steam_api_virtuals.hpp b/src/steam_api/virtuals/steam_api_virtuals.hpp index 3730566..cad4314 100644 --- a/src/steam_api/virtuals/steam_api_virtuals.hpp +++ b/src/steam_api/virtuals/steam_api_virtuals.hpp @@ -13,6 +13,7 @@ VIRTUAL(void*) ISteamClient_GetISteamApps(PARAMS(HSteamUser, HSteamPipe, const c VIRTUAL(void*) ISteamClient_GetISteamUser(PARAMS(HSteamUser, HSteamPipe, const char*)) noexcept; VIRTUAL(void*) ISteamClient_GetISteamGenericInterface(PARAMS(HSteamUser, HSteamPipe, const char*)) noexcept; VIRTUAL(void*) ISteamClient_GetISteamInventory(PARAMS(HSteamUser, HSteamPipe, const char*)) noexcept; +VIRTUAL(void*) ISteamClient_GetISteamUtils(PARAMS(HSteamPipe, const char*)) noexcept; // Unhooked // ISteamHTTP VIRTUAL(bool) ISteamHTTP_GetHTTPResponseBodyData(PARAMS(HTTPRequestHandle, const uint8_t*, uint32_t)) noexcept; @@ -43,7 +44,7 @@ VIRTUAL(bool) ISteamInventory_CheckResultSteamID(PARAMS(SteamInventoryResult_t, VIRTUAL(EUserHasLicenseForAppResult) ISteamUser_UserHasLicenseForApp(PARAMS(CSteamID, AppId_t)) noexcept; // ISteamUtils -VIRTUAL(AppId_t) ISteamUtils_GetAppID(PARAMS()) noexcept; +VIRTUAL(AppId_t) ISteamUtils_GetAppID(PARAMS()) noexcept; // Unhooked // ISteamGameServer VIRTUAL(EUserHasLicenseForAppResult) ISteamGameServer_UserHasLicenseForApp(PARAMS(CSteamID, AppId_t)) noexcept; diff --git a/static/smoke_api/types.hpp b/static/smoke_api/types.hpp index ffad415..6be47d4 100644 --- a/static/smoke_api/types.hpp +++ b/static/smoke_api/types.hpp @@ -46,12 +46,12 @@ #define PARAMS(...) const void* RCX __VA_OPT__(,) __VA_ARGS__ #define ARGS(...) RCX __VA_OPT__(,) __VA_ARGS__ #define THIS RCX -#define DECLARE_ARGS() const void* RCX = nullptr; +#define DECLARE_ARGS() void* RCX = nullptr; #else #define PARAMS(...) const void* ECX, const void* EDX __VA_OPT__(,) __VA_ARGS__ #define ARGS(...) ECX, EDX __VA_OPT__(,) __VA_ARGS__ #define THIS ECX -#define DECLARE_ARGS() const void* ECX = nullptr; const void* EDX = nullptr; +#define DECLARE_ARGS() void* ECX = nullptr; const void* EDX = nullptr; #endif using AppId_t = uint32_t; diff --git a/tools/src/steamworks_downloader.cpp b/tools/src/steamworks_downloader.cpp index db927b2..9f98744 100644 --- a/tools/src/steamworks_downloader.cpp +++ b/tools/src/steamworks_downloader.cpp @@ -140,7 +140,7 @@ namespace { fs::remove(zip_file_path); } -} // namespace +} /** * A tool for downloading Steamworks SDK and unpacking its headers and binaries