diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml
index 6c4624c..9efa2c9 100644
--- a/.idea/dictionaries/project.xml
+++ b/.idea/dictionaries/project.xml
@@ -5,6 +5,7 @@
indicies
koalabox
koaloader
+ wstr
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad17912..1a52075 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -93,6 +93,11 @@ endif ()
add_library(SmokeAPI SHARED ${SMOKE_API_SOURCES} ${VERSION_RESOURCE})
+# There is a weird bug where c++23 features are not enabled in x64 builds,
+# while they are available in x86. I've no idea what causes this discrepancy,
+# but manually setting the MSVC compiler option fixes this issue.
+target_compile_options(SmokeAPI PRIVATE /std:c++latest)
+
configure_linker_exports(
TARGET SmokeAPI
FORWARDED_DLL "${STEAMAPI_DLL}_o"
diff --git a/KoalaBox b/KoalaBox
index afb0998..63837c4 160000
--- a/KoalaBox
+++ b/KoalaBox
@@ -1 +1 @@
-Subproject commit afb09983ca0384e404fd79d0e52c97ae4a95aff3
+Subproject commit 63837c420c5e972f254b1630316d96186f5b96ef
diff --git a/src/common/steamclient_exports.cpp b/src/common/steamclient_exports.cpp
index a6c1be3..d5fab59 100644
--- a/src/common/steamclient_exports.cpp
+++ b/src/common/steamclient_exports.cpp
@@ -1,9 +1,10 @@
+#include
#include
#include
DLL_EXPORT(void*) CreateInterface(const char* interface_string, int* out_result) {
return steam_client::GetGenericInterface(
- __func__, interface_string, [&]() {
+ __func__, interface_string, [&] {
GET_ORIGINAL_HOOKED_FUNCTION(CreateInterface)
return CreateInterface_o(interface_string, out_result);
diff --git a/src/core/types.hpp b/src/core/types.hpp
index 7e8438b..0217a4e 100644
--- a/src/core/types.hpp
+++ b/src/core/types.hpp
@@ -1,8 +1,8 @@
#pragma once
-#include
#include
-#include
+// ReSharper disable once CppUnusedIncludeDirective
+#include // Used by several macros
#include
#include
diff --git a/src/game_mode/exports/steam_api.cpp b/src/game_mode/exports/steam_api.cpp
index 3ec612b..83e3ea3 100644
--- a/src/game_mode/exports/steam_api.cpp
+++ b/src/game_mode/exports/steam_api.cpp
@@ -1,17 +1,5 @@
#include
-
-
-// DLL_EXPORT(void) SteamAPI_Shutdown1() {
-// LOG_INFO("{} -> Game requested shutdown", __func__);
-//
-// ORIGINAL_FUNCTION_STEAMAPI(SteamAPI_Shutdown)();
-// }
-
-/*DLL_EXPORT(void) SteamAPI_Shutdown2() {
- LOG_INFO("{} -> Game requested shutdown", __func__);
-
- ORIGINAL_FUNCTION_STEAMAPI(SteamAPI_Shutdown)();
-}*/
+#include
// TODO: Detour in hook mode
DLL_EXPORT(bool) SteamAPI_RestartAppIfNecessary(
diff --git a/src/game_mode/exports/steam_api_flat.cpp b/src/game_mode/exports/steam_api_flat.cpp
index 3fe5e45..fabe006 100644
--- a/src/game_mode/exports/steam_api_flat.cpp
+++ b/src/game_mode/exports/steam_api_flat.cpp
@@ -1,9 +1,11 @@
+#include
+
+#include
#include
#include
#include
#include
#include
-#include
// ISteamApps
diff --git a/src/game_mode/exports/steam_api_internal.cpp b/src/game_mode/exports/steam_api_internal.cpp
index fad9d51..9a58eb0 100644
--- a/src/game_mode/exports/steam_api_internal.cpp
+++ b/src/game_mode/exports/steam_api_internal.cpp
@@ -1,3 +1,4 @@
+#include
#include
DLL_EXPORT(void*) SteamInternal_FindOrCreateUserInterface(HSteamUser hSteamUser, const char* version) {
diff --git a/src/game_mode/exports/steam_api_unversioned.cpp b/src/game_mode/exports/steam_api_unversioned.cpp
index 2c5eb5a..e628c7d 100644
--- a/src/game_mode/exports/steam_api_unversioned.cpp
+++ b/src/game_mode/exports/steam_api_unversioned.cpp
@@ -1,8 +1,11 @@
-#include
+#include
+
#include
#include
#include
-#include
+
+#include
+#include
/**
* Searches the `.rdata` section of the original dll for the full interface version string
@@ -40,7 +43,7 @@ DLL_EXPORT(void*) SteamClient() {
static auto version = get_versioned_interface(STEAM_CLIENT, "006");
return steam_client::GetGenericInterface(
- __func__, version, [&]() {
+ __func__, version, [&] {
GET_ORIGINAL_FUNCTION_STEAMAPI(SteamClient)
return SteamClient_o();
@@ -64,7 +67,7 @@ DLL_EXPORT(void*) SteamUser() {
static auto version = get_versioned_interface(STEAM_USER, "012");
return steam_client::GetGenericInterface(
- __func__, version, [&]() {
+ __func__, version, [&] {
GET_ORIGINAL_FUNCTION_STEAMAPI(SteamUser)
return SteamUser_o();
@@ -76,7 +79,7 @@ DLL_EXPORT(void*) SteamInventory() {
static auto version = get_versioned_interface(STEAM_INVENTORY, "001");
return steam_client::GetGenericInterface(
- __func__, version, [&]() {
+ __func__, version, [&] {
GET_ORIGINAL_FUNCTION_STEAMAPI(SteamInventory)
return SteamInventory_o();
diff --git a/src/smoke_api/smoke_api.cpp b/src/smoke_api/smoke_api.cpp
index 2daa2f3..1373c41 100644
--- a/src/smoke_api/smoke_api.cpp
+++ b/src/smoke_api/smoke_api.cpp
@@ -80,8 +80,8 @@ namespace {
return false;
}
- // Verify that it's steam from valve, and not some other executable coincidentally named
- // steam
+ // Verify that it's steam from valve,
+ // and not some other executable coincidentally named steam
const HMODULE steam_handle = koalabox::win_util::get_module_handle_or_throw(nullptr);
const auto manifest = koalabox::win_util::get_module_manifest(steam_handle);
diff --git a/src/steam_impl/steam_apps.cpp b/src/steam_impl/steam_apps.cpp
index 29a82c2..209d3fc 100644
--- a/src/steam_impl/steam_apps.cpp
+++ b/src/steam_impl/steam_apps.cpp
@@ -6,7 +6,7 @@
#include
#include
-namespace steam_apps {
+namespace {
/// 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.
@@ -43,9 +43,10 @@ namespace steam_apps {
// by aggregating results from all the sources into a single set.
Vector aggregated_dlcs;
- const auto append_dlcs = [&](const Vector& source, const String& source_name) {
- LOG_DEBUG("App ID {} has {} DLCs defined in {}", app_id, source.size(), source_name);
- aggregated_dlcs < append > source;
+ const auto append_dlcs = [&](const Vector& dlc_list, const String& source_name) {
+ LOG_DEBUG("App ID {} has {} DLCs defined in {}", app_id, dlc_list.size(), source_name);
+ // Append DLCs to aggregated DLCs
+ std::ranges::copy(dlc_list, std::back_inserter(aggregated_dlcs));
};
append_dlcs(smoke_api::config::get_extra_dlcs(app_id), "local config");
@@ -71,6 +72,9 @@ namespace steam_apps {
smoke_api::app_cache::save_dlcs(app_id, aggregated_dlcs);
}
+}
+
+namespace steam_apps {
bool IsDlcUnlocked(
const String& function_name,
@@ -114,7 +118,7 @@ namespace steam_apps {
return total_count(static_cast(app_dlcs[app_id].size()));
} catch (const Exception& e) {
- LOG_ERROR(" Uncaught exception: {}", function_name, e.what());
+ LOG_ERROR("Uncaught exception: {}", function_name, e.what());
return 0;
}
}
diff --git a/src/store_mode/steamclient/client_user.cpp b/src/store_mode/steamclient/client_user.cpp
index 52c4ef6..8adfa65 100644
--- a/src/store_mode/steamclient/client_user.cpp
+++ b/src/store_mode/steamclient/client_user.cpp
@@ -13,7 +13,7 @@ VIRTUAL(bool) IClientUser_BIsSubscribedApp(PARAMS(AppId_t dlc_id)) {
return IClientUser_BIsSubscribedApp_o(ARGS(dlc_id));
});
} catch (const Exception& e) {
- LOG_ERROR("{} -> Error: {}", __func__, e.what())
+ LOG_ERROR("{} -> Error: {}", __func__, e.what());
return false;
}
}
diff --git a/src/store_mode/steamclient/steamclient.cpp b/src/store_mode/steamclient/steamclient.cpp
index 0478ccb..5b5e563 100644
--- a/src/store_mode/steamclient/steamclient.cpp
+++ b/src/store_mode/steamclient/steamclient.cpp
@@ -1,11 +1,11 @@
-#include
-#include
#include
#include
#include
+#include
+#include
-#include
#include
+#include
namespace store::steamclient {
using namespace koalabox;
@@ -31,6 +31,7 @@ namespace store::steamclient {
uintptr_t start_address
);
+ // clang-format off
#define CONSTRUCT_ORDINAL_MAP(INTERFACE) \
construct_ordinal_map(#INTERFACE, ordinal_map[#INTERFACE], function_selector_address);
@@ -38,11 +39,11 @@ namespace store::steamclient {
interface, \
#INTERFACE"_"#FUNC, \
ordinal_map[#INTERFACE][#FUNC], \
- reinterpret_cast(INTERFACE##_##FUNC) \
+ reinterpret_cast(INTERFACE## _## FUNC) \
);
#define SELECTOR_IMPLEMENTATION(INTERFACE, FUNC_BODY) \
- DLL_EXPORT(void) INTERFACE##_Selector( \
+ DLL_EXPORT(void) INTERFACE## _Selector( \
void* interface, \
void* arg2, \
void* arg3, \
@@ -52,8 +53,8 @@ namespace store::steamclient {
interface_name_to_address_map[#INTERFACE] = interface; \
[&]()FUNC_BODY(); \
}) \
- GET_ORIGINAL_HOOKED_FUNCTION(INTERFACE##_Selector) \
- INTERFACE##_Selector_o(interface, arg2, arg3, arg4); \
+ GET_ORIGINAL_HOOKED_FUNCTION(INTERFACE## _Selector) \
+ INTERFACE## _Selector_o(interface, arg2, arg3, arg4); \
}
SELECTOR_IMPLEMENTATION(IClientAppManager, {
@@ -87,10 +88,12 @@ namespace store::steamclient {
#define DETOUR_SELECTOR(INTERFACE) \
if(interface_name == #INTERFACE){ \
CONSTRUCT_ORDINAL_MAP(INTERFACE) \
- DETOUR_ADDRESS(INTERFACE##_Selector, function_selector_address) \
+ DETOUR_ADDRESS(INTERFACE## _Selector, function_selector_address) \
}
+ // clang-format on
- void detour_interface_selector(const String& interface_name, uintptr_t function_selector_address) {
+ void
+ detour_interface_selector(const String& interface_name, uintptr_t function_selector_address) {
LOG_DEBUG("Detected interface: '{}'", interface_name);
DETOUR_SELECTOR(IClientAppManager)
@@ -100,8 +103,12 @@ namespace store::steamclient {
DETOUR_SELECTOR(IClientUtils)
}
- uintptr_t get_absolute_address(ZydisDecodedInstruction instruction, uintptr_t address) {
- const auto operand = instruction.operands[0];
+ uintptr_t get_absolute_address(
+ const ZydisDecodedInstruction instruction, //
+ const ZydisDecodedOperand operands[], //
+ const uintptr_t address
+ ) {
+ const auto operand = operands[0];
if (operand.imm.is_relative) {
ZyanU64 absolute_address;
@@ -110,11 +117,14 @@ namespace store::steamclient {
return absolute_address;
}
- return (uintptr_t) operand.imm.value.u;
+ return (uintptr_t)operand.imm.value.u;
}
- bool is_push_immediate(const ZydisDecodedInstruction& instruction) {
- const auto& operand = instruction.operands[0];
+ bool is_push_immediate(
+ const ZydisDecodedInstruction& instruction, //
+ const ZydisDecodedOperand operands[]
+ ) {
+ const auto& operand = operands[0];
return instruction.mnemonic == ZYDIS_MNEMONIC_PUSH &&
operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
@@ -122,8 +132,11 @@ namespace store::steamclient {
operand.encoding == ZYDIS_OPERAND_ENCODING_SIMM16_32_32;
}
- std::optional get_string_argument(const ZydisDecodedInstruction& instruction) {
- const auto* name_address = reinterpret_cast(instruction.operands[0].imm.value.u);
+ std::optional get_string_argument(
+ const ZydisDecodedInstruction& instruction, //
+ const ZydisDecodedOperand operands[]
+ ) {
+ const auto* name_address = reinterpret_cast(operands[0].imm.value.u);
if (util::is_valid_pointer(name_address)) {
return name_address;
}
@@ -132,20 +145,22 @@ namespace store::steamclient {
std::optional get_instruction_string(
const ZydisDecodedInstruction& instruction,
+ const ZydisDecodedOperand operands[],
const uintptr_t address
) {
const auto buffer_size = 64;
char buffer[buffer_size] = {};
- if (ZYAN_SUCCESS(
- ZydisFormatterFormatInstruction(
+ if (ZYAN_SUCCESS(ZydisFormatterFormatInstruction(
&formatter,
&instruction,
+ operands,
+ instruction.operand_count,
buffer,
buffer_size,
- address
- )
- )) {
+ address,
+ ZYAN_NULL
+ ))) {
return buffer;
}
@@ -155,16 +170,18 @@ namespace store::steamclient {
std::optional find_interface_name(uintptr_t selector_address) {
auto current_address = selector_address;
ZydisDecodedInstruction instruction{};
- while (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(
- &decoder,
- (void*) current_address,
- MAX_INSTRUCTION_SIZE,
- &instruction
+ ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
+ while (ZYAN_SUCCESS(ZydisDecoderDecodeFull(
+ &decoder, // decoder
+ reinterpret_cast(current_address), // buffer
+ MAX_INSTRUCTION_SIZE, // length
+ &instruction, // instruction
+ operands // operands
))) {
- const auto debug_str = get_instruction_string(instruction, current_address);
+ // const auto debug_str = get_instruction_string(instruction, current_address);
- if (is_push_immediate(instruction)) {
- auto string_opt = get_string_argument(instruction);
+ if (is_push_immediate(instruction, operands)) {
+ auto string_opt = get_string_argument(instruction, operands);
if (string_opt && string_opt->starts_with("IClient")) {
return string_opt;
@@ -181,24 +198,29 @@ namespace store::steamclient {
return std::nullopt;
}
+ struct full_instruction {
+ const ZydisDecodedInstruction instruction;
+ const std::vector operands;
+ };
+
/**
* Recursively walks through the code, until a return instruction is reached.
* Recursion occurs whenever a jump/branch is encountered.
*/
template
void visit_code( // NOLINT(misc-no-recursion)
- Set& visited_addresses,
+ std::set& visited_addresses,
uintptr_t start_address,
T context,
- const Function& instruction_list
+ const std::list& instruction_list
)>& callback
) {
- LOG_TRACE("{} -> start_address: {}", __func__, (void*) start_address);
+ LOG_TRACE("{} -> start_address: {}", __func__, reinterpret_cast(start_address));
if (visited_addresses.contains(start_address)) {
LOG_TRACE("Breaking recursion due to visited address");
@@ -206,34 +228,36 @@ namespace store::steamclient {
}
auto current_address = start_address;
- std::list instruction_list{ZydisDecodedInstruction{}};
+ std::list instruction_list;
ZydisDecodedInstruction instruction{};
- while (
- ZYAN_SUCCESS(
- ZydisDecoderDecodeBuffer(
- &decoder,
- (void*) current_address,
- MAX_INSTRUCTION_SIZE,
- &instruction
- )
- )) {
+ ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
+ while (ZYAN_SUCCESS(ZydisDecoderDecodeFull(
+ &decoder, // decoder
+ (void*)current_address, // buffer
+ MAX_INSTRUCTION_SIZE, // length
+ &instruction, // instructions
+ operands // operands
+ ))) {
visited_addresses.insert(current_address);
+
LOG_TRACE(
"{} -> visiting {} │ {}",
- __func__, (void*) current_address, *get_instruction_string(instruction, current_address)
+ __func__,
+ reinterpret_cast(current_address),
+ *get_instruction_string(instruction, operands, current_address)
);
- const auto operand = instruction.operands[0];
-
- const auto should_return = callback(instruction, operand, current_address, context, instruction_list);
+ const auto should_return =
+ callback(instruction, operands, current_address, context, instruction_list);
if (should_return) {
return;
}
if (instruction.meta.category == ZYDIS_CATEGORY_COND_BR) {
- const auto jump_taken_destination = get_absolute_address(instruction, current_address);
+ const auto jump_taken_destination =
+ get_absolute_address(instruction, operands, current_address);
const auto jump_not_taken_destination = current_address + instruction.length;
visit_code(visited_addresses, jump_taken_destination, context, callback);
@@ -243,24 +267,28 @@ namespace store::steamclient {
return;
}
- if (instruction.mnemonic == ZYDIS_MNEMONIC_JMP && operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
- const auto jump_destination = get_absolute_address(instruction, current_address);
+ const auto& operand = operands[0];
+
+ if (instruction.mnemonic == ZYDIS_MNEMONIC_JMP &&
+ operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
+ const auto jump_destination =
+ get_absolute_address(instruction, operands, current_address);
visit_code(visited_addresses, jump_destination, context, callback);
- LOG_TRACE("{} -> Breaking recursion due to an unconditional jump", __func__)
+ LOG_TRACE("{} -> Breaking recursion due to an unconditional jump", __func__);
return;
}
if (instruction.mnemonic == ZYDIS_MNEMONIC_JMP &&
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;
+ 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;
const auto* table_entry = table;
- while (util::is_valid_pointer((void*) *table_entry)) {
+ while (util::is_valid_pointer((void*)*table_entry)) {
visit_code(visited_addresses, *table_entry, context, callback);
table_entry++;
@@ -275,10 +303,16 @@ namespace store::steamclient {
return;
}
-
// We push items to the front so that it becomes easy to iterate over instructions
// in reverse order of addition.
- instruction_list.push_front(instruction);
+
+ std::vector operand_list;
+ for (auto i = 0U; i < instruction.operand_count; i++) {
+ operand_list.emplace_back(operands[i]);
+ }
+ instruction_list.push_front(
+ {.instruction = instruction, .operands = std::move(operand_list)}
+ );
current_address += instruction.length;
}
}
@@ -289,35 +323,39 @@ namespace store::steamclient {
uintptr_t start_address
) {
Set visited_addresses;
- visit_code(visited_addresses, start_address, {}, [&](
- const ZydisDecodedInstruction& instruction,
- const ZydisDecodedOperand& operand,
+ visit_code(
+ visited_addresses,
+ start_address,
+ {},
+ [&](const ZydisDecodedInstruction& instruction,
+ const ZydisDecodedOperand operands[],
const auto&,
InstructionContext& context,
- const std::list& instruction_list
- ) {
- if (context.function_name && function_name_to_ordinal_map.contains(*context.function_name)) {
+ const std::list& instruction_list) {
+ if (context.function_name &&
+ function_name_to_ordinal_map.contains(*context.function_name)) {
// Avoid duplicate work
return true;
}
- const auto& last_instruction = instruction_list.front();
+ const auto& last = instruction_list.front();
const auto is_mov_base_esp = 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;
+ operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
+ operands[1].reg.value == ZYDIS_REGISTER_ESP;
if (!context.base_register && is_mov_base_esp) {
// 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) {
+ context.base_register = operands[0].reg.value;
+ } else if ( //
+ is_push_immediate( last.instruction, last.operands.data() ) &&
+ is_push_immediate(instruction, operands) && !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);
- const auto push_string_2 = get_string_argument(instruction);
+ const auto push_string_1 =
+ get_string_argument(last.instruction, last.operands.data());
+ const auto push_string_2 = get_string_argument(instruction, operands);
if (push_string_1 && push_string_2) {
if (*push_string_1 == target_interface) {
@@ -326,7 +364,8 @@ namespace store::steamclient {
context.function_name = push_string_1;
}
- if (context.function_name && function_name_to_ordinal_map.contains(*context.function_name)) {
+ if (context.function_name &&
+ function_name_to_ordinal_map.contains(*context.function_name)) {
// Bail early to avoid duplicate work
return true;
}
@@ -338,28 +377,30 @@ namespace store::steamclient {
const auto& base_register = *(context.base_register);
const auto& function_name = *(context.function_name);
-
std::optional offset;
auto last_destination_reg = ZYDIS_REGISTER_NONE;
bool is_derived_from_base_reg = false;
+ const auto& operand = operands[0];
+
// Sometimes the offset is present in the call instruction itself,
// hence we can immediately obtain it.
- if (operand.type == ZYDIS_OPERAND_TYPE_MEMORY && operand.mem.base != ZYDIS_REGISTER_NONE) {
+ if (operand.type == ZYDIS_OPERAND_TYPE_MEMORY &&
+ operand.mem.base != ZYDIS_REGISTER_NONE) {
offset = static_cast(operand.mem.disp.value);
last_destination_reg = operand.mem.base;
} else if (operand.type == ZYDIS_OPERAND_TYPE_REGISTER) {
last_destination_reg = operand.reg.value;
}
- for (const auto& previous_instruction: instruction_list) {
- const auto& destination_operand = previous_instruction.operands[0];
- const auto& source_operand = previous_instruction.operands[1];
+ for (const auto& previous : instruction_list) {
+ const auto& destination_operand = previous.operands[0];
+ const auto& source_operand = previous.operands[1];
// Extract offset if necessary
- if (previous_instruction.mnemonic == ZYDIS_MNEMONIC_MOV &&
- previous_instruction.operand_count == 2 &&
+ if (previous.instruction.mnemonic == ZYDIS_MNEMONIC_MOV &&
+ previous.instruction.operand_count == 2 &&
destination_operand.reg.value == last_destination_reg &&
source_operand.type == ZYDIS_OPERAND_TYPE_MEMORY) {
@@ -367,8 +408,9 @@ namespace store::steamclient {
if (source_mem.base == base_register &&
source_mem.disp.has_displacement &&
source_mem.disp.value == 8) {
- // We have verified that the chain eventually leads up to the base register.
- // Hence, we can conclude that the offset is valid.
+ // We have verified that the chain eventually leads up to the
+ // base register. Hence, we can conclude that the offset is
+ // valid.
is_derived_from_base_reg = true;
break;
}
@@ -385,7 +427,12 @@ namespace store::steamclient {
if (offset && is_derived_from_base_reg) {
const auto ordinal = *offset / sizeof(uintptr_t);
- LOG_DEBUG("Found function ordinal {}::{}@{}", target_interface, function_name, ordinal);
+ LOG_DEBUG(
+ "Found function ordinal {}::{}@{}",
+ target_interface,
+ function_name,
+ ordinal
+ );
function_name_to_ordinal_map[function_name] = ordinal;
return true;
@@ -402,17 +449,21 @@ namespace store::steamclient {
const uintptr_t start_address,
Set& visited_addresses
) {
- visit_code(visited_addresses, start_address, nullptr, [](
- const ZydisDecodedInstruction& instruction,
- const ZydisDecodedOperand& operand,
- const auto& current_address,
- auto,
- const auto&
- ) {
- if (instruction.mnemonic == ZYDIS_MNEMONIC_CALL && operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
- LOG_TRACE("Found call instruction at {}", (void*) current_address);
+ visit_code(
+ visited_addresses,
+ start_address,
+ nullptr,
+ [](const ZydisDecodedInstruction& instruction,
+ const ZydisDecodedOperand operands[],
+ const auto& current_address,
+ auto,
+ const auto&) {
+ if (instruction.mnemonic == ZYDIS_MNEMONIC_CALL &&
+ operands[0].type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
+ LOG_TRACE("Found call instruction at {}", (void*)current_address);
- const auto function_selector_address = get_absolute_address(instruction, current_address);
+ const auto function_selector_address =
+ get_absolute_address(instruction, operands, current_address);
const auto interface_name_opt = find_interface_name(function_selector_address);
@@ -429,16 +480,16 @@ namespace store::steamclient {
}
void process_client_engine(uintptr_t interface) {
- const auto* steam_client_internal = ((uintptr_t***) interface)[
- store::config.client_engine_steam_client_internal_ordinal
- ];
- const auto interface_selector_address = (*steam_client_internal)[
- store::config.steam_client_internal_interface_selector_ordinal
- ];
+ const auto* steam_client_internal =
+ ((uintptr_t***)interface)[store::config.client_engine_steam_client_internal_ordinal];
+ const auto interface_selector_address = (*steam_client_internal
+ )[store::config.steam_client_internal_interface_selector_ordinal];
- LOG_DEBUG("Found interface selector at: {}", (void*) interface_selector_address);
+ LOG_DEBUG("Found interface selector at: {}", (void*)interface_selector_address);
- if (ZYAN_FAILED(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LEGACY_32, ZYDIS_STACK_WIDTH_32))) {
+ if (ZYAN_FAILED(
+ ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LEGACY_32, ZYDIS_STACK_WIDTH_32)
+ )) {
LOG_ERROR("Failed to initialize zydis decoder");
return;
}
diff --git a/src/store_mode/store.cpp b/src/store_mode/store.cpp
index 4d09ab1..fc69f47 100644
--- a/src/store_mode/store.cpp
+++ b/src/store_mode/store.cpp
@@ -10,14 +10,14 @@
#include
#include
#include
+#include
-namespace store {
-
- StoreConfig config; // NOLINT(cert-err58-cpp)
+namespace {
+ using namespace store;
/**
- * @return A string representing the source of the config.
- */
+ * @return A string representing the source of the config.
+ */
void init_store_config() {
const auto print_source = [](const String& source) {
LOG_INFO("Loaded Store config from the {}", source);
@@ -50,47 +50,62 @@ namespace store {
config = {};
}
- // Finally, fetch the remote config from GitHub, and inform user about the need to restart Steam,
- // if a new config has been fetched
- NEW_THREAD({
+ // Finally, fetch the remote config from GitHub, and inform user about the need to restart
+ // Steam, if a new config has been fetched
+ std::thread([] {
try {
const auto github_config_opt = api::fetch_store_config();
if (!github_config_opt) {
return;
}
-
const auto github_config = *github_config_opt;
-
store_cache::save_store_config(github_config);
-
if (github_config == config) {
- LOG_DEBUG("Fetched Store config is equal to existing config");
-
+ (spdlog::default_logger_raw())
+ ->log(
+ spdlog::source_loc{
+ "store.cpp", 66, static_cast(__FUNCTION__)
+ },
+ spdlog::level::debug,
+ "Fetched Store config is equal to existing config"
+ );
return;
}
-
- LOG_DEBUG("Fetched a new Store config")
-
- ::MessageBox(
+ (spdlog::default_logger_raw())
+ ->log(
+ spdlog::source_loc{"store.cpp", 66, static_cast(__FUNCTION__)},
+ spdlog::level::debug,
+ "Fetched a new Store config"
+ );
+ MessageBoxW(
nullptr,
- TEXT(
- "SmokeAPI has downloaded an updated config for Store mode. "
- "Please restart Steam in order to apply the new Store config. "
- ),
- TEXT("SmokeAPI - Store"),
- MB_SETFOREGROUND | MB_ICONINFORMATION | MB_OK
+ L"SmokeAPI has downloaded an updated config for Store mode. "
+ "Please restart Steam in order to apply the new Store config. ",
+ L"SmokeAPI - Store",
+ 0x00010000L | 0x00000040L | 0x00000000L
);
} catch (const Exception& ex) {
- LOG_ERROR("Failed to get remote store_mode config: {}", ex.what());
+ (spdlog::default_logger_raw())
+ ->log(
+ spdlog::source_loc{"store.cpp", 66, static_cast(__FUNCTION__)},
+ spdlog::level::err,
+ "Failed to get remote store_mode config: {}",
+ ex.what()
+ );
}
- })
+ }).detach();
}
+}
+
+namespace store {
+ StoreConfig config; // NOLINT(cert-err58-cpp)
void init_store_mode() {
init_store_config();
koalabox::dll_monitor::init_listener(
- {VSTDLIB_DLL, STEAMCLIENT_DLL}, [](const HMODULE& module_handle, const String& name) {
+ {VSTDLIB_DLL, STEAMCLIENT_DLL},
+ [](const HMODULE& module_handle, const String& name) {
try {
if (koalabox::str::eq(name, VSTDLIB_DLL)) {
// VStdLib DLL handles Family Sharing functions
@@ -108,32 +123,35 @@ namespace store {
DETOUR_STEAMCLIENT(CreateInterface)
}
- if (globals::vstdlib_module != nullptr && globals::steamclient_module != nullptr) {
+ if (globals::vstdlib_module != nullptr &&
+ globals::steamclient_module != nullptr) {
koalabox::dll_monitor::shutdown_listener();
}
} catch (const Exception& ex) {
LOG_ERROR(
"Error listening to DLL load events. Module: '{}', Message: {}",
- name, ex.what()
+ name,
+ ex.what()
);
}
}
);
- NEW_THREAD({
- koalabox::ipc::init_pipe_server("smokeapi.store.steam", [](const koalabox::ipc::Request& request) {
- koalabox::ipc::Response response;
-
- if (request.name < equals > "config::reload") {
- smoke_api::config::ReloadConfig();
- response.success = true;
- } else {
- response.success = false;
- response.data["error_message"] = "Invalid request name: " + request.name;
+ std::thread([=] {
+ koalabox::ipc::init_pipe_server(
+ "smokeapi.store.steam",
+ [](const koalabox::ipc::Request& request) {
+ koalabox::ipc::Response response;
+ if (koalabox::str::eq(request.name, "config::reload")) {
+ smoke_api::config::ReloadConfig();
+ response.success = true;
+ } else {
+ response.success = false;
+ response.data["error_message"] = "Invalid request name: " + request.name;
+ }
+ return response;
}
-
- return response;
- });
- })
+ );
+ }).detach();
}
}
diff --git a/src/store_mode/store_api.cpp b/src/store_mode/store_api.cpp
index 7db481c..8da1f81 100644
--- a/src/store_mode/store_api.cpp
+++ b/src/store_mode/store_api.cpp
@@ -11,7 +11,7 @@ namespace store::api {
return kg_config_json.get();
} catch (const Exception& e) {
- LOG_ERROR("Failed to fetch Store config from GitHub: {}", e.what())
+ LOG_ERROR("Failed to fetch Store config from GitHub: {}", e.what());
return std::nullopt;
}
}
diff --git a/src/store_mode/store_cache.cpp b/src/store_mode/store_cache.cpp
index 1faa4f1..7a935dc 100644
--- a/src/store_mode/store_cache.cpp
+++ b/src/store_mode/store_cache.cpp
@@ -9,7 +9,7 @@ namespace store::store_cache {
try {
return koalabox::cache::get(KEY_KG_CONFIG, Json(nullptr)).get();
} catch (const Exception& e) {
- LOG_ERROR("Failed to get cached store_mode config: {}", e.what())
+ LOG_ERROR("Failed to get cached store_mode config: {}", e.what());
return std::nullopt;
}
@@ -17,11 +17,11 @@ namespace store::store_cache {
bool save_store_config(const StoreConfig& config) {
try {
- LOG_DEBUG("Caching store_mode config")
+ LOG_DEBUG("Caching store_mode config");
return koalabox::cache::put(KEY_KG_CONFIG, Json(config));
} catch (const Exception& e) {
- LOG_ERROR("Failed to cache store_mode config: {}", e.what())
+ LOG_ERROR("Failed to cache store_mode config: {}", e.what());
return false;
}
diff --git a/src/store_mode/vstdlib/vstdlib.cpp b/src/store_mode/vstdlib/vstdlib.cpp
index 0c4d694..27bb158 100644
--- a/src/store_mode/vstdlib/vstdlib.cpp
+++ b/src/store_mode/vstdlib/vstdlib.cpp
@@ -2,13 +2,13 @@
namespace store::vstdlib {
VIRTUAL(bool) SharedLicensesLockStatus(PARAMS(void* arg)) {
- LOG_DEBUG("{}(this={}, arg={})", __func__, THIS, arg)
+ LOG_DEBUG("{}(this={}, arg={})", __func__, THIS, arg);
ARGS();
return true;
}
VIRTUAL(bool) SharedLibraryStopPlaying(PARAMS(void* arg)) {
- LOG_DEBUG("{}(this={}, arg={})", __func__, THIS, arg)
+ LOG_DEBUG("{}(this={}, arg={})", __func__, THIS, arg);
ARGS();
return true;
}
@@ -28,7 +28,7 @@ namespace store::vstdlib {
if (data && data->get_callback_name()) {
const auto name = String(data->get_callback_name());
- LOG_TRACE("{}(ecx={}, edx={}, name='{}')", __func__, ARGS(), name)
+ LOG_TRACE("{}(ecx={}, edx={}, name='{}')", __func__, ARGS(), name);
if (name == "SharedLicensesLockStatus" && !lock_status_hooked) {
DETOUR_ADDRESS(SharedLicensesLockStatus, data->get_callback_data()->get_callback_address())
lock_status_hooked = true;