Added `IClientApps`, `IClientAppManager`, `IClientUser`

master
acidicoala 2 years ago
parent be9fa39508
commit 5b76d155a8
No known key found for this signature in database
GPG Key ID: D24C6065B49C645B

@ -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:

@ -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 ()

@ -1 +1 @@
Subproject commit 67545f50bc9e557eaf43a74395cc1461416d0035
Subproject commit d9b0d1a00beb065a9a931a60ef615ce8d1fc7164

@ -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<String> 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);
GET_ORIGINAL_FUNCTION(Log_Interface)
Log_Interface_o(interface_name, function_name);
} catch (const Exception& ex) {
logger->error("{} -> Error: {}", __func__, ex.what());
}
}

@ -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);
}
}

@ -58,6 +58,6 @@ namespace smoke_api {
void shutdown();
bool should_unlock(uint32_t appId);
bool should_unlock(uint32_t app_id);
}

@ -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(

@ -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(

@ -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;

@ -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{
bool IsSubscribedApp(const String& function_name, AppId_t appID) {
const auto subscribed = should_unlock(appID);
logger->info("{} -> App ID: {}, Subscribed: {}", function_name, appID, subscribed);
namespace steam_apps {
return subscribed;
}
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);
bool IsDlcInstalled(const String& function_name, AppId_t appID) {
const auto installed = should_unlock(appID);
const auto installed = app_id_unlocked and dlc_id_unlocked;
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<int()>& original_function) {
int GetDLCCount(const String& function_name, const AppId_t app_id, const std::function<int()>& original_function) {
static std::mutex section;
std::lock_guard<std::mutex> 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<int>(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;
}

@ -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<int()>& original_function);
int GetDLCCount(const String& function_name, AppId_t app_id, const std::function<int()>& 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,

@ -0,0 +1,13 @@
#include <smoke_api/smoke_api.hpp>
#include <steam_impl/steam_impl.hpp>
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);
}

@ -0,0 +1,31 @@
#include <smoke_api/smoke_api.hpp>
#include <steam_impl/steam_impl.hpp>
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)
);
});
}

@ -0,0 +1,8 @@
#include <smoke_api/smoke_api.hpp>
#include <steam_impl/steam_impl.hpp>
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);
}
Loading…
Cancel
Save