mirror of
https://github.com/acidicoala/SmokeAPI.git
synced 2026-01-26 14:32:54 -05:00
Refactored DLC unlocking logic
This commit is contained in:
49
src/core/config.cpp
Normal file
49
src/core/config.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <core/config.hpp>
|
||||
#include <core/paths.hpp>
|
||||
#include <koalabox/config_parser.hpp>
|
||||
|
||||
namespace config {
|
||||
Config instance; // NOLINT(cert-err58-cpp)
|
||||
|
||||
void init() {
|
||||
instance = config_parser::parse<Config>(paths::get_config_path());
|
||||
}
|
||||
|
||||
AppStatus get_app_status(uint32_t app_id) {
|
||||
if (app_id == 0) {
|
||||
// 0 is a special internal value reserved for cases where we don't know app_id.
|
||||
// This is typically the case in non-koalageddon modes, hence we treat it as unlocked.
|
||||
return AppStatus::UNLOCKED;
|
||||
}
|
||||
|
||||
const auto app_id_key = std::to_string(app_id);
|
||||
|
||||
if (instance.override_app_status.contains(app_id_key)) {
|
||||
return instance.override_app_status[app_id_key];
|
||||
}
|
||||
|
||||
return instance.default_app_status;
|
||||
}
|
||||
|
||||
DlcStatus get_dlc_status(uint32_t dlc_id) {
|
||||
const auto dlc_id_key = std::to_string(dlc_id);
|
||||
|
||||
if (instance.override_dlc_status.contains(dlc_id_key)) {
|
||||
return instance.override_dlc_status[dlc_id_key];
|
||||
}
|
||||
|
||||
return instance.default_dlc_status;
|
||||
}
|
||||
|
||||
bool is_dlc_unlocked(uint32_t app_id, uint32_t dlc_id, const std::function<bool()>& original_function) {
|
||||
const auto app_status = config::get_app_status(app_id);
|
||||
const auto dlc_status = config::get_dlc_status(dlc_id);
|
||||
|
||||
const auto app_unlocked = app_status == config::AppStatus::UNLOCKED;
|
||||
const auto dlc_unlocked = dlc_status == config::DlcStatus::UNLOCKED ||
|
||||
dlc_status != config::DlcStatus::LOCKED &&
|
||||
original_function();
|
||||
|
||||
return app_unlocked && dlc_unlocked;
|
||||
}
|
||||
}
|
||||
71
src/core/config.hpp
Normal file
71
src/core/config.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <koalabox/koalabox.hpp>
|
||||
|
||||
namespace config {
|
||||
using namespace koalabox;
|
||||
|
||||
enum class AppStatus {
|
||||
LOCKED,
|
||||
UNLOCKED,
|
||||
UNDEFINED
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(AppStatus, {
|
||||
{ AppStatus::UNDEFINED, nullptr },
|
||||
{ AppStatus::LOCKED, "locked" },
|
||||
{ AppStatus::UNLOCKED, "unlocked" },
|
||||
})
|
||||
|
||||
enum class DlcStatus {
|
||||
LOCKED,
|
||||
UNLOCKED,
|
||||
ORIGINAL,
|
||||
UNDEFINED
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(DlcStatus, {
|
||||
{ DlcStatus::UNDEFINED, nullptr },
|
||||
{ DlcStatus::LOCKED, "locked" },
|
||||
{ DlcStatus::UNLOCKED, "unlocked" },
|
||||
{ DlcStatus::ORIGINAL, "original" },
|
||||
})
|
||||
|
||||
struct Config {
|
||||
uint32_t $version = 2;
|
||||
bool logging = false;
|
||||
bool unlock_family_sharing = true;
|
||||
AppStatus default_app_status = AppStatus::UNLOCKED;
|
||||
DlcStatus default_dlc_status = DlcStatus::UNLOCKED;
|
||||
Map<String, AppStatus> override_app_status;
|
||||
Map<String, DlcStatus> override_dlc_status;
|
||||
Vector<uint32_t> extra_dlc_ids;
|
||||
bool auto_inject_inventory = true;
|
||||
Vector<uint32_t> extra_inventory_items;
|
||||
// We have to use general json type here since the library doesn't support std::optional
|
||||
nlohmann::json koalageddon_config;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
|
||||
Config, $version, // NOLINT(misc-const-correctness)
|
||||
logging,
|
||||
unlock_family_sharing,
|
||||
default_app_status,
|
||||
default_dlc_status,
|
||||
override_app_status,
|
||||
override_dlc_status,
|
||||
extra_dlc_ids,
|
||||
auto_inject_inventory,
|
||||
extra_inventory_items,
|
||||
koalageddon_config
|
||||
)
|
||||
};
|
||||
|
||||
extern Config instance;
|
||||
|
||||
void init();
|
||||
|
||||
AppStatus get_app_status(uint32_t app_id);
|
||||
DlcStatus get_dlc_status(uint32_t dlc_id);
|
||||
bool is_dlc_unlocked(uint32_t app_id, uint32_t dlc_id, const std::function<bool()>& original_function);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <build_config.h>
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
#include <build_config.h>
|
||||
#include <core/cache.hpp>
|
||||
#include <core/config.hpp>
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <koalabox/dll_monitor.hpp>
|
||||
#include <koalabox/http_client.hpp>
|
||||
@@ -12,10 +13,10 @@ namespace koalageddon {
|
||||
* @return A string representing the source of the config.
|
||||
*/
|
||||
String init_koalageddon_config() {
|
||||
if (!smoke_api::config.koalageddon_config.is_null()) {
|
||||
if (!config::instance.koalageddon_config.is_null()) {
|
||||
try {
|
||||
// First try to read a local config override
|
||||
config = smoke_api::config.koalageddon_config.get<decltype(config)>();
|
||||
config = config::instance.koalageddon_config.get<decltype(config)>();
|
||||
|
||||
return "local config override";
|
||||
} catch (const Exception& ex) {
|
||||
@@ -63,7 +64,7 @@ namespace koalageddon {
|
||||
static auto init_count = 0;
|
||||
if (util::strings_are_equal(name, VSTDLIB_DLL)) {
|
||||
// VStdLib DLL handles Family Sharing functions
|
||||
if (smoke_api::config.unlock_family_sharing) {
|
||||
if (config::instance.unlock_family_sharing) {
|
||||
init_vstdlib_hooks();
|
||||
}
|
||||
init_count++;
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <build_config.h>
|
||||
#include <core/config.hpp>
|
||||
#include <core/globals.hpp>
|
||||
#include <core/paths.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <build_config.h>
|
||||
|
||||
#include <koalabox/config_parser.hpp>
|
||||
#include <koalabox/dll_monitor.hpp>
|
||||
#include <koalabox/file_logger.hpp>
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <koalabox/loader.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
#include <core/globals.hpp>
|
||||
|
||||
#ifndef _WIN64
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
#endif
|
||||
|
||||
namespace smoke_api {
|
||||
Config config = {}; // NOLINT(cert-err58-cpp)
|
||||
|
||||
HMODULE original_library = nullptr;
|
||||
|
||||
HMODULE self_module = nullptr;
|
||||
@@ -85,13 +83,13 @@ namespace smoke_api {
|
||||
|
||||
koalabox::project_name = PROJECT_NAME;
|
||||
|
||||
config = config_parser::parse<Config>(paths::get_config_path());
|
||||
config::init();
|
||||
|
||||
const auto exe_path = Path(win_util::get_module_file_name_or_throw(nullptr));
|
||||
const auto exe_name = exe_path.filename().string();
|
||||
const auto exe_bitness = util::is_x64() ? 64 : 32;
|
||||
|
||||
if (config.logging) {
|
||||
if (config::instance.logging) {
|
||||
logger = file_logger::create(paths::get_log_path());
|
||||
}
|
||||
|
||||
@@ -138,10 +136,4 @@ namespace smoke_api {
|
||||
logger->error("Shutdown error: {}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Support for app_id for koalageddon mode
|
||||
bool should_unlock(uint32_t app_id) {
|
||||
return config.unlock_all != config.override.contains(app_id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,34 +24,6 @@
|
||||
namespace smoke_api {
|
||||
using namespace koalabox;
|
||||
|
||||
struct Config {
|
||||
uint32_t $version = 2;
|
||||
bool logging = false;
|
||||
bool unlock_family_sharing = true;
|
||||
bool unlock_all = true;
|
||||
Set<uint32_t> override;
|
||||
Vector<uint32_t> dlc_ids;
|
||||
bool auto_inject_inventory = true;
|
||||
Vector<uint32_t> inventory_items;
|
||||
|
||||
// Have to use general json type here since library doesn't support std::optional
|
||||
nlohmann::json koalageddon_config;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
|
||||
Config, $version, // NOLINT(misc-const-correctness)
|
||||
logging,
|
||||
unlock_family_sharing,
|
||||
unlock_all,
|
||||
override,
|
||||
dlc_ids,
|
||||
auto_inject_inventory,
|
||||
inventory_items,
|
||||
koalageddon_config
|
||||
)
|
||||
};
|
||||
|
||||
extern Config config;
|
||||
|
||||
extern HMODULE self_module;
|
||||
|
||||
extern HMODULE original_library;
|
||||
@@ -62,6 +34,4 @@ namespace smoke_api {
|
||||
|
||||
void shutdown();
|
||||
|
||||
bool should_unlock(uint32_t app_id);
|
||||
|
||||
}
|
||||
|
||||
@@ -8,12 +8,20 @@ using namespace smoke_api;
|
||||
|
||||
// ISteamApps
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsSubscribedApp(ISteamApps*, AppId_t appID) {
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, appID);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsSubscribedApp(ISteamApps* self, AppId_t appID) {
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, appID, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamApps_BIsSubscribedApp)
|
||||
|
||||
return SteamAPI_ISteamApps_BIsSubscribedApp_o(self, appID);
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsDlcInstalled(ISteamApps*, AppId_t appID) {
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, appID);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsDlcInstalled(ISteamApps* self, AppId_t appID) {
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, appID, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(SteamAPI_ISteamApps_BIsDlcInstalled)
|
||||
|
||||
return SteamAPI_ISteamApps_BIsDlcInstalled_o(self, appID);
|
||||
});
|
||||
}
|
||||
|
||||
DLL_EXPORT(int) SteamAPI_ISteamApps_GetDLCCount(ISteamApps* self) {
|
||||
|
||||
@@ -4,11 +4,19 @@
|
||||
using namespace smoke_api;
|
||||
|
||||
VIRTUAL(bool) ISteamApps_BIsSubscribedApp(PARAMS(AppId_t appID)) { // NOLINT(misc-unused-parameters)
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, appID);
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, appID, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(ISteamApps_BIsSubscribedApp)
|
||||
|
||||
return ISteamApps_BIsSubscribedApp_o(ARGS(appID));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(bool) ISteamApps_BIsDlcInstalled(PARAMS(AppId_t appID)) { // NOLINT(misc-unused-parameters)
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, appID);
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, appID, [&]() {
|
||||
GET_ORIGINAL_FUNCTION(ISteamApps_BIsDlcInstalled)
|
||||
|
||||
return ISteamApps_BIsDlcInstalled_o(ARGS(appID));
|
||||
});
|
||||
}
|
||||
|
||||
VIRTUAL(int) ISteamApps_GetDLCCount(PARAMS()) {
|
||||
|
||||
@@ -3,118 +3,120 @@
|
||||
#include <koalabox/io.hpp>
|
||||
#include <koalabox/http_client.hpp>
|
||||
#include <core/cache.hpp>
|
||||
#include <core/config.hpp>
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
/// 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.
|
||||
constexpr auto MAX_DLC = 64;
|
||||
|
||||
// Key: App ID, Value: DLC ID
|
||||
Map<AppId_t, int> original_dlc_count_map; // NOLINT(cert-err58-cpp)
|
||||
Vector<AppId_t> cached_dlcs;
|
||||
|
||||
/**
|
||||
* @param app_id
|
||||
* @return boolean indicating if the function was able to successfully fetch DLC IDs from all sources.
|
||||
*/
|
||||
bool fetch_and_cache_dlcs(AppId_t app_id) {
|
||||
if (not app_id) {
|
||||
try {
|
||||
app_id = steam_functions::get_app_id_or_throw();
|
||||
// TODO: Check what it returns in koalageddon mode
|
||||
logger->info("Detected App ID: {}", app_id);
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Failed to get app ID: {}", ex.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto total_success = true;
|
||||
const auto app_id_str = std::to_string(app_id);
|
||||
|
||||
const auto fetch_from_steam = [&]() {
|
||||
Vector<AppId_t> dlcs;
|
||||
|
||||
try {
|
||||
const auto url = fmt::format("https://store.steampowered.com/dlc/{}/ajaxgetdlclist", app_id_str);
|
||||
const auto json = http_client::fetch_json(url);
|
||||
|
||||
if (json["success"] != 1) {
|
||||
throw util::exception("Web API responded with 'success' != 1");
|
||||
}
|
||||
|
||||
for (const auto& dlc: json["dlcs"]) {
|
||||
const auto app_id = dlc["appid"].get<String>();
|
||||
dlcs.emplace_back(std::stoi(app_id));
|
||||
}
|
||||
} catch (const Exception& e) {
|
||||
logger->error("Failed to fetch dlc list from steam api: {}", e.what());
|
||||
total_success = false;
|
||||
}
|
||||
|
||||
return dlcs;
|
||||
};
|
||||
|
||||
const auto fetch_from_github = [&]() {
|
||||
Vector<AppId_t> dlcs;
|
||||
|
||||
try {
|
||||
const String url = "https://raw.githubusercontent.com/acidicoala/public-entitlements/main/steam/v1/dlc.json";
|
||||
const auto json = http_client::fetch_json(url);
|
||||
|
||||
if (json.contains(app_id_str)) {
|
||||
dlcs = json[app_id_str].get<decltype(dlcs)>();
|
||||
}
|
||||
} catch (const Exception& e) {
|
||||
logger->error("Failed to fetch extra dlc list from github api: {}", e.what());
|
||||
total_success = false;
|
||||
}
|
||||
|
||||
return dlcs;
|
||||
};
|
||||
|
||||
const auto steam_dlcs = fetch_from_steam();
|
||||
const auto github_dlcs = fetch_from_github();
|
||||
|
||||
// Any of the sources might fail, so we try to get optimal result
|
||||
// by combining results from all the sources into a single set.
|
||||
Set<AppId_t> combined_dlcs;
|
||||
combined_dlcs.insert(steam_dlcs.begin(), steam_dlcs.end());
|
||||
combined_dlcs.insert(github_dlcs.begin(), github_dlcs.end());
|
||||
// There is no need to insert cached entries if both steam and GitHub requests were successful.
|
||||
if (!total_success) {
|
||||
const auto cache_dlcs = cache::get_dlc_ids(app_id);
|
||||
combined_dlcs.insert(cached_dlcs.begin(), cached_dlcs.end());
|
||||
}
|
||||
|
||||
// We then transfer that set into a list because we need DLCs to be accessible via index.
|
||||
cached_dlcs.clear();
|
||||
cached_dlcs.insert(cached_dlcs.begin(), combined_dlcs.begin(), combined_dlcs.end());
|
||||
|
||||
cache::save_dlc_ids(app_id, cached_dlcs);
|
||||
|
||||
return total_success;
|
||||
}
|
||||
|
||||
String get_app_id_log(const AppId_t app_id) {
|
||||
return app_id ? fmt::format("App ID: {}, ", app_id) : "";
|
||||
}
|
||||
|
||||
namespace steam_apps {
|
||||
using namespace smoke_api;
|
||||
|
||||
bool IsDlcUnlocked(const String& function_name, AppId_t app_id, AppId_t dlc_id) {
|
||||
/// 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.
|
||||
constexpr auto MAX_DLC = 64;
|
||||
|
||||
// Key: App ID, Value: DLC ID
|
||||
Map<AppId_t, int> original_dlc_count_map; // NOLINT(cert-err58-cpp)
|
||||
Vector<AppId_t> cached_dlcs;
|
||||
|
||||
/**
|
||||
* @param app_id
|
||||
* @return boolean indicating if the function was able to successfully fetch DLC IDs from all sources.
|
||||
*/
|
||||
bool fetch_and_cache_dlcs(AppId_t app_id) {
|
||||
if (not app_id) {
|
||||
try {
|
||||
app_id = steam_functions::get_app_id_or_throw();
|
||||
// TODO: Check what it returns in koalageddon mode
|
||||
logger->info("Detected App ID: {}", app_id);
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Failed to get app ID: {}", ex.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto total_success = true;
|
||||
const auto app_id_str = std::to_string(app_id);
|
||||
|
||||
const auto fetch_from_steam = [&]() {
|
||||
Vector<AppId_t> dlcs;
|
||||
|
||||
try {
|
||||
// TODO: Refactor into api namespace
|
||||
const auto url = fmt::format("https://store.steampowered.com/dlc/{}/ajaxgetdlclist", app_id_str);
|
||||
const auto json = http_client::fetch_json(url);
|
||||
|
||||
if (json["success"] != 1) {
|
||||
throw util::exception("Web API responded with 'success' != 1");
|
||||
}
|
||||
|
||||
for (const auto& dlc: json["dlcs"]) {
|
||||
const auto app_id = dlc["appid"].get<String>();
|
||||
dlcs.emplace_back(std::stoi(app_id));
|
||||
}
|
||||
} catch (const Exception& e) {
|
||||
logger->error("Failed to fetch dlc list from steam api: {}", e.what());
|
||||
total_success = false;
|
||||
}
|
||||
|
||||
return dlcs;
|
||||
};
|
||||
|
||||
const auto fetch_from_github = [&]() {
|
||||
Vector<AppId_t> dlcs;
|
||||
|
||||
try {
|
||||
const String url = "https://raw.githubusercontent.com/acidicoala/public-entitlements/main/steam/v1/dlc.json";
|
||||
const auto json = http_client::fetch_json(url);
|
||||
|
||||
if (json.contains(app_id_str)) {
|
||||
dlcs = json[app_id_str].get<decltype(dlcs)>();
|
||||
}
|
||||
} catch (const Exception& e) {
|
||||
logger->error("Failed to fetch extra dlc list from github api: {}", e.what());
|
||||
total_success = false;
|
||||
}
|
||||
|
||||
return dlcs;
|
||||
};
|
||||
|
||||
const auto steam_dlcs = fetch_from_steam();
|
||||
const auto github_dlcs = fetch_from_github();
|
||||
|
||||
// Any of the sources might fail, so we try to get optimal result
|
||||
// by combining results from all the sources into a single set.
|
||||
Set<AppId_t> combined_dlcs;
|
||||
combined_dlcs.insert(steam_dlcs.begin(), steam_dlcs.end());
|
||||
combined_dlcs.insert(github_dlcs.begin(), github_dlcs.end());
|
||||
// There is no need to insert cached entries if both steam and GitHub requests were successful.
|
||||
if (!total_success) {
|
||||
const auto cache_dlcs = cache::get_dlc_ids(app_id);
|
||||
combined_dlcs.insert(cached_dlcs.begin(), cached_dlcs.end());
|
||||
}
|
||||
|
||||
// We then transfer that set into a list because we need DLCs to be accessible via index.
|
||||
cached_dlcs.clear();
|
||||
cached_dlcs.insert(cached_dlcs.begin(), combined_dlcs.begin(), combined_dlcs.end());
|
||||
|
||||
cache::save_dlc_ids(app_id, cached_dlcs);
|
||||
|
||||
return total_success;
|
||||
}
|
||||
|
||||
String get_app_id_log(const AppId_t app_id) {
|
||||
return app_id ? fmt::format("App ID: {}, ", app_id) : "";
|
||||
}
|
||||
|
||||
bool IsDlcUnlocked(
|
||||
const String& function_name,
|
||||
AppId_t app_id, AppId_t dlc_id,
|
||||
const std::function<bool()>& original_function
|
||||
) {
|
||||
try {
|
||||
const auto app_id_unlocked = not app_id or should_unlock(app_id); // true if app_id == 0
|
||||
const auto dlc_id_unlocked = should_unlock(dlc_id);
|
||||
const auto unlocked = config::is_dlc_unlocked(app_id, dlc_id, original_function);
|
||||
|
||||
const auto installed = app_id_unlocked and dlc_id_unlocked;
|
||||
logger->info("{} -> {}DLC ID: {}, Unlocked: {}", function_name, get_app_id_log(app_id), dlc_id, unlocked);
|
||||
|
||||
logger->info("{} -> {}DLC ID: {}, Unlocked: {}", function_name, get_app_id_log(app_id), dlc_id, installed);
|
||||
|
||||
return installed;
|
||||
return unlocked;
|
||||
} catch (const Exception& e) {
|
||||
logger->error("{} -> Uncaught exception: {}", function_name, e.what());
|
||||
return false;
|
||||
@@ -142,7 +144,7 @@ namespace steam_apps {
|
||||
|
||||
// We need to fetch DLC IDs from all possible sources at this point
|
||||
|
||||
const auto injected_count = static_cast<int>(config.dlc_ids.size());
|
||||
const auto injected_count = static_cast<int>(config::instance.extra_dlc_ids.size());
|
||||
logger->debug("{} -> Injected DLC count: {}", function_name, injected_count);
|
||||
|
||||
// Maintain a list of app_ids for which we have already fetched and cached DLC IDs
|
||||
@@ -191,7 +193,7 @@ namespace steam_apps {
|
||||
|
||||
// Fill the output pointers
|
||||
*pDlcId = dlc_id;
|
||||
*pbAvailable = should_unlock(dlc_id);
|
||||
*pbAvailable = config::is_dlc_unlocked(app_id, dlc_id, []() { return true; });
|
||||
|
||||
auto name = fmt::format("DLC #{} with ID: {} ", iDLC, dlc_id);
|
||||
name = name.substr(0, cchNameBufferSize);
|
||||
@@ -218,7 +220,7 @@ namespace steam_apps {
|
||||
const auto success = original_function();
|
||||
|
||||
if (success) {
|
||||
*pbAvailable = should_unlock(*pDlcId);
|
||||
*pbAvailable = config::is_dlc_unlocked(app_id, *pDlcId, [&]() { return *pbAvailable; });
|
||||
print_dlc_info("original");
|
||||
} else {
|
||||
logger->warn("{} -> original call failed for index: {}", function_name, iDLC);
|
||||
@@ -236,9 +238,9 @@ namespace steam_apps {
|
||||
logger->warn("{} -> Out of bounds DLC index: {}", function_name, iDLC);
|
||||
}
|
||||
|
||||
const int local_dlc_count = static_cast<int>(config.dlc_ids.size());
|
||||
const int local_dlc_count = static_cast<int>(config::instance.extra_dlc_ids.size());
|
||||
if (iDLC < local_dlc_count) {
|
||||
return inject_dlc("local config", config.dlc_ids, iDLC);
|
||||
return inject_dlc("local config", config::instance.extra_dlc_ids, iDLC);
|
||||
}
|
||||
|
||||
const auto adjusted_index = iDLC - local_dlc_count;
|
||||
|
||||
@@ -3,9 +3,18 @@
|
||||
namespace steam_apps {
|
||||
using namespace koalabox;
|
||||
|
||||
bool IsDlcUnlocked(const String& function_name, AppId_t app_id, AppId_t dlc_id);
|
||||
bool IsDlcUnlocked(
|
||||
const String& function_name,
|
||||
AppId_t app_id,
|
||||
AppId_t dlc_id,
|
||||
const std::function<bool()>& original_function
|
||||
);
|
||||
|
||||
int GetDLCCount(const String& function_name, AppId_t app_id, const std::function<int()>& original_function);
|
||||
int GetDLCCount(
|
||||
const String& function_name,
|
||||
AppId_t app_id,
|
||||
const std::function<int()>& original_function
|
||||
);
|
||||
|
||||
bool GetDLCDataByIndex(
|
||||
const String& dlc_id,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_inventory.hpp>
|
||||
#include <core/config.hpp>
|
||||
|
||||
namespace steam_inventory {
|
||||
|
||||
@@ -51,11 +51,11 @@ namespace steam_inventory {
|
||||
);
|
||||
|
||||
static uint32_t original_count = 0;
|
||||
const auto injected_count = smoke_api::config.inventory_items.size();
|
||||
const auto injected_count = config::instance.extra_inventory_items.size();
|
||||
|
||||
// Automatically get inventory items from steam
|
||||
static Vector<SteamItemDef_t> auto_inventory_items;
|
||||
if (smoke_api::config.auto_inject_inventory) {
|
||||
if (config::instance.auto_inject_inventory) {
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [&]() {
|
||||
uint32_t count = 0;
|
||||
@@ -102,7 +102,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 = smoke_api::config.inventory_items[i];
|
||||
const auto item_def_id = config::instance.extra_inventory_items[i];
|
||||
|
||||
item = new_item(item_def_id);
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <koalabox/koalabox.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
namespace steam_inventory {
|
||||
using namespace koalabox;
|
||||
|
||||
EResult GetResultStatus(
|
||||
const String& function_name,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <steam_impl/steam_user.hpp>
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <core/config.hpp>
|
||||
|
||||
namespace steam_user {
|
||||
|
||||
@@ -15,7 +15,9 @@ namespace steam_user {
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto has_license = smoke_api::should_unlock(appID);
|
||||
const auto has_license = config::is_dlc_unlocked(0, appID, [&]() {
|
||||
return result == k_EUserHasLicenseResultHasLicense;
|
||||
});
|
||||
|
||||
logger->info("{} -> App ID: {}, HasLicense: {}", function_name, appID, has_license);
|
||||
|
||||
|
||||
@@ -22,129 +22,4 @@ enum EUserHasLicenseForAppResult {
|
||||
k_EUserHasLicenseResultNoAuth = 2, // User has not been authenticated
|
||||
};
|
||||
|
||||
enum EResult {
|
||||
k_EResultNone = 0, // no result
|
||||
k_EResultOK = 1, // success
|
||||
k_EResultFail = 2, // generic failure
|
||||
k_EResultNoConnection = 3, // no/failed network connection
|
||||
// k_EResultNoConnectionRetry = 4, // OBSOLETE - removed
|
||||
k_EResultInvalidPassword = 5, // password/ticket is invalid
|
||||
k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere
|
||||
k_EResultInvalidProtocolVer = 7, // protocol version is incorrect
|
||||
k_EResultInvalidParam = 8, // a parameter is incorrect
|
||||
k_EResultFileNotFound = 9, // file was not found
|
||||
k_EResultBusy = 10, // called method busy - action not taken
|
||||
k_EResultInvalidState = 11, // called object was in an invalid state
|
||||
k_EResultInvalidName = 12, // name is invalid
|
||||
k_EResultInvalidEmail = 13, // email is invalid
|
||||
k_EResultDuplicateName = 14, // name is not unique
|
||||
k_EResultAccessDenied = 15, // access is denied
|
||||
k_EResultTimeout = 16, // operation timed out
|
||||
k_EResultBanned = 17, // VAC2 banned
|
||||
k_EResultAccountNotFound = 18, // account not found
|
||||
k_EResultInvalidSteamID = 19, // steamID is invalid
|
||||
k_EResultServiceUnavailable = 20, // The requested service is currently unavailable
|
||||
k_EResultNotLoggedOn = 21, // The user is not logged on
|
||||
k_EResultPending = 22, // Request is pending (may be in process, or waiting on third party)
|
||||
k_EResultEncryptionFailure = 23, // Encryption or Decryption failed
|
||||
k_EResultInsufficientPrivilege = 24, // Insufficient privilege
|
||||
k_EResultLimitExceeded = 25, // Too much of a good thing
|
||||
k_EResultRevoked = 26, // Access has been revoked (used for revoked guest passes)
|
||||
k_EResultExpired = 27, // License/Guest pass the user is trying to access is expired
|
||||
k_EResultAlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again
|
||||
k_EResultDuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time
|
||||
k_EResultAlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user
|
||||
k_EResultIPNotFound = 31, // IP address not found
|
||||
k_EResultPersistFailed = 32, // failed to write change to the data store
|
||||
k_EResultLockingFailed = 33, // failed to acquire access lock for this operation
|
||||
k_EResultLogonSessionReplaced = 34,
|
||||
k_EResultConnectFailed = 35,
|
||||
k_EResultHandshakeFailed = 36,
|
||||
k_EResultIOFailure = 37,
|
||||
k_EResultRemoteDisconnect = 38,
|
||||
k_EResultShoppingCartNotFound = 39, // failed to find the shopping cart requested
|
||||
k_EResultBlocked = 40, // a user didn't allow it
|
||||
k_EResultIgnored = 41, // target is ignoring sender
|
||||
k_EResultNoMatch = 42, // nothing matching the request found
|
||||
k_EResultAccountDisabled = 43,
|
||||
k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now
|
||||
k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available
|
||||
k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin
|
||||
k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol.
|
||||
k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another.
|
||||
k_EResultPasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed.
|
||||
k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait
|
||||
k_EResultSuspended = 51, // Long running operation (content download) suspended/paused
|
||||
k_EResultCancelled = 52, // Operation canceled (typically by user: content download)
|
||||
k_EResultDataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable
|
||||
k_EResultDiskFull = 54, // Operation canceled - not enough disk space.
|
||||
k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed
|
||||
k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side
|
||||
k_EResultExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account
|
||||
k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid
|
||||
k_EResultExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first
|
||||
k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files
|
||||
k_EResultIllegalPassword = 61, // The requested new password is not legal
|
||||
k_EResultSameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer )
|
||||
k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure
|
||||
k_EResultCannotUseOldPassword = 64, // The requested new password is not legal
|
||||
k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid
|
||||
k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent
|
||||
k_EResultHardwareNotCapableOfIPT = 67, //
|
||||
k_EResultIPTInitError = 68, //
|
||||
k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user
|
||||
k_EResultFacebookQueryError = 70, // Facebook query returned an error
|
||||
k_EResultExpiredLoginAuthCode = 71, // account login denied due to auth code expired
|
||||
k_EResultIPLoginRestrictionFailed = 72,
|
||||
k_EResultAccountLockedDown = 73,
|
||||
k_EResultAccountLogonDeniedVerifiedEmailRequired = 74,
|
||||
k_EResultNoMatchingURL = 75,
|
||||
k_EResultBadResponse = 76, // parse failure, missing field, etc.
|
||||
k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password
|
||||
k_EResultValueOutOfRange = 78, // the value entered is outside the acceptable range
|
||||
k_EResultUnexpectedError = 79, // something happened that we didn't expect to ever happen
|
||||
k_EResultDisabled = 80, // The requested service has been configured to be unavailable
|
||||
k_EResultInvalidCEGSubmission = 81, // The set of files submitted to the CEG server are not valid !
|
||||
k_EResultRestrictedDevice = 82, // The device being used is not allowed to perform this action
|
||||
k_EResultRegionLocked = 83, // The action could not be complete because it is region restricted
|
||||
k_EResultRateLimitExceeded = 84, // Temporary rate limit exceeded, try again later, different from k_EResultLimitExceeded which may be permanent
|
||||
k_EResultAccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to login
|
||||
k_EResultItemDeleted = 86, // The thing we're trying to access has been deleted
|
||||
k_EResultAccountLoginDeniedThrottle = 87, // login attempt failed, try to throttle response to possible attacker
|
||||
k_EResultTwoFactorCodeMismatch = 88, // two factor code mismatch
|
||||
k_EResultTwoFactorActivationCodeMismatch = 89, // activation code for two-factor didn't match
|
||||
k_EResultAccountAssociatedToMultiplePartners = 90, // account has been associated with multiple partners
|
||||
k_EResultNotModified = 91, // data not modified
|
||||
k_EResultNoMobileDevice = 92, // the account does not have a mobile device associated with it
|
||||
k_EResultTimeNotSynced = 93, // the time presented is out of range or tolerance
|
||||
k_EResultSmsCodeFailed = 94, // SMS code failure (no match, none pending, etc.)
|
||||
k_EResultAccountLimitExceeded = 95, // Too many accounts access this resource
|
||||
k_EResultAccountActivityLimitExceeded = 96, // Too many changes to this account
|
||||
k_EResultPhoneActivityLimitExceeded = 97, // Too many changes to this phone
|
||||
k_EResultRefundToWallet = 98, // Cannot refund to payment method, must use wallet
|
||||
k_EResultEmailSendFailure = 99, // Cannot send an email
|
||||
k_EResultNotSettled = 100, // Can't perform operation till payment has settled
|
||||
k_EResultNeedCaptcha = 101, // Needs to provide a valid captcha
|
||||
k_EResultGSLTDenied = 102, // a game server login token owned by this token's owner has been banned
|
||||
k_EResultGSOwnerDenied = 103, // game server owner is denied for other reason (account lock, community ban, vac ban, missing phone)
|
||||
k_EResultInvalidItemType = 104, // the type of thing we were requested to act on is invalid
|
||||
k_EResultIPBanned = 105, // the ip address has been banned from taking this action
|
||||
k_EResultGSLTExpired = 106, // this token has expired from disuse; can be reset for use
|
||||
k_EResultInsufficientFunds = 107, // user doesn't have enough wallet funds to complete the action
|
||||
k_EResultTooManyPending = 108, // There are too many of this thing pending already
|
||||
k_EResultNoSiteLicensesFound = 109, // No site licenses found
|
||||
k_EResultWGNetworkSendExceeded = 110, // the WG couldn't send a response because we exceeded max network send size
|
||||
k_EResultAccountNotFriends = 111, // the user is not mutually friends
|
||||
k_EResultLimitedUserAccount = 112, // the user is limited
|
||||
k_EResultCantRemoveItem = 113, // item can't be removed
|
||||
k_EResultAccountDeleted = 114, // account has been deleted
|
||||
k_EResultExistingUserCancelledLicense = 115, // A license for this already exists, but cancelled
|
||||
k_EResultCommunityCooldown = 116, // access is denied because of a community cooldown (probably from support profile data resets)
|
||||
k_EResultNoLauncherSpecified = 117, // No launcher was specified, but a launcher was needed to choose correct realm for operation.
|
||||
k_EResultMustAgreeToSSA = 118, // User must agree to china SSA or global SSA before login
|
||||
k_EResultLauncherMigrated = 119, // The specified launcher type is no longer supported; the user should be directed elsewhere
|
||||
k_EResultSteamRealmMismatch = 120, // The user's realm does not match the realm of the requested resource
|
||||
k_EResultInvalidSignature = 121, // signature check did not match
|
||||
k_EResultParseFailure = 122, // Failed to parse input
|
||||
k_EResultNoVerifiedPhone = 123, // account does not have a verified phone number
|
||||
};
|
||||
typedef uint32_t EResult;
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
VIRTUAL(bool) IClientAppManager_IsAppDlcInstalled(
|
||||
PARAMS( // NOLINT(misc-unused-parameters)
|
||||
AppId_t app_id,
|
||||
AppId_t dlc_id
|
||||
)
|
||||
) {
|
||||
return steam_apps::IsDlcUnlocked(__func__, app_id, dlc_id);
|
||||
VIRTUAL(bool) IClientAppManager_IsAppDlcInstalled(PARAMS(AppId_t app_id, AppId_t dlc_id)) {
|
||||
return steam_apps::IsDlcUnlocked(__func__, app_id, dlc_id, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(IClientAppManager_IsAppDlcInstalled)
|
||||
|
||||
return IClientAppManager_IsAppDlcInstalled_o(ARGS(app_id, dlc_id));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
VIRTUAL(bool) IClientUser_BIsSubscribedApp(PARAMS(AppId_t app_id)) { // NOLINT(misc-unused-parameters)
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, app_id);
|
||||
VIRTUAL(bool) IClientUser_BIsSubscribedApp(PARAMS(AppId_t app_id)) {
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, app_id, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(IClientUser_BIsSubscribedApp)
|
||||
|
||||
return IClientUser_BIsSubscribedApp_o(ARGS(app_id));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user