From 5b76d155a87cc3be4bafce982790a3c2aa3eb676 Mon Sep 17 00:00:00 2001 From: acidicoala <67734819+acidicoala@users.noreply.github.com> Date: Sun, 29 May 2022 16:57:34 +0300 Subject: [PATCH] Added `IClientApps`, `IClientAppManager`, `IClientUser` --- .github/workflows/ci.yml | 2 +- CMakeLists.txt | 5 ++ KoalaBox | 2 +- src/koalageddon/steamclient.cpp | 77 +++++++++++-------- src/smoke_api/smoke_api.cpp | 4 +- src/smoke_api/smoke_api.hpp | 2 +- src/steam_api_exports/steam_api_flat.cpp | 8 +- src/steam_api_virtuals/isteamapps.cpp | 8 +- src/steam_functions/steam_functions.hpp | 10 +++ src/steam_impl/steam_apps.cpp | 64 +++++++-------- src/steam_impl/steam_impl.hpp | 9 +-- .../client_app_manager.cpp | 13 ++++ src/steamclient_virtuals/client_apps.cpp | 31 ++++++++ src/steamclient_virtuals/client_inventory.cpp | 0 src/steamclient_virtuals/client_user.cpp | 8 ++ src/steamclient_virtuals/client_utils.cpp | 0 16 files changed, 162 insertions(+), 81 deletions(-) create mode 100644 src/steamclient_virtuals/client_app_manager.cpp create mode 100644 src/steamclient_virtuals/client_apps.cpp create mode 100644 src/steamclient_virtuals/client_inventory.cpp create mode 100644 src/steamclient_virtuals/client_user.cpp create mode 100644 src/steamclient_virtuals/client_utils.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1280ce3..fb79eba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: push jobs: ci: name: CI - uses: acidicoala/KoalaBox/.github/workflows/build-and-package.yml@67545f50bc9e557eaf43a74395cc1461416d0035 + uses: acidicoala/KoalaBox/.github/workflows/build-and-package.yml@d9b0d1a00beb065a9a931a60ef615ce8d1fc7164 permissions: contents: write with: diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cd2ecb..eb3e24f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,11 @@ if (CMAKE_SIZEOF_VOID_P EQUAL 4) SMOKE_API_SOURCES ${SMOKE_API_SOURCES} src/koalageddon/vstdlib.cpp src/koalageddon/steamclient.cpp + src/steamclient_virtuals/client_app_manager.cpp + src/steamclient_virtuals/client_apps.cpp + src/steamclient_virtuals/client_inventory.cpp + src/steamclient_virtuals/client_user.cpp + src/steamclient_virtuals/client_utils.cpp ) endif () diff --git a/KoalaBox b/KoalaBox index 67545f5..d9b0d1a 160000 --- a/KoalaBox +++ b/KoalaBox @@ -1 +1 @@ -Subproject commit 67545f50bc9e557eaf43a74395cc1461416d0035 +Subproject commit d9b0d1a00beb065a9a931a60ef615ce8d1fc7164 diff --git a/src/koalageddon/steamclient.cpp b/src/koalageddon/steamclient.cpp index c444515..1a6ee7a 100644 --- a/src/koalageddon/steamclient.cpp +++ b/src/koalageddon/steamclient.cpp @@ -5,43 +5,54 @@ using namespace smoke_api; + DLL_EXPORT(void) Log_Interface(const char* interface_name, const char* function_name) { - void***** parent_ebp; + try { + void**** parent_ebp; - __asm mov parent_ebp, ebp + __asm mov parent_ebp, ebp - auto* interface_address = *((*parent_ebp)[2]); + auto* interface_address = (*parent_ebp)[2]; - if (util::strings_are_equal(interface_name, "IClientAppManager")) { - if (util::strings_are_equal(function_name, "IsAppDlcInstalled")) { - auto* function_address = interface_address[0x8]; // TODO: Un-hardcode - logger->debug("{} -> {}::{} @ {}", __func__, interface_name, function_name, function_address); - } - } else if (util::strings_are_equal(interface_name, "IClientApps")) { - if (util::strings_are_equal(function_name, "BGetDLCDataByIndex")) { - auto* function_address = interface_address[0x9]; // TODO: Un-hardcode - logger->debug("{} -> {}::{} @ {}", __func__, interface_name, function_name, function_address); - } else if (util::strings_are_equal(function_name, "GetDLCCount")) { - auto* function_address = interface_address[0x8]; // TODO: Un-hardcode - logger->debug("{} -> {}::{} @ {}", __func__, interface_name, function_name, function_address); - } - } else if (util::strings_are_equal(interface_name, "IClientInventory")) { - if (util::strings_are_equal(function_name, "GetResultItems")) { - auto* function_address = interface_address[0x2]; // TODO: Un-hardcode - logger->debug("{} -> {}::{} @ {}", __func__, interface_name, function_name, function_address); - } - } else if (util::strings_are_equal(interface_name, "IClientUser")) { - if (util::strings_are_equal(function_name, "IsSubscribedApp")) { - auto* function_address = interface_address[0xB5]; // TODO: Un-hardcode - logger->debug("{} -> {}::{} @ {}", __func__, interface_name, function_name, function_address); - } - } else if (util::strings_are_equal(interface_name, "IClientUtils")) { - if (util::strings_are_equal(function_name, "GetAppID")) { - auto* function_address = interface_address[0x12]; // TODO: Un-hardcode - logger->debug("{} -> {}::{} @ {}", __func__, interface_name, function_name, function_address); + static Set hooked_functions; + + auto hook_function = [&](const auto hook_function, const String& name, const int ordinal) { + if (hooked_functions.contains(name)) { + return; + } + + hook::swap_virtual_func_or_throw(interface_address, name, ordinal, (FunctionAddress) (hook_function)); + + hooked_functions.insert(name); + }; + + const auto compound_name = interface_name + String("::") + function_name; + +#define HOOK(FUNC, ORDINAL) hook_function(FUNC, #FUNC, ORDINAL); + + if (compound_name == "IClientAppManager::IsAppDlcInstalled") { + HOOK(IClientAppManager_IsAppDlcInstalled, 8) + } else if (compound_name == "IClientApps::GetDLCCount") { + HOOK(IClientApps_GetDLCCount, 8) + } else if (compound_name == "IClientApps::BGetDLCDataByIndex") { + HOOK(IClientApps_BGetDLCDataByIndex, 9) + } else if (compound_name == "IClientUser::IsSubscribedApp") { + HOOK(IClientUser_IsSubscribedApp, 0xB5) + } else if (util::strings_are_equal(interface_name, "IClientInventory")) { + if (util::strings_are_equal(function_name, "GetResultItems")) { + auto* function_address = interface_address[0x2]; // TODO: Un-hardcode + logger->debug("{} -> {}::{} @ {}", __func__, interface_name, function_name, function_address); + } + } else if (util::strings_are_equal(interface_name, "IClientUtils")) { + if (util::strings_are_equal(function_name, "GetAppID")) { + auto* function_address = interface_address[0x12]; // TODO: Un-hardcode + logger->debug("{} -> {}::{} @ {}", __func__, interface_name, function_name, function_address); + } } + + GET_ORIGINAL_FUNCTION(Log_Interface) + Log_Interface_o(interface_name, function_name); + } catch (const Exception& ex) { + logger->error("{} -> Error: {}", __func__, ex.what()); } - - GET_ORIGINAL_FUNCTION(Log_Interface) - Log_Interface_o(interface_name, function_name); } diff --git a/src/smoke_api/smoke_api.cpp b/src/smoke_api/smoke_api.cpp index 5fadaa3..35bc732 100644 --- a/src/smoke_api/smoke_api.cpp +++ b/src/smoke_api/smoke_api.cpp @@ -135,8 +135,8 @@ namespace smoke_api { } } - bool should_unlock(uint32_t appId) { - return config.unlock_all != config.override.contains(appId); + bool should_unlock(uint32_t app_id) { + return config.unlock_all != config.override.contains(app_id); } } diff --git a/src/smoke_api/smoke_api.hpp b/src/smoke_api/smoke_api.hpp index 4f8af98..80e53fe 100644 --- a/src/smoke_api/smoke_api.hpp +++ b/src/smoke_api/smoke_api.hpp @@ -58,6 +58,6 @@ namespace smoke_api { void shutdown(); - bool should_unlock(uint32_t appId); + bool should_unlock(uint32_t app_id); } diff --git a/src/steam_api_exports/steam_api_flat.cpp b/src/steam_api_exports/steam_api_flat.cpp index 1b64531..d9c40c7 100644 --- a/src/steam_api_exports/steam_api_flat.cpp +++ b/src/steam_api_exports/steam_api_flat.cpp @@ -6,15 +6,15 @@ using namespace smoke_api; // ISteamApps DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsSubscribedApp(ISteamApps*, AppId_t appID) { - return steam_apps::IsSubscribedApp(__func__, appID); + return steam_apps::IsDlcUnlocked(__func__, 0, appID); } DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsDlcInstalled(ISteamApps*, AppId_t appID) { - return steam_apps::IsDlcInstalled(__func__, appID); + return steam_apps::IsDlcUnlocked(__func__, 0, appID); } DLL_EXPORT(int) SteamAPI_ISteamApps_GetDLCCount(ISteamApps* self) { - return steam_apps::GetDLCCount(__func__, [&]() { + return steam_apps::GetDLCCount(__func__, 0, [&]() { GET_ORIGINAL_FUNCTION(SteamAPI_ISteamApps_GetDLCCount) return SteamAPI_ISteamApps_GetDLCCount_o(self); @@ -29,7 +29,7 @@ DLL_EXPORT(bool) SteamAPI_ISteamApps_BGetDLCDataByIndex( char* pchName, int cchNameBufferSize ) { - return steam_apps::GetDLCDataByIndex(__func__, iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize, [&]() { + return steam_apps::GetDLCDataByIndex(__func__, 0, iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize, [&]() { GET_ORIGINAL_FUNCTION(SteamAPI_ISteamApps_BGetDLCDataByIndex) return SteamAPI_ISteamApps_BGetDLCDataByIndex_o( diff --git a/src/steam_api_virtuals/isteamapps.cpp b/src/steam_api_virtuals/isteamapps.cpp index f80e261..2acd59a 100644 --- a/src/steam_api_virtuals/isteamapps.cpp +++ b/src/steam_api_virtuals/isteamapps.cpp @@ -4,15 +4,15 @@ using namespace smoke_api; VIRTUAL(bool) ISteamApps_BIsSubscribedApp(PARAMS(AppId_t appID)) { // NOLINT(misc-unused-parameters) - return steam_apps::IsSubscribedApp(__func__, appID); + return steam_apps::IsDlcUnlocked(__func__, 0, appID); } VIRTUAL(bool) ISteamApps_BIsDlcInstalled(PARAMS(AppId_t appID)) { // NOLINT(misc-unused-parameters) - return steam_apps::IsDlcInstalled(__func__, appID); + return steam_apps::IsDlcUnlocked(__func__, 0, appID); } VIRTUAL(int) ISteamApps_GetDLCCount(PARAMS()) { - return steam_apps::GetDLCCount(__func__, [&]() { + return steam_apps::GetDLCCount(__func__, 0, [&]() { GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamApps_GetDLCCount) return ISteamApps_GetDLCCount_o(ARGS()); @@ -28,7 +28,7 @@ VIRTUAL(bool) ISteamApps_BGetDLCDataByIndex( int cchNameBufferSize ) ) { - return steam_apps::GetDLCDataByIndex(__func__, iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize, [&]() { + return steam_apps::GetDLCDataByIndex(__func__, 0, iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize, [&]() { GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamApps_BGetDLCDataByIndex) return ISteamApps_BGetDLCDataByIndex_o( diff --git a/src/steam_functions/steam_functions.hpp b/src/steam_functions/steam_functions.hpp index d5496e5..10b59a6 100644 --- a/src/steam_functions/steam_functions.hpp +++ b/src/steam_functions/steam_functions.hpp @@ -123,6 +123,16 @@ DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionProperty( DLL_EXPORT(HCoroutine) Coroutine_Create(void* callback_address, struct CoroutineData* data); DLL_EXPORT(void) Log_Interface(const char* interface_name, const char* function_name); +// IClientApps +VIRTUAL(int) IClientApps_GetDLCCount(PARAMS(AppId_t)); +VIRTUAL(bool) IClientApps_BGetDLCDataByIndex(PARAMS(AppId_t, int, AppId_t*, bool*, char*, int)); + +// IClientAppManager +VIRTUAL(bool) IClientAppManager_IsAppDlcInstalled(PARAMS(AppId_t, AppId_t)); + +// IClientUser +VIRTUAL(bool) IClientUser_IsSubscribedApp(PARAMS(AppId_t)); + namespace steam_functions { using namespace koalabox; diff --git a/src/steam_impl/steam_apps.cpp b/src/steam_impl/steam_apps.cpp index 805f927..6c1d703 100644 --- a/src/steam_impl/steam_apps.cpp +++ b/src/steam_impl/steam_apps.cpp @@ -51,14 +51,15 @@ void save_to_cache(const String& app_id_str) { } } -void fetch_and_cache_dlcs() { - uint32_t app_id; - try { - app_id = steam_functions::get_app_id_or_throw(); - logger->info("Detected App ID: {}", app_id); - } catch (const Exception& ex) { - logger->error("Failed to get app ID: {}", ex.what()); - return; +void fetch_and_cache_dlcs(AppId_t app_id) { + if (not app_id) { + try { + app_id = steam_functions::get_app_id_or_throw(); + logger->info("Detected App ID: {}", app_id); + } catch (const Exception& ex) { + logger->error("Failed to get app ID: {}", ex.what()); + return; + } } const auto app_id_str = std::to_string(app_id); @@ -131,29 +132,31 @@ void fetch_and_cache_dlcs() { } } +String get_app_id_log(const AppId_t app_id) { + return app_id ? fmt::format("App ID: {}, ", app_id) : ""; +} -namespace steam_apps{ +namespace steam_apps { - bool IsSubscribedApp(const String& function_name, AppId_t appID) { - const auto subscribed = should_unlock(appID); + bool IsDlcUnlocked(const String& function_name, AppId_t app_id, AppId_t dlc_id) { + const auto app_id_unlocked = not app_id or should_unlock(app_id); // true if app_id == 0 + const auto dlc_id_unlocked = should_unlock(dlc_id); - logger->info("{} -> App ID: {}, Subscribed: {}", function_name, appID, subscribed); + const auto installed = app_id_unlocked and dlc_id_unlocked; - return subscribed; - } - - bool IsDlcInstalled(const String& function_name, AppId_t appID) { - const auto installed = should_unlock(appID); - - logger->info("{} -> App ID: {}, Installed: {}", function_name, appID, installed); + logger->info("{} -> {}DLC ID: {}, Unlocked: {}", function_name, get_app_id_log(app_id), dlc_id, installed); return installed; } - int GetDLCCount(const String& function_name, const std::function& original_function) { + int GetDLCCount(const String& function_name, const AppId_t app_id, const std::function& original_function) { static std::mutex section; std::lock_guard guard(section); + if (app_id) { + logger->debug("{} -> App ID: {}", function_name, app_id); + } + // Compute count only once static int total_count = [&]() { original_dlc_count = original_function(); @@ -171,7 +174,7 @@ namespace steam_apps{ } logger->debug("Game has {} or more DLCs. Fetching DLCs from a web API.", max_dlc); - fetch_and_cache_dlcs(); + fetch_and_cache_dlcs(app_id); const auto fetched_count = static_cast(cached_dlcs.size()); logger->debug("{} -> Fetched/cached DLC count: {}", function_name, fetched_count); @@ -186,8 +189,9 @@ namespace steam_apps{ bool GetDLCDataByIndex( const String& function_name, + AppId_t app_id, int iDLC, - AppId_t* pAppID, + AppId_t* pDlcId, bool* pbAvailable, char* pchName, int cchNameBufferSize, @@ -195,13 +199,13 @@ namespace steam_apps{ ) { const auto print_dlc_info = [&](const String& tag) { logger->info( - "{} -> [{}] index: {}, App ID: {}, available: {}, name: '{}'", - function_name, tag, iDLC, *pAppID, *pbAvailable, pchName + "{} -> [{}] {}index: {}, DLC ID: {}, available: {}, name: '{}'", + function_name, tag, get_app_id_log(app_id), iDLC, *pDlcId, *pbAvailable, pchName ); }; const auto fill_dlc_info = [&](const AppId_t id) { - *pAppID = id; + *pDlcId = id; *pbAvailable = should_unlock(id); auto name = fmt::format("DLC #{} with ID: {} ", iDLC, id); @@ -216,8 +220,8 @@ namespace steam_apps{ return false; } - const auto app_id = config.dlc_ids[index]; - fill_dlc_info(app_id); + const auto dlc_id = config.dlc_ids[index]; + fill_dlc_info(dlc_id); print_dlc_info("injected"); return true; }; @@ -229,7 +233,7 @@ namespace steam_apps{ const auto success = original_function(); if (success) { - *pbAvailable = should_unlock(*pAppID); + *pbAvailable = should_unlock(*pDlcId); print_dlc_info("original"); } else { logger->warn("{} -> original function failed for index: {}", function_name, iDLC); @@ -254,8 +258,8 @@ namespace steam_apps{ // Cached index if (iDLC < cached_dlcs.size()) { - const auto app_id = cached_dlcs[iDLC]; - fill_dlc_info(app_id); + const auto dlc_id = cached_dlcs[iDLC]; + fill_dlc_info(dlc_id); print_dlc_info("cached"); return true; } diff --git a/src/steam_impl/steam_impl.hpp b/src/steam_impl/steam_impl.hpp index 884516c..48b4708 100644 --- a/src/steam_impl/steam_impl.hpp +++ b/src/steam_impl/steam_impl.hpp @@ -4,16 +4,15 @@ using namespace koalabox; namespace steam_apps { - bool IsSubscribedApp(const String& function_name, AppId_t appID); + bool IsDlcUnlocked(const String& function_name, AppId_t app_id, AppId_t dlc_id); - bool IsDlcInstalled(const String& function_name, AppId_t appID); - - int GetDLCCount(const String& function_name, const std::function& original_function); + int GetDLCCount(const String& function_name, AppId_t app_id, const std::function& original_function); bool GetDLCDataByIndex( const String& function_name, + AppId_t app_id, int iDLC, - AppId_t* pAppID, + AppId_t* pDlcId, bool* pbAvailable, char* pchName, int cchNameBufferSize, diff --git a/src/steamclient_virtuals/client_app_manager.cpp b/src/steamclient_virtuals/client_app_manager.cpp new file mode 100644 index 0000000..770f9d5 --- /dev/null +++ b/src/steamclient_virtuals/client_app_manager.cpp @@ -0,0 +1,13 @@ +#include +#include + +using namespace smoke_api; + +VIRTUAL(bool) IClientAppManager_IsAppDlcInstalled( + PARAMS( // NOLINT(misc-unused-parameters) + AppId_t app_id, + AppId_t dlc_id + ) +) { + return steam_apps::IsDlcUnlocked(__func__, app_id, dlc_id); +} \ No newline at end of file diff --git a/src/steamclient_virtuals/client_apps.cpp b/src/steamclient_virtuals/client_apps.cpp new file mode 100644 index 0000000..73257a2 --- /dev/null +++ b/src/steamclient_virtuals/client_apps.cpp @@ -0,0 +1,31 @@ +#include +#include + +using namespace smoke_api; + +VIRTUAL(int) IClientApps_GetDLCCount(PARAMS(AppId_t appId)) { + return steam_apps::GetDLCCount(__func__, appId, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(IClientApps_GetDLCCount) + + return IClientApps_GetDLCCount_o(ARGS(appId)); + }); +} + +VIRTUAL(bool) IClientApps_BGetDLCDataByIndex( + PARAMS( + AppId_t appID, + int iDLC, + AppId_t* pDlcID, + bool* pbAvailable, + char* pchName, + int cchNameBufferSize + ) +) { + return steam_apps::GetDLCDataByIndex(__func__, appID, iDLC, pDlcID, pbAvailable, pchName, cchNameBufferSize, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(IClientApps_BGetDLCDataByIndex) + + return IClientApps_BGetDLCDataByIndex_o( + ARGS(appID, iDLC, pDlcID, pbAvailable, pchName, cchNameBufferSize) + ); + }); +} diff --git a/src/steamclient_virtuals/client_inventory.cpp b/src/steamclient_virtuals/client_inventory.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/steamclient_virtuals/client_user.cpp b/src/steamclient_virtuals/client_user.cpp new file mode 100644 index 0000000..776c04b --- /dev/null +++ b/src/steamclient_virtuals/client_user.cpp @@ -0,0 +1,8 @@ +#include +#include + +using namespace smoke_api; + +VIRTUAL(bool) IClientUser_IsSubscribedApp(PARAMS(AppId_t app_id)) { // NOLINT(misc-unused-parameters) + return steam_apps::IsDlcUnlocked(__func__, 0, app_id); +} diff --git a/src/steamclient_virtuals/client_utils.cpp b/src/steamclient_virtuals/client_utils.cpp new file mode 100644 index 0000000..e69de29