mirror of
https://github.com/acidicoala/SmokeAPI.git
synced 2026-01-25 05:52:51 -05:00
Initial commit
This commit is contained in:
14
src/main.cpp
Normal file
14
src/main.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
|
||||
// This header will be populated at build time
|
||||
#include <linker_exports.h>
|
||||
|
||||
EXTERN_C [[maybe_unused]] BOOL WINAPI DllMain(HMODULE module, DWORD reason, LPVOID) {
|
||||
if (reason == DLL_PROCESS_ATTACH) {
|
||||
smoke_api::init(module);
|
||||
} else if (reason == DLL_PROCESS_DETACH) {
|
||||
smoke_api::shutdown();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
123
src/smoke_api/smoke_api.cpp
Normal file
123
src/smoke_api/smoke_api.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <build_config.h>
|
||||
|
||||
#include <koalabox/loader.hpp>
|
||||
#include <koalabox/config_parser.hpp>
|
||||
#include <koalabox/file_logger.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <koalabox/dll_monitor.hpp>
|
||||
|
||||
#define DETOUR(FUNC) hook::detour_or_warn(original_library, #FUNC, reinterpret_cast<FunctionAddress>(FUNC));
|
||||
|
||||
namespace smoke_api {
|
||||
Config config = {}; // NOLINT(cert-err58-cpp)
|
||||
|
||||
HMODULE original_library = nullptr;
|
||||
|
||||
bool is_hook_mode = false;
|
||||
|
||||
Path self_directory;
|
||||
|
||||
void init(HMODULE self_module) {
|
||||
try {
|
||||
DisableThreadLibraryCalls(self_module);
|
||||
|
||||
koalabox::project_name = PROJECT_NAME;
|
||||
|
||||
self_directory = loader::get_module_dir(self_module);
|
||||
|
||||
config = config_parser::parse<Config>(self_directory / PROJECT_NAME".json");
|
||||
|
||||
const auto exe_path = Path(win_util::get_module_file_name_or_throw(nullptr));
|
||||
const auto exe_name = exe_path.filename().string();
|
||||
const auto exe_bitness = util::is_x64() ? 64 : 32;
|
||||
|
||||
if (config.logging) {
|
||||
logger = file_logger::create(self_directory / fmt::format("{}.log", PROJECT_NAME));
|
||||
}
|
||||
|
||||
logger->info("🐨 {} v{}", PROJECT_NAME, PROJECT_VERSION);
|
||||
|
||||
logger->debug(R"(Process name: "{}" [{}-bit])", exe_name, exe_bitness);
|
||||
|
||||
is_hook_mode = hook::is_hook_mode(self_module, ORIGINAL_DLL);
|
||||
|
||||
if (is_hook_mode) {
|
||||
hook::init(true);
|
||||
|
||||
if (util::strings_are_equal(exe_name, "steam.exe")) { // target vstdlib_s.dll
|
||||
logger->info("🐨 Detected Koalageddon mode 💥");
|
||||
|
||||
dll_monitor::init(VSTDLIB_DLL, [](const HMODULE& library) {
|
||||
original_library = library;
|
||||
|
||||
DETOUR(Coroutine_Create)
|
||||
});
|
||||
} else if (config.hook_steamclient) { // target steamclient(64).dll
|
||||
logger->info("🪝 Detected hook mode for SteamClient");
|
||||
|
||||
dll_monitor::init(STEAMCLIENT_DLL, [](const HMODULE& library) {
|
||||
original_library = library;
|
||||
|
||||
DETOUR(CreateInterface)
|
||||
});
|
||||
} else { // target steam_api.dll
|
||||
logger->info("🪝 Detected hook mode for Steam_API");
|
||||
|
||||
dll_monitor::init(ORIGINAL_DLL, [](const HMODULE& library) {
|
||||
original_library = library;
|
||||
|
||||
DETOUR(SteamInternal_FindOrCreateUserInterface)
|
||||
DETOUR(SteamInternal_CreateInterface)
|
||||
DETOUR(SteamApps)
|
||||
DETOUR(SteamClient)
|
||||
DETOUR(SteamUser)
|
||||
|
||||
DETOUR(SteamAPI_ISteamApps_BIsSubscribedApp)
|
||||
DETOUR(SteamAPI_ISteamApps_BIsDlcInstalled)
|
||||
DETOUR(SteamAPI_ISteamApps_GetDLCCount)
|
||||
DETOUR(SteamAPI_ISteamApps_BGetDLCDataByIndex)
|
||||
DETOUR(SteamAPI_ISteamClient_GetISteamGenericInterface)
|
||||
|
||||
DETOUR(SteamAPI_ISteamInventory_GetResultStatus)
|
||||
DETOUR(SteamAPI_ISteamInventory_GetResultItems)
|
||||
DETOUR(SteamAPI_ISteamInventory_GetResultItemProperty)
|
||||
DETOUR(SteamAPI_ISteamInventory_CheckResultSteamID)
|
||||
DETOUR(SteamAPI_ISteamInventory_GetAllItems)
|
||||
DETOUR(SteamAPI_ISteamInventory_GetItemsByID)
|
||||
DETOUR(SteamAPI_ISteamInventory_GetItemDefinitionIDs)
|
||||
DETOUR(SteamAPI_ISteamInventory_GetItemDefinitionProperty)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
logger->info("🔀 Detected proxy mode for Steam_API");
|
||||
|
||||
original_library = loader::load_original_library(self_directory, ORIGINAL_DLL);
|
||||
}
|
||||
logger->info("🚀 Initialization complete");
|
||||
} catch (const Exception& ex) {
|
||||
util::panic(fmt::format("Initialization error: {}", ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
try {
|
||||
if (is_hook_mode) {
|
||||
dll_monitor::shutdown();
|
||||
} else {
|
||||
win_util::free_library(original_library);
|
||||
}
|
||||
|
||||
logger->info("💀 Shutdown complete");
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Shutdown error: {}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool should_unlock(uint32_t appId) {
|
||||
return config.unlock_all != config.override.contains(appId);
|
||||
}
|
||||
|
||||
}
|
||||
63
src/smoke_api/smoke_api.hpp
Normal file
63
src/smoke_api/smoke_api.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalabox/koalabox.hpp>
|
||||
#include <koalabox/hook.hpp> // For macros
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#define GET_ORIGINAL_FUNCTION(FUNC) \
|
||||
static const auto FUNC##_o = hook::get_original_function( \
|
||||
smoke_api::is_hook_mode, \
|
||||
smoke_api::original_library, \
|
||||
#FUNC, \
|
||||
FUNC \
|
||||
);
|
||||
|
||||
#define GET_ORIGINAL_VIRTUAL_FUNCTION(FUNC) \
|
||||
static const auto FUNC##_o = hook::get_original_function( \
|
||||
true, \
|
||||
smoke_api::original_library, \
|
||||
#FUNC, \
|
||||
FUNC \
|
||||
);
|
||||
|
||||
namespace smoke_api {
|
||||
using namespace koalabox;
|
||||
|
||||
struct Config {
|
||||
uint32_t $version = 1;
|
||||
bool logging = false;
|
||||
bool hook_steamclient = true;
|
||||
bool unlock_all = true;
|
||||
Set<uint32_t> override;
|
||||
Vector<uint32_t> dlc_ids;
|
||||
bool auto_inject_inventory = true;
|
||||
Vector<uint32_t> inventory_items;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
|
||||
Config, $version,
|
||||
logging,
|
||||
unlock_all,
|
||||
hook_steamclient,
|
||||
override,
|
||||
dlc_ids,
|
||||
auto_inject_inventory,
|
||||
inventory_items
|
||||
)
|
||||
};
|
||||
|
||||
extern Config config;
|
||||
|
||||
extern HMODULE original_library;
|
||||
|
||||
extern bool is_hook_mode;
|
||||
|
||||
extern Path self_directory;
|
||||
|
||||
void init(HMODULE self_module);
|
||||
|
||||
void shutdown();
|
||||
|
||||
bool should_unlock(uint32_t appId);
|
||||
|
||||
}
|
||||
200
src/steam_api_exports/steam_api_flat.cpp
Normal file
200
src/steam_api_exports/steam_api_flat.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
// ISteamApps
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsSubscribedApp(ISteamApps*, AppId_t appID) {
|
||||
return steam_apps::IsSubscribedApp(__func__, appID);
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsDlcInstalled(ISteamApps*, AppId_t appID) {
|
||||
return steam_apps::IsDlcInstalled(__func__, appID);
|
||||
}
|
||||
|
||||
DLL_EXPORT(int) SteamAPI_ISteamApps_GetDLCCount(ISteamApps* self) {
|
||||
return steam_apps::GetDLCCount(__func__, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamApps_GetDLCCount)
|
||||
|
||||
return SteamAPI_ISteamApps_GetDLCCount_o(self);
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamApps_BGetDLCDataByIndex(
|
||||
ISteamApps* self,
|
||||
int iDLC,
|
||||
AppId_t* pAppID,
|
||||
bool* pbAvailable,
|
||||
char* pchName,
|
||||
int cchNameBufferSize
|
||||
) {
|
||||
return steam_apps::GetDLCDataByIndex(__func__, iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamApps_BGetDLCDataByIndex)
|
||||
|
||||
return SteamAPI_ISteamApps_BGetDLCDataByIndex_o(
|
||||
self, iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ISteamUser
|
||||
|
||||
DLL_EXPORT(EUserHasLicenseForAppResult) SteamAPI_ISteamUser_UserHasLicenseForApp(
|
||||
ISteamUser* self,
|
||||
CSteamID steamID,
|
||||
AppId_t appID
|
||||
) {
|
||||
return steam_user::UserHasLicenseForApp(__func__, appID, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamUser_UserHasLicenseForApp)
|
||||
|
||||
return SteamAPI_ISteamUser_UserHasLicenseForApp_o(self, steamID, appID);
|
||||
});
|
||||
}
|
||||
|
||||
// ISteamClient
|
||||
|
||||
DLL_EXPORT(void*) SteamAPI_ISteamClient_GetISteamGenericInterface(
|
||||
ISteamClient* self,
|
||||
HSteamUser hSteamUser,
|
||||
HSteamPipe hSteamPipe,
|
||||
const char* pchVersion
|
||||
) {
|
||||
return steam_client::GetGenericInterface(__func__, pchVersion, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamClient_GetISteamGenericInterface)
|
||||
|
||||
return SteamAPI_ISteamClient_GetISteamGenericInterface_o(self, hSteamUser, hSteamPipe, pchVersion);
|
||||
});
|
||||
}
|
||||
|
||||
// ISteamInventory
|
||||
|
||||
DLL_EXPORT(EResult) SteamAPI_ISteamInventory_GetResultStatus(
|
||||
ISteamInventory* self,
|
||||
SteamInventoryResult_t resultHandle
|
||||
) {
|
||||
return steam_inventory::GetResultStatus(__func__, resultHandle, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetResultStatus)
|
||||
|
||||
return SteamAPI_ISteamInventory_GetResultStatus_o(self, resultHandle);
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetResultItems(
|
||||
ISteamInventory* self,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
SteamItemDetails_t* pOutItemsArray,
|
||||
uint32_t* punOutItemsArraySize
|
||||
) {
|
||||
return steam_inventory::GetResultItems(
|
||||
__func__, resultHandle, pOutItemsArray, punOutItemsArraySize,
|
||||
[&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetResultItems)
|
||||
|
||||
return SteamAPI_ISteamInventory_GetResultItems_o(self, resultHandle, pOutItemsArray, punOutItemsArraySize);
|
||||
},
|
||||
[&](SteamItemDef_t* pItemDefIDs, uint32_t* punItemDefIDsArraySize) {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetItemDefinitionIDs)
|
||||
|
||||
return SteamAPI_ISteamInventory_GetItemDefinitionIDs_o(self, pItemDefIDs, punItemDefIDsArraySize);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetResultItemProperty(
|
||||
ISteamInventory* self,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
uint32_t unItemIndex,
|
||||
const char* pchPropertyName,
|
||||
char* pchValueBuffer,
|
||||
const uint32_t* punValueBufferSizeOut
|
||||
) {
|
||||
return steam_inventory::GetResultItemProperty(
|
||||
__func__, resultHandle, unItemIndex, pchPropertyName, pchValueBuffer, punValueBufferSizeOut, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(SteamAPI_ISteamInventory_GetResultItemProperty)
|
||||
|
||||
return SteamAPI_ISteamInventory_GetResultItemProperty_o(
|
||||
self, resultHandle, unItemIndex, pchPropertyName, pchValueBuffer, punValueBufferSizeOut
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_CheckResultSteamID(
|
||||
ISteamInventory* self,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
CSteamID steamIDExpected
|
||||
) {
|
||||
return steam_inventory::CheckResultSteamID(__func__, resultHandle, steamIDExpected, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(SteamAPI_ISteamInventory_CheckResultSteamID)
|
||||
|
||||
return SteamAPI_ISteamInventory_CheckResultSteamID_o(self, resultHandle, steamIDExpected);
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetAllItems(
|
||||
ISteamInventory* self,
|
||||
SteamInventoryResult_t* pResultHandle
|
||||
) {
|
||||
return steam_inventory::GetAllItems(__func__, pResultHandle, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetAllItems)
|
||||
|
||||
return SteamAPI_ISteamInventory_GetAllItems_o(self, pResultHandle);
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemsByID(
|
||||
ISteamInventory* self,
|
||||
SteamInventoryResult_t* pResultHandle,
|
||||
const SteamItemInstanceID_t* pInstanceIDs,
|
||||
uint32_t unCountInstanceIDs
|
||||
) {
|
||||
return steam_inventory::GetItemsByID(__func__, pResultHandle, pInstanceIDs, unCountInstanceIDs, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetItemsByID)
|
||||
|
||||
return SteamAPI_ISteamInventory_GetItemsByID_o(self, pResultHandle, pInstanceIDs, unCountInstanceIDs);
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_SerializeResult(
|
||||
ISteamInventory* self,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
void* pOutBuffer,
|
||||
uint32_t* punOutBufferSize
|
||||
) {
|
||||
return steam_inventory::SerializeResult(__func__, resultHandle, pOutBuffer, punOutBufferSize, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_SerializeResult)
|
||||
|
||||
return SteamAPI_ISteamInventory_SerializeResult_o(self, resultHandle, pOutBuffer, punOutBufferSize);
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionIDs(
|
||||
ISteamInventory* self,
|
||||
SteamItemDef_t* pItemDefIDs,
|
||||
uint32_t* punItemDefIDsArraySize
|
||||
) {
|
||||
return steam_inventory::GetItemDefinitionIDs(__func__, pItemDefIDs, punItemDefIDsArraySize, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetItemDefinitionIDs)
|
||||
|
||||
return SteamAPI_ISteamInventory_GetItemDefinitionIDs_o(self, pItemDefIDs, punItemDefIDsArraySize);
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionProperty(
|
||||
ISteamInventory* self,
|
||||
SteamItemDef_t iDefinition,
|
||||
const char* pchPropertyName,
|
||||
char* pchValueBuffer,
|
||||
uint32_t* punValueBufferSizeOut
|
||||
) {
|
||||
return steam_inventory::GetItemDefinitionProperty(
|
||||
__func__, iDefinition, pchPropertyName, pchValueBuffer, punValueBufferSizeOut, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetItemDefinitionProperty)
|
||||
|
||||
return SteamAPI_ISteamInventory_GetItemDefinitionProperty_o(
|
||||
self, iDefinition, pchPropertyName, pchValueBuffer, punValueBufferSizeOut
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
21
src/steam_api_exports/steam_api_internal.cpp
Normal file
21
src/steam_api_exports/steam_api_internal.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
DLL_EXPORT(void*) SteamInternal_FindOrCreateUserInterface(HSteamUser hSteamUser, const char* version) {
|
||||
return steam_client::GetGenericInterface(__func__, version, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamInternal_FindOrCreateUserInterface)
|
||||
|
||||
return SteamInternal_FindOrCreateUserInterface_o(hSteamUser, version);
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(void*) SteamInternal_CreateInterface(const char* version) {
|
||||
return steam_client::GetGenericInterface(__func__, version, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamInternal_CreateInterface)
|
||||
|
||||
return SteamInternal_CreateInterface_o(version);
|
||||
});
|
||||
}
|
||||
83
src/steam_api_exports/steam_api_unversioned.cpp
Normal file
83
src/steam_api_exports/steam_api_unversioned.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
#include <koalabox/win_util.hpp>
|
||||
|
||||
#include <regex>
|
||||
|
||||
using namespace smoke_api;
|
||||
using namespace steam_functions;
|
||||
|
||||
/**
|
||||
* Searches the `.rdata` section of the original dll for the full interface version string
|
||||
* Results are cached for performance.
|
||||
*/
|
||||
String get_versioned_interface(const String& version_prefix, const String& fallback) {
|
||||
static Map<String, String> version_map;
|
||||
|
||||
if (not version_map.contains(version_prefix)) {
|
||||
try {
|
||||
String rdata = win_util::get_pe_section_data_or_throw(original_library, ".rdata");
|
||||
|
||||
std::regex regex(version_prefix + "\\d{3}");
|
||||
std::smatch match;
|
||||
if (std::regex_search(rdata, match, regex)) {
|
||||
version_map[version_prefix] = match[0];
|
||||
return version_map[version_prefix];
|
||||
}
|
||||
|
||||
throw util::exception("No match found for '{}'", version_prefix);
|
||||
} catch (const Exception& ex) {
|
||||
logger->error(
|
||||
"Failed to get versioned interface: {}."
|
||||
"Falling back to version {}", ex.what(), fallback
|
||||
);
|
||||
|
||||
version_map[version_prefix] = version_prefix + fallback;
|
||||
}
|
||||
}
|
||||
|
||||
return version_map[version_prefix];
|
||||
}
|
||||
|
||||
DLL_EXPORT(void*) SteamClient() {
|
||||
static auto version = get_versioned_interface(STEAM_CLIENT, "006");
|
||||
|
||||
return steam_client::GetGenericInterface(__func__, version, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamClient)
|
||||
|
||||
return SteamClient_o();
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(void*) SteamApps() {
|
||||
static auto version = get_versioned_interface(STEAM_APPS, "002");
|
||||
|
||||
return steam_client::GetGenericInterface(__func__, version, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamApps)
|
||||
|
||||
return SteamApps_o();
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(void*) SteamUser() {
|
||||
static auto version = get_versioned_interface(STEAM_USER, "012");
|
||||
|
||||
return steam_client::GetGenericInterface(__func__, version, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamUser)
|
||||
|
||||
return SteamUser_o();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
DLL_EXPORT(void*) SteamInventory() {
|
||||
static auto version = get_versioned_interface(STEAM_INVENTORY, "001");
|
||||
|
||||
return steam_client::GetGenericInterface(__func__, version, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamInventory)
|
||||
|
||||
return SteamInventory_o();
|
||||
});
|
||||
}
|
||||
38
src/steam_api_virtuals/isteamapps.cpp
Normal file
38
src/steam_api_virtuals/isteamapps.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
VIRTUAL(bool) ISteamApps_BIsSubscribedApp(PARAMS(AppId_t appID)) { // NOLINT(misc-unused-parameters)
|
||||
return steam_apps::IsSubscribedApp(__func__, appID);
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamApps_BIsDlcInstalled(PARAMS(AppId_t appID)) { // NOLINT(misc-unused-parameters)
|
||||
return steam_apps::IsDlcInstalled(__func__, appID);
|
||||
}
|
||||
|
||||
VIRTUAL(int) ISteamApps_GetDLCCount(PARAMS()) {
|
||||
return steam_apps::GetDLCCount(__func__, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamApps_GetDLCCount)
|
||||
|
||||
return ISteamApps_GetDLCCount_o(ARGS());
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamApps_BGetDLCDataByIndex(
|
||||
PARAMS(
|
||||
int iDLC,
|
||||
AppId_t* pAppID,
|
||||
bool* pbAvailable,
|
||||
char* pchName,
|
||||
int cchNameBufferSize
|
||||
)
|
||||
) {
|
||||
return steam_apps::GetDLCDataByIndex(__func__, iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamApps_BGetDLCDataByIndex)
|
||||
|
||||
return ISteamApps_BGetDLCDataByIndex_o(
|
||||
ARGS(iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize)
|
||||
);
|
||||
});
|
||||
}
|
||||
61
src/steam_api_virtuals/isteamclient.cpp
Normal file
61
src/steam_api_virtuals/isteamclient.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
VIRTUAL(void*) ISteamClient_GetISteamApps(
|
||||
PARAMS(
|
||||
HSteamUser hSteamUser,
|
||||
HSteamPipe hSteamPipe,
|
||||
const char* version
|
||||
)
|
||||
) {
|
||||
return steam_client::GetGenericInterface(__func__, version, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamClient_GetISteamApps)
|
||||
|
||||
return ISteamClient_GetISteamApps_o(ARGS(hSteamUser, hSteamPipe, version));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(void*) ISteamClient_GetISteamUser(
|
||||
PARAMS(
|
||||
HSteamUser hSteamUser,
|
||||
HSteamPipe hSteamPipe,
|
||||
const char* version
|
||||
)
|
||||
) {
|
||||
return steam_client::GetGenericInterface(__func__, version, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamClient_GetISteamUser)
|
||||
|
||||
return ISteamClient_GetISteamUser_o(ARGS(hSteamUser, hSteamPipe, version));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(void*) ISteamClient_GetISteamGenericInterface(
|
||||
PARAMS(
|
||||
HSteamUser hSteamUser,
|
||||
HSteamPipe hSteamPipe,
|
||||
const char* pchVersion
|
||||
)
|
||||
) {
|
||||
return steam_client::GetGenericInterface(__func__, pchVersion, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamClient_GetISteamGenericInterface)
|
||||
|
||||
return ISteamClient_GetISteamGenericInterface_o(ARGS(hSteamUser, hSteamPipe, pchVersion));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(void*) ISteamClient_GetISteamInventory(
|
||||
PARAMS(
|
||||
HSteamUser hSteamUser,
|
||||
HSteamPipe hSteamPipe,
|
||||
const char* pchVersion
|
||||
)
|
||||
) {
|
||||
return steam_client::GetGenericInterface(__func__, pchVersion, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamClient_GetISteamInventory)
|
||||
|
||||
return ISteamClient_GetISteamInventory_o(ARGS(hSteamUser, hSteamPipe, pchVersion));
|
||||
});
|
||||
}
|
||||
141
src/steam_api_virtuals/isteaminventory.cpp
Normal file
141
src/steam_api_virtuals/isteaminventory.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
VIRTUAL(EResult) ISteamInventory_GetResultStatus(PARAMS(SteamInventoryResult_t resultHandle)) {
|
||||
return steam_inventory::GetResultStatus(__func__, resultHandle, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetResultStatus)
|
||||
|
||||
return ISteamInventory_GetResultStatus_o(ARGS(resultHandle));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamInventory_GetResultItems(
|
||||
PARAMS(
|
||||
SteamInventoryResult_t resultHandle,
|
||||
SteamItemDetails_t* pOutItemsArray,
|
||||
uint32_t * punOutItemsArraySize
|
||||
)
|
||||
) {
|
||||
|
||||
return steam_inventory::GetResultItems(
|
||||
__func__, resultHandle, pOutItemsArray, punOutItemsArraySize,
|
||||
[&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetResultItems)
|
||||
|
||||
return ISteamInventory_GetResultItems_o(ARGS(resultHandle, pOutItemsArray, punOutItemsArraySize));
|
||||
},
|
||||
[&](SteamItemDef_t* pItemDefIDs, uint32_t* punItemDefIDsArraySize) {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetItemDefinitionIDs)
|
||||
|
||||
return ISteamInventory_GetItemDefinitionIDs_o(ARGS(pItemDefIDs, punItemDefIDsArraySize));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamInventory_GetResultItemProperty(
|
||||
PARAMS(
|
||||
SteamInventoryResult_t resultHandle,
|
||||
uint32_t unItemIndex,
|
||||
const char* pchPropertyName,
|
||||
char* pchValueBuffer,
|
||||
const uint32_t* punValueBufferSizeOut
|
||||
)
|
||||
) {
|
||||
return steam_inventory::GetResultItemProperty(
|
||||
__func__, resultHandle, unItemIndex, pchPropertyName, pchValueBuffer, punValueBufferSizeOut, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetResultItemProperty)
|
||||
|
||||
return ISteamInventory_GetResultItemProperty_o(
|
||||
ARGS(resultHandle, unItemIndex, pchPropertyName, pchValueBuffer, punValueBufferSizeOut)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamInventory_CheckResultSteamID(
|
||||
PARAMS(
|
||||
SteamInventoryResult_t resultHandle,
|
||||
CSteamID steamIDExpected
|
||||
)
|
||||
) {
|
||||
return steam_inventory::CheckResultSteamID(__func__, resultHandle, steamIDExpected, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_CheckResultSteamID)
|
||||
|
||||
return ISteamInventory_CheckResultSteamID_o(ARGS(resultHandle, steamIDExpected));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamInventory_GetAllItems(PARAMS(SteamInventoryResult_t* pResultHandle)) {
|
||||
return steam_inventory::GetAllItems(__func__, pResultHandle, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetAllItems)
|
||||
|
||||
return ISteamInventory_GetAllItems_o(ARGS(pResultHandle));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamInventory_GetItemsByID(
|
||||
PARAMS(
|
||||
SteamInventoryResult_t* pResultHandle,
|
||||
const SteamItemInstanceID_t* pInstanceIDs,
|
||||
uint32_t unCountInstanceIDs
|
||||
)
|
||||
) {
|
||||
return steam_inventory::GetItemsByID(__func__, pResultHandle, pInstanceIDs, unCountInstanceIDs, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetItemsByID)
|
||||
|
||||
return ISteamInventory_GetItemsByID_o(ARGS(pResultHandle, pInstanceIDs, unCountInstanceIDs));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamInventory_SerializeResult(
|
||||
PARAMS(
|
||||
SteamInventoryResult_t resultHandle,
|
||||
void* pOutBuffer,
|
||||
uint32_t * punOutBufferSize
|
||||
)
|
||||
) {
|
||||
return steam_inventory::SerializeResult(__func__, resultHandle, pOutBuffer, punOutBufferSize, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_SerializeResult)
|
||||
|
||||
return ISteamInventory_SerializeResult_o(ARGS(resultHandle, pOutBuffer, punOutBufferSize));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamInventory_GetItemDefinitionIDs(
|
||||
PARAMS(
|
||||
SteamItemDef_t* pItemDefIDs,
|
||||
uint32_t * punItemDefIDsArraySize
|
||||
)
|
||||
) {
|
||||
return steam_inventory::GetItemDefinitionIDs(__func__, pItemDefIDs, punItemDefIDsArraySize, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetItemDefinitionIDs)
|
||||
|
||||
return ISteamInventory_GetItemDefinitionIDs_o(ARGS(pItemDefIDs, punItemDefIDsArraySize));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamInventory_GetItemDefinitionProperty(
|
||||
PARAMS(
|
||||
SteamItemDef_t iDefinition,
|
||||
const char* pchPropertyName,
|
||||
char* pchValueBuffer,
|
||||
uint32_t * punValueBufferSizeOut
|
||||
)
|
||||
) {
|
||||
return steam_inventory::GetItemDefinitionProperty(
|
||||
__func__, iDefinition, pchPropertyName, pchValueBuffer, punValueBufferSizeOut, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetItemDefinitionProperty)
|
||||
|
||||
return ISteamInventory_GetItemDefinitionProperty_o(
|
||||
ARGS(
|
||||
iDefinition,
|
||||
pchPropertyName,
|
||||
pchValueBuffer,
|
||||
punValueBufferSizeOut
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
12
src/steam_api_virtuals/isteamuser.cpp
Normal file
12
src/steam_api_virtuals/isteamuser.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
VIRTUAL(EUserHasLicenseForAppResult) ISteamUser_UserHasLicenseForApp(PARAMS(CSteamID steamID, AppId_t appID)) {
|
||||
return steam_user::UserHasLicenseForApp(__func__, appID, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamUser_UserHasLicenseForApp)
|
||||
|
||||
return ISteamUser_UserHasLicenseForApp_o(ARGS(steamID, appID));
|
||||
});
|
||||
}
|
||||
270
src/steam_functions/steam_functions.cpp
Normal file
270
src/steam_functions/steam_functions.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <build_config.h>
|
||||
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
|
||||
#include <polyhook2/Misc.hpp>
|
||||
|
||||
namespace steam_functions {
|
||||
|
||||
typedef Map<String, Map<int, int>> FunctionOrdinalMap;
|
||||
|
||||
FunctionOrdinalMap steam_client_ordinal_map = { // NOLINT(cert-err58-cpp)
|
||||
{"ISteamClient_GetISteamApps",
|
||||
{
|
||||
{6, 16},
|
||||
{7, 18},
|
||||
{8, 15},
|
||||
{9, 16},
|
||||
{12, 15},
|
||||
}
|
||||
},
|
||||
{"ISteamClient_GetISteamUser",
|
||||
{
|
||||
{6, 6},
|
||||
{7, 5},
|
||||
}
|
||||
},
|
||||
{"ISteamClient_GetISteamUtils",
|
||||
{
|
||||
{6, 12},
|
||||
{7, 9},
|
||||
}
|
||||
},
|
||||
{"ISteamClient_GetISteamGenericInterface",
|
||||
{
|
||||
{7, 14},
|
||||
{8, 13},
|
||||
{12, 12},
|
||||
}
|
||||
},
|
||||
{"ISteamClient_GetISteamInventory",
|
||||
{
|
||||
{17, 34},
|
||||
{18, 35},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FunctionOrdinalMap steam_apps_ordinal_map = { // NOLINT(cert-err58-cpp)
|
||||
{"ISteamApps_BIsSubscribedApp", {{2, 6}}},
|
||||
{"ISteamApps_BIsDlcInstalled", {{2, 7}}},
|
||||
{"ISteamApps_GetDLCCount", {{2, 10}}},
|
||||
{"ISteamApps_BGetDLCDataByIndex", {{2, 11}}},
|
||||
};
|
||||
|
||||
FunctionOrdinalMap steam_user_ordinal_map = { // NOLINT(cert-err58-cpp)
|
||||
{"ISteamUser_UserHasLicenseForApp", {
|
||||
{12, 15},
|
||||
{13, 16},
|
||||
{15, 17},
|
||||
}}
|
||||
};
|
||||
|
||||
FunctionOrdinalMap steam_utils_ordinal_map = { // NOLINT(cert-err58-cpp)
|
||||
{"ISteamUtils_GetAppID", {{2, 9}}}
|
||||
};
|
||||
|
||||
FunctionOrdinalMap steam_inventory_ordinal_map = { // NOLINT(cert-err58-cpp)
|
||||
{"ISteamInventory_GetResultStatus", {{1, 0}}},
|
||||
{"ISteamInventory_GetResultItems", {{1, 1}}},
|
||||
{"ISteamInventory_GetResultItemProperty", {{2, 2}}},
|
||||
{"ISteamInventory_CheckResultSteamID", {{1, 3}, {2, 4}}},
|
||||
{"ISteamInventory_GetAllItems", {{1, 5}, {2, 6}}},
|
||||
{"ISteamInventory_GetItemsByID", {{1, 6}, {2, 7}}},
|
||||
{"ISteamInventory_SerializeResult", {{1, 7}, {2, 8}}},
|
||||
{"ISteamInventory_GetItemDefinitionIDs", {{1, 20}, {2, 21}}},
|
||||
{"ISteamInventory_GetItemDefinitionProperty", {{1, 21}, {2, 22}}},
|
||||
};
|
||||
|
||||
int extract_version_number(
|
||||
const String& version_string,
|
||||
const String& prefix,
|
||||
int min_version,
|
||||
int max_version
|
||||
) {
|
||||
logger->debug("Hooking interface '{}'", version_string);
|
||||
|
||||
try {
|
||||
const auto version_number = stoi(version_string.substr(prefix.length()));
|
||||
|
||||
if (version_number < min_version) {
|
||||
util::panic("Unsupported old version of {}: {}", version_string, version_number);
|
||||
}
|
||||
|
||||
if (version_number > max_version) {
|
||||
logger->warn(
|
||||
"Unsupported new version of {}: {}. Fallback version {} will be used",
|
||||
version_string, version_number, max_version
|
||||
);
|
||||
}
|
||||
|
||||
return version_number;
|
||||
} catch (const std::exception& ex) {
|
||||
util::panic("Failed to extract version number from: '{}'", version_string);
|
||||
}
|
||||
}
|
||||
|
||||
int get_ordinal(const FunctionOrdinalMap& ordinal_map, const String& function_name, int interface_version) {
|
||||
const auto& map = ordinal_map.at(function_name);
|
||||
|
||||
for (auto it = map.rbegin(); it != map.rend(); it++) {
|
||||
const auto [version, ordinal] = *it;
|
||||
|
||||
if (interface_version >= version) {
|
||||
return ordinal;
|
||||
}
|
||||
}
|
||||
|
||||
util::panic("Invalid interface version ({}) for function {}", interface_version, function_name);
|
||||
}
|
||||
|
||||
#define HOOK(MAP, FUNC) \
|
||||
hook::swap_virtual_func( \
|
||||
interface, \
|
||||
#FUNC, \
|
||||
get_ordinal(MAP, #FUNC, version_number), \
|
||||
(FunctionAddress) (FUNC) \
|
||||
);
|
||||
|
||||
#define HOOK_STEAM_CLIENT(FUNC) HOOK(steam_client_ordinal_map, FUNC)
|
||||
#define HOOK_STEAM_APPS(FUNC) HOOK(steam_apps_ordinal_map, FUNC)
|
||||
#define HOOK_STEAM_USER(FUNC) HOOK(steam_user_ordinal_map, FUNC)
|
||||
#define HOOK_STEAM_INVENTORY(FUNC) HOOK(steam_inventory_ordinal_map, FUNC)
|
||||
|
||||
void hook_virtuals(void* interface, const String& version_string) {
|
||||
if (interface == nullptr) {
|
||||
// Game has tried to use an interface before initializing steam api
|
||||
return;
|
||||
}
|
||||
|
||||
static Set<void*> hooked_interfaces;
|
||||
|
||||
if (hooked_interfaces.contains(interface)) {
|
||||
// This interface is already hooked. Skipping it.
|
||||
return;
|
||||
}
|
||||
|
||||
static std::mutex section;
|
||||
std::lock_guard<std::mutex> guard(section);
|
||||
|
||||
if (version_string.starts_with(STEAM_CLIENT)) {
|
||||
const auto version_number = extract_version_number(version_string, STEAM_CLIENT, 6, 20);
|
||||
|
||||
HOOK_STEAM_CLIENT(ISteamClient_GetISteamApps)
|
||||
HOOK_STEAM_CLIENT(ISteamClient_GetISteamUser)
|
||||
if (version_number >= 7) {
|
||||
HOOK_STEAM_CLIENT(ISteamClient_GetISteamGenericInterface)
|
||||
}
|
||||
if (version_number >= 17) {
|
||||
HOOK_STEAM_CLIENT(ISteamClient_GetISteamInventory)
|
||||
}
|
||||
} else if (version_string.starts_with(STEAM_APPS)) {
|
||||
const auto version_number = extract_version_number(version_string, STEAM_APPS, 2, 8);
|
||||
|
||||
HOOK_STEAM_APPS(ISteamApps_BIsSubscribedApp)
|
||||
|
||||
if (version_number >= 3) {
|
||||
HOOK_STEAM_APPS(ISteamApps_BIsDlcInstalled)
|
||||
}
|
||||
|
||||
if (version_number >= 4) {
|
||||
HOOK_STEAM_APPS(ISteamApps_GetDLCCount)
|
||||
HOOK_STEAM_APPS(ISteamApps_BGetDLCDataByIndex)
|
||||
}
|
||||
} else if (version_string.starts_with(STEAM_USER)) {
|
||||
const auto version_number = extract_version_number(version_string, STEAM_USER, 9, 21);
|
||||
|
||||
if (version_number >= 12) {
|
||||
HOOK_STEAM_USER(ISteamUser_UserHasLicenseForApp)
|
||||
}
|
||||
} else if (version_string.starts_with(STEAM_INVENTORY)) {
|
||||
const auto version_number = extract_version_number(version_string, STEAM_INVENTORY, 1, 3);
|
||||
|
||||
HOOK_STEAM_INVENTORY(ISteamInventory_GetResultStatus)
|
||||
HOOK_STEAM_INVENTORY(ISteamInventory_GetResultItems)
|
||||
HOOK_STEAM_INVENTORY(ISteamInventory_CheckResultSteamID)
|
||||
HOOK_STEAM_INVENTORY(ISteamInventory_GetAllItems)
|
||||
HOOK_STEAM_INVENTORY(ISteamInventory_GetItemsByID)
|
||||
HOOK_STEAM_INVENTORY(ISteamInventory_SerializeResult)
|
||||
HOOK_STEAM_INVENTORY(ISteamInventory_GetItemDefinitionIDs)
|
||||
HOOK_STEAM_INVENTORY(ISteamInventory_GetItemDefinitionProperty)
|
||||
|
||||
if (version_number >= 2) {
|
||||
HOOK_STEAM_INVENTORY(ISteamInventory_GetResultItemProperty)
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
hooked_interfaces.insert(interface);
|
||||
}
|
||||
|
||||
HSteamPipe get_steam_pipe_or_throw() {
|
||||
const auto& steam_api_module = win_util::get_module_handle_or_throw(ORIGINAL_DLL);
|
||||
void* GetHSteamPipe_address;
|
||||
try {
|
||||
GetHSteamPipe_address = (void*) win_util::get_proc_address_or_throw(
|
||||
steam_api_module, "SteamAPI_GetHSteamPipe"
|
||||
);
|
||||
} catch (const Exception& ex) {
|
||||
GetHSteamPipe_address = (void*) win_util::get_proc_address_or_throw(
|
||||
steam_api_module, "GetHSteamPipe"
|
||||
);
|
||||
}
|
||||
typedef HSteamPipe (__cdecl* GetHSteamPipe_t)();
|
||||
const auto GetHSteamPipe_o = (GetHSteamPipe_t) GetHSteamPipe_address;
|
||||
return GetHSteamPipe_o();
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
F get_virtual_function(void* interface, int ordinal) {
|
||||
auto* v_table = (void***) interface;
|
||||
return (F) (*v_table)[ordinal];
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
auto call_virtual_function(void* interface, F function, Args... args) {
|
||||
#ifdef _WIN64
|
||||
void* RCX = interface;
|
||||
#else
|
||||
void* ECX = interface;
|
||||
void* EDX = nullptr;
|
||||
#endif
|
||||
|
||||
return function(ARGS(args...));
|
||||
}
|
||||
|
||||
uint32_t get_app_id_or_throw() {
|
||||
// Get CreateInterface
|
||||
const auto& steam_client_module = win_util::get_module_handle_or_throw(STEAMCLIENT_DLL);
|
||||
auto* CreateInterface_address = (void*) win_util::get_proc_address_or_throw(
|
||||
steam_client_module, "CreateInterface"
|
||||
);
|
||||
auto* CreateInterface_o = PLH::FnCast(CreateInterface_address, CreateInterface);
|
||||
|
||||
// Get ISteamClient
|
||||
int result;
|
||||
auto* i_steam_client = CreateInterface_o("SteamClient006", &result);
|
||||
if (i_steam_client == nullptr) {
|
||||
throw util::exception("Failed to obtain SteamClient006 interface. Result: {}", result);
|
||||
}
|
||||
|
||||
// Get GetISteamUtils
|
||||
typedef void*** (__fastcall* GetISteamUtils_t)(PARAMS(HSteamPipe hSteamPipe, const char* version));
|
||||
const auto steam_utils_ordinal = steam_client_ordinal_map["ISteamClient_GetISteamUtils"][6];
|
||||
const auto GetISteamUtils_o = get_virtual_function<GetISteamUtils_t>(i_steam_client, steam_utils_ordinal);
|
||||
|
||||
// Get ISteamUtils
|
||||
const auto steam_pipe = get_steam_pipe_or_throw();
|
||||
auto* i_steam_utils = call_virtual_function(i_steam_client, GetISteamUtils_o, steam_pipe, "SteamUtils002");
|
||||
|
||||
// Get GetAppID
|
||||
typedef uint32_t (__fastcall* GetAppID_t)(PARAMS());
|
||||
const auto get_app_id_ordinal = steam_utils_ordinal_map["ISteamUtils_GetAppID"][2];
|
||||
const auto GetAppID_o = get_virtual_function<GetAppID_t>(i_steam_utils, get_app_id_ordinal);
|
||||
|
||||
return call_virtual_function(i_steam_utils, GetAppID_o);
|
||||
}
|
||||
}
|
||||
135
src/steam_functions/steam_functions.hpp
Normal file
135
src/steam_functions/steam_functions.hpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalabox/koalabox.hpp>
|
||||
|
||||
#include <steam_types/steam_types.hpp>
|
||||
|
||||
/**
|
||||
* By default, virtual functions are declared with __thiscall
|
||||
* convention, which is normal since they are class members.
|
||||
* But it presents an issue for us, since we cannot pass *this
|
||||
* pointer as a function argument. This is because *this
|
||||
* pointer is passed via register ECX in __thiscall
|
||||
* convention. Hence, to resolve this issue we declare our
|
||||
* hooked functions with __fastcall convention, to trick
|
||||
* the compiler into reading ECX & EDX registers as 1st
|
||||
* and 2nd function arguments respectively. Similarly, __fastcall
|
||||
* makes the compiler push the first argument into the ECX register,
|
||||
* which mimics the __thiscall calling convention. Register EDX
|
||||
* is not used anywhere in this case, but we still pass it along
|
||||
* to conform to the __fastcall convention. This all applies
|
||||
* to the x86 architecture.
|
||||
*
|
||||
* In x86-64 however, there is only one calling convention,
|
||||
* so __fastcall is simply ignored. However, RDX in this case
|
||||
* will store the 1st actual argument to the function, so we
|
||||
* have to omit it from the function signature.
|
||||
*
|
||||
* The macros below implement the above-mentioned considerations.
|
||||
*/
|
||||
#ifdef _WIN64
|
||||
#define PARAMS(...) void* RCX, ##__VA_ARGS__
|
||||
#define ARGS(...) RCX, ##__VA_ARGS__
|
||||
#define THIS RCX
|
||||
#else
|
||||
#define PARAMS(...) void* ECX, void* EDX, ##__VA_ARGS__
|
||||
#define ARGS(...) ECX, EDX, ##__VA_ARGS__
|
||||
#define THIS ECX
|
||||
#endif
|
||||
|
||||
#define DLL_EXPORT(TYPE) extern "C" __declspec( dllexport ) TYPE __cdecl
|
||||
#define VIRTUAL(TYPE) __declspec(noinline) TYPE __fastcall
|
||||
|
||||
class ISteamClient;
|
||||
|
||||
class ISteamApps;
|
||||
|
||||
class ISteamUser;
|
||||
|
||||
class ISteamInventory;
|
||||
|
||||
typedef __int32 HSteamPipe;
|
||||
typedef __int32 HSteamUser;
|
||||
typedef uint32_t AppId_t;
|
||||
typedef uint64_t CSteamID;
|
||||
|
||||
typedef uint32_t HCoroutine;
|
||||
|
||||
// ISteamClient
|
||||
VIRTUAL(void*) ISteamClient_GetISteamApps(PARAMS(HSteamUser, HSteamPipe, const char*));
|
||||
VIRTUAL(void*) ISteamClient_GetISteamUser(PARAMS(HSteamUser, HSteamPipe, const char*));
|
||||
VIRTUAL(void*) ISteamClient_GetISteamGenericInterface(PARAMS(HSteamUser, HSteamPipe, const char*));
|
||||
VIRTUAL(void*) ISteamClient_GetISteamInventory(PARAMS(HSteamUser, HSteamPipe, const char*));
|
||||
|
||||
// ISteamApps
|
||||
VIRTUAL(bool) ISteamApps_BIsSubscribedApp(PARAMS(AppId_t));
|
||||
VIRTUAL(bool) ISteamApps_BIsDlcInstalled(PARAMS(AppId_t));
|
||||
VIRTUAL(int) ISteamApps_GetDLCCount(PARAMS());
|
||||
VIRTUAL(bool) ISteamApps_BGetDLCDataByIndex(PARAMS(int, AppId_t*, bool*, char*, int));
|
||||
|
||||
// ISteamUser
|
||||
VIRTUAL(EUserHasLicenseForAppResult) ISteamUser_UserHasLicenseForApp(PARAMS(CSteamID, AppId_t));
|
||||
|
||||
// ISteamInventory
|
||||
VIRTUAL(EResult) ISteamInventory_GetResultStatus(PARAMS(uint32_t));
|
||||
VIRTUAL(bool) ISteamInventory_GetResultItems(PARAMS(SteamInventoryResult_t, SteamItemDetails_t*, uint32_t*));
|
||||
VIRTUAL(bool) ISteamInventory_GetResultItemProperty(
|
||||
PARAMS(SteamInventoryResult_t, uint32_t, const char*, char*, const uint32_t*)
|
||||
);
|
||||
VIRTUAL(bool) ISteamInventory_GetAllItems(PARAMS(SteamInventoryResult_t*));
|
||||
VIRTUAL(bool) ISteamInventory_GetItemsByID(PARAMS(SteamInventoryResult_t*, const SteamItemInstanceID_t*, uint32_t));
|
||||
VIRTUAL(bool) ISteamInventory_SerializeResult(PARAMS(SteamInventoryResult_t, void*, uint32_t*));
|
||||
VIRTUAL(bool) ISteamInventory_GetItemDefinitionIDs(PARAMS(SteamItemDef_t*, uint32_t*));
|
||||
VIRTUAL(bool) ISteamInventory_GetItemDefinitionProperty(PARAMS(SteamItemDef_t, const char*, char*, uint32_t*));
|
||||
VIRTUAL(bool) ISteamInventory_CheckResultSteamID(PARAMS(SteamInventoryResult_t, CSteamID));
|
||||
|
||||
// API
|
||||
|
||||
DLL_EXPORT(void*) CreateInterface(const char*, int*);
|
||||
DLL_EXPORT(void*) SteamInternal_FindOrCreateUserInterface(HSteamUser, const char*);
|
||||
DLL_EXPORT(void*) SteamInternal_CreateInterface(const char*);
|
||||
DLL_EXPORT(void*) SteamApps();
|
||||
DLL_EXPORT(void*) SteamClient();
|
||||
DLL_EXPORT(void*) SteamUser();
|
||||
|
||||
// Flat interfaces
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsSubscribedApp(ISteamApps*, AppId_t);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsDlcInstalled(ISteamApps*, AppId_t);
|
||||
DLL_EXPORT(int) SteamAPI_ISteamApps_GetDLCCount(ISteamApps*);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamApps_BGetDLCDataByIndex(ISteamApps*, int, AppId_t*, bool*, char*, int);
|
||||
DLL_EXPORT(EUserHasLicenseForAppResult) SteamAPI_ISteamUser_UserHasLicenseForApp(ISteamUser*, CSteamID, AppId_t);
|
||||
DLL_EXPORT(void*) SteamAPI_ISteamClient_GetISteamGenericInterface(ISteamClient*, HSteamUser, HSteamPipe, const char*);
|
||||
DLL_EXPORT(EResult) SteamAPI_ISteamInventory_GetResultStatus(ISteamInventory*, SteamInventoryResult_t);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetResultItems(
|
||||
ISteamInventory*, SteamInventoryResult_t, SteamItemDetails_t*, uint32_t*
|
||||
);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetResultItemProperty(
|
||||
ISteamInventory*, SteamInventoryResult_t, uint32_t, const char*, char*, const uint32_t*
|
||||
);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_CheckResultSteamID(ISteamInventory*, SteamInventoryResult_t, CSteamID);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetAllItems(ISteamInventory*, SteamInventoryResult_t*);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemsByID(
|
||||
ISteamInventory*, SteamInventoryResult_t*, const SteamItemInstanceID_t*, uint32_t
|
||||
);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_SerializeResult(ISteamInventory*, SteamInventoryResult_t, void*, uint32_t*);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionIDs(ISteamInventory*, SteamItemDef_t*, uint32_t*);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionProperty(
|
||||
ISteamInventory*, SteamItemDef_t, const char*, char*, uint32_t*
|
||||
);
|
||||
|
||||
// vstdlib
|
||||
|
||||
DLL_EXPORT(HCoroutine) Coroutine_Create(void* callback_address, struct CoroutineData* data);
|
||||
|
||||
namespace steam_functions {
|
||||
using namespace koalabox;
|
||||
|
||||
const String STEAM_APPS = "STEAMAPPS_INTERFACE_VERSION"; // NOLINT(cert-err58-cpp)
|
||||
const String STEAM_CLIENT = "SteamClient"; // NOLINT(cert-err58-cpp)
|
||||
const String STEAM_USER = "SteamUser"; // NOLINT(cert-err58-cpp)
|
||||
const String STEAM_INVENTORY = "STEAMINVENTORY_INTERFACE_V"; // NOLINT(cert-err58-cpp)
|
||||
|
||||
void hook_virtuals(void* interface, const String& version_string);
|
||||
uint32_t get_app_id_or_throw();
|
||||
}
|
||||
268
src/steam_impl/steam_apps.cpp
Normal file
268
src/steam_impl/steam_apps.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
|
||||
#include <koalabox/io.hpp>
|
||||
|
||||
#include <cpr/cpr.h>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
constexpr auto max_dlc = 64;
|
||||
|
||||
Vector<AppId_t> cached_dlcs;
|
||||
int original_dlc_count = 0;
|
||||
|
||||
Path get_cache_path() {
|
||||
static const auto path = self_directory / "SmokeAPI.cache.json";
|
||||
return path;
|
||||
}
|
||||
|
||||
void read_from_cache(const String& app_id_str) {
|
||||
try {
|
||||
const auto text = io::read_file(get_cache_path());
|
||||
if (text.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto json = nlohmann::json::parse(text);
|
||||
|
||||
cached_dlcs = json[app_id_str]["dlc"].get<decltype(cached_dlcs)>();
|
||||
|
||||
logger->debug("Read {} DLCs from cache", cached_dlcs.size());
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Error reading DLCs from cache: {}", ex.what());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void save_to_cache(const String& app_id_str) {
|
||||
try {
|
||||
logger->debug("Saving {} DLCs to cache", cached_dlcs.size());
|
||||
|
||||
const nlohmann::json json = {
|
||||
{app_id_str, {
|
||||
{"dlc", cached_dlcs}
|
||||
}}
|
||||
};
|
||||
|
||||
io::write_file(get_cache_path(), json.dump(2));
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Error saving DLCs to cache: {}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const auto app_id_str = std::to_string(app_id);
|
||||
|
||||
const auto fetch_from_steam = [&]() {
|
||||
const auto url = fmt::format("https://store.steampowered.com/dlc/{}/ajaxgetdlclist", app_id_str);
|
||||
const auto res = cpr::Get(cpr::Url{url});
|
||||
|
||||
if (res.status_code != cpr::status::HTTP_OK) {
|
||||
throw util::exception(
|
||||
"Steam Web API didn't responded with HTTP_OK result. Code: {}, Error: {},\n"
|
||||
"Headers:\n{}\nBody:\n{}",
|
||||
res.status_code, (int) res.error.code, res.raw_header, res.text
|
||||
);
|
||||
}
|
||||
|
||||
const auto json = nlohmann::json::parse(res.text);
|
||||
|
||||
|
||||
if (json["success"] != 1) {
|
||||
throw util::exception("Web API responded with 'success': 1.");
|
||||
}
|
||||
|
||||
Vector<AppId_t> dlcs;
|
||||
|
||||
for (const auto& dlc: json["dlcs"]) {
|
||||
const auto app_id = dlc["appid"].get<String>();
|
||||
dlcs.emplace_back(std::stoi(app_id));
|
||||
}
|
||||
|
||||
return dlcs;
|
||||
};
|
||||
|
||||
const auto fetch_from_github = [&]() {
|
||||
const String url = "https://raw.githubusercontent.com/acidicoala/public-entitlements/main/steam/v1/dlc.json";
|
||||
const auto res = cpr::Get(cpr::Url{url});
|
||||
|
||||
if (res.status_code != cpr::status::HTTP_OK) {
|
||||
throw util::exception(
|
||||
"Github Web API didn't responded with HTTP_OK result. Code: {}, Error: {},\n"
|
||||
"Headers:\n{}\nBody:\n{}",
|
||||
res.status_code, (int) res.error.code, res.raw_header, res.text
|
||||
);
|
||||
}
|
||||
|
||||
const auto json = nlohmann::json::parse(res.text);
|
||||
|
||||
if (json.contains(app_id_str)) {
|
||||
return json[app_id_str].get<decltype(cached_dlcs)>();
|
||||
}
|
||||
|
||||
return Vector<AppId_t>{};
|
||||
};
|
||||
|
||||
try {
|
||||
read_from_cache(app_id_str);
|
||||
|
||||
auto list1 = fetch_from_steam();
|
||||
auto list2 = fetch_from_github();
|
||||
list1.insert(list1.end(), list2.begin(), list2.end());
|
||||
Set<AppId_t> fetched_dlcs(list1.begin(), list1.end());
|
||||
|
||||
if (fetched_dlcs.size() > cached_dlcs.size()) {
|
||||
cached_dlcs = Vector<AppId_t>(fetched_dlcs.begin(), fetched_dlcs.end());
|
||||
}
|
||||
|
||||
save_to_cache(app_id_str);
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Failed to fetch DLC: {}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
return installed;
|
||||
}
|
||||
|
||||
int GetDLCCount(const String& function_name, const std::function<int()>& original_function) {
|
||||
static std::mutex section;
|
||||
std::lock_guard<std::mutex> guard(section);
|
||||
|
||||
// Compute count only once
|
||||
static int total_count = [&]() {
|
||||
original_dlc_count = original_function();
|
||||
logger->debug("{} -> Original DLC count: {}", function_name, original_dlc_count);
|
||||
|
||||
const auto injected_count = static_cast<int>(config.dlc_ids.size());
|
||||
logger->debug("{} -> Injected DLC count: {}", function_name, injected_count);
|
||||
|
||||
if (original_dlc_count < max_dlc) {
|
||||
// Steamworks may max out this value at 64, depending on how much unowned DLCs the user has.
|
||||
// Despite this limit, some games with more than 64 DLCs still keep using this method.
|
||||
// This means we have to fetch full list of IDs from web api.
|
||||
|
||||
return original_dlc_count + injected_count;
|
||||
}
|
||||
|
||||
logger->debug("Game has {} or more DLCs. Fetching DLCs from a web API.", max_dlc);
|
||||
fetch_and_cache_dlcs();
|
||||
|
||||
const auto fetched_count = static_cast<int>(cached_dlcs.size());
|
||||
logger->debug("{} -> Fetched/cached DLC count: {}", function_name, fetched_count);
|
||||
|
||||
return fetched_count + injected_count;
|
||||
}();
|
||||
|
||||
logger->info("{} -> Responding with DLC count: {}", function_name, total_count);
|
||||
|
||||
return total_count;
|
||||
}
|
||||
|
||||
bool GetDLCDataByIndex(
|
||||
const String& function_name,
|
||||
int iDLC,
|
||||
AppId_t* pAppID,
|
||||
bool* pbAvailable,
|
||||
char* pchName,
|
||||
int cchNameBufferSize,
|
||||
const std::function<bool()>& original_function
|
||||
) {
|
||||
const auto print_dlc_info = [&](const String& tag) {
|
||||
logger->info(
|
||||
"{} -> [{}] index: {}, App ID: {}, available: {}, name: '{}'",
|
||||
function_name, tag, iDLC, *pAppID, *pbAvailable, pchName
|
||||
);
|
||||
};
|
||||
|
||||
const auto fill_dlc_info = [&](const AppId_t id) {
|
||||
*pAppID = id;
|
||||
*pbAvailable = should_unlock(id);
|
||||
|
||||
auto name = fmt::format("DLC #{} with ID: {} ", iDLC, id);
|
||||
name = name.substr(0, cchNameBufferSize);
|
||||
*name.rbegin() = '\0';
|
||||
memcpy_s(pchName, cchNameBufferSize, name.c_str(), name.size());
|
||||
};
|
||||
|
||||
const auto inject_dlc = [&](const int index) {
|
||||
if (index >= config.dlc_ids.size()) {
|
||||
logger->error("{} -> Out of bounds injected index: {}", function_name, index);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto app_id = config.dlc_ids[index];
|
||||
fill_dlc_info(app_id);
|
||||
print_dlc_info("injected");
|
||||
return true;
|
||||
};
|
||||
|
||||
// Original response
|
||||
if (cached_dlcs.empty()) {
|
||||
// Original DLC index
|
||||
if (iDLC < original_dlc_count) {
|
||||
const auto success = original_function();
|
||||
|
||||
if (success) {
|
||||
*pbAvailable = should_unlock(*pAppID);
|
||||
print_dlc_info("original");
|
||||
} else {
|
||||
logger->warn("{} -> original function failed for index: {}", function_name, iDLC);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// Injected DLC index (after original)
|
||||
const auto index = iDLC - original_dlc_count;
|
||||
return inject_dlc(index);
|
||||
}
|
||||
|
||||
// Cached response
|
||||
const auto total_size = cached_dlcs.size() + config.dlc_ids.size();
|
||||
if (iDLC < 0 or iDLC >= total_size) {
|
||||
logger->error(
|
||||
"{} -> Game accessed out of bounds DLC index: {}. Total size: {}",
|
||||
function_name, iDLC, total_size
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cached index
|
||||
if (iDLC < cached_dlcs.size()) {
|
||||
const auto app_id = cached_dlcs[iDLC];
|
||||
fill_dlc_info(app_id);
|
||||
print_dlc_info("cached");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Injected DLC index (after cached)
|
||||
|
||||
const auto index = iDLC - static_cast<int>(cached_dlcs.size());
|
||||
return inject_dlc(index);
|
||||
}
|
||||
}
|
||||
21
src/steam_impl/steam_client.cpp
Normal file
21
src/steam_impl/steam_client.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
|
||||
namespace steam_client{
|
||||
|
||||
void* GetGenericInterface(
|
||||
const String& function_name,
|
||||
const String& interface_version,
|
||||
const std::function<void*()>& original_function
|
||||
) {
|
||||
logger->debug("{} -> Version: '{}'", function_name, interface_version);
|
||||
|
||||
auto* const interface = original_function();
|
||||
|
||||
logger->debug("{} -> Result: {}", function_name, fmt::ptr(interface));
|
||||
|
||||
steam_functions::hook_virtuals(interface, interface_version);
|
||||
|
||||
return interface;
|
||||
}
|
||||
|
||||
}
|
||||
116
src/steam_impl/steam_impl.hpp
Normal file
116
src/steam_impl/steam_impl.hpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
using namespace koalabox;
|
||||
|
||||
namespace steam_apps {
|
||||
|
||||
bool IsSubscribedApp(const String& function_name, AppId_t appID);
|
||||
|
||||
bool IsDlcInstalled(const String& function_name, AppId_t appID);
|
||||
|
||||
int GetDLCCount(const String& function_name, const std::function<int()>& original_function);
|
||||
|
||||
bool GetDLCDataByIndex(
|
||||
const String& function_name,
|
||||
int iDLC,
|
||||
AppId_t* pAppID,
|
||||
bool* pbAvailable,
|
||||
char* pchName,
|
||||
int cchNameBufferSize,
|
||||
const std::function<bool()>& original_function
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
namespace steam_user {
|
||||
|
||||
EUserHasLicenseForAppResult UserHasLicenseForApp(
|
||||
const String& function_name,
|
||||
AppId_t appID,
|
||||
const std::function<EUserHasLicenseForAppResult()>& original_function
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
namespace steam_client {
|
||||
|
||||
void* GetGenericInterface(
|
||||
const String& function_name,
|
||||
const String& interface_version,
|
||||
const std::function<void*()>& original_function
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
namespace steam_inventory {
|
||||
|
||||
EResult GetResultStatus(
|
||||
const String& function_name,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
const std::function<EResult()>& original_function
|
||||
);
|
||||
|
||||
bool GetResultItems(
|
||||
const String& function_name,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
SteamItemDetails_t* pOutItemsArray,
|
||||
uint32_t* punOutItemsArraySize,
|
||||
const std::function<bool()>& original_function,
|
||||
const std::function<bool(SteamItemDef_t*, uint32_t*)>& get_item_definition_ids
|
||||
);
|
||||
|
||||
bool GetResultItemProperty(
|
||||
const String& function_name,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
uint32_t unItemIndex,
|
||||
const char* pchPropertyName,
|
||||
char* pchValueBuffer,
|
||||
const uint32_t* punValueBufferSizeOut,
|
||||
const std::function<bool()>& original_function
|
||||
);
|
||||
|
||||
bool GetAllItems(
|
||||
const String& function_name,
|
||||
const SteamInventoryResult_t* pResultHandle,
|
||||
const std::function<bool()>& original_function
|
||||
);
|
||||
|
||||
bool GetItemsByID(
|
||||
const String& function_name,
|
||||
SteamInventoryResult_t* pResultHandle,
|
||||
const SteamItemInstanceID_t* pInstanceIDs,
|
||||
uint32_t unCountInstanceIDs,
|
||||
const std::function<bool()>& original_function
|
||||
);
|
||||
|
||||
bool SerializeResult(
|
||||
const String& function_name,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
void* pOutBuffer,
|
||||
uint32_t* punOutBufferSize,
|
||||
const std::function<bool()>& original_function
|
||||
);
|
||||
|
||||
bool GetItemDefinitionIDs(
|
||||
const String& function_name,
|
||||
const SteamItemDef_t* pItemDefIDs,
|
||||
uint32_t* punItemDefIDsArraySize,
|
||||
const std::function<bool()>& original_function
|
||||
);
|
||||
|
||||
bool GetItemDefinitionProperty(
|
||||
const String& function_name,
|
||||
SteamItemDef_t iDefinition,
|
||||
const char* pchPropertyName,
|
||||
char* pchValueBuffer,
|
||||
const uint32_t* punValueBufferSizeOut,
|
||||
const std::function<bool()>& original_function
|
||||
);
|
||||
|
||||
bool CheckResultSteamID(
|
||||
const String& function_name,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
CSteamID steamIDExpected,
|
||||
const std::function<bool()>& original_function
|
||||
);
|
||||
}
|
||||
258
src/steam_impl/steam_inventory.cpp
Normal file
258
src/steam_impl/steam_inventory.cpp
Normal file
@@ -0,0 +1,258 @@
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
|
||||
namespace steam_inventory {
|
||||
|
||||
EResult GetResultStatus(
|
||||
const String& function_name,
|
||||
const SteamInventoryResult_t resultHandle,
|
||||
const std::function<EResult()>& original_function
|
||||
) {
|
||||
const auto status = original_function();
|
||||
|
||||
logger->debug("{} -> handle: {}, status: {}", function_name, resultHandle, (int) status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool GetResultItems(
|
||||
const String& function_name,
|
||||
const SteamInventoryResult_t resultHandle,
|
||||
SteamItemDetails_t* pOutItemsArray,
|
||||
uint32_t* punOutItemsArraySize,
|
||||
const std::function<bool()>& original_function,
|
||||
const std::function<bool(SteamItemDef_t*, uint32_t*)>& get_item_definition_ids
|
||||
) {
|
||||
static std::mutex section;
|
||||
std::lock_guard<std::mutex> guard(section);
|
||||
|
||||
const auto success = original_function();
|
||||
|
||||
auto print_item = [](const String& tag, const SteamItemDetails_t& item) {
|
||||
logger->debug(
|
||||
" [{}] definitionId: {}, itemId: {}, quantity: {}, flags: {}",
|
||||
tag, item.m_iDefinition, item.m_itemId, item.m_unQuantity, item.m_unFlags
|
||||
);
|
||||
};
|
||||
|
||||
if (not success) {
|
||||
logger->debug("{} -> original result is false", function_name);
|
||||
return success;
|
||||
}
|
||||
|
||||
if (punOutItemsArraySize == nullptr) {
|
||||
logger->error("{} -> arraySize pointer is null", function_name);
|
||||
return success;
|
||||
}
|
||||
|
||||
logger->debug(
|
||||
"{} -> handle: {}, pOutItemsArray: {}, arraySize: {}",
|
||||
function_name, resultHandle, fmt::ptr(pOutItemsArray), *punOutItemsArraySize
|
||||
);
|
||||
|
||||
static uint32_t original_count = 0;
|
||||
const auto injected_count = smoke_api::config.inventory_items.size();
|
||||
|
||||
// Automatically get inventory items from steam
|
||||
static Vector<SteamItemDef_t> auto_inventory_items;
|
||||
if (smoke_api::config.auto_inject_inventory) {
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [&]() {
|
||||
uint32_t count = 0;
|
||||
if (get_item_definition_ids(nullptr, &count)) {
|
||||
auto_inventory_items.resize(count);
|
||||
get_item_definition_ids(auto_inventory_items.data(), &count);
|
||||
}
|
||||
});
|
||||
}
|
||||
const auto auto_injected_count = auto_inventory_items.size();
|
||||
|
||||
|
||||
if (not pOutItemsArray) {
|
||||
// If pOutItemsArray is NULL then we must set the array size.
|
||||
original_count = *punOutItemsArraySize;
|
||||
*punOutItemsArraySize += auto_injected_count + injected_count;
|
||||
logger->debug(
|
||||
"{} -> Original count: {}, Total count: {}",
|
||||
function_name, original_count, *punOutItemsArraySize
|
||||
);
|
||||
} else {
|
||||
// Otherwise, we modify the array
|
||||
for (int i = 0; i < original_count; i++) {
|
||||
print_item("original", pOutItemsArray[i]);
|
||||
}
|
||||
|
||||
static auto new_item = [](SteamItemDef_t id) {
|
||||
return SteamItemDetails_t{
|
||||
.m_itemId=id,
|
||||
.m_iDefinition=id,
|
||||
.m_unQuantity=1,
|
||||
.m_unFlags=0,
|
||||
};
|
||||
};
|
||||
|
||||
for (int i = 0; i < auto_injected_count; i++) {
|
||||
auto& item = pOutItemsArray[original_count + i];
|
||||
const auto item_def_id = auto_inventory_items[i];
|
||||
|
||||
item = new_item(item_def_id);
|
||||
|
||||
print_item("auto-injected", item);
|
||||
}
|
||||
|
||||
for (int i = 0; i < injected_count; i++) {
|
||||
auto& item = pOutItemsArray[original_count + auto_injected_count + i];
|
||||
const auto item_def_id = smoke_api::config.inventory_items[i];
|
||||
|
||||
item = new_item(item_def_id);
|
||||
|
||||
print_item("injected", item);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool GetResultItemProperty(
|
||||
const String& function_name,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
uint32_t unItemIndex,
|
||||
const char* pchPropertyName,
|
||||
char* pchValueBuffer,
|
||||
const uint32_t* punValueBufferSizeOut,
|
||||
const std::function<bool()>& original_function
|
||||
) {
|
||||
const auto success = original_function();
|
||||
|
||||
if (!success) {
|
||||
logger->warn("{} -> result is false", function_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
logger->debug(
|
||||
"{} -> handle: {}, index: {}, propertyName: '{}', buffer: {}",
|
||||
function_name, resultHandle, unItemIndex, pchPropertyName,
|
||||
String(pchValueBuffer, *punValueBufferSizeOut - 1)
|
||||
);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool GetAllItems(
|
||||
const String& function_name,
|
||||
const SteamInventoryResult_t* pResultHandle,
|
||||
const std::function<bool()>& original_function
|
||||
) {
|
||||
const auto success = original_function();
|
||||
|
||||
logger->debug("{} -> handle: {}", function_name, fmt::ptr(pResultHandle));
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool GetItemsByID(
|
||||
const String& function_name,
|
||||
SteamInventoryResult_t* pResultHandle,
|
||||
const SteamItemInstanceID_t* pInstanceIDs,
|
||||
const uint32_t unCountInstanceIDs,
|
||||
const std::function<bool()>& original_function
|
||||
) {
|
||||
const auto success = original_function();
|
||||
|
||||
logger->trace("{} -> handle: {}", function_name, fmt::ptr(pResultHandle));
|
||||
|
||||
if (success && pInstanceIDs != nullptr) {
|
||||
for (int i = 0; i < unCountInstanceIDs; i++) {
|
||||
logger->trace(" index: {}, itemId: {}", i, pInstanceIDs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool SerializeResult(
|
||||
const String& function_name,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
void* pOutBuffer,
|
||||
uint32_t* punOutBufferSize,
|
||||
const std::function<bool()>& original_function
|
||||
) {
|
||||
const auto success = original_function();
|
||||
|
||||
if (pOutBuffer != nullptr) {
|
||||
String buffer((char*) pOutBuffer, *punOutBufferSize);
|
||||
logger->debug("{} -> handle: {}, buffer: '{}'", function_name, resultHandle, buffer);
|
||||
} else {
|
||||
logger->debug("{} -> handle: {}, size: '{}'", function_name, resultHandle, *punOutBufferSize);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool GetItemDefinitionIDs(
|
||||
const String& function_name,
|
||||
const SteamItemDef_t* pItemDefIDs,
|
||||
uint32_t* punItemDefIDsArraySize,
|
||||
const std::function<bool()>& original_function
|
||||
) {
|
||||
const auto success = original_function();
|
||||
|
||||
if (!success) {
|
||||
logger->warn("{} -> result is false", function_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (punItemDefIDsArraySize) {
|
||||
logger->debug("{} -> size: '{}'", function_name, *punItemDefIDsArraySize);
|
||||
}
|
||||
|
||||
if (pItemDefIDs) { // Definitions were copied
|
||||
for (int i = 0; i < *punItemDefIDsArraySize; i++) {
|
||||
const auto& def = pItemDefIDs[i];
|
||||
logger->debug(" Definition index: '{}', ID: {}", i, def);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool GetItemDefinitionProperty(
|
||||
const String& function_name,
|
||||
SteamItemDef_t iDefinition,
|
||||
const char* pchPropertyName,
|
||||
char* pchValueBuffer,
|
||||
const uint32_t* punValueBufferSizeOut,
|
||||
const std::function<bool()>& original_function
|
||||
) {
|
||||
const auto success = original_function();
|
||||
|
||||
if (!success) {
|
||||
logger->warn("{} -> result is false", function_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
logger->debug(
|
||||
"{} -> Definition ID: {}, name: '{}', buffer: '{}'",
|
||||
function_name, iDefinition, pchPropertyName, String(pchValueBuffer, *punValueBufferSizeOut - 1)
|
||||
);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool CheckResultSteamID(
|
||||
const String& function_name,
|
||||
SteamInventoryResult_t resultHandle,
|
||||
CSteamID steamIDExpected,
|
||||
const std::function<bool()>& original_function
|
||||
) {
|
||||
const auto result = original_function();
|
||||
|
||||
logger->debug(
|
||||
"{} -> handle: {}, steamID: {}, original result: {}",
|
||||
function_name, resultHandle, steamIDExpected, result
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
27
src/steam_impl/steam_user.cpp
Normal file
27
src/steam_impl/steam_user.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
|
||||
namespace steam_user {
|
||||
|
||||
EUserHasLicenseForAppResult UserHasLicenseForApp(
|
||||
const String& function_name,
|
||||
AppId_t appID,
|
||||
const std::function<EUserHasLicenseForAppResult()>& original_function
|
||||
) {
|
||||
const auto result = original_function();
|
||||
|
||||
if (result == k_EUserHasLicenseResultNoAuth) {
|
||||
logger->warn("{} -> App ID: {}, Result: NoAuth", function_name, appID);
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto has_license = smoke_api::should_unlock(appID);
|
||||
|
||||
logger->info("{} -> App ID: {}, HasLicense: {}", function_name, appID, has_license);
|
||||
|
||||
return has_license
|
||||
? k_EUserHasLicenseResultHasLicense
|
||||
: k_EUserHasLicenseResultDoesNotHaveLicense;
|
||||
}
|
||||
|
||||
}
|
||||
146
src/steam_types/steam_types.hpp
Normal file
146
src/steam_types/steam_types.hpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
|
||||
// results from UserHasLicenseForApp
|
||||
enum EUserHasLicenseForAppResult {
|
||||
k_EUserHasLicenseResultHasLicense = 0, // User has a license for specified app
|
||||
k_EUserHasLicenseResultDoesNotHaveLicense = 1, // User does not have a license for the specified app
|
||||
k_EUserHasLicenseResultNoAuth = 2, // User has not been authenticated
|
||||
};
|
||||
|
||||
typedef uint32_t SteamInventoryResult_t;
|
||||
typedef uint64_t SteamItemInstanceID_t;
|
||||
typedef uint32_t SteamItemDef_t;
|
||||
|
||||
struct SteamItemDetails_t {
|
||||
SteamItemInstanceID_t m_itemId;
|
||||
uint32_t m_iDefinition;
|
||||
uint16_t m_unQuantity;
|
||||
uint16_t m_unFlags; // see ESteamItemFlags
|
||||
};
|
||||
|
||||
enum EResult {
|
||||
k_EResultNone = 0, // no result
|
||||
k_EResultOK = 1, // success
|
||||
k_EResultFail = 2, // generic failure
|
||||
k_EResultNoConnection = 3, // no/failed network connection
|
||||
// k_EResultNoConnectionRetry = 4, // OBSOLETE - removed
|
||||
k_EResultInvalidPassword = 5, // password/ticket is invalid
|
||||
k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere
|
||||
k_EResultInvalidProtocolVer = 7, // protocol version is incorrect
|
||||
k_EResultInvalidParam = 8, // a parameter is incorrect
|
||||
k_EResultFileNotFound = 9, // file was not found
|
||||
k_EResultBusy = 10, // called method busy - action not taken
|
||||
k_EResultInvalidState = 11, // called object was in an invalid state
|
||||
k_EResultInvalidName = 12, // name is invalid
|
||||
k_EResultInvalidEmail = 13, // email is invalid
|
||||
k_EResultDuplicateName = 14, // name is not unique
|
||||
k_EResultAccessDenied = 15, // access is denied
|
||||
k_EResultTimeout = 16, // operation timed out
|
||||
k_EResultBanned = 17, // VAC2 banned
|
||||
k_EResultAccountNotFound = 18, // account not found
|
||||
k_EResultInvalidSteamID = 19, // steamID is invalid
|
||||
k_EResultServiceUnavailable = 20, // The requested service is currently unavailable
|
||||
k_EResultNotLoggedOn = 21, // The user is not logged on
|
||||
k_EResultPending = 22, // Request is pending (may be in process, or waiting on third party)
|
||||
k_EResultEncryptionFailure = 23, // Encryption or Decryption failed
|
||||
k_EResultInsufficientPrivilege = 24, // Insufficient privilege
|
||||
k_EResultLimitExceeded = 25, // Too much of a good thing
|
||||
k_EResultRevoked = 26, // Access has been revoked (used for revoked guest passes)
|
||||
k_EResultExpired = 27, // License/Guest pass the user is trying to access is expired
|
||||
k_EResultAlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again
|
||||
k_EResultDuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time
|
||||
k_EResultAlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user
|
||||
k_EResultIPNotFound = 31, // IP address not found
|
||||
k_EResultPersistFailed = 32, // failed to write change to the data store
|
||||
k_EResultLockingFailed = 33, // failed to acquire access lock for this operation
|
||||
k_EResultLogonSessionReplaced = 34,
|
||||
k_EResultConnectFailed = 35,
|
||||
k_EResultHandshakeFailed = 36,
|
||||
k_EResultIOFailure = 37,
|
||||
k_EResultRemoteDisconnect = 38,
|
||||
k_EResultShoppingCartNotFound = 39, // failed to find the shopping cart requested
|
||||
k_EResultBlocked = 40, // a user didn't allow it
|
||||
k_EResultIgnored = 41, // target is ignoring sender
|
||||
k_EResultNoMatch = 42, // nothing matching the request found
|
||||
k_EResultAccountDisabled = 43,
|
||||
k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now
|
||||
k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available
|
||||
k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin
|
||||
k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol.
|
||||
k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another.
|
||||
k_EResultPasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed.
|
||||
k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait
|
||||
k_EResultSuspended = 51, // Long running operation (content download) suspended/paused
|
||||
k_EResultCancelled = 52, // Operation canceled (typically by user: content download)
|
||||
k_EResultDataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable
|
||||
k_EResultDiskFull = 54, // Operation canceled - not enough disk space.
|
||||
k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed
|
||||
k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side
|
||||
k_EResultExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account
|
||||
k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid
|
||||
k_EResultExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first
|
||||
k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files
|
||||
k_EResultIllegalPassword = 61, // The requested new password is not legal
|
||||
k_EResultSameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer )
|
||||
k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure
|
||||
k_EResultCannotUseOldPassword = 64, // The requested new password is not legal
|
||||
k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid
|
||||
k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent
|
||||
k_EResultHardwareNotCapableOfIPT = 67, //
|
||||
k_EResultIPTInitError = 68, //
|
||||
k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user
|
||||
k_EResultFacebookQueryError = 70, // Facebook query returned an error
|
||||
k_EResultExpiredLoginAuthCode = 71, // account login denied due to auth code expired
|
||||
k_EResultIPLoginRestrictionFailed = 72,
|
||||
k_EResultAccountLockedDown = 73,
|
||||
k_EResultAccountLogonDeniedVerifiedEmailRequired = 74,
|
||||
k_EResultNoMatchingURL = 75,
|
||||
k_EResultBadResponse = 76, // parse failure, missing field, etc.
|
||||
k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password
|
||||
k_EResultValueOutOfRange = 78, // the value entered is outside the acceptable range
|
||||
k_EResultUnexpectedError = 79, // something happened that we didn't expect to ever happen
|
||||
k_EResultDisabled = 80, // The requested service has been configured to be unavailable
|
||||
k_EResultInvalidCEGSubmission = 81, // The set of files submitted to the CEG server are not valid !
|
||||
k_EResultRestrictedDevice = 82, // The device being used is not allowed to perform this action
|
||||
k_EResultRegionLocked = 83, // The action could not be complete because it is region restricted
|
||||
k_EResultRateLimitExceeded = 84, // Temporary rate limit exceeded, try again later, different from k_EResultLimitExceeded which may be permanent
|
||||
k_EResultAccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to login
|
||||
k_EResultItemDeleted = 86, // The thing we're trying to access has been deleted
|
||||
k_EResultAccountLoginDeniedThrottle = 87, // login attempt failed, try to throttle response to possible attacker
|
||||
k_EResultTwoFactorCodeMismatch = 88, // two factor code mismatch
|
||||
k_EResultTwoFactorActivationCodeMismatch = 89, // activation code for two-factor didn't match
|
||||
k_EResultAccountAssociatedToMultiplePartners = 90, // account has been associated with multiple partners
|
||||
k_EResultNotModified = 91, // data not modified
|
||||
k_EResultNoMobileDevice = 92, // the account does not have a mobile device associated with it
|
||||
k_EResultTimeNotSynced = 93, // the time presented is out of range or tolerance
|
||||
k_EResultSmsCodeFailed = 94, // SMS code failure (no match, none pending, etc.)
|
||||
k_EResultAccountLimitExceeded = 95, // Too many accounts access this resource
|
||||
k_EResultAccountActivityLimitExceeded = 96, // Too many changes to this account
|
||||
k_EResultPhoneActivityLimitExceeded = 97, // Too many changes to this phone
|
||||
k_EResultRefundToWallet = 98, // Cannot refund to payment method, must use wallet
|
||||
k_EResultEmailSendFailure = 99, // Cannot send an email
|
||||
k_EResultNotSettled = 100, // Can't perform operation till payment has settled
|
||||
k_EResultNeedCaptcha = 101, // Needs to provide a valid captcha
|
||||
k_EResultGSLTDenied = 102, // a game server login token owned by this token's owner has been banned
|
||||
k_EResultGSOwnerDenied = 103, // game server owner is denied for other reason (account lock, community ban, vac ban, missing phone)
|
||||
k_EResultInvalidItemType = 104, // the type of thing we were requested to act on is invalid
|
||||
k_EResultIPBanned = 105, // the ip address has been banned from taking this action
|
||||
k_EResultGSLTExpired = 106, // this token has expired from disuse; can be reset for use
|
||||
k_EResultInsufficientFunds = 107, // user doesn't have enough wallet funds to complete the action
|
||||
k_EResultTooManyPending = 108, // There are too many of this thing pending already
|
||||
k_EResultNoSiteLicensesFound = 109, // No site licenses found
|
||||
k_EResultWGNetworkSendExceeded = 110, // the WG couldn't send a response because we exceeded max network send size
|
||||
k_EResultAccountNotFriends = 111, // the user is not mutually friends
|
||||
k_EResultLimitedUserAccount = 112, // the user is limited
|
||||
k_EResultCantRemoveItem = 113, // item can't be removed
|
||||
k_EResultAccountDeleted = 114, // account has been deleted
|
||||
k_EResultExistingUserCancelledLicense = 115, // A license for this already exists, but cancelled
|
||||
k_EResultCommunityCooldown = 116, // access is denied because of a community cooldown (probably from support profile data resets)
|
||||
k_EResultNoLauncherSpecified = 117, // No launcher was specified, but a launcher was needed to choose correct realm for operation.
|
||||
k_EResultMustAgreeToSSA = 118, // User must agree to china SSA or global SSA before login
|
||||
k_EResultLauncherMigrated = 119, // The specified launcher type is no longer supported; the user should be directed elsewhere
|
||||
k_EResultSteamRealmMismatch = 120, // The user's realm does not match the realm of the requested resource
|
||||
k_EResultInvalidSignature = 121, // signature check did not match
|
||||
k_EResultParseFailure = 122, // Failed to parse input
|
||||
k_EResultNoVerifiedPhone = 123, // account does not have a verified phone number
|
||||
};
|
||||
13
src/steamclient_exports/steamclient.cpp
Normal file
13
src/steamclient_exports/steamclient.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
DLL_EXPORT(void*) CreateInterface(const char* interface_string, int* out_result) {
|
||||
return steam_client::GetGenericInterface(__func__, interface_string, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(CreateInterface)
|
||||
|
||||
return CreateInterface_o(interface_string, out_result);
|
||||
});
|
||||
}
|
||||
83
src/vstdlib/vstdlib.cpp
Normal file
83
src/vstdlib/vstdlib.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
#include <koalabox/hook.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
#define DETOUR(FUNC, address) hook::detour((FunctionAddress) (address), #FUNC, (FunctionAddress) (FUNC));
|
||||
|
||||
VIRTUAL(bool) SharedLicensesLockStatus(PARAMS(void* arg)) { // NOLINT(misc-unused-parameters)
|
||||
logger->debug("{} -> instance: {}, arg: {}", __func__, fmt::ptr(THIS), fmt::ptr(arg));
|
||||
return true;
|
||||
}
|
||||
|
||||
VIRTUAL(bool) SharedLibraryStopPlaying(PARAMS(void* arg)) { // NOLINT(misc-unused-parameters)
|
||||
logger->debug("{} -> instance: {}, arg: {}", __func__, fmt::ptr(THIS), fmt::ptr(arg));
|
||||
return true;
|
||||
}
|
||||
|
||||
struct CallbackData {
|
||||
[[maybe_unused]] void* pad1[1];
|
||||
void* set_callback_name_address;
|
||||
[[maybe_unused]] void* pad15[15];
|
||||
void* callback_address;
|
||||
};
|
||||
|
||||
struct CoroutineData {
|
||||
CallbackData* callback_data;
|
||||
[[maybe_unused]] uint32_t pad3[3];
|
||||
const char* callback_name;
|
||||
};
|
||||
|
||||
VIRTUAL(void) set_callback_name(PARAMS(const char** p_name)) {
|
||||
GET_ORIGINAL_FUNCTION(set_callback_name)
|
||||
|
||||
set_callback_name_o(ARGS(p_name));
|
||||
|
||||
static auto hooked_functions = 0;
|
||||
|
||||
if (hooked_functions == 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* const data = (CoroutineData*) THIS;
|
||||
|
||||
if (data && data->callback_name) {
|
||||
const auto name = String(data->callback_name);
|
||||
// logger->trace("{} -> instance: {}, name: '{}'", __func__, fmt::ptr(THIS), name);
|
||||
|
||||
if (name == "SharedLicensesLockStatus") {
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [&]() {
|
||||
DETOUR(SharedLicensesLockStatus, data->callback_data->callback_address)
|
||||
hooked_functions++;
|
||||
});
|
||||
} else if (name == "SharedLibraryStopPlaying") {
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [&]() {
|
||||
DETOUR(SharedLibraryStopPlaying, data->callback_data->callback_address)
|
||||
hooked_functions++;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initially, callback data passed into this function is not complete,
|
||||
* hence we must hook an interface method that sets the callback name.
|
||||
*/
|
||||
DLL_EXPORT(HCoroutine) Coroutine_Create(void* callback_address, CoroutineData* data) {
|
||||
GET_ORIGINAL_FUNCTION(Coroutine_Create)
|
||||
|
||||
const auto result = Coroutine_Create_o(callback_address, data);
|
||||
|
||||
// Coroutine callback appears to be always the same
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [&]() {
|
||||
logger->debug("Coroutine_Create -> callback: {}, data: {}", callback_address, fmt::ptr(data));
|
||||
DETOUR(set_callback_name, data->callback_data->set_callback_name_address)
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user