diff --git a/src/api.cpp b/src/api.cpp index 73d0749..8c7c0a9 100644 --- a/src/api.cpp +++ b/src/api.cpp @@ -1,19 +1,64 @@ #include "api.hpp" #include "sdk/IClientAppManager.hpp" +#include "sdk/IClientApps.hpp" #include "config.hpp" #include "filewatcher.hpp" #include "utils.hpp" +#include #include +#include +#include namespace SLSAPI { + struct InstallRequest + { + uint32_t appId; + int32_t library; + bool requestedAppInfo; + bool loggedWaitingForAppInfo; + }; + const char* path = "/tmp/SLSsteam.API"; std::fstream fstream; CFileWatcher* watcher; + + std::atomic_bool hasPendingRequests = false; + std::mutex pendingRequestMutex; + std::vector pendingInstallRequests; + thread_local uint32_t currentInstallApp = 0; + + bool appInfoSectionsReady(uint32_t appId, bool logMissing) + { + if (!g_pClientApps) + { + if (logMissing) + { + g_pLog->info("API g_pClientApps is nullptr! Can't check appinfo for %u.\n", appId); + } + return false; + } + + char sectionData[1] = {}; + int32_t commonSize = g_pClientApps->getAppDataSection(appId, APPINFOSECTION_COMMON, sectionData, sizeof(sectionData)); + bool ready = commonSize >= 0; + + if (!ready && logMissing) + { + g_pLog->info("API Waiting for appinfo %u common=%i\n", appId, commonSize); + } + + return ready; + } +} + +uint32_t SLSAPI::currentInstallAppId() +{ + return currentInstallApp; } bool SLSAPI::isEnabled() @@ -39,22 +84,20 @@ void SLSAPI::onFileChange() g_pLog->debug("API Running %s\n", cmd); auto split = Utils::strsplit(cmd, "|"); - if (strcmp(split[0].c_str(), "install") == 0 && split.size() > 2) + if (split.size() > 2 && strcmp(split[0].c_str(), "install") == 0) { try { uint32_t appId = std::strtoul(split[1].c_str(), nullptr, 10); - uint32_t library = std::strtoul(split[2].c_str(), nullptr, 10); + int32_t library = std::strtol(split[2].c_str(), nullptr, 10); - if (!g_pClientAppManager) { - g_pLog->info("API g_pClientAppManager is nullptr! Aborting...\n"); - return; + std::lock_guard lock(pendingRequestMutex); + pendingInstallRequests.push_back({appId, library, false, false}); } + hasPendingRequests.store(true); - g_pLog->info("API Installing %s to %s\n", split[1].c_str(), split[2].c_str()); - - g_pClientAppManager->installApp(appId, library); + g_pLog->info("API Queued install %s to %s\n", split[1].c_str(), split[2].c_str()); } catch(...) { @@ -63,6 +106,75 @@ void SLSAPI::onFileChange() } } +void SLSAPI::runPendingRequests() +{ + if (!hasPendingRequests.exchange(false)) + { + return; + } + + std::vector requests; + { + std::lock_guard lock(pendingRequestMutex); + requests.swap(pendingInstallRequests); + } + + std::vector requeueRequests; + for (InstallRequest& request : requests) + { + if (!g_pClientAppManager) + { + g_pLog->info("API g_pClientAppManager is nullptr! Aborting queued install...\n"); + continue; + } + + if (request.requestedAppInfo && !appInfoSectionsReady(request.appId, !request.loggedWaitingForAppInfo)) + { + request.loggedWaitingForAppInfo = true; + requeueRequests.push_back(request); + continue; + } + + g_pLog->info("API Installing %u to %i on IClientAppManager::RunIPCFrame\n", request.appId, request.library); + currentInstallApp = request.appId; + EAppUpdateError installResult = g_pClientAppManager->installApp(request.appId, request.library); + currentInstallApp = 0; + g_pLog->info("API InstallApp(%u, %i) -> %i\n", request.appId, request.library, installResult); + + if (installResult == APP_UPDATE_ERROR_MISSING_CONFIG) + { + if (request.requestedAppInfo) + { + g_pLog->info("API InstallApp(%u, %i) still missing configuration after appinfo retry.\n", request.appId, request.library); + continue; + } + + if (!g_pClientApps) + { + g_pLog->info("API g_pClientApps is nullptr! Can't request appinfo for %u.\n", request.appId); + continue; + } + + bool requestAppInfoResult = g_pClientApps->requestAppInfoUpdate(request.appId); + g_pLog->info("API RequestAppInfoUpdate(%u) -> %i after missing configuration\n", request.appId, requestAppInfoResult); + + if (requestAppInfoResult) + { + request.requestedAppInfo = true; + request.loggedWaitingForAppInfo = false; + requeueRequests.push_back(request); + } + } + } + + if (!requeueRequests.empty()) + { + std::lock_guard lock(pendingRequestMutex); + pendingInstallRequests.insert(pendingInstallRequests.end(), requeueRequests.begin(), requeueRequests.end()); + hasPendingRequests.store(true); + } +} + void SLSAPI::init() { fstream = std::fstream(path, std::ios::in | std::ios::out); diff --git a/src/api.hpp b/src/api.hpp index b531cd4..ece9b47 100644 --- a/src/api.hpp +++ b/src/api.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include @@ -12,6 +13,8 @@ namespace SLSAPI extern CFileWatcher* watcher; bool isEnabled(); + uint32_t currentInstallAppId(); void onFileChange(); + void runPendingRequests(); void init(); } diff --git a/src/hooks.cpp b/src/hooks.cpp index 32a19ab..8e736d9 100644 --- a/src/hooks.cpp +++ b/src/hooks.cpp @@ -2,6 +2,7 @@ #include "config.hpp" #include "globals.hpp" +#include "api.hpp" #include "log.hpp" #include "memhlp.hpp" #include "patterns.hpp" @@ -430,28 +431,49 @@ static bool hkClientAppManager_GetUpdateInfo(void* pClientAppManager, uint32_t a return success; } -__attribute__((hot)) -static void hkClientAppManager_RunIPCFrame(void* pClientAppManager, void* a1, void* a2, void* a3) +static bool hkClientAppManager_GetAppOwnershipInfo(void* pClientAppManager, uint32_t appId, uint32_t* pInfo) { - g_pClientAppManager = reinterpret_cast(pClientAppManager); + const bool ret = Hooks::IClientAppManager_GetAppOwnershipInfo.originalFn.fn(pClientAppManager, appId, pInfo); + constexpr uint32_t invalidPlatformFlag = 0x10; - std::shared_ptr vft = std::make_shared(); - LM_VmtNew(*reinterpret_cast(pClientAppManager), vft.get()); + if (ret && pInfo && SLSAPI::currentInstallAppId() == appId && (pInfo[1] & invalidPlatformFlag)) + { + g_pLog->info("API Clearing invalid-platform ownership flag for %u during InstallApp\n", appId); + pInfo[1] &= ~invalidPlatformFlag; + } - Hooks::IClientAppManager_BIsDlcEnabled.setup(vft, VFTIndexes::IClientAppManager::BIsDlcEnabled, hkClientAppManager_BIsDlcEnabled); - Hooks::IClientAppManager_GetAppUpdateInfo.setup(vft, VFTIndexes::IClientAppManager::GetUpdateInfo, hkClientAppManager_GetUpdateInfo); - Hooks::IClientAppManager_LaunchApp.setup(vft, VFTIndexes::IClientAppManager::LaunchApp, hkClientAppManager_LaunchApp); - Hooks::IClientAppManager_IsAppDlcInstalled.setup(vft, VFTIndexes::IClientAppManager::IsAppDlcInstalled, hkClientAppManager_IsAppDlcInstalled); + return ret; +} - Hooks::IClientAppManager_BIsDlcEnabled.place(); - Hooks::IClientAppManager_GetAppUpdateInfo.place(); - Hooks::IClientAppManager_LaunchApp.place(); - Hooks::IClientAppManager_IsAppDlcInstalled.place(); +__attribute__((hot)) +static void hkClientAppManager_RunIPCFrame(void* pClientAppManager, void* a1, void* a2, void* a3) +{ + g_pClientAppManager = reinterpret_cast(pClientAppManager); - g_pLog->debug("IClientAppManager->vft at %p\n", vft->vtable); + static bool vftHooksInitialized = false; + if (!vftHooksInitialized) + { + std::shared_ptr vft = std::make_shared(); + LM_VmtNew(*reinterpret_cast(pClientAppManager), vft.get()); + + Hooks::IClientAppManager_BIsDlcEnabled.setup(vft, VFTIndexes::IClientAppManager::BIsDlcEnabled, hkClientAppManager_BIsDlcEnabled); + Hooks::IClientAppManager_GetAppUpdateInfo.setup(vft, VFTIndexes::IClientAppManager::GetUpdateInfo, hkClientAppManager_GetUpdateInfo); + Hooks::IClientAppManager_LaunchApp.setup(vft, VFTIndexes::IClientAppManager::LaunchApp, hkClientAppManager_LaunchApp); + Hooks::IClientAppManager_IsAppDlcInstalled.setup(vft, VFTIndexes::IClientAppManager::IsAppDlcInstalled, hkClientAppManager_IsAppDlcInstalled); + Hooks::IClientAppManager_GetAppOwnershipInfo.setup(vft, VFTIndexes::IClientAppManager::GetAppOwnershipInfo, hkClientAppManager_GetAppOwnershipInfo); + + Hooks::IClientAppManager_BIsDlcEnabled.place(); + Hooks::IClientAppManager_GetAppUpdateInfo.place(); + Hooks::IClientAppManager_LaunchApp.place(); + Hooks::IClientAppManager_IsAppDlcInstalled.place(); + Hooks::IClientAppManager_GetAppOwnershipInfo.place(); + + g_pLog->debug("IClientAppManager->vft at %p\n", vft->vtable); + vftHooksInitialized = true; + } - Hooks::IClientAppManager_RunIPCFrame.remove(); - Hooks::IClientAppManager_RunIPCFrame.originalFn.fn(pClientAppManager, a1, a2, a3); + Hooks::IClientAppManager_RunIPCFrame.tramp.fn(pClientAppManager, a1, a2, a3); + SLSAPI::runPendingRequests(); } static unsigned int hkClientApps_GetDLCCount(void* pClientApps, uint32_t appId) @@ -528,6 +550,7 @@ static void hkClientApps_RunIPCFrame(void* pClientApps, void* a1, void* a2, void } Hooks::IClientApps_RunIPCFrame.tramp.fn(pClientApps, a1, a2, a3); + SLSAPI::runPendingRequests(); } static bool hkClientRemoteStorage_IsCloudEnabledForApp(void* pClientRemoteStorage, uint32_t appId) @@ -573,6 +596,7 @@ static void hkClientRemoteStorage_RunIPCFrame(void* pClientRemoteStorage, void* FakeAppIds::runIPCFrame(false); Hooks::IClientRemoteStorage_RunIPCFrame.tramp.fn(pClientRemoteStorage, a1, a2, a3); FakeAppIds::runIPCFrame(true); + SLSAPI::runPendingRequests(); } static void hkClientUGC_RunIPCFrame(void* pClientUGC, void* a1, void* a2, void* a3) @@ -581,6 +605,7 @@ static void hkClientUGC_RunIPCFrame(void* pClientUGC, void* a1, void* a2, void* FakeAppIds::runIPCFrame(false); Hooks::IClientUGC_RunIPCFrame.tramp.fn(pClientUGC, a1, a2, a3); FakeAppIds::runIPCFrame(true); + SLSAPI::runPendingRequests(); } static uint32_t hkClientUtils_GetAppId(void* pClientUtils) @@ -640,6 +665,7 @@ static void hkClientUtils_RunIPCFrame(void* pClientUtils, void* a1, void* a2, vo } Hooks::IClientUtils_RunIPCFrame.tramp.fn(pClientUtils, a1, a2, a3); + SLSAPI::runPendingRequests(); } static bool hkClientUser_BLoggedOn(void* pClientUser) @@ -793,6 +819,7 @@ static void hkClientUser_RunIPCFrame(void* pClientUser, void* a1, void* a2, void //FakeAppIds::pipeLoop(false); Hooks::IClientUser_RunIPCFrame.tramp.fn(pClientUser, a1, a2, a3); + SLSAPI::runPendingRequests(); //FakeAppIds::pipeLoop(true); } @@ -802,6 +829,7 @@ static void hkClientUserStats_RunIPCFrame(void* pClientUserStats, void* a1, void FakeAppIds::runIPCFrame(false); Hooks::IClientUserStats_RunIPCFrame.tramp.fn(pClientUserStats, a1, a2, a3); FakeAppIds::runIPCFrame(true); + SLSAPI::runPendingRequests(); } static void hkSteamMatchmakingPingResponse_ServerResponded(void* pSteamMatchingPingResponse, gameserverdetails_t* details) @@ -968,6 +996,7 @@ namespace Hooks VFTHook IClientAppManager_GetAppUpdateInfo("IClientAppManager::GetAppUpdateInfo"); VFTHook IClientAppManager_LaunchApp("IClientAppManager::LaunchApp"); VFTHook IClientAppManager_IsAppDlcInstalled("IClientAppManager::IsAppDlcInstalled"); + VFTHook IClientAppManager_GetAppOwnershipInfo("IClientAppManager::GetAppOwnershipInfo"); VFTHook IClientApps_GetDLCDataByIndex("IClientApps::GetDLCDataByIndex"); VFTHook IClientApps_GetDLCCount("IClientApps::GetDLCCount"); @@ -1120,6 +1149,7 @@ void Hooks::remove() IClientAppManager_GetAppUpdateInfo.remove(); IClientAppManager_LaunchApp.remove(); IClientAppManager_IsAppDlcInstalled.remove(); + IClientAppManager_GetAppOwnershipInfo.remove(); IClientApps_GetDLCDataByIndex.remove(); IClientApps_GetDLCCount.remove(); diff --git a/src/hooks.hpp b/src/hooks.hpp index 7b25fcd..8d0c9a0 100644 --- a/src/hooks.hpp +++ b/src/hooks.hpp @@ -139,6 +139,7 @@ namespace Hooks typedef bool(*IClientAppManager_GetAppUpdateInfo_t)(void*, uint32_t, uint32_t*); typedef void*(*IClientAppManager_LaunchApp_t)(void*, uint32_t*, void*, void*, void*); typedef bool(*IClientAppManager_IsAppDlcInstalled_t)(void*, uint32_t, uint32_t); + typedef bool(*IClientAppManager_GetAppOwnershipInfo_t)(void*, uint32_t, uint32_t*); typedef unsigned int(*IClientApps_GetDLCCount_t)(void*, uint32_t); typedef bool(*IClientApps_GetDLCDataByIndex_t)(void*, uint32_t, int, uint32_t*, bool*, char*, size_t); typedef bool(*IClientRemoteStorage_IsCloudEnabledForApp_t)(void*, uint32_t); @@ -148,6 +149,7 @@ namespace Hooks extern VFTHook IClientAppManager_GetAppUpdateInfo; extern VFTHook IClientAppManager_LaunchApp; extern VFTHook IClientAppManager_IsAppDlcInstalled; + extern VFTHook IClientAppManager_GetAppOwnershipInfo; extern VFTHook IClientApps_GetDLCDataByIndex; extern VFTHook IClientApps_GetDLCCount; diff --git a/src/sdk/IClientAppManager.cpp b/src/sdk/IClientAppManager.cpp index 6ca9b0f..d9294f0 100644 --- a/src/sdk/IClientAppManager.cpp +++ b/src/sdk/IClientAppManager.cpp @@ -5,9 +5,9 @@ #include -bool IClientAppManager::installApp(uint32_t appId, uint32_t librarIndex) +EAppUpdateError IClientAppManager::installApp(uint32_t appId, int32_t libraryIndex) { - return MemHlp::callVFunc(VFTIndexes::IClientAppManager::InstallApp, this, appId, librarIndex, 0); + return MemHlp::callVFunc(VFTIndexes::IClientAppManager::InstallApp, this, appId, libraryIndex, 0); } EAppState IClientAppManager::getAppInstallState(uint32_t appId) diff --git a/src/sdk/IClientAppManager.hpp b/src/sdk/IClientAppManager.hpp index d985e04..67c5024 100644 --- a/src/sdk/IClientAppManager.hpp +++ b/src/sdk/IClientAppManager.hpp @@ -27,10 +27,16 @@ enum EAppState : int APPSTATE_UPDATED_DISABLED_BY_APP = 0x80000, }; +enum EAppUpdateError : int32_t +{ + APP_UPDATE_ERROR_NONE = 0, + APP_UPDATE_ERROR_MISSING_CONFIG = 9, +}; + class IClientAppManager { public: - bool installApp(uint32_t appId, uint32_t librarIndex); + EAppUpdateError installApp(uint32_t appId, int32_t libraryIndex); EAppState getAppInstallState(uint32_t appId); }; diff --git a/src/sdk/IClientApps.cpp b/src/sdk/IClientApps.cpp index 3733921..a0bcb49 100644 --- a/src/sdk/IClientApps.cpp +++ b/src/sdk/IClientApps.cpp @@ -18,9 +18,9 @@ int32_t IClientApps::getAppData(uint32_t appId, const char* name, const char* pC ); } -uint32_t IClientApps::getAppDataSection(uint32_t appId, EAppInfoSection section, const char* pChOut, uint32_t outSize) +int32_t IClientApps::getAppDataSection(uint32_t appId, EAppInfoSection section, const char* pChOut, uint32_t outSize) { - return MemHlp::callVFunc + return MemHlp::callVFunc ( VFTIndexes::IClientApps::GetAppDataSection, this, @@ -32,6 +32,11 @@ uint32_t IClientApps::getAppDataSection(uint32_t appId, EAppInfoSection section, ); } +bool IClientApps::requestAppInfoUpdate(uint32_t appId) +{ + return MemHlp::callVFunc(VFTIndexes::IClientApps::RequestAppInfoUpdate, this, &appId, 1); +} + EAppType IClientApps::getAppType(uint32_t appId) { return MemHlp::callVFunc(VFTIndexes::IClientApps::GetAppType, this, appId); diff --git a/src/sdk/IClientApps.hpp b/src/sdk/IClientApps.hpp index 36dc79d..e121972 100644 --- a/src/sdk/IClientApps.hpp +++ b/src/sdk/IClientApps.hpp @@ -55,7 +55,8 @@ class IClientApps { public: int32_t getAppData(uint32_t appId, const char* name, const char* pChOut, uint32_t outSize); - uint32_t getAppDataSection(uint32_t appId, EAppInfoSection section, const char* pChOut, uint32_t outSize); + int32_t getAppDataSection(uint32_t appId, EAppInfoSection section, const char* pChOut, uint32_t outSize); + bool requestAppInfoUpdate(uint32_t appId); EAppType getAppType(uint32_t appId); }; diff --git a/src/vftableinfo.hpp b/src/vftableinfo.hpp index edfbf59..9c90233 100644 --- a/src/vftableinfo.hpp +++ b/src/vftableinfo.hpp @@ -26,6 +26,7 @@ namespace VFTIndexes constexpr int IsAppDlcInstalled = 9; constexpr int BIsDlcEnabled = 11; constexpr int GetUpdateInfo = 20; + constexpr int GetAppOwnershipInfo = 71; } namespace IClientRemoteStorage