|
|
@ -2,43 +2,32 @@
|
|
|
|
#include <steam_impl/steam_apps.hpp>
|
|
|
|
#include <steam_impl/steam_apps.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
#include <koalabox/io.hpp>
|
|
|
|
#include <koalabox/io.hpp>
|
|
|
|
|
|
|
|
#include <koalabox/http_client.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
#include <cpr/cpr.h>
|
|
|
|
#include <cpr/cpr.h>
|
|
|
|
|
|
|
|
|
|
|
|
using namespace smoke_api;
|
|
|
|
using namespace smoke_api;
|
|
|
|
|
|
|
|
|
|
|
|
constexpr auto max_dlc = 64;
|
|
|
|
/// 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/cache.
|
|
|
|
|
|
|
|
constexpr auto MAX_DLC = 64;
|
|
|
|
|
|
|
|
|
|
|
|
Vector<AppId_t> cached_dlcs;
|
|
|
|
Vector<AppId_t> cached_dlcs;
|
|
|
|
int original_dlc_count = 0;
|
|
|
|
// Key: App ID, Value: DLC ID
|
|
|
|
|
|
|
|
Map<AppId_t, int> original_dlc_count_map; // NOLINT(cert-err58-cpp)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// FIXME: Path in koalageddon mode
|
|
|
|
Path get_cache_path() {
|
|
|
|
Path get_cache_path() {
|
|
|
|
static const auto path = self_directory / "SmokeAPI.cache.json";
|
|
|
|
static const auto path = self_directory / "SmokeAPI.cache.json";
|
|
|
|
return path;
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void read_from_cache(const String& app_id_str) {
|
|
|
|
void save_cache_to_disk(const String& app_id_str) {
|
|
|
|
try {
|
|
|
|
|
|
|
|
const auto text = io::read_file(get_cache_path());
|
|
|
|
|
|
|
|
if (text.empty()) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto json = nlohmann::json::parse(text);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cached_dlcs = json[app_id_str]["dlc"].get<decltype(cached_dlcs)>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger->debug("Read {} DLCs from cache", cached_dlcs.size());
|
|
|
|
|
|
|
|
} catch (const Exception& ex) {
|
|
|
|
|
|
|
|
logger->error("Error reading DLCs from cache: {}", ex.what());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void save_to_cache(const String& app_id_str) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
logger->debug("Saving {} DLCs to cache", cached_dlcs.size());
|
|
|
|
logger->debug("Saving {} DLCs to cache", cached_dlcs.size());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Combine this with existing cache
|
|
|
|
const nlohmann::json json = {
|
|
|
|
const nlohmann::json json = {
|
|
|
|
{app_id_str, {
|
|
|
|
{app_id_str, {
|
|
|
|
{"dlc", cached_dlcs}
|
|
|
|
{"dlc", cached_dlcs}
|
|
|
@ -51,85 +40,107 @@ void save_to_cache(const String& app_id_str) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void fetch_and_cache_dlcs(AppId_t app_id) {
|
|
|
|
/**
|
|
|
|
|
|
|
|
* @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) {
|
|
|
|
if (not app_id) {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
app_id = steam_functions::get_app_id_or_throw();
|
|
|
|
app_id = steam_functions::get_app_id_or_throw();
|
|
|
|
|
|
|
|
// TODO: Check what it returns in koalageddon mode
|
|
|
|
logger->info("Detected App ID: {}", app_id);
|
|
|
|
logger->info("Detected App ID: {}", app_id);
|
|
|
|
} catch (const Exception& ex) {
|
|
|
|
} catch (const Exception& ex) {
|
|
|
|
logger->error("Failed to get app ID: {}", ex.what());
|
|
|
|
logger->error("Failed to get app ID: {}", ex.what());
|
|
|
|
return;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto total_success = true;
|
|
|
|
const auto app_id_str = std::to_string(app_id);
|
|
|
|
const auto app_id_str = std::to_string(app_id);
|
|
|
|
|
|
|
|
|
|
|
|
const auto fetch_from_steam = [&]() {
|
|
|
|
const auto read_cache_from_disk = [&]() {
|
|
|
|
const auto url = fmt::format("https://store.steampowered.com/dlc/{}/ajaxgetdlclist", app_id_str);
|
|
|
|
Vector<AppId_t> dlcs;
|
|
|
|
const auto res = cpr::Get(cpr::Url{url});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (res.status_code != cpr::status::HTTP_OK) {
|
|
|
|
|
|
|
|
throw util::exception(
|
|
|
|
|
|
|
|
"Steam Web API didn't responded with HTTP_OK result. Code: {}, Error: {},\n"
|
|
|
|
|
|
|
|
"Headers:\n{}\nBody:\n{}",
|
|
|
|
|
|
|
|
res.status_code, (int) res.error.code, res.raw_header, res.text
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto json = nlohmann::json::parse(res.text);
|
|
|
|
try {
|
|
|
|
|
|
|
|
const auto text = io::read_file(get_cache_path());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (text.empty()) {
|
|
|
|
|
|
|
|
return dlcs;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto json = nlohmann::json::parse(text);
|
|
|
|
|
|
|
|
|
|
|
|
if (json["success"] != 1) {
|
|
|
|
dlcs = json[app_id_str]["dlc"].get<decltype(cached_dlcs)>();
|
|
|
|
throw util::exception("Web API responded with 'success': 1.");
|
|
|
|
|
|
|
|
|
|
|
|
logger->debug("Read {} DLCs from cache", dlcs.size());
|
|
|
|
|
|
|
|
} catch (const Exception& ex) {
|
|
|
|
|
|
|
|
logger->error("Error reading DLCs from cache: {}", ex.what());
|
|
|
|
|
|
|
|
total_success = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return dlcs;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto fetch_from_steam = [&]() {
|
|
|
|
Vector<AppId_t> dlcs;
|
|
|
|
Vector<AppId_t> dlcs;
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& dlc: json["dlcs"]) {
|
|
|
|
try {
|
|
|
|
const auto app_id = dlc["appid"].get<String>();
|
|
|
|
const auto url = fmt::format("https://store.steampowered.com/dlc/{}/ajaxgetdlclist", app_id_str);
|
|
|
|
dlcs.emplace_back(std::stoi(app_id));
|
|
|
|
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;
|
|
|
|
return dlcs;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const auto fetch_from_github = [&]() {
|
|
|
|
const auto fetch_from_github = [&]() {
|
|
|
|
const String url = "https://raw.githubusercontent.com/acidicoala/public-entitlements/main/steam/v1/dlc.json";
|
|
|
|
Vector<AppId_t> dlcs;
|
|
|
|
const auto res = cpr::Get(cpr::Url{url});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (res.status_code != cpr::status::HTTP_OK) {
|
|
|
|
|
|
|
|
throw util::exception(
|
|
|
|
|
|
|
|
"Github Web API didn't responded with HTTP_OK result. Code: {}, Error: {},\n"
|
|
|
|
|
|
|
|
"Headers:\n{}\nBody:\n{}",
|
|
|
|
|
|
|
|
res.status_code, (int) res.error.code, res.raw_header, res.text
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto json = nlohmann::json::parse(res.text);
|
|
|
|
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)) {
|
|
|
|
if (json.contains(app_id_str)) {
|
|
|
|
return json[app_id_str].get<decltype(cached_dlcs)>();
|
|
|
|
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 Vector<AppId_t>{};
|
|
|
|
return dlcs;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
const auto cache_dlcs = read_cache_from_disk();
|
|
|
|
read_from_cache(app_id_str);
|
|
|
|
const auto steam_dlcs = fetch_from_steam();
|
|
|
|
|
|
|
|
const auto github_dlcs = fetch_from_github();
|
|
|
|
|
|
|
|
|
|
|
|
auto list1 = fetch_from_steam();
|
|
|
|
// Any of the sources might fail, so we try to get optimal result
|
|
|
|
auto list2 = fetch_from_github();
|
|
|
|
// by combining results from all the sources into a single set.
|
|
|
|
list1.insert(list1.end(), list2.begin(), list2.end());
|
|
|
|
Set<AppId_t> combined_dlcs;
|
|
|
|
Set<AppId_t> fetched_dlcs(list1.begin(), list1.end());
|
|
|
|
combined_dlcs.insert(cached_dlcs.begin(), cached_dlcs.end());
|
|
|
|
|
|
|
|
combined_dlcs.insert(steam_dlcs.begin(), steam_dlcs.end());
|
|
|
|
|
|
|
|
combined_dlcs.insert(github_dlcs.begin(), github_dlcs.end());
|
|
|
|
|
|
|
|
|
|
|
|
if (fetched_dlcs.size() > cached_dlcs.size()) {
|
|
|
|
// We then transfer that set into a list because we need DLCs to be accessible via index.
|
|
|
|
cached_dlcs = Vector<AppId_t>(fetched_dlcs.begin(), fetched_dlcs.end());
|
|
|
|
cached_dlcs.clear();
|
|
|
|
}
|
|
|
|
cached_dlcs.insert(cached_dlcs.begin(), combined_dlcs.begin(), combined_dlcs.end());
|
|
|
|
|
|
|
|
|
|
|
|
save_to_cache(app_id_str);
|
|
|
|
save_cache_to_disk(app_id_str);
|
|
|
|
} catch (const Exception& ex) {
|
|
|
|
|
|
|
|
logger->error("Failed to fetch DLC: {}", ex.what());
|
|
|
|
return total_success;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
String get_app_id_log(const AppId_t app_id) {
|
|
|
|
String get_app_id_log(const AppId_t app_id) {
|
|
|
@ -139,21 +150,23 @@ String get_app_id_log(const AppId_t app_id) {
|
|
|
|
namespace steam_apps {
|
|
|
|
namespace steam_apps {
|
|
|
|
|
|
|
|
|
|
|
|
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 auto app_id_unlocked = not app_id or should_unlock(app_id); // true if app_id == 0
|
|
|
|
try {
|
|
|
|
const auto dlc_id_unlocked = should_unlock(dlc_id);
|
|
|
|
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 installed = app_id_unlocked and dlc_id_unlocked;
|
|
|
|
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, installed);
|
|
|
|
logger->info("{} -> {}DLC ID: {}, Unlocked: {}", function_name, get_app_id_log(app_id), dlc_id, installed);
|
|
|
|
|
|
|
|
|
|
|
|
return installed;
|
|
|
|
return installed;
|
|
|
|
|
|
|
|
} catch (const Exception& e) {
|
|
|
|
|
|
|
|
logger->error("{} -> Uncaught exception: {}", function_name, e.what());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// std::mutex section;
|
|
|
|
|
|
|
|
int GetDLCCount(const String& function_name, const AppId_t app_id, const std::function<int()>& original_function) {
|
|
|
|
int GetDLCCount(const String& function_name, const AppId_t app_id, const std::function<int()>& original_function) {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
// std::lock_guard<std::mutex> guard(section);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto total_count = [&](int count) {
|
|
|
|
const auto total_count = [&](int count) {
|
|
|
|
logger->info("{} -> Responding with DLC count: {}", function_name, count);
|
|
|
|
logger->info("{} -> Responding with DLC count: {}", function_name, count);
|
|
|
|
return count;
|
|
|
|
return count;
|
|
|
@ -163,31 +176,38 @@ namespace steam_apps {
|
|
|
|
logger->debug("{} -> App ID: {}", function_name, app_id);
|
|
|
|
logger->debug("{} -> App ID: {}", function_name, app_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
original_dlc_count = original_function();
|
|
|
|
const auto original_count = original_function();
|
|
|
|
logger->debug("{} -> Original DLC count: {}", function_name, original_dlc_count);
|
|
|
|
original_dlc_count_map[app_id] = original_count;
|
|
|
|
|
|
|
|
logger->debug("{} -> Original DLC count: {}", function_name, original_count);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (original_count < MAX_DLC) {
|
|
|
|
|
|
|
|
return total_count(original_count);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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.dlc_ids.size());
|
|
|
|
logger->debug("{} -> Injected DLC count: {}", function_name, injected_count);
|
|
|
|
logger->debug("{} -> Injected DLC count: {}", function_name, injected_count);
|
|
|
|
|
|
|
|
|
|
|
|
if (original_dlc_count < max_dlc) {
|
|
|
|
// Maintain a list of app_ids for which we have already fetched and cached DLC IDs
|
|
|
|
return total_count(original_dlc_count + injected_count);
|
|
|
|
static Set<AppId_t> cached_apps;
|
|
|
|
}
|
|
|
|
if (!cached_apps.contains(app_id)) {
|
|
|
|
|
|
|
|
static std::mutex mutex;
|
|
|
|
|
|
|
|
const std::lock_guard<std::mutex> guard(mutex);
|
|
|
|
|
|
|
|
|
|
|
|
// Steamworks may max out this value at 64, depending on how much unowned DLCs the user has.
|
|
|
|
logger->debug("Game has {} or more DLCs. Fetching DLCs from a web API.", MAX_DLC);
|
|
|
|
// Despite this limit, some games with more than 64 DLCs still keep using this method.
|
|
|
|
|
|
|
|
// This means we have to fetch full list of IDs from web api.
|
|
|
|
if (fetch_and_cache_dlcs(app_id)) {
|
|
|
|
static std::once_flag flag;
|
|
|
|
cached_apps.insert(app_id);
|
|
|
|
std::call_once(flag, [&]() {
|
|
|
|
}
|
|
|
|
logger->debug("Game has {} or more DLCs. Fetching DLCs from a web API.", max_dlc);
|
|
|
|
}
|
|
|
|
fetch_and_cache_dlcs(app_id);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto fetched_count = static_cast<int>(cached_dlcs.size());
|
|
|
|
const auto cached_count = static_cast<int>(cached_dlcs.size());
|
|
|
|
logger->debug("{} -> Fetched/cached DLC count: {}", function_name, fetched_count);
|
|
|
|
logger->debug("{} -> Cached DLC count: {}", function_name, cached_count);
|
|
|
|
|
|
|
|
|
|
|
|
return total_count(fetched_count + injected_count);
|
|
|
|
return total_count(injected_count + cached_count);
|
|
|
|
} catch (const Exception& ex) {
|
|
|
|
} catch (const Exception& e) {
|
|
|
|
logger->error("{} -> {}", function_name, ex.what());
|
|
|
|
logger->error("{} -> Uncaught exception: {}", function_name, e.what());
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -202,76 +222,84 @@ namespace steam_apps {
|
|
|
|
int cchNameBufferSize,
|
|
|
|
int cchNameBufferSize,
|
|
|
|
const std::function<bool()>& original_function
|
|
|
|
const std::function<bool()>& original_function
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
const auto print_dlc_info = [&](const String& tag) {
|
|
|
|
try {
|
|
|
|
logger->info(
|
|
|
|
const auto print_dlc_info = [&](const String& tag) {
|
|
|
|
"{} -> [{}] {}index: {}, DLC ID: {}, available: {}, name: '{}'",
|
|
|
|
logger->info(
|
|
|
|
function_name, tag, get_app_id_log(app_id), iDLC, *pDlcId, *pbAvailable, pchName
|
|
|
|
"{} -> [{}] {}index: {}, DLC ID: {}, available: {}, name: '{}'",
|
|
|
|
);
|
|
|
|
function_name, tag, get_app_id_log(app_id), iDLC, *pDlcId, *pbAvailable, pchName
|
|
|
|
};
|
|
|
|
);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const auto fill_dlc_info = [&](const AppId_t id) {
|
|
|
|
const auto inject_dlc = [&](const String& tag, const Vector<AppId_t>& dlc_ids, const int index) {
|
|
|
|
*pDlcId = id;
|
|
|
|
const auto dlc_id = dlc_ids[index];
|
|
|
|
*pbAvailable = should_unlock(id);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto name = fmt::format("DLC #{} with ID: {} ", iDLC, id);
|
|
|
|
// Fill the output pointers
|
|
|
|
name = name.substr(0, cchNameBufferSize);
|
|
|
|
*pDlcId = dlc_id;
|
|
|
|
*name.rbegin() = '\0';
|
|
|
|
*pbAvailable = should_unlock(dlc_id);
|
|
|
|
memcpy_s(pchName, cchNameBufferSize, name.c_str(), name.size());
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto inject_dlc = [&](const int index) {
|
|
|
|
auto name = fmt::format("DLC #{} with ID: {} ", iDLC, dlc_id);
|
|
|
|
if (index >= config.dlc_ids.size()) {
|
|
|
|
name = name.substr(0, cchNameBufferSize);
|
|
|
|
logger->error("{} -> Out of bounds injected index: {}", function_name, index);
|
|
|
|
*name.rbegin() = '\0';
|
|
|
|
return false;
|
|
|
|
memcpy_s(pchName, cchNameBufferSize, name.c_str(), name.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto dlc_id = config.dlc_ids[index];
|
|
|
|
print_dlc_info(tag);
|
|
|
|
fill_dlc_info(dlc_id);
|
|
|
|
return true;
|
|
|
|
print_dlc_info("injected");
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Original response
|
|
|
|
const auto get_original_dlc_count = [](const AppId_t& app_id) {
|
|
|
|
if (cached_dlcs.empty()) {
|
|
|
|
if (original_dlc_count_map.contains(app_id)) {
|
|
|
|
// Original DLC index
|
|
|
|
return original_dlc_count_map[app_id];
|
|
|
|
if (iDLC < original_dlc_count) {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto original_count = get_original_dlc_count(app_id);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Original count less than MAX_DLC implies that we need to redirect the call to original function.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (original_count < MAX_DLC) {
|
|
|
|
const auto success = original_function();
|
|
|
|
const auto success = original_function();
|
|
|
|
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
if (success) {
|
|
|
|
*pbAvailable = should_unlock(*pDlcId);
|
|
|
|
*pbAvailable = should_unlock(*pDlcId);
|
|
|
|
print_dlc_info("original");
|
|
|
|
print_dlc_info("original");
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
logger->warn("{} -> original function failed for index: {}", function_name, iDLC);
|
|
|
|
logger->warn("{} -> original call failed for index: {}", function_name, iDLC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Injected DLC index (after original)
|
|
|
|
// We must have had cached DLC IDs at this point.
|
|
|
|
const auto index = iDLC - original_dlc_count;
|
|
|
|
// It does not matter if we begin the list with injected DLC IDs or cached ones.
|
|
|
|
return inject_dlc(index);
|
|
|
|
// However, we must be consistent at all times. Hence, the convention will be that
|
|
|
|
}
|
|
|
|
// cached DLC should always follow the injected DLCs as follows:
|
|
|
|
|
|
|
|
// [injected-dlc-0, injected-dlc-1, ..., cached-dlc-0, cached-dlc-1, ...]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (iDLC < 0) {
|
|
|
|
|
|
|
|
logger->warn("{} -> Out of bounds DLC index: {}", function_name, iDLC);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int local_dlc_count = static_cast<int>(config.dlc_ids.size());
|
|
|
|
|
|
|
|
if (iDLC < local_dlc_count) {
|
|
|
|
|
|
|
|
return inject_dlc("local config", config.dlc_ids, iDLC);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto adjusted_index = iDLC - local_dlc_count;
|
|
|
|
|
|
|
|
const int cached_dlc_count = static_cast<int>(cached_dlcs.size());
|
|
|
|
|
|
|
|
if (iDLC < cached_dlc_count) {
|
|
|
|
|
|
|
|
return inject_dlc("memory cache", cached_dlcs, adjusted_index);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Cached response
|
|
|
|
|
|
|
|
const auto total_size = cached_dlcs.size() + config.dlc_ids.size();
|
|
|
|
|
|
|
|
if (iDLC < 0 or iDLC >= total_size) {
|
|
|
|
|
|
|
|
logger->error(
|
|
|
|
logger->error(
|
|
|
|
"{} -> Game accessed out of bounds DLC index: {}. Total size: {}",
|
|
|
|
"{} -> Out of bounds DLC index: {}, local dlc count: {}, cached dlc count: {}",
|
|
|
|
function_name, iDLC, total_size
|
|
|
|
function_name, iDLC, local_dlc_count, cached_dlc_count
|
|
|
|
);
|
|
|
|
);
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Cached index
|
|
|
|
return false;
|
|
|
|
if (iDLC < cached_dlcs.size()) {
|
|
|
|
} catch (const Exception& e) {
|
|
|
|
const auto dlc_id = cached_dlcs[iDLC];
|
|
|
|
logger->error("{} -> Uncaught exception: {}", function_name, e.what());
|
|
|
|
fill_dlc_info(dlc_id);
|
|
|
|
return false;
|
|
|
|
print_dlc_info("cached");
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Injected DLC index (after cached)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto index = iDLC - static_cast<int>(cached_dlcs.size());
|
|
|
|
|
|
|
|
return inject_dlc(index);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|