mirror of
https://github.com/acidicoala/SmokeAPI.git
synced 2026-01-24 13:32:51 -05:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bd112c8f4 | ||
|
|
7b54b4bc7b | ||
|
|
077096b7ed | ||
|
|
bc8ee85e19 | ||
|
|
5aaa9ed151 | ||
|
|
e2b126c8b6 | ||
|
|
2f6d6cc9aa | ||
|
|
8784df5f45 | ||
|
|
dc12301090 | ||
|
|
907e939b67 | ||
|
|
7b82994b17 | ||
|
|
11bd820921 | ||
|
|
9acf7312d3 |
@@ -1,5 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="steamworks_downloader [prompt]" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="$Prompt$" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$/res" PASS_PARENT_ENVS_2="true" PROJECT_NAME="SmokeAPI" TARGET_NAME="steamworks_downloader" CONFIG_NAME="Debug [32]" RUN_TARGET_PROJECT_NAME="SmokeAPI" RUN_TARGET_NAME="steamworks_downloader">
|
<configuration default="false" name="steamworks_downloader [prompt]" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="$Prompt$" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$/res" PASS_PARENT_ENVS_2="true" PROJECT_NAME="SmokeAPI" TARGET_NAME="steamworks_downloader" CONFIG_NAME="Debug [64]" RUN_TARGET_PROJECT_NAME="SmokeAPI" RUN_TARGET_NAME="steamworks_downloader">
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
2
.idea/runConfigurations/sync.xml
generated
2
.idea/runConfigurations/sync.xml
generated
@@ -1,5 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="sync" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$" PASS_PARENT_ENVS_2="true" PROJECT_NAME="SmokeAPI" TARGET_NAME="sync" CONFIG_NAME="Debug [32]" RUN_TARGET_PROJECT_NAME="SmokeAPI" RUN_TARGET_NAME="sync">
|
<configuration default="false" name="sync" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$" PASS_PARENT_ENVS_2="true" PROJECT_NAME="SmokeAPI" TARGET_NAME="sync" CONFIG_NAME="Debug [64]" RUN_TARGET_PROJECT_NAME="SmokeAPI" RUN_TARGET_NAME="sync">
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.24)
|
cmake_minimum_required(VERSION 3.24)
|
||||||
|
|
||||||
project(SmokeAPI VERSION 4.1.0)
|
project(SmokeAPI VERSION 4.1.3)
|
||||||
|
|
||||||
include(KoalaBox/cmake/KoalaBox.cmake)
|
include(KoalaBox/cmake/KoalaBox.cmake)
|
||||||
add_subdirectory(KoalaBox)
|
add_subdirectory(KoalaBox)
|
||||||
|
|||||||
2
KoalaBox
2
KoalaBox
Submodule KoalaBox updated: 57cee7c22b...997b5db2ee
32
README.md
32
README.md
@@ -145,14 +145,15 @@ In such cases, it might be worth trying [Special K], which can inject SmokeAPI a
|
|||||||
|
|
||||||
### ✔️ Requirements
|
### ✔️ Requirements
|
||||||
|
|
||||||
Linux builds of SmokeAPI depend on several libraries. Make sure they are installed on your system.
|
Linux builds of SmokeAPI depend on several libraries. These libraries are usually included in the
|
||||||
|
[Steam Linux Runtime 3.0 (sniper)](https://github.com/ValveSoftware/steam-runtime#readme),
|
||||||
|
so nothing is required to be installed on the system. But when launching a game directly via its executable
|
||||||
|
(such as in case of hook mode) your system needs to have the following required libraries installed.
|
||||||
|
|
||||||
The following list features links in Arch Linux repositories, but if you are using a different distribution,
|
The following list features links in Arch Linux repositories, but if you are using a different distribution,
|
||||||
you should use the equivalent package for your distro.
|
you should use the equivalent package for your distro.
|
||||||
|
|
||||||
Required libraries:
|
Required libraries:
|
||||||
- [brotli](https://archlinux.org/packages/core/x86_64/brotli/)
|
|
||||||
[[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-brotli/)]
|
|
||||||
|
|
||||||
- [gcc-libs](https://archlinux.org/packages/core/x86_64/gcc-libs/)
|
- [gcc-libs](https://archlinux.org/packages/core/x86_64/gcc-libs/)
|
||||||
[[32-bit](https://archlinux.org/packages/core/x86_64/lib32-gcc-libs/)]
|
[[32-bit](https://archlinux.org/packages/core/x86_64/lib32-gcc-libs/)]
|
||||||
|
|
||||||
@@ -165,12 +166,6 @@ Required libraries:
|
|||||||
- [libnghttp2](https://archlinux.org/packages/core/x86_64/libnghttp2/)
|
- [libnghttp2](https://archlinux.org/packages/core/x86_64/libnghttp2/)
|
||||||
[[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-libnghttp2/)]
|
[[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-libnghttp2/)]
|
||||||
|
|
||||||
- [libssh2](https://archlinux.org/packages/core/x86_64/libssh2/)
|
|
||||||
[[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-libssh2/)]
|
|
||||||
|
|
||||||
- [openssl](https://archlinux.org/packages/core/x86_64/openssl/)
|
|
||||||
[[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-openssl/)]
|
|
||||||
|
|
||||||
- [zlib](https://archlinux.org/packages/core/x86_64/zlib/)
|
- [zlib](https://archlinux.org/packages/core/x86_64/zlib/)
|
||||||
[[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-zlib/)]
|
[[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-zlib/)]
|
||||||
|
|
||||||
@@ -194,7 +189,6 @@ wrappers might cause issues in theory. However, in practice real tests show that
|
|||||||
chance of success compared to proxy mode. So, at the end of the day, try both modes to see which one works
|
chance of success compared to proxy mode. So, at the end of the day, try both modes to see which one works
|
||||||
best for you.
|
best for you.
|
||||||
|
|
||||||
|
|
||||||
### 🔀 Proxy mode (🐧 Linux)
|
### 🔀 Proxy mode (🐧 Linux)
|
||||||
|
|
||||||
Same as on Windows:
|
Same as on Windows:
|
||||||
@@ -214,17 +208,17 @@ For example:
|
|||||||
1. Extract and paste the `libsmoke_api32.so` or `libsmoke_api64.so` in the root of game's installation directory.
|
1. Extract and paste the `libsmoke_api32.so` or `libsmoke_api64.so` in the root of game's installation directory.
|
||||||
2. In Steam _Library_ open game's _Properties_, switch to the _General_ tab, and set the following _LAUNCH OPTIONS_:
|
2. In Steam _Library_ open game's _Properties_, switch to the _General_ tab, and set the following _LAUNCH OPTIONS_:
|
||||||
|
|
||||||
| Bitness | Launch Options |
|
| Bitness | Launch Options |
|
||||||
|---------|------------------------------------------------------------------------------------------------------------------------|
|
|---------|---------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| 32-bit | `LD_PRELOAD="./libsmoke_api32.so $HOME/.local/share/Steam/ubuntu12_32/gameoverlayrenderer.so" ./<GameExe32> %command%` |
|
| 32-bit | `LD_PRELOAD="./libsmoke_api32.so $HOME/.local/share/Steam/ubuntu12_32/gameoverlayrenderer.so" ./<GameExe32> ; exit ; %command%` |
|
||||||
| 64-bit | `LD_PRELOAD="./libsmoke_api64.so $HOME/.local/share/Steam/ubuntu12_64/gameoverlayrenderer.so" ./<GameExe64> %command%` |
|
| 64-bit | `LD_PRELOAD="./libsmoke_api64.so $HOME/.local/share/Steam/ubuntu12_64/gameoverlayrenderer.so" ./<GameExe64> ; exit ; %command%` |
|
||||||
|
|
||||||
Where `<GameExe32>` and `<GameExe64>` correspond to the actual filename of the game executable. For example:
|
Where `<GameExe32>` and `<GameExe64>` correspond to the actual filename of the game executable. For example:
|
||||||
- `TheEscapists2.x86` (32-bit)
|
- `TheEscapists2.x86` (32-bit)
|
||||||
- `TheEscapists2.x86_64` (64-bit)
|
- `TheEscapists2.x86_64` (64-bit)
|
||||||
- `_linux/darkest.bin.x86` (32-bit)
|
- `_linux/darkest.bin.x86` (32-bit)
|
||||||
- `_linux/darkest.bin.x86_64` (64-bit)
|
- `_linux/darkest.bin.x86_64` (64-bit)
|
||||||
- `bin/linux_x64/eurotrucks2` (64-bit)
|
- `eurotrucks2` (64-bit)
|
||||||
- `binaries/victoria3` (64-bit)
|
- `binaries/victoria3` (64-bit)
|
||||||
|
|
||||||
And so on. Notice that Linux executables do not have `.exe` extension like on Windows, so make sure to copy the entire
|
And so on. Notice that Linux executables do not have `.exe` extension like on Windows, so make sure to copy the entire
|
||||||
@@ -234,6 +228,9 @@ If you have other environment variables, and you don't know how to correctly com
|
|||||||
then please make extensive use of search engines and LLMs for guidance and examples
|
then please make extensive use of search engines and LLMs for guidance and examples
|
||||||
before seeking help the official forum topic.
|
before seeking help the official forum topic.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> The `; exit ; %command%` at the end of launch options
|
||||||
|
> is a trick used to force Steam to directly run the game executable.
|
||||||
|
|
||||||
## ⚙ Configuration
|
## ⚙ Configuration
|
||||||
|
|
||||||
@@ -438,7 +435,8 @@ This project makes use of the following open source projects:
|
|||||||
- [p-ranav/glob](https://github.com/p-ranav/glob)
|
- [p-ranav/glob](https://github.com/p-ranav/glob)
|
||||||
- [pantor/inja](https://github.com/pantor/inja)
|
- [pantor/inja](https://github.com/pantor/inja)
|
||||||
- [jarro2783/cxxopts](https://github.com/jarro2783/cxxopts)
|
- [jarro2783/cxxopts](https://github.com/jarro2783/cxxopts)
|
||||||
- [serge1/ELFIO](https://github.com/serge1/ELFIO)
|
- [serge1/ELFIO](https://github.com/serge1/ELFIO)
|
||||||
|
- [Mbed-TLS/mbedtls](https://github.com/Mbed-TLS/mbedtls)
|
||||||
- [bshoshany/thread-pool](https://github.com/bshoshany/thread-pool)
|
- [bshoshany/thread-pool](https://github.com/bshoshany/thread-pool)
|
||||||
- [batterycenter/embed](https://github.com/batterycenter/embed)
|
- [batterycenter/embed](https://github.com/batterycenter/embed)
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
"logging": {
|
"logging": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"x-packaged-default": true,
|
|
||||||
"description": "Enables logging to SmokeAPI.log.log file.",
|
"description": "Enables logging to SmokeAPI.log.log file.",
|
||||||
|
"x-packaged-default": true,
|
||||||
"x-valid-values": "`true` or `false`."
|
"x-valid-values": "`true` or `false`."
|
||||||
},
|
},
|
||||||
"log_steam_http": {
|
"log_steam_http": {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@
|
|||||||
#include <koalabox/config.hpp>
|
#include <koalabox/config.hpp>
|
||||||
#include <koalabox/globals.hpp>
|
#include <koalabox/globals.hpp>
|
||||||
#include <koalabox/hook.hpp>
|
#include <koalabox/hook.hpp>
|
||||||
|
#include <koalabox/http_client.hpp>
|
||||||
#include <koalabox/lib.hpp>
|
#include <koalabox/lib.hpp>
|
||||||
#include <koalabox/lib_monitor.hpp>
|
#include <koalabox/lib_monitor.hpp>
|
||||||
#include <koalabox/logger.hpp>
|
#include <koalabox/logger.hpp>
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
|
|
||||||
#include "build_config.h"
|
#include "build_config.h"
|
||||||
|
|
||||||
#if defined(KB_WIN)
|
#ifdef KB_WIN
|
||||||
#include "koalabox/win.hpp"
|
#include "koalabox/win.hpp"
|
||||||
#elif defined(KB_LINUX) && defined(KB_32)
|
#elif defined(KB_LINUX) && defined(KB_32)
|
||||||
#include "generated/32/proxy_exports.hpp"
|
#include "generated/32/proxy_exports.hpp"
|
||||||
@@ -57,6 +58,31 @@ namespace {
|
|||||||
void* original_steamapi_handle = nullptr;
|
void* original_steamapi_handle = nullptr;
|
||||||
bool is_hook_mode;
|
bool is_hook_mode;
|
||||||
|
|
||||||
|
void check_for_updates() {
|
||||||
|
try {
|
||||||
|
const auto latest_release_url = std::format(
|
||||||
|
"https://api.github.com/repos/acidicoala/{}/releases/latest",
|
||||||
|
PROJECT_NAME
|
||||||
|
);
|
||||||
|
const auto res = kb::http_client::get_json(latest_release_url);
|
||||||
|
const auto latest_tag = res["tag_name"].get<std::string>();
|
||||||
|
const auto current_tag = std::format("v{}", PROJECT_VERSION);
|
||||||
|
|
||||||
|
if(current_tag == latest_tag) {
|
||||||
|
LOG_DEBUG("Running the latest version");
|
||||||
|
} else {
|
||||||
|
const auto release_page = std::format(
|
||||||
|
"https://github.com/acidicoala/{}/releases/{}",
|
||||||
|
PROJECT_NAME, latest_tag
|
||||||
|
);
|
||||||
|
|
||||||
|
LOG_WARN("New version {} available: {}", latest_tag, release_page);
|
||||||
|
}
|
||||||
|
} catch(const std::exception& e) {
|
||||||
|
LOG_ERROR("{} -> Unexpected error: {}", __func__, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::set<std::string> find_steamclient_versions(void* steamapi_handle) {
|
std::set<std::string> find_steamclient_versions(void* steamapi_handle) {
|
||||||
if(!steamapi_handle) {
|
if(!steamapi_handle) {
|
||||||
kb::util::panic("Invalid state. steamapi_handle is null.");
|
kb::util::panic("Invalid state. steamapi_handle is null.");
|
||||||
@@ -64,11 +90,14 @@ namespace {
|
|||||||
|
|
||||||
std::set<std::string> versions;
|
std::set<std::string> versions;
|
||||||
|
|
||||||
const auto rdata_section = kb::lib::get_section_or_throw(steamapi_handle, kb::lib::CONST_STR_SECTION);
|
// On Linux the section name depends on individual lib file, so we can't use generic constants
|
||||||
const auto rdata = rdata_section.to_string();
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
|
const std::string str_section_name = kb::platform::is_windows ? ".rdata" : ".rodata";
|
||||||
|
const auto str_section = kb::lib::get_section_or_throw(steamapi_handle, str_section_name);
|
||||||
|
const auto str_section_str = str_section.to_string();
|
||||||
|
|
||||||
const std::regex pattern(R"(SteamClient\d{3})");
|
const std::regex pattern(R"(SteamClient\d{3})");
|
||||||
const auto matches_begin = std::sregex_iterator(rdata.begin(), rdata.end(), pattern);
|
const auto matches_begin = std::sregex_iterator(str_section_str.begin(), str_section_str.end(), pattern);
|
||||||
const auto matches_end = std::sregex_iterator();
|
const auto matches_end = std::sregex_iterator();
|
||||||
|
|
||||||
for(std::sregex_iterator i = matches_begin; i != matches_end; ++i) {
|
for(std::sregex_iterator i = matches_begin; i != matches_end; ++i) {
|
||||||
@@ -109,6 +138,15 @@ namespace {
|
|||||||
static const auto CreateInterface$ = KB_LIB_GET_FUNC(steamclient_handle, CreateInterface);
|
static const auto CreateInterface$ = KB_LIB_GET_FUNC(steamclient_handle, CreateInterface);
|
||||||
|
|
||||||
if(auto* steamapi_handle = kb::lib::get_lib_handle(STEAM_API_MODULE)) {
|
if(auto* steamapi_handle = kb::lib::get_lib_handle(STEAM_API_MODULE)) {
|
||||||
|
if(original_steamapi_handle == nullptr) { // hook mode on Windows
|
||||||
|
original_steamapi_handle = steamapi_handle;
|
||||||
|
} else if(steamapi_handle != original_steamapi_handle) {
|
||||||
|
LOG_WARN(
|
||||||
|
"{} -> steamapi_handle ({}) != original_steamapi_handle ({})",
|
||||||
|
__func__, steamapi_handle, original_steamapi_handle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// SteamAPI might have been initialized.
|
// SteamAPI might have been initialized.
|
||||||
// Hence, we need to query SteamClient interfaces and hook them if needed.
|
// Hence, we need to query SteamClient interfaces and hook them if needed.
|
||||||
const auto steamclient_versions = find_steamclient_versions(steamapi_handle);
|
const auto steamclient_versions = find_steamclient_versions(steamapi_handle);
|
||||||
@@ -123,6 +161,8 @@ namespace {
|
|||||||
LOG_INFO("'{}' has not been initialized. Waiting for initialization.", steamclient_version);
|
LOG_INFO("'{}' has not been initialized. Waiting for initialization.", steamclient_version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("{} -> steamapi_handle is null", __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -190,7 +230,10 @@ namespace {
|
|||||||
if(const auto lib_bitness = kb::lib::get_bitness(lib_path)) {
|
if(const auto lib_bitness = kb::lib::get_bitness(lib_path)) {
|
||||||
if(static_cast<uint8_t>(*lib_bitness) == kb::platform::bitness) {
|
if(static_cast<uint8_t>(*lib_bitness) == kb::platform::bitness) {
|
||||||
if(const auto lib_handle = kb::lib::load(lib_path)) {
|
if(const auto lib_handle = kb::lib::load(lib_path)) {
|
||||||
LOG_INFO("Found original library: {}", kb::path::to_str(lib_path));
|
LOG_INFO(
|
||||||
|
"Found & loaded original library '{}' @ {}",
|
||||||
|
kb::path::to_str(lib_path), *lib_handle
|
||||||
|
);
|
||||||
|
|
||||||
original_steamapi_handle = *lib_handle;
|
original_steamapi_handle = *lib_handle;
|
||||||
proxy_exports::init(self_module_handle, original_steamapi_handle);
|
proxy_exports::init(self_module_handle, original_steamapi_handle);
|
||||||
@@ -207,7 +250,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init_proxy_mode([[maybe_unused]] void* self_module_handle) {
|
void init_proxy_mode([[maybe_unused]] void* self_module_handle) {
|
||||||
is_hook_mode = true;
|
is_hook_mode = false;
|
||||||
|
|
||||||
original_steamapi_handle = kb::lib::load_original_library(kb::paths::get_self_dir(), STEAM_API_MODULE);
|
original_steamapi_handle = kb::lib::load_original_library(kb::paths::get_self_dir(), STEAM_API_MODULE);
|
||||||
#ifdef KB_LINUX
|
#ifdef KB_LINUX
|
||||||
@@ -229,16 +272,16 @@ namespace smoke_api {
|
|||||||
|
|
||||||
kb::globals::init_globals(self_module_handle, PROJECT_NAME);
|
kb::globals::init_globals(self_module_handle, PROJECT_NAME);
|
||||||
|
|
||||||
config::instance = kb::config::parse<config::Config>();
|
config::get() = kb::config::parse<config::Config>();
|
||||||
|
|
||||||
if(config::instance.logging) {
|
if(config::get().logging) {
|
||||||
kb::logger::init_file_logger(kb::paths::get_log_path());
|
kb::logger::init_file_logger(kb::paths::get_log_path());
|
||||||
} else {
|
} else {
|
||||||
kb::logger::init_null_logger();
|
kb::logger::init_null_logger();
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("{} v{}{} | Built at '{}'", PROJECT_NAME, PROJECT_VERSION, VERSION_SUFFIX, __TIMESTAMP__);
|
LOG_INFO("{} v{}{} | Built at '{}'", PROJECT_NAME, PROJECT_VERSION, VERSION_SUFFIX, __TIMESTAMP__);
|
||||||
LOG_DEBUG("Parsed config:\n{}", nlohmann::ordered_json(config::instance).dump(2));
|
LOG_DEBUG("Parsed config:\n{}", nlohmann::ordered_json(config::get()).dump(2));
|
||||||
|
|
||||||
const auto exe_path = kb::lib::get_fs_path(nullptr);
|
const auto exe_path = kb::lib::get_fs_path(nullptr);
|
||||||
const auto exe_name = kb::path::to_str(exe_path.filename());
|
const auto exe_name = kb::path::to_str(exe_path.filename());
|
||||||
@@ -270,6 +313,14 @@ namespace smoke_api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void post_init() {
|
||||||
|
#ifdef KB_DEBUG
|
||||||
|
// TODO: Add config option to toggle this and show native OS notification
|
||||||
|
// The real reason behind this is for automatic testing of HTTPs dependencies
|
||||||
|
std::thread(check_for_updates).detach();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void shutdown() {
|
void shutdown() {
|
||||||
try {
|
try {
|
||||||
static bool shutdown_complete = false;
|
static bool shutdown_complete = false;
|
||||||
|
|||||||
@@ -4,6 +4,14 @@
|
|||||||
|
|
||||||
namespace smoke_api {
|
namespace smoke_api {
|
||||||
void init(void* self_module_handle);
|
void init(void* self_module_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post-initialization procedures that must be done after the module is finished loading.
|
||||||
|
* Reason being that on Windows we should not start new threads while being in DllMain callback,
|
||||||
|
* otherwise we would run into deadlocks/race-conditions/undefined behavior.
|
||||||
|
*/
|
||||||
|
void post_init();
|
||||||
|
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
AppId_t get_app_id();
|
AppId_t get_app_id();
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ namespace steam_client {
|
|||||||
if(interface_version) {
|
if(interface_version) {
|
||||||
LOG_DEBUG("{} -> '{}' @ {}", function_name, interface_version, interface);
|
LOG_DEBUG("{} -> '{}' @ {}", function_name, interface_version, interface);
|
||||||
|
|
||||||
steam_interfaces::hook_virtuals(interface, interface_version);
|
if(interface) {
|
||||||
|
steam_interfaces::hook_virtuals(interface, interface_version);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return interface;
|
return interface;
|
||||||
|
|||||||
@@ -237,13 +237,10 @@ namespace steam_interfaces {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* const interface_ptr = ISteamClient_GetISteamGenericInterface(
|
ISteamClient_GetISteamGenericInterface(
|
||||||
ARGS(steam_user, steam_pipe, interface_version.c_str())
|
ARGS(steam_user, steam_pipe, interface_version.c_str())
|
||||||
);
|
);
|
||||||
|
|
||||||
if(not interface_ptr) {
|
|
||||||
LOG_ERROR("Failed to get generic interface: '{}'", interface_version)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch(const std::exception& e) {
|
} catch(const std::exception& e) {
|
||||||
LOG_ERROR("{} -> Unhandled exception: {}", __func__, e.what());
|
LOG_ERROR("{} -> Unhandled exception: {}", __func__, e.what());
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <koalabox/logger.hpp>
|
#include <koalabox/logger.hpp>
|
||||||
|
|
||||||
#include "smoke_api/steamclient/steamclient.hpp"
|
#include "smoke_api/steamclient/steamclient.hpp"
|
||||||
|
#include "smoke_api/smoke_api.hpp"
|
||||||
#include "smoke_api/types.hpp"
|
#include "smoke_api/types.hpp"
|
||||||
|
|
||||||
#include "steam_api/steam_client.hpp"
|
#include "steam_api/steam_client.hpp"
|
||||||
@@ -16,6 +17,9 @@ C_DECL(void*) CreateInterface(const char* interface_version, create_interface_re
|
|||||||
static std::mutex section;
|
static std::mutex section;
|
||||||
const std::lock_guard lock(section);
|
const std::lock_guard lock(section);
|
||||||
|
|
||||||
|
static std::once_flag once_flag;
|
||||||
|
std::call_once(once_flag, smoke_api::post_init);
|
||||||
|
|
||||||
return steam_client::GetGenericInterface(
|
return steam_client::GetGenericInterface(
|
||||||
__func__,
|
__func__,
|
||||||
interface_version,
|
interface_version,
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
#include <koalabox/config.hpp>
|
#include <koalabox/config.hpp>
|
||||||
#include <koalabox/io.hpp>
|
|
||||||
#include <koalabox/logger.hpp>
|
#include <koalabox/logger.hpp>
|
||||||
|
|
||||||
#include "smoke_api/config.hpp"
|
#include "smoke_api/config.hpp"
|
||||||
|
|
||||||
namespace smoke_api::config {
|
namespace smoke_api::config {
|
||||||
Config instance; // NOLINT(cert-err58-cpp)
|
Config& get() noexcept {
|
||||||
|
static Config config;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<DLC> get_extra_dlcs(const uint32_t app_id) {
|
std::vector<DLC> get_extra_dlcs(const uint32_t app_id) {
|
||||||
return DLC::get_dlcs_from_apps(instance.extra_dlcs, app_id);
|
return DLC::get_dlcs_from_apps(get().extra_dlcs, app_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_dlc_unlocked(
|
bool is_dlc_unlocked(
|
||||||
@@ -16,16 +18,16 @@ namespace smoke_api::config {
|
|||||||
const AppId_t dlc_id,
|
const AppId_t dlc_id,
|
||||||
const std::function<bool()>& original_function
|
const std::function<bool()>& original_function
|
||||||
) {
|
) {
|
||||||
auto status = instance.default_app_status;
|
auto status = get().default_app_status;
|
||||||
|
|
||||||
const auto app_id_str = std::to_string(app_id);
|
const auto app_id_str = std::to_string(app_id);
|
||||||
if(instance.override_app_status.contains(app_id_str)) {
|
if(get().override_app_status.contains(app_id_str)) {
|
||||||
status = instance.override_app_status[app_id_str];
|
status = get().override_app_status[app_id_str];
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto dlc_id_str = std::to_string(dlc_id);
|
const auto dlc_id_str = std::to_string(dlc_id);
|
||||||
if(instance.override_dlc_status.contains(dlc_id_str)) {
|
if(get().override_dlc_status.contains(dlc_id_str)) {
|
||||||
status = instance.override_dlc_status[dlc_id_str];
|
status = get().override_dlc_status[dlc_id_str];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_unlocked;
|
bool is_unlocked;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace smoke_api::config {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Config instance;
|
Config& get() noexcept;
|
||||||
|
|
||||||
std::vector<DLC> get_extra_dlcs(AppId_t app_id);
|
std::vector<DLC> get_extra_dlcs(AppId_t app_id);
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,15 @@ namespace {
|
|||||||
/// This means we have to get extra DLC IDs from local config, remote config, or cache.
|
/// This means we have to get extra DLC IDs from local config, remote config, or cache.
|
||||||
constexpr auto MAX_DLC = 64;
|
constexpr auto MAX_DLC = 64;
|
||||||
|
|
||||||
std::map<uint32_t, std::vector<DLC>> app_dlcs; // NOLINT(cert-err58-cpp)
|
auto& get_fully_fetched_apps() {
|
||||||
std::set<uint32_t> fully_fetched; // NOLINT(cert-err58-cpp)
|
static std::set<uint32_t> fully_fetched_apps;
|
||||||
|
return fully_fetched_apps;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& get_app_dlc_map() {
|
||||||
|
static std::map<uint32_t, std::vector<DLC>> app_dlc_map;
|
||||||
|
return app_dlc_map;
|
||||||
|
}
|
||||||
|
|
||||||
std::string get_app_id_log(const uint32_t app_id) {
|
std::string get_app_id_log(const uint32_t app_id) {
|
||||||
return app_id ? std::format("App ID: {:>8}, ", app_id) : "";
|
return app_id ? std::format("App ID: {:>8}, ", app_id) : "";
|
||||||
@@ -31,13 +38,13 @@ namespace {
|
|||||||
|
|
||||||
if(app_id == 0) {
|
if(app_id == 0) {
|
||||||
LOG_ERROR("{} -> App ID is 0", __func__);
|
LOG_ERROR("{} -> App ID is 0", __func__);
|
||||||
app_dlcs[app_id] = {}; // Dummy value to avoid checking for presence on each access
|
get_app_dlc_map()[app_id] = {}; // Dummy value to avoid checking for presence on each access
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to fetch data only once. However, if any of the remote sources have failed
|
// We want to fetch data only once. However, if any of the remote sources have failed
|
||||||
// previously, we want to attempt fetching again.
|
// previously, we want to attempt fetching again.
|
||||||
if(fully_fetched.contains(app_id)) {
|
if(get_fully_fetched_apps().contains(app_id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,13 +74,13 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(github_dlcs_opt && steam_dlcs_opt) {
|
if(github_dlcs_opt && steam_dlcs_opt) {
|
||||||
fully_fetched.insert(app_id);
|
get_fully_fetched_apps().insert(app_id);
|
||||||
} else {
|
} else {
|
||||||
append_dlcs(smoke_api::cache::get_dlcs(app_id), "disk cache");
|
append_dlcs(smoke_api::cache::get_dlcs(app_id), "disk cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache DLCs in memory and cache for future use
|
// Cache DLCs in memory and cache for future use
|
||||||
app_dlcs[app_id] = aggregated_dlcs;
|
get_app_dlc_map()[app_id] = aggregated_dlcs;
|
||||||
|
|
||||||
smoke_api::cache::save_dlcs(app_id, aggregated_dlcs);
|
smoke_api::cache::save_dlcs(app_id, aggregated_dlcs);
|
||||||
}
|
}
|
||||||
@@ -137,12 +144,12 @@ namespace smoke_api::steam_apps {
|
|||||||
|
|
||||||
fetch_and_cache_dlcs(app_id);
|
fetch_and_cache_dlcs(app_id);
|
||||||
|
|
||||||
if(app_dlcs.empty()) {
|
if(get_app_dlc_map().empty()) {
|
||||||
LOG_DEBUG("{} -> No cached DLCs, responding with original count", function_name);
|
LOG_DEBUG("{} -> No cached DLCs, responding with original count", function_name);
|
||||||
return total_count(original_count);
|
return total_count(original_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
return total_count(static_cast<int>(app_dlcs[app_id].size()));
|
return total_count(static_cast<int>(get_app_dlc_map()[app_id].size()));
|
||||||
} catch(const std::exception& e) {
|
} catch(const std::exception& e) {
|
||||||
LOG_ERROR("{} -> Uncaught exception: {}", function_name, e.what());
|
LOG_ERROR("{} -> Uncaught exception: {}", function_name, e.what());
|
||||||
return 0;
|
return 0;
|
||||||
@@ -188,8 +195,8 @@ namespace smoke_api::steam_apps {
|
|||||||
pchName[bytes_to_copy] = '\0'; // Ensure null-termination
|
pchName[bytes_to_copy] = '\0'; // Ensure null-termination
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!app_dlcs.empty() && app_dlcs.contains(app_id)) {
|
if(!get_app_dlc_map().empty() && get_app_dlc_map().contains(app_id)) {
|
||||||
const auto& dlcs = app_dlcs[app_id];
|
const auto& dlcs = get_app_dlc_map()[app_id];
|
||||||
|
|
||||||
if(iDLC >= 0 && iDLC < dlcs.size()) {
|
if(iDLC >= 0 && iDLC < dlcs.size()) {
|
||||||
output_dlc(dlcs[iDLC]);
|
output_dlc(dlcs[iDLC]);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace smoke_api::steam_http {
|
|||||||
try {
|
try {
|
||||||
const auto result = original_function();
|
const auto result = original_function();
|
||||||
|
|
||||||
if(config::instance.log_steam_http) {
|
if(config::get().log_steam_http) {
|
||||||
const std::string_view buffer =
|
const std::string_view buffer =
|
||||||
pBodyDataBuffer && unBufferSize
|
pBodyDataBuffer && unBufferSize
|
||||||
? std::string_view(
|
? std::string_view(
|
||||||
@@ -50,7 +50,7 @@ namespace smoke_api::steam_http {
|
|||||||
try {
|
try {
|
||||||
const auto result = original_function();
|
const auto result = original_function();
|
||||||
|
|
||||||
if(config::instance.log_steam_http) {
|
if(config::get().log_steam_http) {
|
||||||
const std::string_view buffer =
|
const std::string_view buffer =
|
||||||
pBodyDataBuffer && unBufferSize
|
pBodyDataBuffer && unBufferSize
|
||||||
? std::string_view(
|
? std::string_view(
|
||||||
@@ -87,7 +87,7 @@ namespace smoke_api::steam_http {
|
|||||||
try {
|
try {
|
||||||
const auto result = original_function();
|
const auto result = original_function();
|
||||||
|
|
||||||
if(config::instance.log_steam_http) {
|
if(config::get().log_steam_http) {
|
||||||
const std::string_view content_type =
|
const std::string_view content_type =
|
||||||
pchContentType ? pchContentType : "smoke_api::N/A";
|
pchContentType ? pchContentType : "smoke_api::N/A";
|
||||||
|
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ namespace smoke_api::steam_inventory {
|
|||||||
);
|
);
|
||||||
|
|
||||||
static uint32_t original_count = 0;
|
static uint32_t original_count = 0;
|
||||||
const auto injected_count = config::instance.extra_inventory_items.size();
|
const auto injected_count = config::get().extra_inventory_items.size();
|
||||||
|
|
||||||
// Automatically get inventory items from steam
|
// Automatically get inventory items from steam
|
||||||
static std::vector<SteamItemDef_t> auto_inventory_items;
|
static std::vector<SteamItemDef_t> auto_inventory_items;
|
||||||
if(config::instance.auto_inject_inventory) {
|
if(config::get().auto_inject_inventory) {
|
||||||
static std::once_flag inventory_inject_flag;
|
static std::once_flag inventory_inject_flag;
|
||||||
std::call_once(
|
std::call_once(
|
||||||
inventory_inject_flag,
|
inventory_inject_flag,
|
||||||
@@ -126,7 +126,7 @@ namespace smoke_api::steam_inventory {
|
|||||||
|
|
||||||
for(int i = 0; i < injected_count; i++) {
|
for(int i = 0; i < injected_count; i++) {
|
||||||
auto& item = pOutItemsArray[original_count + auto_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 = config::get().extra_inventory_items[i];
|
||||||
|
|
||||||
item = new_item(item_def_id);
|
item = new_item(item_def_id);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user