mirror of
https://github.com/acidicoala/SmokeAPI.git
synced 2025-12-05 21:15:39 -05:00
Refactor
This commit is contained in:
2
.idea/codeStyles/Project.xml
generated
2
.idea/codeStyles/Project.xml
generated
@@ -35,6 +35,8 @@
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="BLANK_LINES_BEFORE_IMPORTS" value="0" />
|
||||
<option name="BLANK_LINES_AFTER_IMPORTS" value="0" />
|
||||
<option name="BLANK_LINES_AROUND_CLASS" value="0" />
|
||||
<option name="BLANK_LINES_AROUND_METHOD" value="0" />
|
||||
<option name="BLANK_LINES_AROUND_METHOD_IN_INTERFACE" value="0" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="SOFT_MARGINS" value="120" />
|
||||
|
||||
@@ -35,19 +35,21 @@ configure_build_config(extra_build_config)
|
||||
|
||||
set(
|
||||
SMOKE_API_SOURCES
|
||||
src/core/config.cpp
|
||||
src/core/config.hpp
|
||||
src/core/api.cpp
|
||||
src/core/api.hpp
|
||||
src/core/globals.cpp
|
||||
src/core/globals.hpp
|
||||
src/core/macros.hpp
|
||||
src/core/paths.cpp
|
||||
src/core/paths.hpp
|
||||
src/core/types.cpp
|
||||
src/core/types.hpp
|
||||
src/smoke_api/smoke_api.cpp
|
||||
src/smoke_api/smoke_api.hpp
|
||||
src/smoke_api/app_cache.cpp
|
||||
src/smoke_api/app_cache.hpp
|
||||
src/smoke_api/config.cpp
|
||||
src/smoke_api/config.hpp
|
||||
src/smoke_api/smoke_api.cpp
|
||||
src/smoke_api/smoke_api.hpp
|
||||
src/steam_api_exports/steam_api_exports.hpp
|
||||
src/steam_api_exports/steam_api_flat.cpp
|
||||
src/steam_api_exports/steam_api_internal.cpp
|
||||
src/steam_api_exports/steam_api_unversioned.cpp
|
||||
@@ -55,6 +57,7 @@ set(
|
||||
src/steam_api_virtuals/isteamclient.cpp
|
||||
src/steam_api_virtuals/isteaminventory.cpp
|
||||
src/steam_api_virtuals/isteamuser.cpp
|
||||
src/steam_api_virtuals/steam_api_virtuals.hpp
|
||||
src/steam_impl/steam_apps.cpp
|
||||
src/steam_impl/steam_apps.hpp
|
||||
src/steam_impl/steam_client.cpp
|
||||
@@ -63,8 +66,8 @@ set(
|
||||
src/steam_impl/steam_inventory.hpp
|
||||
src/steam_impl/steam_user.cpp
|
||||
src/steam_impl/steam_user.hpp
|
||||
src/steam_functions/steam_functions.cpp
|
||||
src/steam_functions/steam_functions.hpp
|
||||
src/steam_impl/steam_impl.cpp
|
||||
src/steam_impl/steam_impl.hpp
|
||||
src/steamclient_exports/steamclient.cpp
|
||||
src/main.cpp
|
||||
${GENERATED_LINKER_EXPORTS}
|
||||
@@ -74,20 +77,18 @@ set(
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(
|
||||
SMOKE_API_SOURCES ${SMOKE_API_SOURCES}
|
||||
src/koalageddon/cache.hpp
|
||||
src/koalageddon/cache.cpp
|
||||
src/koalageddon/kg_cache.hpp
|
||||
src/koalageddon/kg_cache.cpp
|
||||
src/koalageddon/koalageddon.hpp
|
||||
src/koalageddon/koalageddon.cpp
|
||||
src/koalageddon/steamclient.cpp
|
||||
src/koalageddon/steamclient.hpp
|
||||
src/koalageddon/types.hpp
|
||||
src/koalageddon/vstdlib.cpp
|
||||
src/koalageddon/vstdlib.hpp
|
||||
# TODO: Move to koalageddon package
|
||||
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/koalageddon/steamclient/client_app_manager.cpp
|
||||
src/koalageddon/steamclient/client_apps.cpp
|
||||
src/koalageddon/steamclient/client_inventory.cpp
|
||||
src/koalageddon/steamclient/client_user.cpp
|
||||
src/koalageddon/steamclient/steamclient.cpp
|
||||
src/koalageddon/steamclient/steamclient.hpp
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
2
KoalaBox
2
KoalaBox
Submodule KoalaBox updated: 960ad86f2c...2e6832007f
@@ -14,16 +14,14 @@
|
||||
"16384": "original"
|
||||
},
|
||||
"extra_dlcs": {
|
||||
"extra_dlcs": {
|
||||
"1234": {
|
||||
"dlcs": {
|
||||
"56789": "Example DLC 1"
|
||||
}
|
||||
},
|
||||
"4321": {
|
||||
"dlcs": {
|
||||
"98765": "Example DLC 2"
|
||||
}
|
||||
"1234": {
|
||||
"dlcs": {
|
||||
"56789": "Example DLC 1"
|
||||
}
|
||||
},
|
||||
"4321": {
|
||||
"dlcs": {
|
||||
"98765": "Example DLC 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
48
src/core/api.cpp
Normal file
48
src/core/api.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <core/api.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
#include <koalabox/http_client.hpp>
|
||||
|
||||
namespace api {
|
||||
|
||||
struct SteamResponse {
|
||||
uint32_t success = 0;
|
||||
Vector<DLC> dlcs;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(SteamResponse, success, dlcs) // NOLINT(misc-const-correctness)
|
||||
};
|
||||
|
||||
std::optional<Vector<DLC>> fetch_dlcs_from_github(AppId_t app_id) noexcept {
|
||||
try {
|
||||
const auto* url =
|
||||
"https://raw.githubusercontent.com/acidicoala/public-entitlements/main/steam/v2/dlc.json";
|
||||
const auto json = koalabox::http_client::fetch_json(url);
|
||||
const auto response = json.get<AppDlcNameMap>();
|
||||
|
||||
return DLC::get_dlcs_from_apps(response, app_id);
|
||||
} catch (const Json::exception& e) {
|
||||
LOG_ERROR("Failed to fetch dlc list from GitHub: {}", e.what())
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Vector<DLC>> fetch_dlcs_from_steam(AppId_t app_id) noexcept {
|
||||
try {
|
||||
const auto url = fmt::format("https://store.steampowered.com/dlc/{}/ajaxgetdlclist", app_id);
|
||||
const auto json = koalabox::http_client::fetch_json(url);
|
||||
|
||||
LOG_TRACE("Steam response: \n{}", json.dump(2))
|
||||
|
||||
const auto response = json.get<SteamResponse>();
|
||||
|
||||
if (response.success != 1) {
|
||||
throw std::runtime_error("Web API responded with 'success' != 1");
|
||||
}
|
||||
|
||||
return response.dlcs;
|
||||
} catch (const Exception& e) {
|
||||
LOG_ERROR("Failed to fetch dlc list from Steam: {}", e.what())
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
src/core/api.hpp
Normal file
11
src/core/api.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/types.hpp>
|
||||
|
||||
namespace api {
|
||||
|
||||
std::optional<Vector<DLC>> fetch_dlcs_from_github(AppId_t app_id) noexcept;
|
||||
|
||||
std::optional<Vector<DLC>> fetch_dlcs_from_steam(AppId_t app_id) noexcept;
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/globals.hpp>
|
||||
#include <koalabox/hook.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(...) const void* ECX, const void* EDX, __VA_ARGS__
|
||||
#define ARGS(...) ECX, EDX, __VA_ARGS__
|
||||
#define THIS ECX
|
||||
#endif
|
||||
|
||||
// Names beginning with $ designate macros that are not meant to be used directly by the sources consuming this file
|
||||
|
||||
#define DLL_EXPORT(TYPE) extern "C" [[maybe_unused]] __declspec( dllexport ) TYPE __cdecl
|
||||
#define VIRTUAL(TYPE) __declspec(noinline) TYPE __fastcall
|
||||
|
||||
#define GET_ORIGINAL_HOOKED_FUNCTION(FUNC) \
|
||||
static const auto FUNC##_o = koalabox::hook::get_original_hooked_function(globals::address_map, #FUNC, FUNC);
|
||||
|
||||
#define GET_ORIGINAL_FUNCTION_STEAMAPI(FUNC) \
|
||||
static const auto FUNC##_o = koalabox::hook::get_original_function(globals::steamapi_module, #FUNC, FUNC);
|
||||
|
||||
|
||||
#define DETOUR_ADDRESS(FUNC, ADDRESS) \
|
||||
koalabox::hook::detour_or_warn(globals::address_map, ADDRESS, #FUNC, reinterpret_cast<uintptr_t>(FUNC));
|
||||
|
||||
#define $DETOUR(FUNC, NAME, MODULE_HANDLE) \
|
||||
koalabox::hook::detour_or_warn(globals::address_map, MODULE_HANDLE, NAME, reinterpret_cast<uintptr_t>(FUNC));
|
||||
|
||||
#define DETOUR_STEAMCLIENT(FUNC) $DETOUR(FUNC, #FUNC, globals::steamclient_module)
|
||||
#define DETOUR_VSTDLIB(FUNC) $DETOUR(vstdlib::FUNC, #FUNC, globals::vstdlib_module)
|
||||
@@ -1,7 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalabox/core.hpp>
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <core/globals.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(...) const void* ECX, const void* EDX, __VA_ARGS__
|
||||
#define ARGS(...) ECX, EDX, __VA_ARGS__
|
||||
#define THIS ECX
|
||||
#endif
|
||||
|
||||
// Names beginning with $ designate macros that are not meant to be used directly by the sources consuming this file
|
||||
|
||||
#define DLL_EXPORT(TYPE) extern "C" [[maybe_unused]] __declspec( dllexport ) TYPE __cdecl
|
||||
#define VIRTUAL(TYPE) __declspec(noinline) TYPE __fastcall
|
||||
|
||||
#define GET_ORIGINAL_HOOKED_FUNCTION(FUNC) \
|
||||
static const auto FUNC##_o = koalabox::hook::get_original_hooked_function(globals::address_map, #FUNC, FUNC);
|
||||
|
||||
#define GET_ORIGINAL_FUNCTION_STEAMAPI(FUNC) \
|
||||
static const auto FUNC##_o = koalabox::hook::get_original_function(globals::steamapi_module, #FUNC, FUNC);
|
||||
|
||||
|
||||
#define DETOUR_ADDRESS(FUNC, ADDRESS) \
|
||||
koalabox::hook::detour_or_warn(globals::address_map, ADDRESS, #FUNC, reinterpret_cast<uintptr_t>(FUNC));
|
||||
|
||||
#define $DETOUR(FUNC, NAME, MODULE_HANDLE) \
|
||||
koalabox::hook::detour_or_warn(globals::address_map, MODULE_HANDLE, NAME, reinterpret_cast<uintptr_t>(FUNC));
|
||||
|
||||
#define DETOUR_STEAMCLIENT(FUNC) $DETOUR(FUNC, #FUNC, globals::steamclient_module)
|
||||
#define DETOUR_VSTDLIB(FUNC) $DETOUR(vstdlib::FUNC, #FUNC, globals::vstdlib_module)
|
||||
|
||||
#ifdef _WIN64
|
||||
#define COMPILE_KOALAGEDDON 0
|
||||
@@ -9,6 +65,12 @@
|
||||
#define COMPILE_KOALAGEDDON 1
|
||||
#endif
|
||||
|
||||
constexpr auto STEAM_APPS = "STEAMAPPS_INTERFACE_VERSION";
|
||||
constexpr auto STEAM_CLIENT = "SteamClient";
|
||||
constexpr auto STEAM_USER = "SteamUser";
|
||||
constexpr auto STEAM_INVENTORY = "STEAMINVENTORY_INTERFACE_V";
|
||||
constexpr auto CLIENT_ENGINE = "CLIENTENGINE_INTERFACE_VERSION";
|
||||
|
||||
using AppId_t = uint32_t;
|
||||
using SteamInventoryResult_t = uint32_t;
|
||||
using SteamItemInstanceID_t = uint64_t;
|
||||
@@ -33,11 +95,8 @@ enum EUserHasLicenseForAppResult {
|
||||
};
|
||||
|
||||
class ISteamClient;
|
||||
|
||||
class ISteamApps;
|
||||
|
||||
class ISteamUser;
|
||||
|
||||
class ISteamInventory;
|
||||
|
||||
// These aliases exist solely to increase code readability
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <koalageddon/cache.hpp>
|
||||
#include <koalageddon/kg_cache.hpp>
|
||||
#include <koalabox/cache.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
|
||||
constexpr auto KEY_KG_CONFIG = "koalageddon_config";
|
||||
|
||||
namespace koalageddon::cache {
|
||||
namespace koalageddon::kg_cache {
|
||||
|
||||
std::optional<KoalageddonConfig> get_koalageddon_config() {
|
||||
try {
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalageddon/types.hpp>
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
|
||||
namespace koalageddon::cache {
|
||||
namespace koalageddon::kg_cache {
|
||||
|
||||
std::optional<KoalageddonConfig> get_koalageddon_config();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
#include <koalageddon/vstdlib.hpp>
|
||||
#include <koalageddon/cache.hpp>
|
||||
#include <koalageddon/kg_cache.hpp>
|
||||
#include <build_config.h>
|
||||
#include <core/config.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <smoke_api/config.hpp>
|
||||
#include <steam_api_exports/steam_api_exports.hpp>
|
||||
#include <koalabox/dll_monitor.hpp>
|
||||
#include <koalabox/http_client.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
@@ -17,10 +17,10 @@ namespace koalageddon {
|
||||
* @return A string representing the source of the config.
|
||||
*/
|
||||
String init_koalageddon_config() {
|
||||
if (!config::instance.koalageddon_config.is_null()) {
|
||||
if (!smoke_api::config::instance.koalageddon_config.is_null()) {
|
||||
try {
|
||||
// First try to read a local config override
|
||||
config = config::instance.koalageddon_config.get<decltype(config)>();
|
||||
config = smoke_api::config::instance.koalageddon_config.get<decltype(config)>();
|
||||
|
||||
return "local config override";
|
||||
} catch (const Exception& ex) {
|
||||
@@ -33,7 +33,7 @@ namespace koalageddon {
|
||||
const String url = "https://raw.githubusercontent.com/acidicoala/public-entitlements/main/koalageddon/v2/steam.json";
|
||||
config = koalabox::http_client::fetch_json(url).get<decltype(config)>();
|
||||
|
||||
cache::save_koalageddon_config(config);
|
||||
kg_cache::save_koalageddon_config(config);
|
||||
|
||||
return "GitHub repository";
|
||||
} catch (const Exception& ex) {
|
||||
@@ -43,7 +43,7 @@ namespace koalageddon {
|
||||
try {
|
||||
// Then try to get a cached copy of a previously fetched config.
|
||||
// We expect this unboxing to throw exception if no koalageddon config is present.
|
||||
config = cache::get_koalageddon_config().value();
|
||||
config = kg_cache::get_koalageddon_config().value();
|
||||
|
||||
return "disk cache";
|
||||
} catch (const Exception& ex) {
|
||||
@@ -71,7 +71,7 @@ namespace koalageddon {
|
||||
|
||||
globals::vstdlib_module = module_handle;
|
||||
|
||||
if (config::instance.unlock_family_sharing) {
|
||||
if (smoke_api::config::instance.unlock_family_sharing) {
|
||||
DETOUR_VSTDLIB(Coroutine_Create)
|
||||
}
|
||||
} else if (name < equals > STEAMCLIENT_DLL) {
|
||||
@@ -87,8 +87,9 @@ namespace koalageddon {
|
||||
}
|
||||
} catch (const Exception& ex) {
|
||||
LOG_ERROR(
|
||||
"Koalageddon mode dll monitor process_interface_selector error. Module: '{}', Message: {}",
|
||||
name, ex.what())
|
||||
"Error listening to DLL load events. Module: '{}', Message: {}",
|
||||
name, ex.what()
|
||||
)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,8 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalageddon/types.hpp>
|
||||
#include <core/types.hpp>
|
||||
|
||||
namespace koalageddon {
|
||||
// Offset values are interpreted according to pointer arithmetic rules, i.e.
|
||||
// 1 unit offset represents 4 and 8 bytes in 32-bit and 64-bit architectures respectively.
|
||||
struct KoalageddonConfig {
|
||||
uint32_t client_engine_steam_client_internal_ordinal = 12;
|
||||
uint32_t steam_client_internal_interface_selector_ordinal = 18;
|
||||
uint32_t vstdlib_callback_address_offset = 20;
|
||||
uint32_t vstdlib_callback_data_offset = 0;
|
||||
uint32_t vstdlib_callback_interceptor_address_offset = 1;
|
||||
uint32_t vstdlib_callback_name_offset = 4;
|
||||
|
||||
// We do not use *_WITH_DEFAULT macro to ensure that overriding
|
||||
// the koalageddon config requires definition of all keys
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(
|
||||
KoalageddonConfig, // NOLINT(misc-const-correctness)
|
||||
client_engine_steam_client_internal_ordinal,
|
||||
steam_client_internal_interface_selector_ordinal,
|
||||
vstdlib_callback_address_offset,
|
||||
vstdlib_callback_data_offset,
|
||||
vstdlib_callback_interceptor_address_offset,
|
||||
vstdlib_callback_name_offset
|
||||
)
|
||||
};
|
||||
|
||||
/// We need this interface in other IClient* functions in order to query original DLC status
|
||||
extern const void* client_app_manager_interface;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace koalageddon::steamclient {
|
||||
|
||||
void process_client_engine(uintptr_t interface);
|
||||
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
#include <steam_impl/steam_apps.hpp>
|
||||
#include <core/macros.hpp>
|
||||
#include <core/types.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
VIRTUAL(bool) IClientAppManager_IsAppDlcInstalled(PARAMS(AppId_t app_id, AppId_t dlc_id)) {
|
||||
return steam_apps::IsDlcUnlocked(
|
||||
@@ -1,8 +1,6 @@
|
||||
#include <core/macros.hpp>
|
||||
#include <core/types.hpp>
|
||||
#include <steam_impl/steam_apps.hpp>
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <koalageddon/steamclient/steamclient.hpp>
|
||||
|
||||
VIRTUAL(int) IClientApps_GetDLCCount(PARAMS(AppId_t appId)) {
|
||||
return steam_apps::GetDLCCount(
|
||||
@@ -1,7 +1,5 @@
|
||||
#include <core/macros.hpp>
|
||||
#include <core/types.hpp>
|
||||
#include <koalageddon/steamclient/steamclient.hpp>
|
||||
#include <steam_impl/steam_inventory.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
VIRTUAL(EResult) IClientInventory_GetResultStatus(PARAMS(SteamInventoryResult_t resultHandle)) {
|
||||
return steam_inventory::GetResultStatus(
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <core/macros.hpp>
|
||||
#include <koalageddon/steamclient/steamclient.hpp>
|
||||
#include <steam_impl/steam_apps.hpp>
|
||||
|
||||
VIRTUAL(bool) IClientUser_BIsSubscribedApp(PARAMS(AppId_t app_id)) {
|
||||
@@ -1,8 +1,9 @@
|
||||
#include <koalageddon/steamclient/steamclient.hpp>
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
#include <koalabox/util.hpp>
|
||||
|
||||
#include <Zydis/Zydis.h>
|
||||
#include <Zydis/DecoderTypes.h>
|
||||
|
||||
@@ -85,9 +86,9 @@ namespace koalageddon::steamclient {
|
||||
const auto& operand = instruction.operands[0];
|
||||
|
||||
return instruction.mnemonic == ZYDIS_MNEMONIC_PUSH &&
|
||||
operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
|
||||
operand.visibility == ZYDIS_OPERAND_VISIBILITY_EXPLICIT &&
|
||||
operand.encoding == ZYDIS_OPERAND_ENCODING_SIMM16_32_32;
|
||||
operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
|
||||
operand.visibility == ZYDIS_OPERAND_VISIBILITY_EXPLICIT &&
|
||||
operand.encoding == ZYDIS_OPERAND_ENCODING_SIMM16_32_32;
|
||||
}
|
||||
|
||||
std::optional<String> get_string_argument(const ZydisDecodedInstruction& instruction) {
|
||||
@@ -141,9 +142,9 @@ namespace koalageddon::steamclient {
|
||||
|
||||
const auto is_mov_base_esp = [](const ZydisDecodedInstruction& instruction) {
|
||||
return instruction.mnemonic == ZYDIS_MNEMONIC_MOV &&
|
||||
instruction.operand_count == 2 &&
|
||||
instruction.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
instruction.operands[1].reg.value == ZYDIS_REGISTER_ESP;
|
||||
instruction.operand_count == 2 &&
|
||||
instruction.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
instruction.operands[1].reg.value == ZYDIS_REGISTER_ESP;
|
||||
};
|
||||
|
||||
// Initialize with a dummy previous instruction
|
||||
@@ -158,7 +159,7 @@ namespace koalageddon::steamclient {
|
||||
&instruction
|
||||
))) {
|
||||
LOG_TRACE(
|
||||
"{} visiting {} | {}", __func__,
|
||||
"{} visiting {} │ {}", __func__,
|
||||
(void*) current_address, *get_instruction_string(instruction, current_address)
|
||||
)
|
||||
|
||||
@@ -168,8 +169,8 @@ namespace koalageddon::steamclient {
|
||||
// Save base register
|
||||
context.base_register = instruction.operands[0].reg.value;
|
||||
} else if (is_push_immediate(last_instruction) &&
|
||||
is_push_immediate(instruction) &&
|
||||
!context.function_name) {
|
||||
is_push_immediate(instruction) &&
|
||||
!context.function_name) {
|
||||
// The very first 2 consecutive pushes indicate interface and function names.
|
||||
// However, subsequent pushes may contain irrelevant strings.
|
||||
const auto push_string_1 = get_string_argument(last_instruction);
|
||||
@@ -198,7 +199,7 @@ namespace koalageddon::steamclient {
|
||||
// But not continue forward, in order to avoid duplicate processing
|
||||
return;
|
||||
} else if (instruction.mnemonic == ZYDIS_MNEMONIC_JMP &&
|
||||
instruction.operands[0].type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
|
||||
instruction.operands[0].type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
|
||||
// On unconditional jump we should recurse as well
|
||||
const auto jump_destination = get_absolute_address(instruction, current_address);
|
||||
|
||||
@@ -341,7 +342,7 @@ namespace koalageddon::steamclient {
|
||||
))) {
|
||||
visited_addresses.insert(current_address);
|
||||
LOG_TRACE(
|
||||
"{} visiting {} | {}", __func__,
|
||||
"{} visiting {} │ {}", __func__,
|
||||
(void*) current_address, *get_instruction_string(instruction, current_address)
|
||||
)
|
||||
|
||||
@@ -376,7 +377,7 @@ namespace koalageddon::steamclient {
|
||||
LOG_TRACE("Breaking recursion due to conditional branch")
|
||||
return;
|
||||
} else if (instruction.mnemonic == ZYDIS_MNEMONIC_JMP &&
|
||||
operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE
|
||||
operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE
|
||||
) {
|
||||
const auto jump_destination = get_absolute_address(instruction, current_address);
|
||||
|
||||
@@ -385,9 +386,9 @@ namespace koalageddon::steamclient {
|
||||
LOG_TRACE("Breaking recursion due to unconditional branch")
|
||||
return;
|
||||
} else if (instruction.mnemonic == ZYDIS_MNEMONIC_JMP &&
|
||||
operand.type == ZYDIS_OPERAND_TYPE_MEMORY &&
|
||||
operand.mem.scale == sizeof(uintptr_t) &&
|
||||
operand.mem.disp.has_displacement
|
||||
operand.type == ZYDIS_OPERAND_TYPE_MEMORY &&
|
||||
operand.mem.scale == sizeof(uintptr_t) &&
|
||||
operand.mem.disp.has_displacement
|
||||
) {
|
||||
// Special handling for jump tables. Guaranteed to be present in the interface selector.
|
||||
const auto* table = (uintptr_t*) operand.mem.disp.value;
|
||||
33
src/koalageddon/steamclient/steamclient.hpp
Normal file
33
src/koalageddon/steamclient/steamclient.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/types.hpp>
|
||||
|
||||
// IClientAppManager
|
||||
VIRTUAL(bool) IClientAppManager_IsAppDlcInstalled(PARAMS(AppId_t, AppId_t));
|
||||
|
||||
// IClientApps
|
||||
VIRTUAL(int) IClientApps_GetDLCCount(PARAMS(AppId_t));
|
||||
VIRTUAL(bool) IClientApps_BGetDLCDataByIndex(PARAMS(AppId_t, int, AppId_t*, bool*, char*, int));
|
||||
|
||||
// IClientInventory
|
||||
VIRTUAL(EResult) IClientInventory_GetResultStatus(PARAMS(SteamInventoryResult_t));
|
||||
VIRTUAL(bool) IClientInventory_GetResultItems(
|
||||
PARAMS(SteamInventoryResult_t, SteamItemDetails_t*, uint32_t, uint32_t *)
|
||||
);
|
||||
VIRTUAL(bool) IClientInventory_GetResultItemProperty(
|
||||
PARAMS(SteamInventoryResult_t, uint32_t, const char*, char*, uint32_t, uint32_t*)
|
||||
);
|
||||
VIRTUAL(bool) IClientInventory_CheckResultSteamID(PARAMS(SteamInventoryResult_t, CSteamID));
|
||||
VIRTUAL(bool) IClientInventory_GetAllItems(PARAMS(SteamInventoryResult_t*));
|
||||
VIRTUAL(bool) IClientInventory_GetItemsByID(PARAMS(SteamInventoryResult_t*, const SteamItemInstanceID_t*, uint32_t));
|
||||
VIRTUAL(bool) IClientInventory_SerializeResult(PARAMS(SteamInventoryResult_t, void*, uint32_t, uint32_t *));
|
||||
VIRTUAL(bool) IClientInventory_GetItemDefinitionIDs(PARAMS(SteamItemDef_t*, uint32_t, uint32_t *));
|
||||
|
||||
// IClientUser
|
||||
VIRTUAL(bool) IClientUser_BIsSubscribedApp(PARAMS(AppId_t));
|
||||
|
||||
namespace koalageddon::steamclient {
|
||||
|
||||
void process_client_engine(uintptr_t interface);
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalabox/core.hpp>
|
||||
|
||||
// Offset values are interpreted according to pointer arithmetic rules, i.e.
|
||||
// 1 unit offset represents 4 and 8 bytes in 32-bit and 64-bit architectures respectively.
|
||||
struct KoalageddonConfig {
|
||||
uint32_t client_engine_steam_client_internal_ordinal = 12;
|
||||
uint32_t steam_client_internal_interface_selector_ordinal = 18;
|
||||
uint32_t vstdlib_callback_address_offset = 20;
|
||||
uint32_t vstdlib_callback_data_offset = 0;
|
||||
uint32_t vstdlib_callback_interceptor_address_offset = 1;
|
||||
uint32_t vstdlib_callback_name_offset = 4;
|
||||
|
||||
// We do not use *_WITH_DEFAULT macro to ensure that overriding
|
||||
// the koalageddon config requires definition of all keys
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(
|
||||
KoalageddonConfig, // NOLINT(misc-const-correctness)
|
||||
client_engine_steam_client_internal_ordinal,
|
||||
steam_client_internal_interface_selector_ordinal,
|
||||
vstdlib_callback_address_offset,
|
||||
vstdlib_callback_data_offset,
|
||||
vstdlib_callback_interceptor_address_offset,
|
||||
vstdlib_callback_name_offset
|
||||
)
|
||||
};
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <koalageddon/vstdlib.hpp>
|
||||
#include <core/macros.hpp>
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <core/macros.hpp>
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
|
||||
namespace koalageddon::vstdlib {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <core/config.hpp>
|
||||
#include <smoke_api/config.hpp>
|
||||
#include <core/paths.hpp>
|
||||
#include <koalabox/util.hpp>
|
||||
#include <koalabox/io.hpp>
|
||||
|
||||
namespace config {
|
||||
namespace smoke_api::config {
|
||||
Config instance; // NOLINT(cert-err58-cpp)
|
||||
|
||||
// TODO: Reloading via export
|
||||
@@ -3,8 +3,7 @@
|
||||
#include <core/types.hpp>
|
||||
#include <koalabox/core.hpp>
|
||||
|
||||
// TODO: move to smoke_api namespace
|
||||
namespace config {
|
||||
namespace smoke_api::config {
|
||||
enum class AppStatus {
|
||||
LOCKED,
|
||||
UNLOCKED,
|
||||
@@ -1,9 +1,8 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <build_config.h>
|
||||
#include <core/config.hpp>
|
||||
#include <smoke_api/config.hpp>
|
||||
#include <core/globals.hpp>
|
||||
#include <core/paths.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <koalabox/dll_monitor.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
#include <koalabox/hook.hpp>
|
||||
@@ -11,6 +10,7 @@
|
||||
#include <koalabox/loader.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
#include <koalabox/util.hpp>
|
||||
#include <steam_api_exports/steam_api_exports.hpp>
|
||||
|
||||
#if COMPILE_KOALAGEDDON
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
|
||||
35
src/steam_api_exports/steam_api_exports.hpp
Normal file
35
src/steam_api_exports/steam_api_exports.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/types.hpp>
|
||||
|
||||
// Flat
|
||||
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*, 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*);
|
||||
|
||||
// Internal
|
||||
DLL_EXPORT(void*) SteamInternal_FindOrCreateUserInterface(HSteamUser, const char*);
|
||||
DLL_EXPORT(void*) SteamInternal_CreateInterface(const char*);
|
||||
|
||||
// Unversioned
|
||||
DLL_EXPORT(void*) CreateInterface(const char*, int*);
|
||||
DLL_EXPORT(void*) SteamApps();
|
||||
DLL_EXPORT(void*) SteamClient();
|
||||
DLL_EXPORT(void*) SteamUser();
|
||||
@@ -1,12 +1,8 @@
|
||||
#include <core/macros.hpp>
|
||||
#include <core/types.hpp>
|
||||
#include <steam_api_exports/steam_api_exports.hpp>
|
||||
#include <steam_impl/steam_apps.hpp>
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
#include <steam_impl/steam_inventory.hpp>
|
||||
#include <steam_impl/steam_user.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
using namespace koalabox;
|
||||
|
||||
// ISteamApps
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
#include <core/macros.hpp>
|
||||
#include <core/types.hpp>
|
||||
#include <steam_api_exports/steam_api_exports.hpp>
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
#include <koalabox/hook.hpp>
|
||||
|
||||
using namespace koalabox;
|
||||
|
||||
DLL_EXPORT(void*) SteamInternal_FindOrCreateUserInterface(HSteamUser hSteamUser, const char* version) {
|
||||
return steam_client::GetGenericInterface(
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
#include <steam_api_exports/steam_api_exports.hpp>
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
#include <core/macros.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
#include <koalabox/util.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <regex>
|
||||
|
||||
using namespace koalabox;
|
||||
|
||||
/**
|
||||
* Searches the `.rdata` section of the original dll for the full interface version string
|
||||
* Results are cached for performance.
|
||||
@@ -17,7 +14,7 @@ String get_versioned_interface(const String& version_prefix, const String& fallb
|
||||
|
||||
if (not version_map.contains(version_prefix)) {
|
||||
try {
|
||||
const String rdata = win_util::get_pe_section_data_or_throw(globals::steamapi_module, ".rdata");
|
||||
const String rdata = koalabox::win_util::get_pe_section_data_or_throw(globals::steamapi_module, ".rdata");
|
||||
|
||||
const std::regex regex(version_prefix + "\\d{3}");
|
||||
std::smatch match;
|
||||
@@ -26,7 +23,7 @@ String get_versioned_interface(const String& version_prefix, const String& fallb
|
||||
return version_map[version_prefix];
|
||||
}
|
||||
|
||||
throw util::exception("No match found for '{}'", version_prefix);
|
||||
throw koalabox::util::exception("No match found for '{}'", version_prefix);
|
||||
} catch (const Exception& ex) {
|
||||
LOG_ERROR(
|
||||
"Failed to get versioned interface: {}."
|
||||
@@ -41,7 +38,7 @@ String get_versioned_interface(const String& version_prefix, const String& fallb
|
||||
}
|
||||
|
||||
DLL_EXPORT(void*) SteamClient() {
|
||||
static auto version = get_versioned_interface(steam_functions::STEAM_CLIENT, "006");
|
||||
static auto version = get_versioned_interface(STEAM_CLIENT, "006");
|
||||
|
||||
return steam_client::GetGenericInterface(
|
||||
__func__, version, [&]() {
|
||||
@@ -53,7 +50,7 @@ DLL_EXPORT(void*) SteamClient() {
|
||||
}
|
||||
|
||||
DLL_EXPORT(void*) SteamApps() {
|
||||
static auto version = get_versioned_interface(steam_functions::STEAM_APPS, "002");
|
||||
static auto version = get_versioned_interface(STEAM_APPS, "002");
|
||||
|
||||
return steam_client::GetGenericInterface(
|
||||
__func__, version, [&]() {
|
||||
@@ -65,7 +62,7 @@ DLL_EXPORT(void*) SteamApps() {
|
||||
}
|
||||
|
||||
DLL_EXPORT(void*) SteamUser() {
|
||||
static auto version = get_versioned_interface(steam_functions::STEAM_USER, "012");
|
||||
static auto version = get_versioned_interface(STEAM_USER, "012");
|
||||
|
||||
return steam_client::GetGenericInterface(
|
||||
__func__, version, [&]() {
|
||||
@@ -77,7 +74,7 @@ DLL_EXPORT(void*) SteamUser() {
|
||||
}
|
||||
|
||||
DLL_EXPORT(void*) SteamInventory() {
|
||||
static auto version = get_versioned_interface(steam_functions::STEAM_INVENTORY, "001");
|
||||
static auto version = get_versioned_interface(STEAM_INVENTORY, "001");
|
||||
|
||||
return steam_client::GetGenericInterface(
|
||||
__func__, version, [&]() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <steam_api_virtuals/steam_api_virtuals.hpp>
|
||||
#include <steam_impl/steam_apps.hpp>
|
||||
#include <core/macros.hpp>
|
||||
|
||||
VIRTUAL(bool) ISteamApps_BIsSubscribedApp(PARAMS(AppId_t appID)) {
|
||||
return steam_apps::IsDlcUnlocked(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <steam_api_virtuals/steam_api_virtuals.hpp>
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
VIRTUAL(void*) ISteamClient_GetISteamApps(
|
||||
PARAMS(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <steam_api_virtuals/steam_api_virtuals.hpp>
|
||||
#include <steam_impl/steam_inventory.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
VIRTUAL(EResult) ISteamInventory_GetResultStatus(PARAMS(SteamInventoryResult_t resultHandle)) {
|
||||
return steam_inventory::GetResultStatus(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <steam_api_virtuals/steam_api_virtuals.hpp>
|
||||
#include <steam_impl/steam_user.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
VIRTUAL(EUserHasLicenseForAppResult) ISteamUser_UserHasLicenseForApp(PARAMS(CSteamID steamID, AppId_t appID)) {
|
||||
return steam_user::UserHasLicenseForApp(
|
||||
|
||||
31
src/steam_api_virtuals/steam_api_virtuals.hpp
Normal file
31
src/steam_api_virtuals/steam_api_virtuals.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/types.hpp>
|
||||
|
||||
// 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));
|
||||
|
||||
// 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*));
|
||||
|
||||
|
||||
// ISteamInventory
|
||||
VIRTUAL(EResult) ISteamInventory_GetResultStatus(PARAMS(SteamInventoryResult_t));
|
||||
VIRTUAL(bool) ISteamInventory_GetResultItems(PARAMS(SteamInventoryResult_t, SteamItemDetails_t*, uint32_t*));
|
||||
VIRTUAL(bool) ISteamInventory_GetResultItemProperty(
|
||||
PARAMS(SteamInventoryResult_t, uint32_t, const char*, char*, 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_CheckResultSteamID(PARAMS(SteamInventoryResult_t, CSteamID));
|
||||
|
||||
// ISteamUser
|
||||
VIRTUAL(EUserHasLicenseForAppResult) ISteamUser_UserHasLicenseForApp(PARAMS(CSteamID, AppId_t));
|
||||
@@ -1,102 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/macros.hpp>
|
||||
#include <core/types.hpp>
|
||||
|
||||
// TODO: Refactor into multiple headers
|
||||
|
||||
// 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(SteamInventoryResult_t));
|
||||
VIRTUAL(bool) ISteamInventory_GetResultItems(PARAMS(SteamInventoryResult_t, SteamItemDetails_t*, uint32_t*));
|
||||
VIRTUAL(bool) ISteamInventory_GetResultItemProperty(
|
||||
PARAMS(SteamInventoryResult_t, uint32_t, const char*, char*, 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_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*, 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*);
|
||||
|
||||
// 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_BIsSubscribedApp(PARAMS(AppId_t));
|
||||
|
||||
// IClientInventory
|
||||
VIRTUAL(EResult) IClientInventory_GetResultStatus(PARAMS(SteamInventoryResult_t));
|
||||
VIRTUAL(bool) IClientInventory_GetResultItems(
|
||||
PARAMS(SteamInventoryResult_t, SteamItemDetails_t*, uint32_t, uint32_t *)
|
||||
);
|
||||
VIRTUAL(bool) IClientInventory_GetResultItemProperty(
|
||||
PARAMS(SteamInventoryResult_t, uint32_t, const char*, char*, uint32_t, uint32_t*)
|
||||
);
|
||||
VIRTUAL(bool) IClientInventory_CheckResultSteamID(PARAMS(SteamInventoryResult_t, CSteamID));
|
||||
VIRTUAL(bool) IClientInventory_GetAllItems(PARAMS(SteamInventoryResult_t*));
|
||||
VIRTUAL(bool) IClientInventory_GetItemsByID(PARAMS(SteamInventoryResult_t*, const SteamItemInstanceID_t*, uint32_t));
|
||||
VIRTUAL(bool) IClientInventory_SerializeResult(PARAMS(SteamInventoryResult_t, void*, uint32_t, uint32_t *));
|
||||
VIRTUAL(bool) IClientInventory_GetItemDefinitionIDs(PARAMS(SteamItemDef_t*, uint32_t, uint32_t *));
|
||||
|
||||
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)
|
||||
const String CLIENT_ENGINE = "CLIENTENGINE_INTERFACE_VERSION"; // NOLINT(cert-err58-cpp)
|
||||
|
||||
void hook_virtuals(void* interface, const String& version_string);
|
||||
uint32_t get_app_id_or_throw();
|
||||
}
|
||||
@@ -1,23 +1,13 @@
|
||||
#include <steam_impl/steam_apps.hpp>
|
||||
#include <koalabox/http_client.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
#include <smoke_api/app_cache.hpp>
|
||||
#include <core/config.hpp>
|
||||
#include <smoke_api/config.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
#include <koalabox/util.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <core/types.hpp>
|
||||
#include <utility>
|
||||
#include <core/api.hpp>
|
||||
|
||||
namespace steam_apps {
|
||||
// TODO: Needs to go to API
|
||||
|
||||
struct SteamResponse {
|
||||
uint32_t success = 0;
|
||||
Vector<DLC> dlcs;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(SteamResponse, success, dlcs) // NOLINT(misc-const-correctness)
|
||||
};
|
||||
|
||||
/// Steamworks may max GetDLCCount 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 get extra DLC IDs from local config, remote config, or cache.
|
||||
@@ -26,38 +16,8 @@ namespace steam_apps {
|
||||
Map<AppId_t, Vector<DLC>> app_dlcs; // NOLINT(cert-err58-cpp)
|
||||
Set<AppId_t> fully_fetched; // NOLINT(cert-err58-cpp)
|
||||
|
||||
std::optional<Vector<DLC>> fetch_from_github(AppId_t app_id) noexcept {
|
||||
try {
|
||||
const auto* url
|
||||
= "https://raw.githubusercontent.com/acidicoala/public-entitlements/main/steam/v2/dlc.json";
|
||||
const auto json = koalabox::http_client::fetch_json(url);
|
||||
const auto response = json.get<AppDlcNameMap>();
|
||||
|
||||
return DLC::get_dlcs_from_apps(response, app_id);
|
||||
} catch (const Json::exception& e) {
|
||||
LOG_ERROR("Failed to fetch dlc list from GitHub: {}", e.what())
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Vector<DLC>> fetch_from_steam(AppId_t app_id) noexcept {
|
||||
try {
|
||||
const auto url = fmt::format("https://store.steampowered.com/dlc/{}/ajaxgetdlclist", app_id);
|
||||
const auto json = koalabox::http_client::fetch_json(url);
|
||||
|
||||
LOG_TRACE("Steam response: \n{}", json.dump(2))
|
||||
|
||||
const auto response = json.get<SteamResponse>();
|
||||
|
||||
if (response.success != 1) {
|
||||
throw std::runtime_error("Web API responded with 'success' != 1");
|
||||
}
|
||||
|
||||
return response.dlcs;
|
||||
} catch (const Exception& e) {
|
||||
LOG_ERROR("Failed to fetch dlc list from Steam: {}", e.what())
|
||||
return std::nullopt;
|
||||
}
|
||||
String get_app_id_log(const AppId_t app_id) {
|
||||
return app_id ? fmt::format("App ID: {:>8}, ", app_id) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +32,7 @@ namespace steam_apps {
|
||||
// No app id means we are operating in game mode.
|
||||
// Hence, we need to use utility functions to get app id.
|
||||
try {
|
||||
app_id = steam_functions::get_app_id_or_throw();
|
||||
app_id = steam_impl::get_app_id_or_throw();
|
||||
LOG_INFO("Detected App ID: {}", app_id)
|
||||
} catch (const Exception& ex) {
|
||||
LOG_ERROR("Failed to get app ID: {}", ex.what())
|
||||
@@ -96,19 +56,19 @@ namespace steam_apps {
|
||||
aggregated_dlcs < append > source;
|
||||
};
|
||||
|
||||
append_dlcs(config::get_extra_dlcs(app_id), "local config");
|
||||
append_dlcs(smoke_api::config::get_extra_dlcs(app_id), "local config");
|
||||
|
||||
const auto github_dlcs = fetch_from_github(app_id);
|
||||
if (github_dlcs) {
|
||||
append_dlcs(*github_dlcs, "GitHub repository");
|
||||
const auto github_dlcs_opt = api::fetch_dlcs_from_github(app_id);
|
||||
if (github_dlcs_opt) {
|
||||
append_dlcs(*github_dlcs_opt, "GitHub repository");
|
||||
}
|
||||
|
||||
const auto steam_dlcs = fetch_from_steam(app_id);
|
||||
if (steam_dlcs) {
|
||||
append_dlcs(*steam_dlcs, "Steam API");
|
||||
const auto steam_dlcs_opt = api::fetch_dlcs_from_steam(app_id);
|
||||
if (steam_dlcs_opt) {
|
||||
append_dlcs(*steam_dlcs_opt, "Steam API");
|
||||
}
|
||||
|
||||
if (github_dlcs && steam_dlcs) {
|
||||
if (github_dlcs_opt && steam_dlcs_opt) {
|
||||
fully_fetched.insert(app_id);
|
||||
} else {
|
||||
append_dlcs(smoke_api::app_cache::get_dlcs(app_id), "disk cache");
|
||||
@@ -121,10 +81,6 @@ namespace steam_apps {
|
||||
smoke_api::app_cache::save_dlcs(app_id, aggregated_dlcs);
|
||||
}
|
||||
|
||||
String get_app_id_log(const AppId_t app_id) {
|
||||
return app_id ? fmt::format("App ID: {:>8}, ", app_id) : "";
|
||||
}
|
||||
|
||||
bool IsDlcUnlocked(
|
||||
const String& function_name,
|
||||
AppId_t app_id,
|
||||
@@ -132,7 +88,7 @@ namespace steam_apps {
|
||||
const Function<bool()>& original_function
|
||||
) {
|
||||
try {
|
||||
const auto unlocked = config::is_dlc_unlocked(app_id, dlc_id, original_function);
|
||||
const auto unlocked = smoke_api::config::is_dlc_unlocked(app_id, dlc_id, original_function);
|
||||
|
||||
LOG_INFO("{} -> {}DLC ID: {:>8}, Unlocked: {}", function_name, get_app_id_log(app_id), dlc_id, unlocked)
|
||||
|
||||
@@ -196,7 +152,7 @@ namespace steam_apps {
|
||||
const auto inject_dlc = [&](const DLC& dlc) {
|
||||
// Fill the output pointers
|
||||
*pDlcId = dlc.get_id();
|
||||
*pbAvailable = config::is_dlc_unlocked(
|
||||
*pbAvailable = smoke_api::config::is_dlc_unlocked(
|
||||
app_id, *pDlcId, [&]() {
|
||||
return is_originally_unlocked(*pDlcId);
|
||||
}
|
||||
@@ -223,7 +179,9 @@ namespace steam_apps {
|
||||
const auto success = original_function();
|
||||
|
||||
if (success) {
|
||||
*pbAvailable = config::is_dlc_unlocked(app_id, *pDlcId, [&]() { return *pbAvailable; });
|
||||
*pbAvailable = smoke_api::config::is_dlc_unlocked(
|
||||
app_id, *pDlcId, [&]() { return *pbAvailable; }
|
||||
);
|
||||
print_dlc_info("original");
|
||||
} else {
|
||||
LOG_WARN("{} -> original call failed for index: {}", function_name, iDLC)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/types.hpp>
|
||||
#include <koalabox/core.hpp>
|
||||
|
||||
namespace steam_apps {
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
namespace steam_client {
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace steam_client {
|
||||
|
||||
LOG_DEBUG("{} -> '{}' @ {}", function_name, interface_version, interface)
|
||||
|
||||
steam_functions::hook_virtuals(interface, interface_version);
|
||||
steam_impl::hook_virtuals(interface, interface_version);
|
||||
|
||||
return interface;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalabox/core.hpp>
|
||||
#include <core/types.hpp>
|
||||
|
||||
namespace steam_client {
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
#include <steam_api_virtuals/steam_api_virtuals.hpp>
|
||||
#include <steam_api_exports/steam_api_exports.hpp>
|
||||
#include <core/globals.hpp>
|
||||
#include <build_config.h>
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
@@ -7,10 +10,10 @@
|
||||
#include <polyhook2/Misc.hpp>
|
||||
|
||||
#if COMPILE_KOALAGEDDON
|
||||
#include <koalageddon/steamclient.hpp>
|
||||
#include <koalageddon/steamclient/steamclient.hpp>
|
||||
#endif
|
||||
|
||||
namespace steam_functions {
|
||||
namespace steam_impl {
|
||||
|
||||
typedef Map<String, Map<int, int>> FunctionOrdinalMap;
|
||||
|
||||
@@ -19,7 +22,7 @@ namespace steam_functions {
|
||||
{
|
||||
{6, 16},
|
||||
{7, 18},
|
||||
{8, 15},
|
||||
{8, 15},
|
||||
{9, 16},
|
||||
{12, 15},
|
||||
}
|
||||
@@ -105,7 +108,7 @@ namespace steam_functions {
|
||||
|
||||
return version_number;
|
||||
} catch (const std::exception& ex) {
|
||||
util::panic("Failed to extract version number from: '{}'", version_string);
|
||||
koalabox::util::panic("Failed to extract version number from: '{}'", version_string);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,11 +123,11 @@ namespace steam_functions {
|
||||
}
|
||||
}
|
||||
|
||||
util::panic("Invalid interface version ({}) for function {}", interface_version, function_name);
|
||||
koalabox::util::panic("Invalid interface version ({}) for function {}", interface_version, function_name);
|
||||
}
|
||||
|
||||
#define HOOK_VIRTUALS(MAP, FUNC) \
|
||||
hook::swap_virtual_func( \
|
||||
koalabox::hook::swap_virtual_func( \
|
||||
globals::address_map, \
|
||||
interface, \
|
||||
#FUNC, \
|
||||
@@ -209,14 +212,14 @@ namespace steam_functions {
|
||||
}
|
||||
|
||||
HSteamPipe get_steam_pipe_or_throw() {
|
||||
const auto& steam_api_module = win_util::get_module_handle_or_throw(STEAMAPI_DLL);
|
||||
const auto& steam_api_module = koalabox::win_util::get_module_handle_or_throw(STEAMAPI_DLL);
|
||||
void* GetHSteamPipe_address;
|
||||
try {
|
||||
GetHSteamPipe_address = (void*) win_util::get_proc_address_or_throw(
|
||||
GetHSteamPipe_address = (void*) koalabox::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(
|
||||
GetHSteamPipe_address = (void*) koalabox::win_util::get_proc_address_or_throw(
|
||||
steam_api_module, "GetHSteamPipe"
|
||||
);
|
||||
}
|
||||
@@ -245,8 +248,8 @@ namespace steam_functions {
|
||||
|
||||
AppId_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(
|
||||
const auto& steam_client_module = koalabox::win_util::get_module_handle_or_throw(STEAMCLIENT_DLL);
|
||||
auto* CreateInterface_address = (void*) koalabox::win_util::get_proc_address_or_throw(
|
||||
steam_client_module, "CreateInterface"
|
||||
);
|
||||
auto* CreateInterface_o = PLH::FnCast(CreateInterface_address, CreateInterface);
|
||||
@@ -255,7 +258,7 @@ namespace steam_functions {
|
||||
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);
|
||||
throw koalabox::util::exception("Failed to obtain SteamClient006 interface. Result: {}", result);
|
||||
}
|
||||
|
||||
// Get GetISteamUtils
|
||||
11
src/steam_impl/steam_impl.hpp
Normal file
11
src/steam_impl/steam_impl.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/types.hpp>
|
||||
|
||||
namespace steam_impl {
|
||||
|
||||
void hook_virtuals(void* interface, const String& version_string);
|
||||
|
||||
uint32_t get_app_id_or_throw();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <steam_impl/steam_inventory.hpp>
|
||||
#include <core/config.hpp>
|
||||
#include <smoke_api/config.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
|
||||
namespace steam_inventory {
|
||||
@@ -53,11 +53,11 @@ namespace steam_inventory {
|
||||
)
|
||||
|
||||
static uint32_t original_count = 0;
|
||||
const auto injected_count = config::instance.extra_inventory_items.size();
|
||||
const auto injected_count = smoke_api::config::instance.extra_inventory_items.size();
|
||||
|
||||
// Automatically get inventory items from steam
|
||||
static Vector<SteamItemDef_t> auto_inventory_items;
|
||||
if (config::instance.auto_inject_inventory) {
|
||||
if (smoke_api::config::instance.auto_inject_inventory) {
|
||||
CALL_ONCE({
|
||||
uint32_t count = 0;
|
||||
if (get_item_definition_ids(nullptr, &count)) {
|
||||
@@ -103,7 +103,7 @@ namespace steam_inventory {
|
||||
|
||||
for (int i = 0; i < injected_count; i++) {
|
||||
auto& item = pOutItemsArray[original_count + auto_injected_count + i];
|
||||
const auto item_def_id = config::instance.extra_inventory_items[i];
|
||||
const auto item_def_id = smoke_api::config::instance.extra_inventory_items[i];
|
||||
|
||||
item = new_item(item_def_id);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <steam_impl/steam_user.hpp>
|
||||
#include <core/config.hpp>
|
||||
#include <smoke_api/config.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
|
||||
namespace steam_user {
|
||||
@@ -16,7 +16,7 @@ namespace steam_user {
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto has_license = config::is_dlc_unlocked(
|
||||
const auto has_license = smoke_api::config::is_dlc_unlocked(
|
||||
0, appID, [&]() {
|
||||
return result == k_EUserHasLicenseResultHasLicense;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <core/macros.hpp>
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
|
||||
DLL_EXPORT(void*) CreateInterface(const char* interface_string, int* out_result) {
|
||||
|
||||
Reference in New Issue
Block a user