* parse and use branches data from branches.json

* deprecate build_id in the .ini file
* change Steam_Apps::SetDlcContext() to mimic Steam_Apps::BIsDlcInstalled(), not sure if that's correct
This commit is contained in:
otavepto 2024-07-03 01:30:34 +03:00
parent 715cb70bc5
commit 3f7ec00719
4 changed files with 184 additions and 60 deletions

View File

@ -170,7 +170,21 @@ struct Overlay_Appearance {
static NotificationPosition translate_notification_position(const std::string &str);
};
struct Branch_Info {
std::string name{};
std::string description{};
bool branch_protected = false;
EBetaBranchFlags flags = EBetaBranchFlags::k_EBetaBranch_None;
uint32 build_id = 10; // not sure if 0 as an initial value is a good idea
uint32 time_updated_epoch = (uint32)std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
// may be changed by the game, I assume only 1 branch should be active
// added in sdk 1.60 and currently unused
bool active = false;
};
class Settings {
private:
CSteamID steam_id{}; // user id
CGameID game_id{};
std::string name{};
@ -197,7 +211,6 @@ class Settings {
std::string supported_languages{};
public:
//Depots
std::vector<DepotId_t> depots{};
@ -214,9 +227,6 @@ public:
bool matchmaking_server_list_always_lan_type = true;
bool matchmaking_server_details_via_source_query = false;
//app build id
int build_id = 10;
//make lobby creation fail in the matchmaking interface
bool disable_lobby_creation = false;
@ -259,9 +269,11 @@ public:
// get the alpha-2 code from: https://www.iban.com/country-codes
std::string ip_country = "US";
// branches info
//is playing on beta branch + current/forced branch name
bool is_beta_branch = false;
std::string current_branch_name = "public";
long selected_branch_idx{};
std::vector<Branch_Info> branches{}; // in settings parser we must ensure we have the default "public" branch, force-add it if not defined by the user
//controller
struct Controller_Settings controller_settings{};

View File

@ -1135,18 +1135,6 @@ static void parse_mods_folder(class Settings *settings_client, Settings *setting
// app::general::build_id
static void parse_build_id(class Settings *settings_client, class Settings *settings_server)
{
std::string line(common_helpers::string_strip(ini.GetValue("app::general", "build_id", "")));
if (line.size()) {
int build_id = std::stoi(line);
PRINT_DEBUG(" setting build id = %i", build_id);
settings_client->build_id = build_id;
settings_server->build_id = build_id;
}
}
// main::general::crash_printer_location
static void parse_crash_printer_location()
{
@ -1195,6 +1183,96 @@ static void parse_auto_accept_invite(class Settings *settings_client, class Sett
}
}
// branches.json
static bool parse_branches_file(
const std::string &base_path, const bool force_load,
class Settings *settings_client, class Settings *settings_server, class Local_Storage *local_storage)
{
static constexpr auto branches_json_file = "branches.json";
std::vector<Branch_Info> result{};
long public_branch_idx = -1;
long user_branch_idx = -1;
std::string branches_file = base_path + branches_json_file;
auto branches = nlohmann::json{};
if (!local_storage->load_json(branches_file, branches) && !force_load) {
return false;
}
// app::general::branch_name
std::string selected_branch = common_helpers::string_strip(ini.GetValue("app::general", "branch_name", ""));
if (selected_branch.empty()) {
selected_branch = "public";
PRINT_DEBUG("no branch name specified, defaulting to 'public'");
}
PRINT_DEBUG("selected branch name '%s'", selected_branch.c_str());
PRINT_DEBUG("loaded %zu branches from file '%s'", branches.size(), branches_file.c_str());
auto current_epoch = (uint32)std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
for (const auto &branch_data : branches) {
auto &new_banch = result.emplace_back(Branch_Info{});
new_banch.name = branch_data.value("name", new_banch.name);
new_banch.description = branch_data.value("description", new_banch.description);
new_banch.branch_protected = branch_data.value("protected", new_banch.branch_protected);
new_banch.build_id = branch_data.value("build_id", new_banch.build_id);
new_banch.time_updated_epoch = branch_data.value("time_updated", new_banch.time_updated_epoch);
new_banch.flags = EBetaBranchFlags::k_EBetaBranch_Available;
if (new_banch.branch_protected) {
new_banch.flags = static_cast<EBetaBranchFlags>(new_banch.flags | EBetaBranchFlags::k_EBetaBranch_Private);
}
if (public_branch_idx < 0 && common_helpers::str_cmp_insensitive("public", new_banch.name)) {
public_branch_idx = static_cast<long>(result.size() - 1);
PRINT_DEBUG("found default 'public' branch [%li]", public_branch_idx);
}
if (user_branch_idx < 0 && common_helpers::str_cmp_insensitive(selected_branch, new_banch.name)) {
user_branch_idx = static_cast<long>(result.size() - 1);
PRINT_DEBUG("found your branch '%s' [%li]", selected_branch.c_str(), user_branch_idx);
}
PRINT_DEBUG("added branch '%s'", new_banch.name.c_str());
PRINT_DEBUG(" description '%s'", new_banch.description.c_str());
PRINT_DEBUG(" branch_protected %i", (int)new_banch.branch_protected);
PRINT_DEBUG(" build_id %u", new_banch.build_id);
PRINT_DEBUG(" time_updated_epoch %u", new_banch.time_updated_epoch);
}
if (public_branch_idx < 0) {
PRINT_DEBUG("[?] 'public' branch not found, adding it");
auto &public_branch = result.emplace_back(Branch_Info{});
public_branch_idx = static_cast<long>(result.size() - 1);
}
if (user_branch_idx < 0) {
PRINT_DEBUG("[?] selected branch '%s' wasn't loaded, forcing selection to the default 'public'", selected_branch.c_str());
user_branch_idx = public_branch_idx;
}
{
auto& public_branch = result[public_branch_idx];
public_branch.name = "public";
public_branch.flags = static_cast<EBetaBranchFlags>(public_branch.flags | EBetaBranchFlags::k_EBetaBranch_Default | EBetaBranchFlags::k_EBetaBranch_Available);
}
{
auto& user_branch = result[user_branch_idx];
user_branch.active = true;
user_branch.flags = static_cast<EBetaBranchFlags>(user_branch.flags | EBetaBranchFlags::k_EBetaBranch_Available | EBetaBranchFlags::k_EBetaBranch_Installed | EBetaBranchFlags::k_EBetaBranch_Selected);
}
settings_client->branches = result;
settings_server->branches = result;
settings_client->selected_branch_idx = user_branch_idx;
settings_server->selected_branch_idx = user_branch_idx;
PRINT_DEBUG("selected branch index in the list [%li]", user_branch_idx);
return true;
}
// user::general::ip_country
static void parse_ip_country(class Local_Storage *local_storage, class Settings *settings_client, class Settings *settings_server)
{
@ -1264,16 +1342,6 @@ static void parse_overlay_general_config(class Settings *settings_client, class
// mainly enable/disable features
static void parse_simple_features(class Settings *settings_client, class Settings *settings_server)
{
// app::general::branch_name
{
std::string line(common_helpers::string_strip(ini.GetValue("app::general", "branch_name", "")));
if (line.size()) {
settings_client->current_branch_name = line;
settings_server->current_branch_name = line;
PRINT_DEBUG("setting current branch name to '%s'", line.c_str());
}
}
// [main::general]
settings_client->enable_new_app_ticket = ini.GetBoolValue("main::general", "new_app_ticket", settings_client->enable_new_app_ticket);
settings_server->enable_new_app_ticket = ini.GetBoolValue("main::general", "new_app_ticket", settings_server->enable_new_app_ticket);
@ -1595,8 +1663,6 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
settings_client->set_supported_languages(supported_languages);
settings_server->set_supported_languages(supported_languages);
parse_build_id(settings_client, settings_server);
parse_simple_features(settings_client, settings_server);
parse_dlc(settings_client, settings_server);
@ -1614,6 +1680,11 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
load_gamecontroller_settings(settings_client);
parse_auto_accept_invite(settings_client, settings_server);
parse_ip_country(local_storage, settings_client, settings_server);
// try local "steam_settings" then saves path, on second trial force load defaults
if (!parse_branches_file(steam_settings_path, false, settings_client, settings_server, local_storage)) {
parse_branches_file(local_storage->get_global_settings_path(), true, settings_client, settings_server, local_storage);
}
parse_overlay_general_config(settings_client, settings_server);
load_overlay_appearance(settings_client, settings_server, local_storage);

View File

@ -243,9 +243,11 @@ bool Steam_Apps::GetCurrentBetaName( char *pchName, int cchNameBufferSize )
{
PRINT_DEBUG("%p [%i]", pchName, cchNameBufferSize);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pchName && cchNameBufferSize > 0 && static_cast<size_t>(cchNameBufferSize) > settings->current_branch_name.size()) {
const auto &current_branch_name = settings->branches[settings->selected_branch_idx].name;
if (pchName && cchNameBufferSize > 0 && static_cast<size_t>(cchNameBufferSize) > current_branch_name.size()) {
memset(pchName, 0, cchNameBufferSize);
memcpy(pchName, settings->current_branch_name.c_str(), settings->current_branch_name.size());
memcpy(pchName, current_branch_name.c_str(), current_branch_name.size());
}
PRINT_DEBUG("returned '%s'", pchName);
@ -365,7 +367,7 @@ int Steam_Apps::GetAppBuildId()
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return this->settings->build_id;
return static_cast<int>(this->settings->branches[settings->selected_branch_idx].build_id);
}
@ -462,24 +464,53 @@ bool Steam_Apps::BIsTimedTrial( uint32* punSecondsAllowed, uint32* punSecondsPla
return false;
}
// TODO no public docs
// set current DLC AppID being played (or 0 if none). Allows Steam to track usage of major DLC extensions
bool Steam_Apps::SetDlcContext( AppId_t nAppID )
{
PRINT_DEBUG("%u", nAppID);
PRINT_DEBUG("%u // TODO", nAppID);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
if (nAppID == 0) return false; // TODO is this correct? (see Steam_Apps::BIsDlcInstalled)
if (nAppID == UINT32_MAX) return false; // TODO is this correct? (see Steam_Apps::BIsDlcInstalled)
if (nAppID == settings->get_local_game_id().AppID()) return true; // TODO is this correct?
return settings->hasDLC(nAppID);
}
// TODO no public docs
// returns total number of known app beta branches (including default "public" branch )
int Steam_Apps::GetNumBetas( int *pnAvailable, int *pnPrivate )
{
PRINT_DEBUG("%p, %p", pnAvailable, pnPrivate);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pnAvailable) *pnAvailable = 1; // TODO what is this?
if (pnPrivate) *pnPrivate = 0; // TODO what is this?
// There is no "betas.txt" we, we always return 1 since "public" branch
return 1;
// I assume 'available' means installed on the user's disk and could be used
// in that case only 1 should be *available* since the user can only have 1 active and usable branch with the emu, unlike real steam
// the user can switch the active (available) branch from configs.app.ini
// right??
if (pnAvailable) { // TODO what is this?
*pnAvailable = 0;
for (const auto &item : settings->branches) {
if (item.flags & EBetaBranchFlags::k_EBetaBranch_Available) {
*pnAvailable += 1;
}
}
PRINT_DEBUG("available branches = %i", *pnAvailable);
}
if (pnPrivate) {
*pnPrivate = 0;
for (const auto &item : settings->branches) {
if (item.flags & EBetaBranchFlags::k_EBetaBranch_Private) {
*pnPrivate += 1;
}
}
PRINT_DEBUG("private branches = %i", *pnPrivate);
}
return static_cast<int>(settings->branches.size()); // we always return at least 1 since "public" branch
}
// TODO no public docs
@ -487,45 +518,57 @@ int Steam_Apps::GetNumBetas( int *pnAvailable, int *pnPrivate )
bool Steam_Apps::GetBetaInfo( int iBetaIndex, uint32 *punFlags, uint32 *punBuildID, char *pchBetaName, int cchBetaName, char *pchDescription, int cchDescription ) // iterate through
{
// I assume this API is like "Steam_User_Stats::GetNextMostAchievedAchievementInfo()", it returns 'ok' until index is out of range
PRINT_DEBUG("%i %p %p --- %p %i --- %p %i", iBetaIndex, punFlags, punBuildID, pchBetaName, cchBetaName, pchDescription, cchDescription);
PRINT_DEBUG("[%i] %p %p --- %p %i --- %p %i", iBetaIndex, punFlags, punBuildID, pchBetaName, cchBetaName, pchDescription, cchDescription);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (iBetaIndex < 0) return false;
if (iBetaIndex != 0) return false; // TODO remove this once we have a proper betas/branches list
// if (iBetaIndex >= settings->beta_branches.size()) return false; // TODO implement this
if (static_cast<size_t>(iBetaIndex) >= settings->branches.size()) return false;
if (punFlags) {
*punFlags = EBetaBranchFlags::k_EBetaBranch_Default | EBetaBranchFlags::k_EBetaBranch_Available |
EBetaBranchFlags::k_EBetaBranch_Selected | EBetaBranchFlags::k_EBetaBranch_Installed;
}
const auto &branch = settings->branches[iBetaIndex];
if (punFlags) *punFlags = branch.flags;
if (punBuildID) *punBuildID = branch.build_id;
if (punBuildID) *punBuildID = 0;
if (pchBetaName && cchBetaName > 0 && static_cast<size_t>(cchBetaName) > settings->current_branch_name.size()) {
if (pchBetaName && cchBetaName > 0 && static_cast<size_t>(cchBetaName) > branch.name.size()) {
memset(pchBetaName, 0, cchBetaName);
memcpy(pchBetaName, settings->current_branch_name.c_str(), settings->current_branch_name.size());
memcpy(pchBetaName, branch.name.c_str(), branch.name.size());
}
std::string description = "public";
if (pchDescription && cchDescription > 0 && static_cast<size_t>(cchDescription) > description.size()) {
if (pchDescription && cchDescription > 0 && static_cast<size_t>(cchDescription) > branch.description.size()) {
memset(pchDescription, 0, cchDescription);
memcpy(pchDescription, description.c_str(), description.size());
memcpy(pchDescription, branch.description.c_str(), branch.description.size());
}
return true;
}
// TODO no public docs
// select this beta branch for this app as active, might need the game to restart so Steam can update to that branch
bool Steam_Apps::SetActiveBeta( const char *pchBetaName )
{
PRINT_DEBUG("'%s'", pchBetaName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!pchBetaName || !pchBetaName[0]) return false;
// (sdk 1.60) apparently steam doesn't verify this condition, tested by 'universal963' on appid 480
//if (!pchBetaName) return false;
// TODO check if branch name in betas.txt once we implement that
std::string beta_name = pchBetaName ? pchBetaName : "";
auto branch_it = std::find_if(settings->branches.begin(), settings->branches.end(), [&beta_name](const Branch_Info &item){
return common_helpers::str_cmp_insensitive(beta_name, item.name);
});
return true;
if (settings->branches.end() != branch_it) {
// reset the 'active' flag for all branches
for (auto &item : settings->branches) {
item.active = false;
}
// then set the flag for this branch
branch_it->active = true;
PRINT_DEBUG("game changed active beta branch!");
return true;
}
return true; // (sdk 1.60) apparently steam doesn't even care and just returns true anyway, tested by 'universal963' on appid 480
}

View File

@ -3,13 +3,11 @@
# ############################################################################## #
[app::general]
# allow the app/game to show the correct build id
# 0 means you're not running a build downloaded from steam
build_id=1234
# by default the emu will report a `non-beta` branch with the name `public` when the game calls `Steam_Apps::GetCurrentBetaName()`
# by default the emu will report a `non-beta` branch when the game calls `Steam_Apps::GetCurrentBetaName()`
# make the game/app think we're playing on a beta branch
is_beta_branch=0
# the name of the beta branch
# the name of the current branch, this must also exist in branches.json
# otherwise will be ignored by the emu and the default 'public' branch will be used
branch_name=public
[app::dlcs]