Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 120 additions & 8 deletions src/api.cpp
Original file line number Diff line number Diff line change
@@ -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 <atomic>
#include <ios>
#include <mutex>
#include <vector>


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<InstallRequest> 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()
Expand All @@ -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<std::mutex> 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(...)
{
Expand All @@ -63,6 +106,75 @@ void SLSAPI::onFileChange()
}
}

void SLSAPI::runPendingRequests()
{
if (!hasPendingRequests.exchange(false))
{
return;
}

std::vector<InstallRequest> requests;
{
std::lock_guard<std::mutex> lock(pendingRequestMutex);
requests.swap(pendingInstallRequests);
}

std::vector<InstallRequest> 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<std::mutex> 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);
Expand Down
3 changes: 3 additions & 0 deletions src/api.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <cstdint>
#include <fstream>


Expand All @@ -12,6 +13,8 @@ namespace SLSAPI
extern CFileWatcher* watcher;

bool isEnabled();
uint32_t currentInstallAppId();
void onFileChange();
void runPendingRequests();
void init();
}
62 changes: 46 additions & 16 deletions src/hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "config.hpp"
#include "globals.hpp"
#include "api.hpp"
#include "log.hpp"
#include "memhlp.hpp"
#include "patterns.hpp"
Expand Down Expand Up @@ -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<IClientAppManager*>(pClientAppManager);
const bool ret = Hooks::IClientAppManager_GetAppOwnershipInfo.originalFn.fn(pClientAppManager, appId, pInfo);
constexpr uint32_t invalidPlatformFlag = 0x10;

std::shared_ptr<lm_vmt_t> vft = std::make_shared<lm_vmt_t>();
LM_VmtNew(*reinterpret_cast<lm_address_t**>(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<IClientAppManager*>(pClientAppManager);

g_pLog->debug("IClientAppManager->vft at %p\n", vft->vtable);
static bool vftHooksInitialized = false;
if (!vftHooksInitialized)
{
std::shared_ptr<lm_vmt_t> vft = std::make_shared<lm_vmt_t>();
LM_VmtNew(*reinterpret_cast<lm_address_t**>(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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}

Expand All @@ -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)
Expand Down Expand Up @@ -968,6 +996,7 @@ namespace Hooks
VFTHook<IClientAppManager_GetAppUpdateInfo_t> IClientAppManager_GetAppUpdateInfo("IClientAppManager::GetAppUpdateInfo");
VFTHook<IClientAppManager_LaunchApp_t> IClientAppManager_LaunchApp("IClientAppManager::LaunchApp");
VFTHook<IClientAppManager_IsAppDlcInstalled_t> IClientAppManager_IsAppDlcInstalled("IClientAppManager::IsAppDlcInstalled");
VFTHook<IClientAppManager_GetAppOwnershipInfo_t> IClientAppManager_GetAppOwnershipInfo("IClientAppManager::GetAppOwnershipInfo");

VFTHook<IClientApps_GetDLCDataByIndex_t> IClientApps_GetDLCDataByIndex("IClientApps::GetDLCDataByIndex");
VFTHook<IClientApps_GetDLCCount_t> IClientApps_GetDLCCount("IClientApps::GetDLCCount");
Expand Down Expand Up @@ -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();
Expand Down
2 changes: 2 additions & 0 deletions src/hooks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -148,6 +149,7 @@ namespace Hooks
extern VFTHook<IClientAppManager_GetAppUpdateInfo_t> IClientAppManager_GetAppUpdateInfo;
extern VFTHook<IClientAppManager_LaunchApp_t> IClientAppManager_LaunchApp;
extern VFTHook<IClientAppManager_IsAppDlcInstalled_t> IClientAppManager_IsAppDlcInstalled;
extern VFTHook<IClientAppManager_GetAppOwnershipInfo_t> IClientAppManager_GetAppOwnershipInfo;

extern VFTHook<IClientApps_GetDLCDataByIndex_t> IClientApps_GetDLCDataByIndex;
extern VFTHook<IClientApps_GetDLCCount_t> IClientApps_GetDLCCount;
Expand Down
4 changes: 2 additions & 2 deletions src/sdk/IClientAppManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

#include <cstdint>

bool IClientAppManager::installApp(uint32_t appId, uint32_t librarIndex)
EAppUpdateError IClientAppManager::installApp(uint32_t appId, int32_t libraryIndex)
{
return MemHlp::callVFunc<bool(*)(void*, uint32_t, uint32_t, uint8_t)>(VFTIndexes::IClientAppManager::InstallApp, this, appId, librarIndex, 0);
return MemHlp::callVFunc<EAppUpdateError(*)(void*, uint32_t, int32_t, uint8_t)>(VFTIndexes::IClientAppManager::InstallApp, this, appId, libraryIndex, 0);
}

EAppState IClientAppManager::getAppInstallState(uint32_t appId)
Expand Down
8 changes: 7 additions & 1 deletion src/sdk/IClientAppManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};

Expand Down
9 changes: 7 additions & 2 deletions src/sdk/IClientApps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint32_t(*)(void*, uint32_t, uint32_t, const char*, uint32_t, uint8_t)>
return MemHlp::callVFunc<int32_t(*)(void*, uint32_t, uint32_t, const char*, uint32_t, uint8_t)>
(
VFTIndexes::IClientApps::GetAppDataSection,
this,
Expand All @@ -32,6 +32,11 @@ uint32_t IClientApps::getAppDataSection(uint32_t appId, EAppInfoSection section,
);
}

bool IClientApps::requestAppInfoUpdate(uint32_t appId)
{
return MemHlp::callVFunc<bool(*)(void*, const uint32_t*, int32_t)>(VFTIndexes::IClientApps::RequestAppInfoUpdate, this, &appId, 1);
}

EAppType IClientApps::getAppType(uint32_t appId)
{
return MemHlp::callVFunc<EAppType(*)(void*, uint32_t)>(VFTIndexes::IClientApps::GetAppType, this, appId);
Expand Down
Loading