diff --git a/dll/dll/common_includes.h b/dll/dll/common_includes.h index 89939fa5..8f4436b6 100644 --- a/dll/dll/common_includes.h +++ b/dll/dll/common_includes.h @@ -146,7 +146,7 @@ static inline void reset_LastError() #endif // Other libs includes -#include "gamepad/gamepad.h" +// #include "gamepad/gamepad.h" // Steamsdk includes #include "steam/steam_api.h" diff --git a/dll/dll/settings.h b/dll/dll/settings.h index 327151c3..09fd64f1 100644 --- a/dll/dll/settings.h +++ b/dll/dll/settings.h @@ -307,7 +307,10 @@ public: //controller struct Controller_Settings controller_settings{}; std::string glyphs_directory{}; - + bool flip_nintendo_layout = false; + bool combine_joycons = true; + uint16 inner_deadzone = 2000; + uint16 outer_deadzone = 3000; // allow Steam_User_Stats::FindLeaderboard() to always succeed and create the given unknown leaderboard bool disable_leaderboards_create_unknown = false; diff --git a/dll/dll/steam_controller.h b/dll/dll/steam_controller.h index 0d9b0ed7..29eb1230 100644 --- a/dll/dll/steam_controller.h +++ b/dll/dll/steam_controller.h @@ -17,9 +17,10 @@ #ifndef __INCLUDED_STEAM_CONTROLLER_H__ #define __INCLUDED_STEAM_CONTROLLER_H__ +#define GAMEPAD_COUNT 4 #include "base.h" - +#include "gamepad_provider/gamepad_provider.hpp" struct Controller_Map { std::map> active_digital{}; @@ -30,10 +31,13 @@ struct Controller_Action { ControllerHandle_t controller_handle{}; struct Controller_Map active_map{}; ControllerDigitalActionHandle_t active_set{}; + std::map active_layers{}; Controller_Action(ControllerHandle_t controller_handle); void activate_action_set(ControllerDigitalActionHandle_t active_set, std::map &controller_maps); + void activate_action_set_layer(ControllerActionSetHandle_t active_layer, std::map &controller_maps); + void deactivate_action_set_layer(ControllerActionSetHandle_t active_layer); std::set button_id(ControllerDigitalActionHandle_t handle); std::pair, enum EInputSourceMode> analog_id(ControllerAnalogActionHandle_t handle); }; @@ -101,6 +105,7 @@ public ISteamInput std::map steaminput_glyphs{}; std::map steamcontroller_glyphs{}; + std::thread sdl_thread{}; std::thread background_rumble_thread{}; Rumble_Thread_Data *rumble_thread_data{}; @@ -108,7 +113,8 @@ public ISteamInput bool initialized{}; bool explicitly_call_run_frame{}; - void set_handles(std::map, std::string>>> action_sets); + void set_handles(std::map, std::string>>> action_sets, + std::map, std::string>>> action_set_layers); void RunCallbacks(); diff --git a/dll/settings_parser.cpp b/dll/settings_parser.cpp index c827b153..57a8c883 100644 --- a/dll/settings_parser.cpp +++ b/dll/settings_parser.cpp @@ -1,1856 +1,1872 @@ -/* Copyright (C) 2019 Mr Goldberg - This file is part of the Goldberg Emulator +/* Copyright (C) 2019 Mr Goldberg + This file is part of the Goldberg Emulator + + The Goldberg Emulator is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + The Goldberg Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the Goldberg Emulator; if not, see + . */ + +#include "dll/settings_parser.h" + +#define SI_CONVERT_GENERIC +#define SI_SUPPORT_IOSTREAMS +#define SI_NO_MBCS +#include "simpleini/SimpleIni.h" +#include "gamepad_provider/gamepad_provider.hpp" + + +constexpr const static char config_ini_app[] = "configs.app.ini"; +constexpr const static char config_ini_main[] = "configs.main.ini"; +constexpr const static char config_ini_overlay[] = "configs.overlay.ini"; +constexpr const static char config_ini_user[] = "configs.user.ini"; + +static CSimpleIniA ini{}; + +typedef struct IniValue { + enum class Type { + STR, + BOOL, + DOUBLE, + LONG, + }; + + explicit IniValue(const char *new_val): type(Type::STR), val_str(new_val) {} + explicit IniValue(bool new_val): type(Type::BOOL), val_bool(new_val) {} + explicit IniValue(double new_val): type(Type::DOUBLE), val_double(new_val) {} + explicit IniValue(long new_val): type(Type::LONG), val_long(new_val) {} + + Type type; + union { + const char *val_str; + bool val_bool; + double val_double; + long val_long; + + }; +} IniValue; + +static void save_global_ini_value(class Local_Storage *local_storage, const char *filename, const char *section, const char *key, IniValue val, const char *comment = nullptr) { + CSimpleIniA new_ini{}; + new_ini.SetUnicode(); + new_ini.SetSpaces(false); + + auto sz = local_storage->data_settings_size(filename); + if (sz > 0) { + std::vector ini_file_data(sz); + auto read = local_storage->get_data_settings(filename, &ini_file_data[0], static_cast(ini_file_data.size())); + if (read == sz) { + new_ini.LoadData(&ini_file_data[0], ini_file_data.size()); + } + } + + std::string comment_str{}; + if (comment && comment[0]) { + comment_str.append("# ").append(comment); + comment = comment_str.c_str(); + } + + switch (val.type) + { + case IniValue::Type::STR: + new_ini.SetValue(section, key, val.val_str, comment); + break; + + case IniValue::Type::BOOL: + new_ini.SetBoolValue(section, key, val.val_bool, comment); + break; + + case IniValue::Type::DOUBLE: + new_ini.SetDoubleValue(section, key, val.val_double, comment); + break; + + case IniValue::Type::LONG: + new_ini.SetLongValue(section, key, val.val_long, comment); + break; + + default: break; + } + + std::string ini_buff{}; + if (new_ini.Save(ini_buff, false) == SI_OK) { + local_storage->store_data_settings(filename, &ini_buff[0], static_cast(ini_buff.size())); + } + +} + +static void merge_ini(const CSimpleIniA &new_ini, bool overwrite = false) { + std::list sections{}; + new_ini.GetAllSections(sections); + for (auto const &sec : sections) { + std::list keys{}; + new_ini.GetAllKeys(sec.pItem, keys); + for (auto const &key : keys) { + // only add the key if it didn't exist already + if (!ini.KeyExists(sec.pItem, key.pItem) || overwrite) { + std::list vals{}; + new_ini.GetAllValues(sec.pItem, key.pItem, vals); + for (const auto &val : vals) { + ini.SetValue(sec.pItem, key.pItem, val.pItem); + } + } + } + } +} + + +Overlay_Appearance::NotificationPosition Overlay_Appearance::translate_notification_position(const std::string &str) +{ + if (str == "top_left") return NotificationPosition::top_left; + else if (str == "top_center") return NotificationPosition::top_center; + else if (str == "top_right") return NotificationPosition::top_right; + else if (str == "bot_left") return NotificationPosition::bot_left; + else if (str == "bot_center") return NotificationPosition::bot_center; + else if (str == "bot_right") return NotificationPosition::bot_right; + + PRINT_DEBUG("Invalid position '%s'", str.c_str()); + return default_pos; +} + + +// custom_broadcasts.txt +static void load_custom_broadcasts(const std::string &base_path, std::set &custom_broadcasts) +{ + const std::string broadcasts_filepath(base_path + "custom_broadcasts.txt"); + std::ifstream broadcasts_file(std::filesystem::u8path(broadcasts_filepath)); + if (broadcasts_file.is_open()) { + common_helpers::consume_bom(broadcasts_file); + PRINT_DEBUG("loading broadcasts file '%s'", broadcasts_filepath.c_str()); + std::string line{}; + while (std::getline(broadcasts_file, line)) { + if (line.length() <= 0) continue; + + std::set ips = Networking::resolve_ip(line); + custom_broadcasts.insert(ips.begin(), ips.end()); + PRINT_DEBUG("added ip/port to broadcast list '%s'", line.c_str()); + } + } +} + +// subscribed_groups_clans.txt +static void load_subscribed_groups_clans(const std::string &base_path, Settings *settings_client, Settings *settings_server) +{ + const std::string clans_filepath(base_path + "subscribed_groups_clans.txt"); + std::ifstream clans_file(std::filesystem::u8path(clans_filepath)); + if (clans_file.is_open()) { + common_helpers::consume_bom(clans_file); + PRINT_DEBUG("loading group clans file '%s'", clans_filepath.c_str()); + std::string line{}; + while (std::getline(clans_file, line)) { + if (line.length() <= 0) continue; + + std::size_t seperator1 = line.find("\t\t"); + std::size_t seperator2 = line.rfind("\t\t"); + std::string clan_id; + std::string clan_name; + std::string clan_tag; + if ((seperator1 != std::string::npos) && (seperator2 != std::string::npos)) { + clan_id = line.substr(0, seperator1); + clan_name = line.substr(seperator1+2, seperator2-2); + clan_tag = line.substr(seperator2+2); + + // fix persistant tabbing problem for clan name + std::size_t seperator3 = clan_name.find("\t"); + std::string clan_name_fix = clan_name.substr(0, seperator3); + clan_name = clan_name_fix; + } + + Group_Clans nclan; + nclan.id = CSteamID( std::stoull(clan_id.c_str(), NULL, 0) ); + nclan.name = clan_name; + nclan.tag = clan_tag; + + try { + settings_client->subscribed_groups_clans.push_back(nclan); + settings_server->subscribed_groups_clans.push_back(nclan); + PRINT_DEBUG("Added clan %s", clan_name.c_str()); + } catch (...) {} + } + } +} + +// overlay::appearance +static void load_overlay_appearance(class Settings *settings_client, class Settings *settings_server, class Local_Storage *local_storage) +{ + std::list names{}; + if (!ini.GetAllKeys("overlay::appearance", names) || names.empty()) return; + + for (const auto &name_ent : names) { + auto val_ptr = ini.GetValue("overlay::appearance", name_ent.pItem); + if (!val_ptr || !val_ptr[0]) continue; + + std::string name(name_ent.pItem); + std::string value(val_ptr); + PRINT_DEBUG(" Overlay appearance line '%s'='%s'", name.c_str(), value.c_str()); + try { + if (name.compare("Font_Override") == 0) { + value = common_helpers::string_strip(value); + // first try the local settings folder + std::string nfont_override(common_helpers::to_absolute(value, Local_Storage::get_game_settings_path() + "fonts")); + if (!common_helpers::file_exist(nfont_override)) { + nfont_override.clear(); + } + + // then try the global settings folder + if (nfont_override.empty()) { + nfont_override = common_helpers::to_absolute(value, local_storage->get_global_settings_path() + "fonts"); + if (!common_helpers::file_exist(nfont_override)) { + nfont_override.clear(); + } + } + + if (nfont_override.size()) { + settings_client->overlay_appearance.font_override = nfont_override; + settings_server->overlay_appearance.font_override = nfont_override; + PRINT_DEBUG(" loaded font '%s'", nfont_override.c_str()); + } else { + PRINT_DEBUG(" ERROR font file '%s' doesn't exist and will be ignored", value.c_str()); + } + } else if (name.compare("Font_Size") == 0) { + float nfont_size = std::stof(value, NULL); + settings_client->overlay_appearance.font_size = nfont_size; + settings_server->overlay_appearance.font_size = nfont_size; + } else if (name.compare("Icon_Size") == 0) { + float nicon_size = std::stof(value, NULL); + settings_client->overlay_appearance.icon_size = nicon_size; + settings_server->overlay_appearance.icon_size = nicon_size; + } else if (name.compare("Font_Glyph_Extra_Spacing_x") == 0) { + float size = std::stof(value, NULL); + settings_client->overlay_appearance.font_glyph_extra_spacing_x = size; + settings_server->overlay_appearance.font_glyph_extra_spacing_x = size; + } else if (name.compare("Font_Glyph_Extra_Spacing_y") == 0) { + float size = std::stof(value, NULL); + settings_client->overlay_appearance.font_glyph_extra_spacing_y = size; + settings_server->overlay_appearance.font_glyph_extra_spacing_y = size; + } else if (name.compare("Notification_R") == 0) { + float nnotification_r = std::stof(value, NULL); + settings_client->overlay_appearance.notification_r = nnotification_r; + settings_server->overlay_appearance.notification_r = nnotification_r; + } else if (name.compare("Notification_G") == 0) { + float nnotification_g = std::stof(value, NULL); + settings_client->overlay_appearance.notification_g = nnotification_g; + settings_server->overlay_appearance.notification_g = nnotification_g; + } else if (name.compare("Notification_B") == 0) { + float nnotification_b = std::stof(value, NULL); + settings_client->overlay_appearance.notification_b = nnotification_b; + settings_server->overlay_appearance.notification_b = nnotification_b; + } else if (name.compare("Notification_A") == 0) { + float nnotification_a = std::stof(value, NULL); + settings_client->overlay_appearance.notification_a = nnotification_a; + settings_server->overlay_appearance.notification_a = nnotification_a; + } else if (name.compare("Notification_Rounding") == 0) { + float nnotification_rounding = std::stof(value, NULL); + settings_client->overlay_appearance.notification_rounding = nnotification_rounding; + settings_server->overlay_appearance.notification_rounding = nnotification_rounding; + } else if (name.compare("Notification_Margin_x") == 0) { + float val = std::stof(value, NULL); + settings_client->overlay_appearance.notification_margin_x = val; + settings_server->overlay_appearance.notification_margin_x = val; + } else if (name.compare("Notification_Margin_y") == 0) { + float val = std::stof(value, NULL); + settings_client->overlay_appearance.notification_margin_y = val; + settings_server->overlay_appearance.notification_margin_y = val; + } else if (name.compare("Notification_Animation") == 0) { + uint32 nnotification_animation = (uint32)(std::stof(value, NULL) * 1000.0f); // convert sec to milli + settings_client->overlay_appearance.notification_animation = nnotification_animation; + settings_server->overlay_appearance.notification_animation = nnotification_animation; + } else if (name.compare("Notification_Duration_Progress") == 0) { + uint32 time = (uint32)(std::stof(value, NULL) * 1000.0f); // convert sec to milli + settings_client->overlay_appearance.notification_duration_progress = time; + settings_server->overlay_appearance.notification_duration_progress = time; + } else if (name.compare("Notification_Duration_Achievement") == 0) { + uint32 time = (uint32)(std::stof(value, NULL) * 1000.0f); // convert sec to milli + settings_client->overlay_appearance.notification_duration_achievement = time; + settings_server->overlay_appearance.notification_duration_achievement = time; + } else if (name.compare("Notification_Duration_Invitation") == 0) { + uint32 time = (uint32)(std::stof(value, NULL) * 1000.0f); // convert sec to milli + settings_client->overlay_appearance.notification_duration_invitation = time; + settings_server->overlay_appearance.notification_duration_invitation = time; + } else if (name.compare("Notification_Duration_Chat") == 0) { + uint32 time = (uint32)(std::stof(value, NULL) * 1000.0f); // convert sec to milli + settings_client->overlay_appearance.notification_duration_chat = time; + settings_server->overlay_appearance.notification_duration_chat = time; + } else if (name.compare("Achievement_Unlock_Datetime_Format") == 0) { + settings_client->overlay_appearance.ach_unlock_datetime_format = value; + settings_server->overlay_appearance.ach_unlock_datetime_format = value; + } else if (name.compare("Background_R") == 0) { + float nbackground_r = std::stof(value, NULL); + settings_client->overlay_appearance.background_r = nbackground_r; + settings_server->overlay_appearance.background_r = nbackground_r; + } else if (name.compare("Background_G") == 0) { + float nbackground_g = std::stof(value, NULL); + settings_client->overlay_appearance.background_g = nbackground_g; + settings_server->overlay_appearance.background_g = nbackground_g; + } else if (name.compare("Background_B") == 0) { + float nbackground_b = std::stof(value, NULL); + settings_client->overlay_appearance.background_b = nbackground_b; + settings_server->overlay_appearance.background_b = nbackground_b; + } else if (name.compare("Background_A") == 0) { + float nbackground_a = std::stof(value, NULL); + settings_client->overlay_appearance.background_a = nbackground_a; + settings_server->overlay_appearance.background_a = nbackground_a; + } else if (name.compare("Element_R") == 0) { + float nelement_r = std::stof(value, NULL); + settings_client->overlay_appearance.element_r = nelement_r; + settings_server->overlay_appearance.element_r = nelement_r; + } else if (name.compare("Element_G") == 0) { + float nelement_g = std::stof(value, NULL); + settings_client->overlay_appearance.element_g = nelement_g; + settings_server->overlay_appearance.element_g = nelement_g; + } else if (name.compare("Element_B") == 0) { + float nelement_b = std::stof(value, NULL); + settings_client->overlay_appearance.element_b = nelement_b; + settings_server->overlay_appearance.element_b = nelement_b; + } else if (name.compare("Element_A") == 0) { + float nelement_a = std::stof(value, NULL); + settings_client->overlay_appearance.element_a = nelement_a; + settings_server->overlay_appearance.element_a = nelement_a; + } else if (name.compare("ElementHovered_R") == 0) { + float nelement_hovered_r = std::stof(value, NULL); + settings_client->overlay_appearance.element_hovered_r = nelement_hovered_r; + settings_server->overlay_appearance.element_hovered_r = nelement_hovered_r; + } else if (name.compare("ElementHovered_G") == 0) { + float nelement_hovered_g = std::stof(value, NULL); + settings_client->overlay_appearance.element_hovered_g = nelement_hovered_g; + settings_server->overlay_appearance.element_hovered_g = nelement_hovered_g; + } else if (name.compare("ElementHovered_B") == 0) { + float nelement_hovered_b = std::stof(value, NULL); + settings_client->overlay_appearance.element_hovered_b = nelement_hovered_b; + settings_server->overlay_appearance.element_hovered_b = nelement_hovered_b; + } else if (name.compare("ElementHovered_A") == 0) { + float nelement_hovered_a = std::stof(value, NULL); + settings_client->overlay_appearance.element_hovered_a = nelement_hovered_a; + settings_server->overlay_appearance.element_hovered_a = nelement_hovered_a; + } else if (name.compare("ElementActive_R") == 0) { + float nelement_active_r = std::stof(value, NULL); + settings_client->overlay_appearance.element_active_r = nelement_active_r; + settings_server->overlay_appearance.element_active_r = nelement_active_r; + } else if (name.compare("ElementActive_G") == 0) { + float nelement_active_g = std::stof(value, NULL); + settings_client->overlay_appearance.element_active_g = nelement_active_g; + settings_server->overlay_appearance.element_active_g = nelement_active_g; + } else if (name.compare("ElementActive_B") == 0) { + float nelement_active_b = std::stof(value, NULL); + settings_client->overlay_appearance.element_active_b = nelement_active_b; + settings_server->overlay_appearance.element_active_b = nelement_active_b; + } else if (name.compare("ElementActive_A") == 0) { + float nelement_active_a = std::stof(value, NULL); + settings_client->overlay_appearance.element_active_a = nelement_active_a; + settings_server->overlay_appearance.element_active_a = nelement_active_a; + } else if (name.compare("PosAchievement") == 0) { + auto pos = Overlay_Appearance::translate_notification_position(value); + settings_client->overlay_appearance.ach_earned_pos = pos; + settings_server->overlay_appearance.ach_earned_pos = pos; + } else if (name.compare("PosInvitation") == 0) { + auto pos = Overlay_Appearance::translate_notification_position(value); + settings_client->overlay_appearance.invite_pos = pos; + settings_server->overlay_appearance.invite_pos = pos; + } else if (name.compare("PosChatMsg") == 0) { + auto pos = Overlay_Appearance::translate_notification_position(value); + settings_client->overlay_appearance.chat_msg_pos = pos; + settings_server->overlay_appearance.chat_msg_pos = pos; + // >>> FPS background + } else if (name.compare("Stats_Background_R") == 0) { + float val = std::stof(value, NULL); + settings_client->overlay_appearance.stats_background_r = val; + settings_server->overlay_appearance.stats_background_r = val; + } else if (name.compare("Stats_Background_G") == 0) { + float val = std::stof(value, NULL); + settings_client->overlay_appearance.stats_background_g = val; + settings_server->overlay_appearance.stats_background_g = val; + } else if (name.compare("Stats_Background_B") == 0) { + float val = std::stof(value, NULL); + settings_client->overlay_appearance.stats_background_b = val; + settings_server->overlay_appearance.stats_background_b = val; + } else if (name.compare("Stats_Background_A") == 0) { + float val = std::stof(value, NULL); + settings_client->overlay_appearance.stats_background_a = val; + settings_server->overlay_appearance.stats_background_a = val; + // FPS background END <<< + // >>> FPS text color + } else if (name.compare("Stats_Text_R") == 0) { + float val = std::stof(value, NULL); + settings_client->overlay_appearance.stats_text_r = val; + settings_server->overlay_appearance.stats_text_r = val; + } else if (name.compare("Stats_Text_G") == 0) { + float val = std::stof(value, NULL); + settings_client->overlay_appearance.stats_text_g = val; + settings_server->overlay_appearance.stats_text_g = val; + } else if (name.compare("Stats_Text_B") == 0) { + float val = std::stof(value, NULL); + settings_client->overlay_appearance.stats_text_b = val; + settings_server->overlay_appearance.stats_text_b = val; + } else if (name.compare("Stats_Text_A") == 0) { + float val = std::stof(value, NULL); + settings_client->overlay_appearance.stats_text_a = val; + settings_server->overlay_appearance.stats_text_a = val; + // FPS text color END <<< + // >>> FPS position + } else if (name.compare("Stats_Pos_x") == 0) { + auto pos = std::stof(value); + if (pos < 0) { + pos = 0; + } else if (pos > 1.0f) { + pos = 1.0f; + } + settings_client->overlay_stats_pos_x = pos; + settings_server->overlay_stats_pos_x = pos; + } else if (name.compare("Stats_Pos_y") == 0) { + auto pos = std::stof(value); + if (pos < 0) { + pos = 0; + } else if (pos > 1.0f) { + pos = 1.0f; + } + settings_client->overlay_stats_pos_y = pos; + settings_server->overlay_stats_pos_y = pos; + // FPS position END <<< + } else { + PRINT_DEBUG("unknown overlay appearance setting"); + } + + } catch (...) { } + } +} + +template +static void split_string(const std::string &s, char delim, Out result) { + std::stringstream ss(s); + std::string item{}; + while (std::getline(ss, item, delim)) { + *(result++) = item; + } +} + +// folder "controller" +static void load_gamecontroller_settings(Settings *settings) +{ + std::string path = Local_Storage::get_game_settings_path() + "controller"; + std::vector paths = Local_Storage::get_filenames_path(path); + + for (auto & p: paths) { + size_t length = p.length(); + if (length < 4) continue; + if ( std::toupper(p.back()) != 'T') continue; + if ( std::toupper(p[length - 2]) != 'X') continue; + if ( std::toupper(p[length - 3]) != 'T') continue; + if (p[length - 4] != '.') continue; + + PRINT_DEBUG("controller config %s", p.c_str()); + std::string action_set_name = p.substr(0, length - 4); + std::transform(action_set_name.begin(), action_set_name.end(), action_set_name.begin(),[](unsigned char c){ return std::toupper(c); }); + + std::string controller_config_path = path + PATH_SEPARATOR + p; + std::ifstream input( std::filesystem::u8path(controller_config_path) ); + if (input.is_open()) { + common_helpers::consume_bom(input); + std::map, std::string>> button_pairs; + + for( std::string line; getline( input, line ); ) { + if (!line.empty() && line[line.length()-1] == '\n') { + line.pop_back(); + } + + if (!line.empty() && line[line.length()-1] == '\r') { + line.pop_back(); + } + + std::string action_name; + std::string button_name; + std::string source_mode; + + std::size_t deliminator = line.find("="); + if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) { + action_name = line.substr(0, deliminator); + std::size_t deliminator2 = line.find("=", deliminator + 1); + + if (deliminator2 != std::string::npos && deliminator2 != line.size()) { + button_name = line.substr(deliminator + 1, deliminator2 - (deliminator + 1)); + source_mode = line.substr(deliminator2 + 1); + } else { + button_name = line.substr(deliminator + 1); + source_mode = ""; + } + } + + std::transform(action_name.begin(), action_name.end(), action_name.begin(),[](unsigned char c){ return std::toupper(c); }); + std::transform(button_name.begin(), button_name.end(), button_name.begin(),[](unsigned char c){ return std::toupper(c); }); + std::pair, std::string> button_config = {{}, source_mode}; + split_string(button_name, ',', std::inserter(button_config.first, button_config.first.begin())); + button_pairs[action_name] = button_config; + PRINT_DEBUG("Added %s %s %s", action_name.c_str(), button_name.c_str(), source_mode.c_str()); + } + + settings->controller_settings.action_sets[action_set_name] = button_pairs; + PRINT_DEBUG("Added %zu action names to %s", button_pairs.size(), action_set_name.c_str()); + } + } + + settings->glyphs_directory = path + (PATH_SEPARATOR "glyphs" PATH_SEPARATOR); +} + +// steam_appid.txt +static uint32 parse_steam_app_id(const std::string &program_path) +{ + uint32 appid = 0; + + // try steam_settings folder + char array[10] = {}; + array[0] = '0'; + Local_Storage::get_file_data(Local_Storage::get_game_settings_path() + "steam_appid.txt", array, sizeof(array) - 1); + try { + appid = std::stoul(array); + } catch (...) {} + + // try current dir + if (!appid) { + memset(array, 0, sizeof(array)); + array[0] = '0'; + Local_Storage::get_file_data("steam_appid.txt", array, sizeof(array) - 1); + try { + appid = std::stoul(array); + } catch (...) {} + } + + // try exe dir + if (!appid) { + memset(array, 0, sizeof(array)); + array[0] = '0'; + Local_Storage::get_file_data(program_path + "steam_appid.txt", array, sizeof(array) - 1); + try { + appid = std::stoul(array); + } catch (...) {} + } + + // try env vars + if (!appid) { + std::string str_appid = get_env_variable("SteamAppId"); + std::string str_gameid = get_env_variable("SteamGameId"); + std::string str_overlay_gameid = get_env_variable("SteamOverlayGameId"); + + PRINT_DEBUG("str_appid %s str_gameid: %s str_overlay_gameid: %s", str_appid.c_str(), str_gameid.c_str(), str_overlay_gameid.c_str()); + uint32 appid_env = 0; + uint32 gameid_env = 0; + uint32 overlay_gameid = 0; + + if (str_appid.size() > 0) { + try { + appid_env = std::stoul(str_appid); + } catch (...) { + appid_env = 0; + } + } + + if (str_gameid.size() > 0) { + try { + gameid_env = std::stoul(str_gameid); + } catch (...) { + gameid_env = 0; + } + } + + if (str_overlay_gameid.size() > 0) { + try { + overlay_gameid = std::stoul(str_overlay_gameid); + } catch (...) { + overlay_gameid = 0; + } + } + + PRINT_DEBUG("appid_env %u gameid_env: %u overlay_gameid: %u", appid_env, gameid_env, overlay_gameid); + if (appid_env) { + appid = appid_env; + } + + if (gameid_env) { + appid = gameid_env; + } + + if (overlay_gameid) { + appid = overlay_gameid; + } + } + + PRINT_DEBUG("final appid = %u", appid); + return appid; +} + +// user::saves::local_save_path +static bool parse_local_save(std::string &save_path) +{ + auto ptr = ini.GetValue("user::saves", "local_save_path"); + if (!ptr || !ptr[0]) return false; + + save_path = common_helpers::to_absolute(common_helpers::string_strip(ptr), Local_Storage::get_program_path()); + if (save_path.size() && save_path.back() != *PATH_SEPARATOR) { + save_path.push_back(*PATH_SEPARATOR); + } + PRINT_DEBUG("using local save path '%s'", save_path.c_str()); + return true; +} + +// main::connectivity::listen_port +static uint16 parse_listen_port(class Local_Storage *local_storage) +{ + uint16 port = static_cast(ini.GetLongValue("main::connectivity", "listen_port")); + if (port == 0) { + port = DEFAULT_PORT; + } + return port; +} + +// user::general::account_name +static std::string parse_account_name(class Local_Storage *local_storage) +{ + auto name = ini.GetValue("user::general", "account_name"); + if (!name || !name[0]) { + name = DEFAULT_NAME; + save_global_ini_value( + local_storage, + config_ini_user, + "user::general", "account_name", IniValue(name), + "user account name" + ); + } + return std::string(name); +} + +// user::general::account_steamid +static CSteamID parse_user_steam_id(class Local_Storage *local_storage) +{ + char array_steam_id[32] = {}; + CSteamID user_id((uint64)std::atoll(ini.GetValue("user::general", "account_steamid", "0"))); + if (!user_id.IsValid()) { + user_id = generate_steam_id_user(); + char temp_text[32]{}; + snprintf(temp_text, sizeof(temp_text), "%llu", (uint64)user_id.ConvertToUint64()); + save_global_ini_value( + local_storage, + config_ini_user, + "user::general", "account_steamid", IniValue(temp_text), + "Steam64 format" + ); + } + + return user_id; +} + +// user::general::language +// valid list: https://partner.steamgames.com/doc/store/localization/languages +static std::string parse_current_language(class Local_Storage *local_storage) +{ + auto lang = ini.GetValue("user::general", "language"); + if (!lang || !lang[0]) { + lang = DEFAULT_LANGUAGE; + save_global_ini_value( + local_storage, + config_ini_user, + "user::general", "language", IniValue(lang), + "the language reported to the game, default is 'english', check 'API language code' in https://partner.steamgames.com/doc/store/localization/languages" + ); + } + + return common_helpers::to_lower(std::string(lang)); +} + +// supported_languages.txt +// valid list: https://partner.steamgames.com/doc/store/localization/languages +static std::set parse_supported_languages(class Local_Storage *local_storage, std::string &language) +{ + std::set supported_languages{}; + + std::string lang_config_path = Local_Storage::get_game_settings_path() + "supported_languages.txt"; + std::ifstream input( std::filesystem::u8path(lang_config_path) ); + + std::string first_language{}; + if (input.is_open()) { + common_helpers::consume_bom(input); + for( std::string line; getline( input, line ); ) { + if (!line.empty() && line.back() == '\n') { + line.pop_back(); + } + + if (!line.empty() && line.back() == '\r') { + line.pop_back(); + } + + try { + std::string lang = common_helpers::to_lower(line); + if (!first_language.size()) first_language = lang; + supported_languages.insert(lang); + PRINT_DEBUG("Added supported_language %s", lang.c_str()); + } catch (...) {} + } + } + + // if the current emu language is not in the supported languages list + if (!supported_languages.count(language)) { + if (first_language.size()) { // get the first supported language if the list wasn't empty + PRINT_DEBUG("[?] Your language '%s' isn't found in supported_languages.txt, using '%s' instead", language.c_str(), first_language.c_str()); + language = first_language; + } else { // otherwise just lie and add it then! + supported_languages.insert(language); + PRINT_DEBUG("Forced current language '%s' into supported_languages", language.c_str()); + } + } + + return supported_languages; +} + +// app::dlcs +static void parse_dlc(class Settings *settings_client, class Settings *settings_server) +{ + constexpr static const char unlock_all_key[] = "unlock_all"; + + bool unlock_all = ini.GetBoolValue("app::dlcs", unlock_all_key, true); + if (unlock_all) { + PRINT_DEBUG("unlocking all DLCs"); + settings_client->unlockAllDLC(true); + settings_server->unlockAllDLC(true); + } else { + PRINT_DEBUG("locking all DLCs"); + settings_client->unlockAllDLC(false); + settings_server->unlockAllDLC(false); + } + + std::list dlcs_keys{}; + if (!ini.GetAllKeys("app::dlcs", dlcs_keys) || dlcs_keys.empty()) return; + + // remove the unlock all key so we can iterate through the DLCs + dlcs_keys.remove_if([](const CSimpleIniA::Entry &item){ + return common_helpers::str_cmp_insensitive(item.pItem, unlock_all_key); + }); + + for (const auto &dlc_key : dlcs_keys) { + AppId_t appid = (AppId_t)std::stoul(dlc_key.pItem); + if (!appid) continue; + + auto name = ini.GetValue("app::dlcs", dlc_key.pItem, "unknown DLC"); + PRINT_DEBUG("adding DLC: [%u] = '%s'", appid, name); + settings_client->addDLC(appid, name, true); + settings_server->addDLC(appid, name, true); + } +} + +// app::paths +static void parse_app_paths(class Settings *settings_client, Settings *settings_server, const std::string &program_path) +{ + std::list ids{}; + if (!ini.GetAllKeys("app::paths", ids) || ids.empty()) return; + + for (const auto &id : ids) { + auto val_ptr = ini.GetValue("app::paths", id.pItem); + // NOTE: empty path means we actively disable the path to the appid specified + if (!val_ptr) continue; + + AppId_t appid = (AppId_t)std::stoul(id.pItem); + std::string rel_path(val_ptr); + std::string path{}; + if (rel_path.size()) + path = canonical_path(program_path + rel_path); + + if (appid) { + if (!rel_path.size() || path.size()) { + PRINT_DEBUG("Adding app path: %u|%s|%s", appid, rel_path.c_str(), path.c_str()); + settings_client->setAppInstallPath(appid, path); + settings_server->setAppInstallPath(appid, path); + } else { + PRINT_DEBUG("Error adding app path for: %u does this path exist? |%s|", appid, rel_path.c_str()); + } + } + } +} + +// leaderboards.txt +static void parse_leaderboards(class Settings *settings_client, class Settings *settings_server) +{ + std::string dlc_config_path = Local_Storage::get_game_settings_path() + "leaderboards.txt"; + std::ifstream input( std::filesystem::u8path(dlc_config_path) ); + if (input.is_open()) { + common_helpers::consume_bom(input); + + for( std::string line; getline( input, line ); ) { + if (!line.empty() && line[line.length()-1] == '\n') { + line.pop_back(); + } + + if (!line.empty() && line[line.length()-1] == '\r') { + line.pop_back(); + } + + std::string leaderboard{}; + unsigned int sort_method = 0; + unsigned int display_type = 0; + + std::size_t deliminator = line.find("="); + if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) { + leaderboard = line.substr(0, deliminator); + std::size_t deliminator2 = line.find("=", deliminator + 1); + if (deliminator2 != std::string::npos && deliminator2 != line.size()) { + sort_method = stol(line.substr(deliminator + 1, deliminator2 - (deliminator + 1))); + display_type = stol(line.substr(deliminator2 + 1)); + } + } + + if (leaderboard.size() && sort_method <= k_ELeaderboardSortMethodDescending && display_type <= k_ELeaderboardDisplayTypeTimeMilliSeconds) { + PRINT_DEBUG("Adding leaderboard: %s|%u|%u", leaderboard.c_str(), sort_method, display_type); + settings_client->setLeaderboard(leaderboard, (ELeaderboardSortMethod)sort_method, (ELeaderboardDisplayType)display_type); + settings_server->setLeaderboard(leaderboard, (ELeaderboardSortMethod)sort_method, (ELeaderboardDisplayType)display_type); + } else { + PRINT_DEBUG("Error adding leaderboard for: '%s', are sort method %u or display type %u valid?", leaderboard.c_str(), sort_method, display_type); + } + } + } + +} + +// stats.json +static void parse_stats(class Settings *settings_client, class Settings *settings_server, class Local_Storage *local_storage) +{ + nlohmann::json stats_items; + std::string stats_json_path = Local_Storage::get_game_settings_path() + "stats.json"; + if (local_storage->load_json(stats_json_path, stats_items)) { + for (const auto &stats : stats_items) { + std::string stat_name; + std::string stat_type; + std::string stat_default_value = "0"; + std::string stat_global_value = "0"; + + try { + stat_name = stats.value("name", std::string("")); + stat_type = stats.value("type", std::string("")); + stat_default_value = stats.value("default", std::string("0")); + stat_global_value = stats.value("global", std::string("0")); + } + catch (const std::exception &e) { + PRINT_DEBUG("Error reading current stat item in stats.json, reason: %s", e.what()); + continue; + } + + std::transform(stat_type.begin(), stat_type.end(), stat_type.begin(),[](unsigned char c){ return std::tolower(c); }); + struct Stat_config config = {}; + + try { + if (stat_type == "float") { + config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT; + config.default_value_float = std::stof(stat_default_value); + config.global_value_double = std::stod(stat_global_value); + } else if (stat_type == "int") { + config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_INT; + config.default_value_int = std::stol(stat_default_value); + config.global_value_int64 = std::stoll(stat_global_value); + } else if (stat_type == "avgrate") { + config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_AVGRATE; + config.default_value_float = std::stof(stat_default_value); + config.global_value_double = std::stod(stat_global_value); + } else { + PRINT_DEBUG("Error adding stat %s, type %s isn't valid", stat_name.c_str(), stat_type.c_str()); + continue; + } + } catch (...) { + PRINT_DEBUG("Error adding stat %s, default value %s or global value %s isn't valid", stat_name.c_str(), stat_default_value.c_str(), stat_global_value.c_str()); + continue; + } + + if (stat_name.size()) { + PRINT_DEBUG("Adding stat type: %s|%u|%f|%i", stat_name.c_str(), config.type, config.default_value_float, config.default_value_int); + settings_client->setStatDefiniton(stat_name, config); + settings_server->setStatDefiniton(stat_name, config); + } else { + PRINT_DEBUG("Error adding stat for: %s, empty name", stat_name.c_str()); + } + } + } + +} + +// depots.txt +static void parse_depots(class Settings *settings_client, class Settings *settings_server) +{ + std::string depots_config_path = Local_Storage::get_game_settings_path() + "depots.txt"; + std::ifstream input( std::filesystem::u8path(depots_config_path) ); + if (input.is_open()) { + common_helpers::consume_bom(input); + for( std::string line; getline( input, line ); ) { + if (!line.empty() && line[line.length()-1] == '\n') { + line.pop_back(); + } + + if (!line.empty() && line[line.length()-1] == '\r') { + line.pop_back(); + } + + try { + DepotId_t depot_id = std::stoul(line); + settings_client->depots.push_back(depot_id); + settings_server->depots.push_back(depot_id); + PRINT_DEBUG("Added depot %u", depot_id); + } catch (...) {} + } + } + +} + +// subscribed_groups.txt +static void parse_subscribed_groups(class Settings *settings_client, class Settings *settings_server) +{ + std::string depots_config_path = Local_Storage::get_game_settings_path() + "subscribed_groups.txt"; + std::ifstream input( std::filesystem::u8path(depots_config_path) ); + if (input.is_open()) { + common_helpers::consume_bom(input); + for( std::string line; getline( input, line ); ) { + if (!line.empty() && line[line.length()-1] == '\n') { + line.pop_back(); + } + + if (!line.empty() && line[line.length()-1] == '\r') { + line.pop_back(); + } + + try { + uint64 source_id = std::stoull(line); + settings_client->subscribed_groups.insert(source_id); + settings_server->subscribed_groups.insert(source_id); + PRINT_DEBUG("Added source %llu", source_id); + } catch (...) {} + } + } + +} + +// installed_app_ids.txt +static void parse_installed_app_Ids(class Settings *settings_client, class Settings *settings_server) +{ + std::string installed_apps_list_path = Local_Storage::get_game_settings_path() + "installed_app_ids.txt"; + std::ifstream input( std::filesystem::u8path(installed_apps_list_path) ); + if (input.is_open()) { + settings_client->assumeAnyAppInstalled(false); + settings_server->assumeAnyAppInstalled(false); + PRINT_DEBUG("Limiting/Locking installed apps"); + common_helpers::consume_bom(input); + for( std::string line; getline( input, line ); ) { + if (!line.empty() && line[line.length()-1] == '\n') { + line.pop_back(); + } + + if (!line.empty() && line[line.length()-1] == '\r') { + line.pop_back(); + } + + try { + AppId_t app_id = std::stoul(line); + settings_client->addInstalledApp(app_id); + settings_server->addInstalledApp(app_id); + PRINT_DEBUG("Added installed app with ID %u", app_id); + } catch (...) {} + } + } else { + settings_client->assumeAnyAppInstalled(true); + settings_server->assumeAnyAppInstalled(true); + PRINT_DEBUG("Assuming any app is installed"); + } + +} + + + +// steam_settings/mods +static const auto one_week_ago_epoch = std::chrono::duration_cast( + ( startup_time - std::chrono::hours(24 * 7) ).time_since_epoch() +).count(); +static const auto two_week_ago_epoch = std::chrono::duration_cast( + ( startup_time - std::chrono::hours(24 * 7 * 2) ).time_since_epoch() +).count(); +static const auto three_week_ago_epoch = std::chrono::duration_cast( + ( startup_time - std::chrono::hours(24 * 7 * 3) ).time_since_epoch() +).count(); + +static size_t get_file_size_safe(const std::string &filepath, const std::string &basepath, int32 default_val = 0) +{ + try + { + const auto file_p = common_helpers::to_absolute(filepath, basepath); + if (file_p.empty()) return default_val; + + size_t size = 0; + if (common_helpers::file_size(file_p, size)) { + return size; + } + } catch(...) {} + return default_val; +} + +static std::string get_mod_preview_url(const std::string &previewFileName, const std::string &mod_id) +{ + if (previewFileName.empty()) { + return std::string(); + } else { + auto settings_folder = std::string(Local_Storage::get_game_settings_path()); + std::replace(settings_folder.begin(), settings_folder.end(), '\\', '/'); + + return + +#if defined(__WINDOWS__) + "file:///" +#else // on Linux absolute paths start like this: /my/path, so the 3rd slash is already appended + "file://" +#endif + + + settings_folder + "mod_images/" + mod_id + "/" + previewFileName; + } + +} + +static void try_parse_mods_file(class Settings *settings_client, Settings *settings_server, nlohmann::json &mod_items, const std::string &mods_folder) +{ + for (auto mod = mod_items.begin(); mod != mod_items.end(); ++mod) { + try { + std::string mod_images_fullpath = Local_Storage::get_game_settings_path() + "mod_images" + PATH_SEPARATOR + std::string(mod.key()); + Mod_entry newMod; + newMod.id = std::stoull(mod.key()); + newMod.title = mod.value().value("title", std::string(mod.key())); + + // make sure this is never empty + newMod.path = mod.value().value("path", std::string("")); + if (newMod.path.empty()) { + newMod.path = mods_folder + PATH_SEPARATOR + std::string(mod.key()); + } else { + // make sure the path is normalized for current OS, and absolute + newMod.path = common_helpers::to_absolute( + newMod.path, + get_full_program_path() + ); + } + + newMod.fileType = k_EWorkshopFileTypeCommunity; + newMod.description = mod.value().value("description", std::string("")); + newMod.steamIDOwner = mod.value().value("steam_id_owner", settings_client->get_local_steam_id().ConvertToUint64()); + newMod.timeCreated = mod.value().value("time_created", (uint32)three_week_ago_epoch); + newMod.timeUpdated = mod.value().value("time_updated", (uint32)two_week_ago_epoch); + newMod.timeAddedToUserList = mod.value().value("time_added", (uint32)one_week_ago_epoch); + newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic; + newMod.banned = false; + newMod.acceptedForUse = true; + newMod.tagsTruncated = false; + newMod.tags = mod.value().value("tags", std::string("")); + + newMod.primaryFileName = mod.value().value("primary_filename", std::string("")); + int32 primary_filesize = 0; + if (!newMod.primaryFileName.empty()) { + primary_filesize = (int32)get_file_size_safe(newMod.primaryFileName, newMod.path, primary_filesize); + } + newMod.primaryFileSize = mod.value().value("primary_filesize", primary_filesize); + + newMod.previewFileName = mod.value().value("preview_filename", std::string("")); + int32 preview_filesize = 0; + if (!newMod.previewFileName.empty()) { + preview_filesize = (int32)get_file_size_safe(newMod.previewFileName, mod_images_fullpath, preview_filesize); + } + newMod.previewFileSize = mod.value().value("preview_filesize", preview_filesize); + + newMod.total_files_sizes = mod.value().value("total_files_sizes", newMod.primaryFileSize); + newMod.min_game_branch = mod.value().value("min_game_branch", ""); + newMod.max_game_branch = mod.value().value("max_game_branch", ""); + + newMod.workshopItemURL = mod.value().value("workshop_item_url", "https://steamcommunity.com/sharedfiles/filedetails/?id=" + std::string(mod.key())); + newMod.votesUp = mod.value().value("upvotes", (uint32)500); + newMod.votesDown = mod.value().value("downvotes", (uint32)12); + + float score = 0.97f; + try + { + score = newMod.votesUp / (float)(newMod.votesUp + newMod.votesDown); + } catch(...) {} + newMod.score = mod.value().value("score", score); + + newMod.numChildren = mod.value().value("num_children", (uint32)0); + newMod.previewURL = mod.value().value("preview_url", get_mod_preview_url(newMod.previewFileName, std::string(mod.key()))); + + settings_client->addMod(newMod.id, newMod.title, newMod.path); + settings_server->addMod(newMod.id, newMod.title, newMod.path); + settings_client->addModDetails(newMod.id, newMod); + settings_server->addModDetails(newMod.id, newMod); + + PRINT_DEBUG(" parsed mod '%s':", std::string(mod.key()).c_str()); + PRINT_DEBUG(" path (will be used for primary file): '%s'", newMod.path.c_str()); + PRINT_DEBUG(" images path (will be used for preview file): '%s'", mod_images_fullpath.c_str()); + PRINT_DEBUG(" primary_filename: '%s'", newMod.primaryFileName.c_str()); + PRINT_DEBUG(" primary_filesize: %i bytes", newMod.primaryFileSize); + PRINT_DEBUG(" primary file handle: %llu", settings_client->getMod(newMod.id).handleFile); + PRINT_DEBUG(" preview_filename: '%s'", newMod.previewFileName.c_str()); + PRINT_DEBUG(" preview_filesize: %i bytes", newMod.previewFileSize); + PRINT_DEBUG(" preview file handle: %llu", settings_client->getMod(newMod.id).handlePreviewFile); + PRINT_DEBUG(" total_files_sizes: %llu", settings_client->getMod(newMod.id).total_files_sizes); + PRINT_DEBUG(" min_game_branch: '%s'", settings_client->getMod(newMod.id).min_game_branch.c_str()); + PRINT_DEBUG(" max_game_branch: '%s'", settings_client->getMod(newMod.id).max_game_branch.c_str()); + PRINT_DEBUG(" workshop_item_url: '%s'", newMod.workshopItemURL.c_str()); + PRINT_DEBUG(" preview_url: '%s'", newMod.previewURL.c_str()); + } catch (std::exception& e) { + const char *errorMessage = e.what(); + PRINT_DEBUG("MODLOADER ERROR: %s", errorMessage); + } + } +} + +// called if mods.json doesn't exist or invalid +static void try_detect_mods_folder(class Settings *settings_client, Settings *settings_server, const std::string &mods_folder) +{ + std::vector all_mods = Local_Storage::get_folders_path(mods_folder); + for (auto & mod_folder: all_mods) { + std::string mod_images_fullpath = Local_Storage::get_game_settings_path() + "mod_images" + PATH_SEPARATOR + mod_folder; + try { + Mod_entry newMod; + newMod.id = std::stoull(mod_folder); + newMod.title = mod_folder; + + // make sure this is never empty + newMod.path = mods_folder + PATH_SEPARATOR + mod_folder; + + newMod.fileType = k_EWorkshopFileTypeCommunity; + newMod.description = "mod #" + mod_folder; + newMod.steamIDOwner = settings_client->get_local_steam_id().ConvertToUint64(); + newMod.timeCreated = (uint32)three_week_ago_epoch; + newMod.timeUpdated = (uint32)two_week_ago_epoch; + newMod.timeAddedToUserList = (uint32)one_week_ago_epoch; + newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic; + newMod.banned = false; + newMod.acceptedForUse = true; + newMod.tagsTruncated = false; + newMod.tags = ""; + + std::vector mod_primary_files = Local_Storage::get_filenames_path(newMod.path); + newMod.primaryFileName = mod_primary_files.size() ? mod_primary_files[0] : ""; + newMod.primaryFileSize = (int32)get_file_size_safe(newMod.primaryFileName, newMod.path); + + std::vector mod_preview_files = Local_Storage::get_filenames_path(mod_images_fullpath); + newMod.previewFileName = mod_preview_files.size() ? mod_preview_files[0] : ""; + newMod.previewFileSize = (int32)get_file_size_safe(newMod.previewFileName, mod_images_fullpath); + + newMod.total_files_sizes = newMod.primaryFileSize; + + newMod.workshopItemURL = "https://steamcommunity.com/sharedfiles/filedetails/?id=" + mod_folder; + newMod.votesUp = (uint32)500; + newMod.votesDown = (uint32)12; + newMod.score = 0.97f; + newMod.numChildren = (uint32)0; + newMod.previewURL = get_mod_preview_url(newMod.previewFileName, mod_folder); + + settings_client->addMod(newMod.id, newMod.title, newMod.path); + settings_server->addMod(newMod.id, newMod.title, newMod.path); + settings_client->addModDetails(newMod.id, newMod); + settings_server->addModDetails(newMod.id, newMod); + PRINT_DEBUG(" detected mod '%s':", mod_folder.c_str()); + PRINT_DEBUG(" path (will be used for primary file): '%s'", newMod.path.c_str()); + PRINT_DEBUG(" images path (will be used for preview file): '%s'", mod_images_fullpath.c_str()); + PRINT_DEBUG(" primary_filename: '%s'", newMod.primaryFileName.c_str()); + PRINT_DEBUG(" primary_filesize: %i bytes", newMod.primaryFileSize); + PRINT_DEBUG(" primary file handle: %llu", settings_client->getMod(newMod.id).handleFile); + PRINT_DEBUG(" preview_filename: '%s'", newMod.previewFileName.c_str()); + PRINT_DEBUG(" preview_filesize: %i bytes", newMod.previewFileSize); + PRINT_DEBUG(" preview file handle: %llu", settings_client->getMod(newMod.id).handlePreviewFile); + PRINT_DEBUG(" total_files_sizes: '%llu'", newMod.total_files_sizes); + PRINT_DEBUG(" min_game_branch: '%s'", newMod.min_game_branch.c_str()); + PRINT_DEBUG(" max_game_branch: '%s'", newMod.max_game_branch.c_str()); + PRINT_DEBUG(" workshop_item_url: '%s'", newMod.workshopItemURL.c_str()); + PRINT_DEBUG(" preview_url: '%s'", newMod.previewURL.c_str()); + } catch (...) {} + } +} + +static void parse_mods_folder(class Settings *settings_client, Settings *settings_server, class Local_Storage *local_storage) +{ + std::string mods_folder = Local_Storage::get_game_settings_path() + "mods"; + nlohmann::json mod_items = nlohmann::json::object(); + static constexpr auto mods_json_file = "mods.json"; + std::string mods_json_path = Local_Storage::get_game_settings_path() + mods_json_file; + if (local_storage->load_json(mods_json_path, mod_items)) { + PRINT_DEBUG("Attempting to parse mods.json"); + try_parse_mods_file(settings_client, settings_server, mod_items, mods_folder); + } else { // invalid mods.json or doesn't exist + PRINT_DEBUG("Failed to load mods.json, attempting to auto detect mods folder"); + try_detect_mods_folder(settings_client, settings_server, mods_folder); + } + +} + + + +// main::general::crash_printer_location +static void parse_crash_printer_location() +{ + std::string line(common_helpers::string_strip(ini.GetValue("main::general", "crash_printer_location", ""))); + if (line.size()) { + auto crash_path = common_helpers::to_absolute(line, get_full_program_path()); + if (crash_path.size()) { + if (crash_printer::init(crash_path)) { + PRINT_DEBUG("Unhandled crashes will be saved to '%s'", crash_path.c_str()); + } else { + PRINT_DEBUG("Failed to setup unhandled crash printer with path: '%s'", crash_path.c_str()); + } + } + } +} + +// auto_accept_invite.txt +static void parse_auto_accept_invite(class Settings *settings_client, class Settings *settings_server) +{ + std::string auto_accept_list_path = Local_Storage::get_game_settings_path() + "auto_accept_invite.txt"; + std::ifstream input( std::filesystem::u8path(auto_accept_list_path) ); + if (input.is_open()) { + bool accept_any_invite = true; + common_helpers::consume_bom(input); + for( std::string line; getline( input, line ); ) { + line = common_helpers::string_strip(line); + if (!line.empty()) { + accept_any_invite = false; + try { + auto friend_id = std::stoull(line); + settings_client->addFriendToOverlayAutoAccept((uint64_t)friend_id); + settings_server->addFriendToOverlayAutoAccept((uint64_t)friend_id); + PRINT_DEBUG("Auto accepting overlay invitations from user with ID (SteamID64) = %llu", friend_id); + } catch (...) {} + } + } + + if (accept_any_invite) { + PRINT_DEBUG("Auto accepting any overlay invitation"); + settings_client->acceptAnyOverlayInvites(true); + settings_server->acceptAnyOverlayInvites(true); + } else { + settings_client->acceptAnyOverlayInvites(false); + settings_server->acceptAnyOverlayInvites(false); + } + } +} + +// 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 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; + } + + settings_client->is_beta_branch = ini.GetBoolValue("app::general", "is_beta_branch", settings_client->is_beta_branch); + settings_server->is_beta_branch = ini.GetBoolValue("app::general", "is_beta_branch", settings_server->is_beta_branch); + + + // 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::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(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(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(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(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(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(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) +{ + std::string line(common_helpers::to_upper(common_helpers::string_strip(ini.GetValue("user::general", "ip_country", "")))); + if (line.empty()) { + line = DEFAULT_IP_COUNTRY; + save_global_ini_value( + local_storage, + config_ini_user, + "user::general", "ip_country", IniValue(line.c_str()), + "ISO 3166-1-alpha-2 format, use this link to get the 'Alpha-2' country code: https://www.iban.com/country-codes" + ); + } + + // ISO 3166-1-alpha-2 format is 2 chars only + if (line.size() == 2) { + settings_client->ip_country = line; + settings_server->ip_country = line; + PRINT_DEBUG("Setting IP country to: '%s'", line.c_str()); + } +} + +// overlay::general +static void parse_overlay_general_config(class Settings *settings_client, class Settings *settings_server) +{ + settings_client->disable_overlay = !ini.GetBoolValue("overlay::general", "enable_experimental_overlay", !settings_client->disable_overlay); + settings_server->disable_overlay = !ini.GetBoolValue("overlay::general", "enable_experimental_overlay", !settings_server->disable_overlay); + + { + auto val = ini.GetLongValue("overlay::general", "hook_delay_sec", -1); + if (val >= 0) { + settings_client->overlay_hook_delay_sec = val; + settings_server->overlay_hook_delay_sec = val; + PRINT_DEBUG("Setting overlay hook delay to %i seconds", (int)val); + } + } + + { + auto val = ini.GetLongValue("overlay::general", "renderer_detector_timeout_sec", -1); + if (val > 0) { + settings_client->overlay_renderer_detector_timeout_sec = val; + settings_server->overlay_renderer_detector_timeout_sec = val; + PRINT_DEBUG("Setting overlay renderer detector timeout to %i seconds", (int)val); + } + } + + settings_client->disable_overlay_achievement_notification = ini.GetBoolValue("overlay::general", "disable_achievement_notification", settings_client->disable_overlay_achievement_notification); + settings_server->disable_overlay_achievement_notification = ini.GetBoolValue("overlay::general", "disable_achievement_notification", settings_server->disable_overlay_achievement_notification); + + settings_client->disable_overlay_friend_notification = ini.GetBoolValue("overlay::general", "disable_friend_notification", settings_client->disable_overlay_friend_notification); + settings_server->disable_overlay_friend_notification = ini.GetBoolValue("overlay::general", "disable_friend_notification", settings_server->disable_overlay_friend_notification); + + settings_client->disable_overlay_achievement_progress = ini.GetBoolValue("overlay::general", "disable_achievement_progress", settings_client->disable_overlay_achievement_progress); + settings_server->disable_overlay_achievement_progress = ini.GetBoolValue("overlay::general", "disable_achievement_progress", settings_server->disable_overlay_achievement_progress); + + settings_client->disable_overlay_warning_any = ini.GetBoolValue("overlay::general", "disable_warning_any", settings_client->disable_overlay_warning_any); + settings_server->disable_overlay_warning_any = ini.GetBoolValue("overlay::general", "disable_warning_any", settings_server->disable_overlay_warning_any); + + settings_client->disable_overlay_warning_bad_appid = ini.GetBoolValue("overlay::general", "disable_warning_bad_appid", settings_client->disable_overlay_warning_bad_appid); + settings_server->disable_overlay_warning_bad_appid = ini.GetBoolValue("overlay::general", "disable_warning_bad_appid", settings_server->disable_overlay_warning_bad_appid); + + settings_client->disable_overlay_warning_local_save = ini.GetBoolValue("overlay::general", "disable_warning_local_save", settings_client->disable_overlay_warning_local_save); + settings_server->disable_overlay_warning_local_save = ini.GetBoolValue("overlay::general", "disable_warning_local_save", settings_server->disable_overlay_warning_local_save); + + settings_client->overlay_upload_achs_icons_to_gpu = ini.GetBoolValue("overlay::general", "upload_achievements_icons_to_gpu", settings_client->overlay_upload_achs_icons_to_gpu); + settings_server->overlay_upload_achs_icons_to_gpu = ini.GetBoolValue("overlay::general", "upload_achievements_icons_to_gpu", settings_server->overlay_upload_achs_icons_to_gpu); + + settings_client->overlay_always_show_user_info = ini.GetBoolValue("overlay::general", "overlay_always_show_user_info", settings_client->overlay_always_show_user_info); + settings_server->overlay_always_show_user_info = ini.GetBoolValue("overlay::general", "overlay_always_show_user_info", settings_server->overlay_always_show_user_info); + + settings_client->overlay_always_show_fps = ini.GetBoolValue("overlay::general", "overlay_always_show_fps", settings_client->overlay_always_show_fps); + settings_server->overlay_always_show_fps = ini.GetBoolValue("overlay::general", "overlay_always_show_fps", settings_server->overlay_always_show_fps); + + settings_client->overlay_always_show_frametime = ini.GetBoolValue("overlay::general", "overlay_always_show_frametime", settings_client->overlay_always_show_frametime); + settings_server->overlay_always_show_frametime = ini.GetBoolValue("overlay::general", "overlay_always_show_frametime", settings_server->overlay_always_show_frametime); + + settings_client->overlay_always_show_playtime = ini.GetBoolValue("overlay::general", "overlay_always_show_playtime", settings_client->overlay_always_show_playtime); + settings_server->overlay_always_show_playtime = ini.GetBoolValue("overlay::general", "overlay_always_show_playtime", settings_server->overlay_always_show_playtime); + + { + auto val = ini.GetLongValue("overlay::general", "fps_averaging_window", settings_client->overlay_fps_avg_window); + if (val > 0) { + settings_client->overlay_fps_avg_window = val; + } + } + + { + auto val = ini.GetLongValue("overlay::general", "fps_averaging_window", settings_server->overlay_fps_avg_window); + if (val > 0) { + settings_server->overlay_fps_avg_window = val; + } + } + +} + +// main::misc::steam_game_stats_reports_dir +static void parse_steam_game_stats_reports_dir(class Settings *settings_client, class Settings *settings_server) +{ + std::string line(common_helpers::string_strip(ini.GetValue("main::misc", "steam_game_stats_reports_dir", ""))); + if (line.size()) { + auto folder = common_helpers::to_absolute(line, get_full_program_path()); + if (folder.size()) { + PRINT_DEBUG("ISteamGameStats reports will be saved to '%s'", folder.c_str()); + settings_client->steam_game_stats_reports_dir = folder; + settings_server->steam_game_stats_reports_dir = folder; + } + } +} + +// mainly enable/disable features +static void parse_simple_features(class Settings *settings_client, class Settings *settings_server) +{ + // [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); + + settings_client->use_gc_token = ini.GetBoolValue("main::general", "gc_token", settings_client->use_gc_token); + settings_server->use_gc_token = ini.GetBoolValue("main::general", "gc_token", settings_server->use_gc_token); + + settings_client->disable_account_avatar = !ini.GetBoolValue("main::general", "enable_account_avatar", !settings_client->disable_account_avatar); + settings_server->disable_account_avatar = !ini.GetBoolValue("main::general", "enable_account_avatar", !settings_server->disable_account_avatar); + + settings_client->steam_deck = ini.GetBoolValue("main::general", "steam_deck", settings_client->steam_deck); + settings_server->steam_deck = ini.GetBoolValue("main::general", "steam_deck", settings_server->steam_deck); + + settings_client->immediate_gameserver_stats = ini.GetBoolValue("main::general", "immediate_gameserver_stats", settings_client->immediate_gameserver_stats); + settings_server->immediate_gameserver_stats = ini.GetBoolValue("main::general", "immediate_gameserver_stats", settings_server->immediate_gameserver_stats); + + settings_client->matchmaking_server_details_via_source_query = ini.GetBoolValue("main::general", "matchmaking_server_details_via_source_query", settings_client->matchmaking_server_details_via_source_query); + settings_server->matchmaking_server_details_via_source_query = ini.GetBoolValue("main::general", "matchmaking_server_details_via_source_query", settings_server->matchmaking_server_details_via_source_query); + + settings_client->matchmaking_server_list_always_lan_type = !ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", !settings_client->matchmaking_server_list_always_lan_type); + settings_server->matchmaking_server_list_always_lan_type = !ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", !settings_server->matchmaking_server_list_always_lan_type); + + + // [main::connectivity] + settings_client->disable_networking = ini.GetBoolValue("main::connectivity", "disable_networking", settings_client->disable_networking); + settings_server->disable_networking = ini.GetBoolValue("main::connectivity", "disable_networking", settings_server->disable_networking); + + settings_client->disable_sharing_stats_with_gameserver = ini.GetBoolValue("main::connectivity", "disable_sharing_stats_with_gameserver", settings_client->disable_sharing_stats_with_gameserver); + settings_server->disable_sharing_stats_with_gameserver = ini.GetBoolValue("main::connectivity", "disable_sharing_stats_with_gameserver", settings_server->disable_sharing_stats_with_gameserver); + + settings_client->disable_source_query = ini.GetBoolValue("main::connectivity", "disable_source_query", settings_client->disable_source_query); + settings_server->disable_source_query = ini.GetBoolValue("main::connectivity", "disable_source_query", settings_server->disable_source_query); + + settings_client->share_leaderboards_over_network = ini.GetBoolValue("main::connectivity", "share_leaderboards_over_network", settings_client->share_leaderboards_over_network); + settings_server->share_leaderboards_over_network = ini.GetBoolValue("main::connectivity", "share_leaderboards_over_network", settings_server->share_leaderboards_over_network); + + settings_client->disable_lobby_creation = ini.GetBoolValue("main::connectivity", "disable_lobby_creation", settings_client->disable_lobby_creation); + settings_server->disable_lobby_creation = ini.GetBoolValue("main::connectivity", "disable_lobby_creation", settings_server->disable_lobby_creation); + + settings_client->download_steamhttp_requests = ini.GetBoolValue("main::connectivity", "download_steamhttp_requests", settings_client->download_steamhttp_requests); + settings_server->download_steamhttp_requests = ini.GetBoolValue("main::connectivity", "download_steamhttp_requests", settings_server->download_steamhttp_requests); + + + // [main::misc] + settings_client->achievement_bypass = ini.GetBoolValue("main::misc", "achievements_bypass", settings_client->achievement_bypass); + settings_server->achievement_bypass = ini.GetBoolValue("main::misc", "achievements_bypass", settings_server->achievement_bypass); + + settings_client->force_steamhttp_success = ini.GetBoolValue("main::misc", "force_steamhttp_success", settings_client->force_steamhttp_success); + settings_server->force_steamhttp_success = ini.GetBoolValue("main::misc", "force_steamhttp_success", settings_server->force_steamhttp_success); + + settings_client->disable_steamoverlaygameid_env_var = ini.GetBoolValue("main::misc", "disable_steamoverlaygameid_env_var", settings_client->disable_steamoverlaygameid_env_var); + settings_server->disable_steamoverlaygameid_env_var = ini.GetBoolValue("main::misc", "disable_steamoverlaygameid_env_var", settings_server->disable_steamoverlaygameid_env_var); + + settings_client->enable_builtin_preowned_ids = ini.GetBoolValue("main::misc", "enable_steam_preowned_ids", settings_client->enable_builtin_preowned_ids); + settings_server->enable_builtin_preowned_ids = ini.GetBoolValue("main::misc", "enable_steam_preowned_ids", settings_server->enable_builtin_preowned_ids); +} - The Goldberg Emulator is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. +// [main::gamepad] +static void parse_user_gamepad_settings(class Settings* settings_client, class Settings* settings_server) { + settings_client->flip_nintendo_layout = ini.GetBoolValue("main::gamepad", "flip_nintendo_layout", settings_client->flip_nintendo_layout); + settings_server->flip_nintendo_layout = ini.GetBoolValue("main::gamepad", "flip_nintendo_layout", settings_server->flip_nintendo_layout); - The Goldberg Emulator is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + settings_client->combine_joycons = ini.GetBoolValue("main::gamepad", "combine_joycons", settings_client->combine_joycons); + settings_server->combine_joycons = ini.GetBoolValue("main::gamepad", "combine_joycons", settings_server->combine_joycons); - You should have received a copy of the GNU Lesser General Public - License along with the Goldberg Emulator; if not, see - . */ - -#include "dll/settings_parser.h" - -#define SI_CONVERT_GENERIC -#define SI_SUPPORT_IOSTREAMS -#define SI_NO_MBCS -#include "simpleini/SimpleIni.h" - - -constexpr const static char config_ini_app[] = "configs.app.ini"; -constexpr const static char config_ini_main[] = "configs.main.ini"; -constexpr const static char config_ini_overlay[] = "configs.overlay.ini"; -constexpr const static char config_ini_user[] = "configs.user.ini"; - -static CSimpleIniA ini{}; - -typedef struct IniValue { - enum class Type { - STR, - BOOL, - DOUBLE, - LONG, - }; - - explicit IniValue(const char *new_val): type(Type::STR), val_str(new_val) {} - explicit IniValue(bool new_val): type(Type::BOOL), val_bool(new_val) {} - explicit IniValue(double new_val): type(Type::DOUBLE), val_double(new_val) {} - explicit IniValue(long new_val): type(Type::LONG), val_long(new_val) {} - - Type type; - union { - const char *val_str; - bool val_bool; - double val_double; - long val_long; - - }; -} IniValue; - -static void save_global_ini_value(class Local_Storage *local_storage, const char *filename, const char *section, const char *key, IniValue val, const char *comment = nullptr) { - CSimpleIniA new_ini{}; - new_ini.SetUnicode(); - new_ini.SetSpaces(false); - - auto sz = local_storage->data_settings_size(filename); - if (sz > 0) { - std::vector ini_file_data(sz); - auto read = local_storage->get_data_settings(filename, &ini_file_data[0], static_cast(ini_file_data.size())); - if (read == sz) { - new_ini.LoadData(&ini_file_data[0], ini_file_data.size()); - } - } - - std::string comment_str{}; - if (comment && comment[0]) { - comment_str.append("# ").append(comment); - comment = comment_str.c_str(); - } - - switch (val.type) - { - case IniValue::Type::STR: - new_ini.SetValue(section, key, val.val_str, comment); - break; - - case IniValue::Type::BOOL: - new_ini.SetBoolValue(section, key, val.val_bool, comment); - break; - - case IniValue::Type::DOUBLE: - new_ini.SetDoubleValue(section, key, val.val_double, comment); - break; - - case IniValue::Type::LONG: - new_ini.SetLongValue(section, key, val.val_long, comment); - break; - - default: break; - } - - std::string ini_buff{}; - if (new_ini.Save(ini_buff, false) == SI_OK) { - local_storage->store_data_settings(filename, &ini_buff[0], static_cast(ini_buff.size())); - } - -} - -static void merge_ini(const CSimpleIniA &new_ini, bool overwrite = false) { - std::list sections{}; - new_ini.GetAllSections(sections); - for (auto const &sec : sections) { - std::list keys{}; - new_ini.GetAllKeys(sec.pItem, keys); - for (auto const &key : keys) { - // only add the key if it didn't exist already - if (!ini.KeyExists(sec.pItem, key.pItem) || overwrite) { - std::list vals{}; - new_ini.GetAllValues(sec.pItem, key.pItem, vals); - for (const auto &val : vals) { - ini.SetValue(sec.pItem, key.pItem, val.pItem); - } - } - } - } -} - - -Overlay_Appearance::NotificationPosition Overlay_Appearance::translate_notification_position(const std::string &str) -{ - if (str == "top_left") return NotificationPosition::top_left; - else if (str == "top_center") return NotificationPosition::top_center; - else if (str == "top_right") return NotificationPosition::top_right; - else if (str == "bot_left") return NotificationPosition::bot_left; - else if (str == "bot_center") return NotificationPosition::bot_center; - else if (str == "bot_right") return NotificationPosition::bot_right; - - PRINT_DEBUG("Invalid position '%s'", str.c_str()); - return default_pos; -} - - -// custom_broadcasts.txt -static void load_custom_broadcasts(const std::string &base_path, std::set &custom_broadcasts) -{ - const std::string broadcasts_filepath(base_path + "custom_broadcasts.txt"); - std::ifstream broadcasts_file(std::filesystem::u8path(broadcasts_filepath)); - if (broadcasts_file.is_open()) { - common_helpers::consume_bom(broadcasts_file); - PRINT_DEBUG("loading broadcasts file '%s'", broadcasts_filepath.c_str()); - std::string line{}; - while (std::getline(broadcasts_file, line)) { - if (line.length() <= 0) continue; - - std::set ips = Networking::resolve_ip(line); - custom_broadcasts.insert(ips.begin(), ips.end()); - PRINT_DEBUG("added ip/port to broadcast list '%s'", line.c_str()); - } - } -} - -// subscribed_groups_clans.txt -static void load_subscribed_groups_clans(const std::string &base_path, Settings *settings_client, Settings *settings_server) -{ - const std::string clans_filepath(base_path + "subscribed_groups_clans.txt"); - std::ifstream clans_file(std::filesystem::u8path(clans_filepath)); - if (clans_file.is_open()) { - common_helpers::consume_bom(clans_file); - PRINT_DEBUG("loading group clans file '%s'", clans_filepath.c_str()); - std::string line{}; - while (std::getline(clans_file, line)) { - if (line.length() <= 0) continue; - - std::size_t seperator1 = line.find("\t\t"); - std::size_t seperator2 = line.rfind("\t\t"); - std::string clan_id; - std::string clan_name; - std::string clan_tag; - if ((seperator1 != std::string::npos) && (seperator2 != std::string::npos)) { - clan_id = line.substr(0, seperator1); - clan_name = line.substr(seperator1+2, seperator2-2); - clan_tag = line.substr(seperator2+2); - - // fix persistant tabbing problem for clan name - std::size_t seperator3 = clan_name.find("\t"); - std::string clan_name_fix = clan_name.substr(0, seperator3); - clan_name = clan_name_fix; - } - - Group_Clans nclan; - nclan.id = CSteamID( std::stoull(clan_id.c_str(), NULL, 0) ); - nclan.name = clan_name; - nclan.tag = clan_tag; - - try { - settings_client->subscribed_groups_clans.push_back(nclan); - settings_server->subscribed_groups_clans.push_back(nclan); - PRINT_DEBUG("Added clan %s", clan_name.c_str()); - } catch (...) {} - } - } -} - -// overlay::appearance -static void load_overlay_appearance(class Settings *settings_client, class Settings *settings_server, class Local_Storage *local_storage) -{ - std::list names{}; - if (!ini.GetAllKeys("overlay::appearance", names) || names.empty()) return; - - for (const auto &name_ent : names) { - auto val_ptr = ini.GetValue("overlay::appearance", name_ent.pItem); - if (!val_ptr || !val_ptr[0]) continue; - - std::string name(name_ent.pItem); - std::string value(val_ptr); - PRINT_DEBUG(" Overlay appearance line '%s'='%s'", name.c_str(), value.c_str()); - try { - if (name.compare("Font_Override") == 0) { - value = common_helpers::string_strip(value); - // first try the local settings folder - std::string nfont_override(common_helpers::to_absolute(value, Local_Storage::get_game_settings_path() + "fonts")); - if (!common_helpers::file_exist(nfont_override)) { - nfont_override.clear(); - } - - // then try the global settings folder - if (nfont_override.empty()) { - nfont_override = common_helpers::to_absolute(value, local_storage->get_global_settings_path() + "fonts"); - if (!common_helpers::file_exist(nfont_override)) { - nfont_override.clear(); - } - } - - if (nfont_override.size()) { - settings_client->overlay_appearance.font_override = nfont_override; - settings_server->overlay_appearance.font_override = nfont_override; - PRINT_DEBUG(" loaded font '%s'", nfont_override.c_str()); - } else { - PRINT_DEBUG(" ERROR font file '%s' doesn't exist and will be ignored", value.c_str()); - } - } else if (name.compare("Font_Size") == 0) { - float nfont_size = std::stof(value, NULL); - settings_client->overlay_appearance.font_size = nfont_size; - settings_server->overlay_appearance.font_size = nfont_size; - } else if (name.compare("Icon_Size") == 0) { - float nicon_size = std::stof(value, NULL); - settings_client->overlay_appearance.icon_size = nicon_size; - settings_server->overlay_appearance.icon_size = nicon_size; - } else if (name.compare("Font_Glyph_Extra_Spacing_x") == 0) { - float size = std::stof(value, NULL); - settings_client->overlay_appearance.font_glyph_extra_spacing_x = size; - settings_server->overlay_appearance.font_glyph_extra_spacing_x = size; - } else if (name.compare("Font_Glyph_Extra_Spacing_y") == 0) { - float size = std::stof(value, NULL); - settings_client->overlay_appearance.font_glyph_extra_spacing_y = size; - settings_server->overlay_appearance.font_glyph_extra_spacing_y = size; - } else if (name.compare("Notification_R") == 0) { - float nnotification_r = std::stof(value, NULL); - settings_client->overlay_appearance.notification_r = nnotification_r; - settings_server->overlay_appearance.notification_r = nnotification_r; - } else if (name.compare("Notification_G") == 0) { - float nnotification_g = std::stof(value, NULL); - settings_client->overlay_appearance.notification_g = nnotification_g; - settings_server->overlay_appearance.notification_g = nnotification_g; - } else if (name.compare("Notification_B") == 0) { - float nnotification_b = std::stof(value, NULL); - settings_client->overlay_appearance.notification_b = nnotification_b; - settings_server->overlay_appearance.notification_b = nnotification_b; - } else if (name.compare("Notification_A") == 0) { - float nnotification_a = std::stof(value, NULL); - settings_client->overlay_appearance.notification_a = nnotification_a; - settings_server->overlay_appearance.notification_a = nnotification_a; - } else if (name.compare("Notification_Rounding") == 0) { - float nnotification_rounding = std::stof(value, NULL); - settings_client->overlay_appearance.notification_rounding = nnotification_rounding; - settings_server->overlay_appearance.notification_rounding = nnotification_rounding; - } else if (name.compare("Notification_Margin_x") == 0) { - float val = std::stof(value, NULL); - settings_client->overlay_appearance.notification_margin_x = val; - settings_server->overlay_appearance.notification_margin_x = val; - } else if (name.compare("Notification_Margin_y") == 0) { - float val = std::stof(value, NULL); - settings_client->overlay_appearance.notification_margin_y = val; - settings_server->overlay_appearance.notification_margin_y = val; - } else if (name.compare("Notification_Animation") == 0) { - uint32 nnotification_animation = (uint32)(std::stof(value, NULL) * 1000.0f); // convert sec to milli - settings_client->overlay_appearance.notification_animation = nnotification_animation; - settings_server->overlay_appearance.notification_animation = nnotification_animation; - } else if (name.compare("Notification_Duration_Progress") == 0) { - uint32 time = (uint32)(std::stof(value, NULL) * 1000.0f); // convert sec to milli - settings_client->overlay_appearance.notification_duration_progress = time; - settings_server->overlay_appearance.notification_duration_progress = time; - } else if (name.compare("Notification_Duration_Achievement") == 0) { - uint32 time = (uint32)(std::stof(value, NULL) * 1000.0f); // convert sec to milli - settings_client->overlay_appearance.notification_duration_achievement = time; - settings_server->overlay_appearance.notification_duration_achievement = time; - } else if (name.compare("Notification_Duration_Invitation") == 0) { - uint32 time = (uint32)(std::stof(value, NULL) * 1000.0f); // convert sec to milli - settings_client->overlay_appearance.notification_duration_invitation = time; - settings_server->overlay_appearance.notification_duration_invitation = time; - } else if (name.compare("Notification_Duration_Chat") == 0) { - uint32 time = (uint32)(std::stof(value, NULL) * 1000.0f); // convert sec to milli - settings_client->overlay_appearance.notification_duration_chat = time; - settings_server->overlay_appearance.notification_duration_chat = time; - } else if (name.compare("Achievement_Unlock_Datetime_Format") == 0) { - settings_client->overlay_appearance.ach_unlock_datetime_format = value; - settings_server->overlay_appearance.ach_unlock_datetime_format = value; - } else if (name.compare("Background_R") == 0) { - float nbackground_r = std::stof(value, NULL); - settings_client->overlay_appearance.background_r = nbackground_r; - settings_server->overlay_appearance.background_r = nbackground_r; - } else if (name.compare("Background_G") == 0) { - float nbackground_g = std::stof(value, NULL); - settings_client->overlay_appearance.background_g = nbackground_g; - settings_server->overlay_appearance.background_g = nbackground_g; - } else if (name.compare("Background_B") == 0) { - float nbackground_b = std::stof(value, NULL); - settings_client->overlay_appearance.background_b = nbackground_b; - settings_server->overlay_appearance.background_b = nbackground_b; - } else if (name.compare("Background_A") == 0) { - float nbackground_a = std::stof(value, NULL); - settings_client->overlay_appearance.background_a = nbackground_a; - settings_server->overlay_appearance.background_a = nbackground_a; - } else if (name.compare("Element_R") == 0) { - float nelement_r = std::stof(value, NULL); - settings_client->overlay_appearance.element_r = nelement_r; - settings_server->overlay_appearance.element_r = nelement_r; - } else if (name.compare("Element_G") == 0) { - float nelement_g = std::stof(value, NULL); - settings_client->overlay_appearance.element_g = nelement_g; - settings_server->overlay_appearance.element_g = nelement_g; - } else if (name.compare("Element_B") == 0) { - float nelement_b = std::stof(value, NULL); - settings_client->overlay_appearance.element_b = nelement_b; - settings_server->overlay_appearance.element_b = nelement_b; - } else if (name.compare("Element_A") == 0) { - float nelement_a = std::stof(value, NULL); - settings_client->overlay_appearance.element_a = nelement_a; - settings_server->overlay_appearance.element_a = nelement_a; - } else if (name.compare("ElementHovered_R") == 0) { - float nelement_hovered_r = std::stof(value, NULL); - settings_client->overlay_appearance.element_hovered_r = nelement_hovered_r; - settings_server->overlay_appearance.element_hovered_r = nelement_hovered_r; - } else if (name.compare("ElementHovered_G") == 0) { - float nelement_hovered_g = std::stof(value, NULL); - settings_client->overlay_appearance.element_hovered_g = nelement_hovered_g; - settings_server->overlay_appearance.element_hovered_g = nelement_hovered_g; - } else if (name.compare("ElementHovered_B") == 0) { - float nelement_hovered_b = std::stof(value, NULL); - settings_client->overlay_appearance.element_hovered_b = nelement_hovered_b; - settings_server->overlay_appearance.element_hovered_b = nelement_hovered_b; - } else if (name.compare("ElementHovered_A") == 0) { - float nelement_hovered_a = std::stof(value, NULL); - settings_client->overlay_appearance.element_hovered_a = nelement_hovered_a; - settings_server->overlay_appearance.element_hovered_a = nelement_hovered_a; - } else if (name.compare("ElementActive_R") == 0) { - float nelement_active_r = std::stof(value, NULL); - settings_client->overlay_appearance.element_active_r = nelement_active_r; - settings_server->overlay_appearance.element_active_r = nelement_active_r; - } else if (name.compare("ElementActive_G") == 0) { - float nelement_active_g = std::stof(value, NULL); - settings_client->overlay_appearance.element_active_g = nelement_active_g; - settings_server->overlay_appearance.element_active_g = nelement_active_g; - } else if (name.compare("ElementActive_B") == 0) { - float nelement_active_b = std::stof(value, NULL); - settings_client->overlay_appearance.element_active_b = nelement_active_b; - settings_server->overlay_appearance.element_active_b = nelement_active_b; - } else if (name.compare("ElementActive_A") == 0) { - float nelement_active_a = std::stof(value, NULL); - settings_client->overlay_appearance.element_active_a = nelement_active_a; - settings_server->overlay_appearance.element_active_a = nelement_active_a; - } else if (name.compare("PosAchievement") == 0) { - auto pos = Overlay_Appearance::translate_notification_position(value); - settings_client->overlay_appearance.ach_earned_pos = pos; - settings_server->overlay_appearance.ach_earned_pos = pos; - } else if (name.compare("PosInvitation") == 0) { - auto pos = Overlay_Appearance::translate_notification_position(value); - settings_client->overlay_appearance.invite_pos = pos; - settings_server->overlay_appearance.invite_pos = pos; - } else if (name.compare("PosChatMsg") == 0) { - auto pos = Overlay_Appearance::translate_notification_position(value); - settings_client->overlay_appearance.chat_msg_pos = pos; - settings_server->overlay_appearance.chat_msg_pos = pos; - // >>> FPS background - } else if (name.compare("Stats_Background_R") == 0) { - float val = std::stof(value, NULL); - settings_client->overlay_appearance.stats_background_r = val; - settings_server->overlay_appearance.stats_background_r = val; - } else if (name.compare("Stats_Background_G") == 0) { - float val = std::stof(value, NULL); - settings_client->overlay_appearance.stats_background_g = val; - settings_server->overlay_appearance.stats_background_g = val; - } else if (name.compare("Stats_Background_B") == 0) { - float val = std::stof(value, NULL); - settings_client->overlay_appearance.stats_background_b = val; - settings_server->overlay_appearance.stats_background_b = val; - } else if (name.compare("Stats_Background_A") == 0) { - float val = std::stof(value, NULL); - settings_client->overlay_appearance.stats_background_a = val; - settings_server->overlay_appearance.stats_background_a = val; - // FPS background END <<< - // >>> FPS text color - } else if (name.compare("Stats_Text_R") == 0) { - float val = std::stof(value, NULL); - settings_client->overlay_appearance.stats_text_r = val; - settings_server->overlay_appearance.stats_text_r = val; - } else if (name.compare("Stats_Text_G") == 0) { - float val = std::stof(value, NULL); - settings_client->overlay_appearance.stats_text_g = val; - settings_server->overlay_appearance.stats_text_g = val; - } else if (name.compare("Stats_Text_B") == 0) { - float val = std::stof(value, NULL); - settings_client->overlay_appearance.stats_text_b = val; - settings_server->overlay_appearance.stats_text_b = val; - } else if (name.compare("Stats_Text_A") == 0) { - float val = std::stof(value, NULL); - settings_client->overlay_appearance.stats_text_a = val; - settings_server->overlay_appearance.stats_text_a = val; - // FPS text color END <<< - // >>> FPS position - } else if (name.compare("Stats_Pos_x") == 0) { - auto pos = std::stof(value); - if (pos < 0) { - pos = 0; - } else if (pos > 1.0f) { - pos = 1.0f; - } - settings_client->overlay_stats_pos_x = pos; - settings_server->overlay_stats_pos_x = pos; - } else if (name.compare("Stats_Pos_y") == 0) { - auto pos = std::stof(value); - if (pos < 0) { - pos = 0; - } else if (pos > 1.0f) { - pos = 1.0f; - } - settings_client->overlay_stats_pos_y = pos; - settings_server->overlay_stats_pos_y = pos; - // FPS position END <<< - } else { - PRINT_DEBUG("unknown overlay appearance setting"); - } - - } catch (...) { } - } -} - -template -static void split_string(const std::string &s, char delim, Out result) { - std::stringstream ss(s); - std::string item{}; - while (std::getline(ss, item, delim)) { - *(result++) = item; - } -} - -// folder "controller" -static void load_gamecontroller_settings(Settings *settings) -{ - std::string path = Local_Storage::get_game_settings_path() + "controller"; - std::vector paths = Local_Storage::get_filenames_path(path); - - for (auto & p: paths) { - size_t length = p.length(); - if (length < 4) continue; - if ( std::toupper(p.back()) != 'T') continue; - if ( std::toupper(p[length - 2]) != 'X') continue; - if ( std::toupper(p[length - 3]) != 'T') continue; - if (p[length - 4] != '.') continue; - - PRINT_DEBUG("controller config %s", p.c_str()); - std::string action_set_name = p.substr(0, length - 4); - std::transform(action_set_name.begin(), action_set_name.end(), action_set_name.begin(),[](unsigned char c){ return std::toupper(c); }); - - std::string controller_config_path = path + PATH_SEPARATOR + p; - std::ifstream input( std::filesystem::u8path(controller_config_path) ); - if (input.is_open()) { - common_helpers::consume_bom(input); - std::map, std::string>> button_pairs; - - for( std::string line; getline( input, line ); ) { - if (!line.empty() && line[line.length()-1] == '\n') { - line.pop_back(); - } - - if (!line.empty() && line[line.length()-1] == '\r') { - line.pop_back(); - } - - std::string action_name; - std::string button_name; - std::string source_mode; - - std::size_t deliminator = line.find("="); - if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) { - action_name = line.substr(0, deliminator); - std::size_t deliminator2 = line.find("=", deliminator + 1); - - if (deliminator2 != std::string::npos && deliminator2 != line.size()) { - button_name = line.substr(deliminator + 1, deliminator2 - (deliminator + 1)); - source_mode = line.substr(deliminator2 + 1); - } else { - button_name = line.substr(deliminator + 1); - source_mode = ""; - } - } - - std::transform(action_name.begin(), action_name.end(), action_name.begin(),[](unsigned char c){ return std::toupper(c); }); - std::transform(button_name.begin(), button_name.end(), button_name.begin(),[](unsigned char c){ return std::toupper(c); }); - std::pair, std::string> button_config = {{}, source_mode}; - split_string(button_name, ',', std::inserter(button_config.first, button_config.first.begin())); - button_pairs[action_name] = button_config; - PRINT_DEBUG("Added %s %s %s", action_name.c_str(), button_name.c_str(), source_mode.c_str()); - } - - settings->controller_settings.action_sets[action_set_name] = button_pairs; - PRINT_DEBUG("Added %zu action names to %s", button_pairs.size(), action_set_name.c_str()); - } - } - - settings->glyphs_directory = path + (PATH_SEPARATOR "glyphs" PATH_SEPARATOR); -} - -// steam_appid.txt -static uint32 parse_steam_app_id(const std::string &program_path) -{ - uint32 appid = 0; - - // try steam_settings folder - char array[10] = {}; - array[0] = '0'; - Local_Storage::get_file_data(Local_Storage::get_game_settings_path() + "steam_appid.txt", array, sizeof(array) - 1); - try { - appid = std::stoul(array); - } catch (...) {} - - // try current dir - if (!appid) { - memset(array, 0, sizeof(array)); - array[0] = '0'; - Local_Storage::get_file_data("steam_appid.txt", array, sizeof(array) - 1); - try { - appid = std::stoul(array); - } catch (...) {} - } - - // try exe dir - if (!appid) { - memset(array, 0, sizeof(array)); - array[0] = '0'; - Local_Storage::get_file_data(program_path + "steam_appid.txt", array, sizeof(array) - 1); - try { - appid = std::stoul(array); - } catch (...) {} - } - - // try env vars - if (!appid) { - std::string str_appid = get_env_variable("SteamAppId"); - std::string str_gameid = get_env_variable("SteamGameId"); - std::string str_overlay_gameid = get_env_variable("SteamOverlayGameId"); - - PRINT_DEBUG("str_appid %s str_gameid: %s str_overlay_gameid: %s", str_appid.c_str(), str_gameid.c_str(), str_overlay_gameid.c_str()); - uint32 appid_env = 0; - uint32 gameid_env = 0; - uint32 overlay_gameid = 0; - - if (str_appid.size() > 0) { - try { - appid_env = std::stoul(str_appid); - } catch (...) { - appid_env = 0; - } - } - - if (str_gameid.size() > 0) { - try { - gameid_env = std::stoul(str_gameid); - } catch (...) { - gameid_env = 0; - } - } - - if (str_overlay_gameid.size() > 0) { - try { - overlay_gameid = std::stoul(str_overlay_gameid); - } catch (...) { - overlay_gameid = 0; - } - } - - PRINT_DEBUG("appid_env %u gameid_env: %u overlay_gameid: %u", appid_env, gameid_env, overlay_gameid); - if (appid_env) { - appid = appid_env; - } - - if (gameid_env) { - appid = gameid_env; - } - - if (overlay_gameid) { - appid = overlay_gameid; - } - } - - PRINT_DEBUG("final appid = %u", appid); - return appid; -} - -// user::saves::local_save_path -static bool parse_local_save(std::string &save_path) -{ - auto ptr = ini.GetValue("user::saves", "local_save_path"); - if (!ptr || !ptr[0]) return false; - - save_path = common_helpers::to_absolute(common_helpers::string_strip(ptr), Local_Storage::get_program_path()); - if (save_path.size() && save_path.back() != *PATH_SEPARATOR) { - save_path.push_back(*PATH_SEPARATOR); - } - PRINT_DEBUG("using local save path '%s'", save_path.c_str()); - return true; -} - -// main::connectivity::listen_port -static uint16 parse_listen_port(class Local_Storage *local_storage) -{ - uint16 port = static_cast(ini.GetLongValue("main::connectivity", "listen_port")); - if (port == 0) { - port = DEFAULT_PORT; - } - return port; -} - -// user::general::account_name -static std::string parse_account_name(class Local_Storage *local_storage) -{ - auto name = ini.GetValue("user::general", "account_name"); - if (!name || !name[0]) { - name = DEFAULT_NAME; - save_global_ini_value( - local_storage, - config_ini_user, - "user::general", "account_name", IniValue(name), - "user account name" - ); - } - return std::string(name); -} - -// user::general::account_steamid -static CSteamID parse_user_steam_id(class Local_Storage *local_storage) -{ - char array_steam_id[32] = {}; - CSteamID user_id((uint64)std::atoll(ini.GetValue("user::general", "account_steamid", "0"))); - if (!user_id.IsValid()) { - user_id = generate_steam_id_user(); - char temp_text[32]{}; - snprintf(temp_text, sizeof(temp_text), "%llu", (uint64)user_id.ConvertToUint64()); - save_global_ini_value( - local_storage, - config_ini_user, - "user::general", "account_steamid", IniValue(temp_text), - "Steam64 format" - ); - } - - return user_id; -} - -// user::general::language -// valid list: https://partner.steamgames.com/doc/store/localization/languages -static std::string parse_current_language(class Local_Storage *local_storage) -{ - auto lang = ini.GetValue("user::general", "language"); - if (!lang || !lang[0]) { - lang = DEFAULT_LANGUAGE; - save_global_ini_value( - local_storage, - config_ini_user, - "user::general", "language", IniValue(lang), - "the language reported to the game, default is 'english', check 'API language code' in https://partner.steamgames.com/doc/store/localization/languages" - ); - } - - return common_helpers::to_lower(std::string(lang)); -} - -// supported_languages.txt -// valid list: https://partner.steamgames.com/doc/store/localization/languages -static std::set parse_supported_languages(class Local_Storage *local_storage, std::string &language) -{ - std::set supported_languages{}; - - std::string lang_config_path = Local_Storage::get_game_settings_path() + "supported_languages.txt"; - std::ifstream input( std::filesystem::u8path(lang_config_path) ); - - std::string first_language{}; - if (input.is_open()) { - common_helpers::consume_bom(input); - for( std::string line; getline( input, line ); ) { - if (!line.empty() && line.back() == '\n') { - line.pop_back(); - } - - if (!line.empty() && line.back() == '\r') { - line.pop_back(); - } - - try { - std::string lang = common_helpers::to_lower(line); - if (!first_language.size()) first_language = lang; - supported_languages.insert(lang); - PRINT_DEBUG("Added supported_language %s", lang.c_str()); - } catch (...) {} - } - } - - // if the current emu language is not in the supported languages list - if (!supported_languages.count(language)) { - if (first_language.size()) { // get the first supported language if the list wasn't empty - PRINT_DEBUG("[?] Your language '%s' isn't found in supported_languages.txt, using '%s' instead", language.c_str(), first_language.c_str()); - language = first_language; - } else { // otherwise just lie and add it then! - supported_languages.insert(language); - PRINT_DEBUG("Forced current language '%s' into supported_languages", language.c_str()); - } - } - - return supported_languages; -} - -// app::dlcs -static void parse_dlc(class Settings *settings_client, class Settings *settings_server) -{ - constexpr static const char unlock_all_key[] = "unlock_all"; - - bool unlock_all = ini.GetBoolValue("app::dlcs", unlock_all_key, true); - if (unlock_all) { - PRINT_DEBUG("unlocking all DLCs"); - settings_client->unlockAllDLC(true); - settings_server->unlockAllDLC(true); - } else { - PRINT_DEBUG("locking all DLCs"); - settings_client->unlockAllDLC(false); - settings_server->unlockAllDLC(false); - } - - std::list dlcs_keys{}; - if (!ini.GetAllKeys("app::dlcs", dlcs_keys) || dlcs_keys.empty()) return; - - // remove the unlock all key so we can iterate through the DLCs - dlcs_keys.remove_if([](const CSimpleIniA::Entry &item){ - return common_helpers::str_cmp_insensitive(item.pItem, unlock_all_key); - }); - - for (const auto &dlc_key : dlcs_keys) { - AppId_t appid = (AppId_t)std::stoul(dlc_key.pItem); - if (!appid) continue; - - auto name = ini.GetValue("app::dlcs", dlc_key.pItem, "unknown DLC"); - PRINT_DEBUG("adding DLC: [%u] = '%s'", appid, name); - settings_client->addDLC(appid, name, true); - settings_server->addDLC(appid, name, true); - } -} - -// app::paths -static void parse_app_paths(class Settings *settings_client, Settings *settings_server, const std::string &program_path) -{ - std::list ids{}; - if (!ini.GetAllKeys("app::paths", ids) || ids.empty()) return; - - for (const auto &id : ids) { - auto val_ptr = ini.GetValue("app::paths", id.pItem); - // NOTE: empty path means we actively disable the path to the appid specified - if (!val_ptr) continue; - - AppId_t appid = (AppId_t)std::stoul(id.pItem); - std::string rel_path(val_ptr); - std::string path{}; - if (rel_path.size()) - path = canonical_path(program_path + rel_path); - - if (appid) { - if (!rel_path.size() || path.size()) { - PRINT_DEBUG("Adding app path: %u|%s|%s", appid, rel_path.c_str(), path.c_str()); - settings_client->setAppInstallPath(appid, path); - settings_server->setAppInstallPath(appid, path); - } else { - PRINT_DEBUG("Error adding app path for: %u does this path exist? |%s|", appid, rel_path.c_str()); - } - } - } -} - -// leaderboards.txt -static void parse_leaderboards(class Settings *settings_client, class Settings *settings_server) -{ - std::string dlc_config_path = Local_Storage::get_game_settings_path() + "leaderboards.txt"; - std::ifstream input( std::filesystem::u8path(dlc_config_path) ); - if (input.is_open()) { - common_helpers::consume_bom(input); - - for( std::string line; getline( input, line ); ) { - if (!line.empty() && line[line.length()-1] == '\n') { - line.pop_back(); - } - - if (!line.empty() && line[line.length()-1] == '\r') { - line.pop_back(); - } - - std::string leaderboard{}; - unsigned int sort_method = 0; - unsigned int display_type = 0; - - std::size_t deliminator = line.find("="); - if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) { - leaderboard = line.substr(0, deliminator); - std::size_t deliminator2 = line.find("=", deliminator + 1); - if (deliminator2 != std::string::npos && deliminator2 != line.size()) { - sort_method = stol(line.substr(deliminator + 1, deliminator2 - (deliminator + 1))); - display_type = stol(line.substr(deliminator2 + 1)); - } - } - - if (leaderboard.size() && sort_method <= k_ELeaderboardSortMethodDescending && display_type <= k_ELeaderboardDisplayTypeTimeMilliSeconds) { - PRINT_DEBUG("Adding leaderboard: %s|%u|%u", leaderboard.c_str(), sort_method, display_type); - settings_client->setLeaderboard(leaderboard, (ELeaderboardSortMethod)sort_method, (ELeaderboardDisplayType)display_type); - settings_server->setLeaderboard(leaderboard, (ELeaderboardSortMethod)sort_method, (ELeaderboardDisplayType)display_type); - } else { - PRINT_DEBUG("Error adding leaderboard for: '%s', are sort method %u or display type %u valid?", leaderboard.c_str(), sort_method, display_type); - } - } - } - -} - -// stats.json -static void parse_stats(class Settings *settings_client, class Settings *settings_server, class Local_Storage *local_storage) -{ - nlohmann::json stats_items; - std::string stats_json_path = Local_Storage::get_game_settings_path() + "stats.json"; - if (local_storage->load_json(stats_json_path, stats_items)) { - for (const auto &stats : stats_items) { - std::string stat_name; - std::string stat_type; - std::string stat_default_value = "0"; - std::string stat_global_value = "0"; - - try { - stat_name = stats.value("name", std::string("")); - stat_type = stats.value("type", std::string("")); - stat_default_value = stats.value("default", std::string("0")); - stat_global_value = stats.value("global", std::string("0")); - } - catch (const std::exception &e) { - PRINT_DEBUG("Error reading current stat item in stats.json, reason: %s", e.what()); - continue; - } - - std::transform(stat_type.begin(), stat_type.end(), stat_type.begin(),[](unsigned char c){ return std::tolower(c); }); - struct Stat_config config = {}; - - try { - if (stat_type == "float") { - config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT; - config.default_value_float = std::stof(stat_default_value); - config.global_value_double = std::stod(stat_global_value); - } else if (stat_type == "int") { - config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_INT; - config.default_value_int = std::stol(stat_default_value); - config.global_value_int64 = std::stoll(stat_global_value); - } else if (stat_type == "avgrate") { - config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_AVGRATE; - config.default_value_float = std::stof(stat_default_value); - config.global_value_double = std::stod(stat_global_value); - } else { - PRINT_DEBUG("Error adding stat %s, type %s isn't valid", stat_name.c_str(), stat_type.c_str()); - continue; - } - } catch (...) { - PRINT_DEBUG("Error adding stat %s, default value %s or global value %s isn't valid", stat_name.c_str(), stat_default_value.c_str(), stat_global_value.c_str()); - continue; - } - - if (stat_name.size()) { - PRINT_DEBUG("Adding stat type: %s|%u|%f|%i", stat_name.c_str(), config.type, config.default_value_float, config.default_value_int); - settings_client->setStatDefiniton(stat_name, config); - settings_server->setStatDefiniton(stat_name, config); - } else { - PRINT_DEBUG("Error adding stat for: %s, empty name", stat_name.c_str()); - } - } - } - -} - -// depots.txt -static void parse_depots(class Settings *settings_client, class Settings *settings_server) -{ - std::string depots_config_path = Local_Storage::get_game_settings_path() + "depots.txt"; - std::ifstream input( std::filesystem::u8path(depots_config_path) ); - if (input.is_open()) { - common_helpers::consume_bom(input); - for( std::string line; getline( input, line ); ) { - if (!line.empty() && line[line.length()-1] == '\n') { - line.pop_back(); - } - - if (!line.empty() && line[line.length()-1] == '\r') { - line.pop_back(); - } - - try { - DepotId_t depot_id = std::stoul(line); - settings_client->depots.push_back(depot_id); - settings_server->depots.push_back(depot_id); - PRINT_DEBUG("Added depot %u", depot_id); - } catch (...) {} - } - } - -} - -// subscribed_groups.txt -static void parse_subscribed_groups(class Settings *settings_client, class Settings *settings_server) -{ - std::string depots_config_path = Local_Storage::get_game_settings_path() + "subscribed_groups.txt"; - std::ifstream input( std::filesystem::u8path(depots_config_path) ); - if (input.is_open()) { - common_helpers::consume_bom(input); - for( std::string line; getline( input, line ); ) { - if (!line.empty() && line[line.length()-1] == '\n') { - line.pop_back(); - } - - if (!line.empty() && line[line.length()-1] == '\r') { - line.pop_back(); - } - - try { - uint64 source_id = std::stoull(line); - settings_client->subscribed_groups.insert(source_id); - settings_server->subscribed_groups.insert(source_id); - PRINT_DEBUG("Added source %llu", source_id); - } catch (...) {} - } - } - -} - -// installed_app_ids.txt -static void parse_installed_app_Ids(class Settings *settings_client, class Settings *settings_server) -{ - std::string installed_apps_list_path = Local_Storage::get_game_settings_path() + "installed_app_ids.txt"; - std::ifstream input( std::filesystem::u8path(installed_apps_list_path) ); - if (input.is_open()) { - settings_client->assumeAnyAppInstalled(false); - settings_server->assumeAnyAppInstalled(false); - PRINT_DEBUG("Limiting/Locking installed apps"); - common_helpers::consume_bom(input); - for( std::string line; getline( input, line ); ) { - if (!line.empty() && line[line.length()-1] == '\n') { - line.pop_back(); - } - - if (!line.empty() && line[line.length()-1] == '\r') { - line.pop_back(); - } - - try { - AppId_t app_id = std::stoul(line); - settings_client->addInstalledApp(app_id); - settings_server->addInstalledApp(app_id); - PRINT_DEBUG("Added installed app with ID %u", app_id); - } catch (...) {} - } - } else { - settings_client->assumeAnyAppInstalled(true); - settings_server->assumeAnyAppInstalled(true); - PRINT_DEBUG("Assuming any app is installed"); - } - -} - - - -// steam_settings/mods -static const auto one_week_ago_epoch = std::chrono::duration_cast( - ( startup_time - std::chrono::hours(24 * 7) ).time_since_epoch() -).count(); -static const auto two_week_ago_epoch = std::chrono::duration_cast( - ( startup_time - std::chrono::hours(24 * 7 * 2) ).time_since_epoch() -).count(); -static const auto three_week_ago_epoch = std::chrono::duration_cast( - ( startup_time - std::chrono::hours(24 * 7 * 3) ).time_since_epoch() -).count(); - -static size_t get_file_size_safe(const std::string &filepath, const std::string &basepath, int32 default_val = 0) -{ - try - { - const auto file_p = common_helpers::to_absolute(filepath, basepath); - if (file_p.empty()) return default_val; - - size_t size = 0; - if (common_helpers::file_size(file_p, size)) { - return size; - } - } catch(...) {} - return default_val; -} - -static std::string get_mod_preview_url(const std::string &previewFileName, const std::string &mod_id) -{ - if (previewFileName.empty()) { - return std::string(); - } else { - auto settings_folder = std::string(Local_Storage::get_game_settings_path()); - std::replace(settings_folder.begin(), settings_folder.end(), '\\', '/'); - - return - -#if defined(__WINDOWS__) - "file:///" -#else // on Linux absolute paths start like this: /my/path, so the 3rd slash is already appended - "file://" -#endif - - + settings_folder + "mod_images/" + mod_id + "/" + previewFileName; - } - -} - -static void try_parse_mods_file(class Settings *settings_client, Settings *settings_server, nlohmann::json &mod_items, const std::string &mods_folder) -{ - for (auto mod = mod_items.begin(); mod != mod_items.end(); ++mod) { - try { - std::string mod_images_fullpath = Local_Storage::get_game_settings_path() + "mod_images" + PATH_SEPARATOR + std::string(mod.key()); - Mod_entry newMod; - newMod.id = std::stoull(mod.key()); - newMod.title = mod.value().value("title", std::string(mod.key())); - - // make sure this is never empty - newMod.path = mod.value().value("path", std::string("")); - if (newMod.path.empty()) { - newMod.path = mods_folder + PATH_SEPARATOR + std::string(mod.key()); - } else { - // make sure the path is normalized for current OS, and absolute - newMod.path = common_helpers::to_absolute( - newMod.path, - get_full_program_path() - ); - } - - newMod.fileType = k_EWorkshopFileTypeCommunity; - newMod.description = mod.value().value("description", std::string("")); - newMod.steamIDOwner = mod.value().value("steam_id_owner", settings_client->get_local_steam_id().ConvertToUint64()); - newMod.timeCreated = mod.value().value("time_created", (uint32)three_week_ago_epoch); - newMod.timeUpdated = mod.value().value("time_updated", (uint32)two_week_ago_epoch); - newMod.timeAddedToUserList = mod.value().value("time_added", (uint32)one_week_ago_epoch); - newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic; - newMod.banned = false; - newMod.acceptedForUse = true; - newMod.tagsTruncated = false; - newMod.tags = mod.value().value("tags", std::string("")); - - newMod.primaryFileName = mod.value().value("primary_filename", std::string("")); - int32 primary_filesize = 0; - if (!newMod.primaryFileName.empty()) { - primary_filesize = (int32)get_file_size_safe(newMod.primaryFileName, newMod.path, primary_filesize); - } - newMod.primaryFileSize = mod.value().value("primary_filesize", primary_filesize); - - newMod.previewFileName = mod.value().value("preview_filename", std::string("")); - int32 preview_filesize = 0; - if (!newMod.previewFileName.empty()) { - preview_filesize = (int32)get_file_size_safe(newMod.previewFileName, mod_images_fullpath, preview_filesize); - } - newMod.previewFileSize = mod.value().value("preview_filesize", preview_filesize); - - newMod.total_files_sizes = mod.value().value("total_files_sizes", newMod.primaryFileSize); - newMod.min_game_branch = mod.value().value("min_game_branch", ""); - newMod.max_game_branch = mod.value().value("max_game_branch", ""); - - newMod.workshopItemURL = mod.value().value("workshop_item_url", "https://steamcommunity.com/sharedfiles/filedetails/?id=" + std::string(mod.key())); - newMod.votesUp = mod.value().value("upvotes", (uint32)500); - newMod.votesDown = mod.value().value("downvotes", (uint32)12); - - float score = 0.97f; - try - { - score = newMod.votesUp / (float)(newMod.votesUp + newMod.votesDown); - } catch(...) {} - newMod.score = mod.value().value("score", score); - - newMod.numChildren = mod.value().value("num_children", (uint32)0); - newMod.previewURL = mod.value().value("preview_url", get_mod_preview_url(newMod.previewFileName, std::string(mod.key()))); - - settings_client->addMod(newMod.id, newMod.title, newMod.path); - settings_server->addMod(newMod.id, newMod.title, newMod.path); - settings_client->addModDetails(newMod.id, newMod); - settings_server->addModDetails(newMod.id, newMod); - - PRINT_DEBUG(" parsed mod '%s':", std::string(mod.key()).c_str()); - PRINT_DEBUG(" path (will be used for primary file): '%s'", newMod.path.c_str()); - PRINT_DEBUG(" images path (will be used for preview file): '%s'", mod_images_fullpath.c_str()); - PRINT_DEBUG(" primary_filename: '%s'", newMod.primaryFileName.c_str()); - PRINT_DEBUG(" primary_filesize: %i bytes", newMod.primaryFileSize); - PRINT_DEBUG(" primary file handle: %llu", settings_client->getMod(newMod.id).handleFile); - PRINT_DEBUG(" preview_filename: '%s'", newMod.previewFileName.c_str()); - PRINT_DEBUG(" preview_filesize: %i bytes", newMod.previewFileSize); - PRINT_DEBUG(" preview file handle: %llu", settings_client->getMod(newMod.id).handlePreviewFile); - PRINT_DEBUG(" total_files_sizes: %llu", settings_client->getMod(newMod.id).total_files_sizes); - PRINT_DEBUG(" min_game_branch: '%s'", settings_client->getMod(newMod.id).min_game_branch.c_str()); - PRINT_DEBUG(" max_game_branch: '%s'", settings_client->getMod(newMod.id).max_game_branch.c_str()); - PRINT_DEBUG(" workshop_item_url: '%s'", newMod.workshopItemURL.c_str()); - PRINT_DEBUG(" preview_url: '%s'", newMod.previewURL.c_str()); - } catch (std::exception& e) { - const char *errorMessage = e.what(); - PRINT_DEBUG("MODLOADER ERROR: %s", errorMessage); - } - } -} - -// called if mods.json doesn't exist or invalid -static void try_detect_mods_folder(class Settings *settings_client, Settings *settings_server, const std::string &mods_folder) -{ - std::vector all_mods = Local_Storage::get_folders_path(mods_folder); - for (auto & mod_folder: all_mods) { - std::string mod_images_fullpath = Local_Storage::get_game_settings_path() + "mod_images" + PATH_SEPARATOR + mod_folder; - try { - Mod_entry newMod; - newMod.id = std::stoull(mod_folder); - newMod.title = mod_folder; - - // make sure this is never empty - newMod.path = mods_folder + PATH_SEPARATOR + mod_folder; - - newMod.fileType = k_EWorkshopFileTypeCommunity; - newMod.description = "mod #" + mod_folder; - newMod.steamIDOwner = settings_client->get_local_steam_id().ConvertToUint64(); - newMod.timeCreated = (uint32)three_week_ago_epoch; - newMod.timeUpdated = (uint32)two_week_ago_epoch; - newMod.timeAddedToUserList = (uint32)one_week_ago_epoch; - newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic; - newMod.banned = false; - newMod.acceptedForUse = true; - newMod.tagsTruncated = false; - newMod.tags = ""; - - std::vector mod_primary_files = Local_Storage::get_filenames_path(newMod.path); - newMod.primaryFileName = mod_primary_files.size() ? mod_primary_files[0] : ""; - newMod.primaryFileSize = (int32)get_file_size_safe(newMod.primaryFileName, newMod.path); - - std::vector mod_preview_files = Local_Storage::get_filenames_path(mod_images_fullpath); - newMod.previewFileName = mod_preview_files.size() ? mod_preview_files[0] : ""; - newMod.previewFileSize = (int32)get_file_size_safe(newMod.previewFileName, mod_images_fullpath); - - newMod.total_files_sizes = newMod.primaryFileSize; - - newMod.workshopItemURL = "https://steamcommunity.com/sharedfiles/filedetails/?id=" + mod_folder; - newMod.votesUp = (uint32)500; - newMod.votesDown = (uint32)12; - newMod.score = 0.97f; - newMod.numChildren = (uint32)0; - newMod.previewURL = get_mod_preview_url(newMod.previewFileName, mod_folder); - - settings_client->addMod(newMod.id, newMod.title, newMod.path); - settings_server->addMod(newMod.id, newMod.title, newMod.path); - settings_client->addModDetails(newMod.id, newMod); - settings_server->addModDetails(newMod.id, newMod); - PRINT_DEBUG(" detected mod '%s':", mod_folder.c_str()); - PRINT_DEBUG(" path (will be used for primary file): '%s'", newMod.path.c_str()); - PRINT_DEBUG(" images path (will be used for preview file): '%s'", mod_images_fullpath.c_str()); - PRINT_DEBUG(" primary_filename: '%s'", newMod.primaryFileName.c_str()); - PRINT_DEBUG(" primary_filesize: %i bytes", newMod.primaryFileSize); - PRINT_DEBUG(" primary file handle: %llu", settings_client->getMod(newMod.id).handleFile); - PRINT_DEBUG(" preview_filename: '%s'", newMod.previewFileName.c_str()); - PRINT_DEBUG(" preview_filesize: %i bytes", newMod.previewFileSize); - PRINT_DEBUG(" preview file handle: %llu", settings_client->getMod(newMod.id).handlePreviewFile); - PRINT_DEBUG(" total_files_sizes: '%llu'", newMod.total_files_sizes); - PRINT_DEBUG(" min_game_branch: '%s'", newMod.min_game_branch.c_str()); - PRINT_DEBUG(" max_game_branch: '%s'", newMod.max_game_branch.c_str()); - PRINT_DEBUG(" workshop_item_url: '%s'", newMod.workshopItemURL.c_str()); - PRINT_DEBUG(" preview_url: '%s'", newMod.previewURL.c_str()); - } catch (...) {} - } -} - -static void parse_mods_folder(class Settings *settings_client, Settings *settings_server, class Local_Storage *local_storage) -{ - std::string mods_folder = Local_Storage::get_game_settings_path() + "mods"; - nlohmann::json mod_items = nlohmann::json::object(); - static constexpr auto mods_json_file = "mods.json"; - std::string mods_json_path = Local_Storage::get_game_settings_path() + mods_json_file; - if (local_storage->load_json(mods_json_path, mod_items)) { - PRINT_DEBUG("Attempting to parse mods.json"); - try_parse_mods_file(settings_client, settings_server, mod_items, mods_folder); - } else { // invalid mods.json or doesn't exist - PRINT_DEBUG("Failed to load mods.json, attempting to auto detect mods folder"); - try_detect_mods_folder(settings_client, settings_server, mods_folder); - } - -} - - - -// main::general::crash_printer_location -static void parse_crash_printer_location() -{ - std::string line(common_helpers::string_strip(ini.GetValue("main::general", "crash_printer_location", ""))); - if (line.size()) { - auto crash_path = common_helpers::to_absolute(line, get_full_program_path()); - if (crash_path.size()) { - if (crash_printer::init(crash_path)) { - PRINT_DEBUG("Unhandled crashes will be saved to '%s'", crash_path.c_str()); - } else { - PRINT_DEBUG("Failed to setup unhandled crash printer with path: '%s'", crash_path.c_str()); - } - } - } -} - -// auto_accept_invite.txt -static void parse_auto_accept_invite(class Settings *settings_client, class Settings *settings_server) -{ - std::string auto_accept_list_path = Local_Storage::get_game_settings_path() + "auto_accept_invite.txt"; - std::ifstream input( std::filesystem::u8path(auto_accept_list_path) ); - if (input.is_open()) { - bool accept_any_invite = true; - common_helpers::consume_bom(input); - for( std::string line; getline( input, line ); ) { - line = common_helpers::string_strip(line); - if (!line.empty()) { - accept_any_invite = false; - try { - auto friend_id = std::stoull(line); - settings_client->addFriendToOverlayAutoAccept((uint64_t)friend_id); - settings_server->addFriendToOverlayAutoAccept((uint64_t)friend_id); - PRINT_DEBUG("Auto accepting overlay invitations from user with ID (SteamID64) = %llu", friend_id); - } catch (...) {} - } - } - - if (accept_any_invite) { - PRINT_DEBUG("Auto accepting any overlay invitation"); - settings_client->acceptAnyOverlayInvites(true); - settings_server->acceptAnyOverlayInvites(true); - } else { - settings_client->acceptAnyOverlayInvites(false); - settings_server->acceptAnyOverlayInvites(false); - } - } -} - -// 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 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; - } - - settings_client->is_beta_branch = ini.GetBoolValue("app::general", "is_beta_branch", settings_client->is_beta_branch); - settings_server->is_beta_branch = ini.GetBoolValue("app::general", "is_beta_branch", settings_server->is_beta_branch); - - - // 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::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(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(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(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(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(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(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) -{ - std::string line(common_helpers::to_upper(common_helpers::string_strip(ini.GetValue("user::general", "ip_country", "")))); - if (line.empty()) { - line = DEFAULT_IP_COUNTRY; - save_global_ini_value( - local_storage, - config_ini_user, - "user::general", "ip_country", IniValue(line.c_str()), - "ISO 3166-1-alpha-2 format, use this link to get the 'Alpha-2' country code: https://www.iban.com/country-codes" - ); - } - - // ISO 3166-1-alpha-2 format is 2 chars only - if (line.size() == 2) { - settings_client->ip_country = line; - settings_server->ip_country = line; - PRINT_DEBUG("Setting IP country to: '%s'", line.c_str()); - } -} - -// overlay::general -static void parse_overlay_general_config(class Settings *settings_client, class Settings *settings_server) -{ - settings_client->disable_overlay = !ini.GetBoolValue("overlay::general", "enable_experimental_overlay", !settings_client->disable_overlay); - settings_server->disable_overlay = !ini.GetBoolValue("overlay::general", "enable_experimental_overlay", !settings_server->disable_overlay); - - { - auto val = ini.GetLongValue("overlay::general", "hook_delay_sec", -1); - if (val >= 0) { - settings_client->overlay_hook_delay_sec = val; - settings_server->overlay_hook_delay_sec = val; - PRINT_DEBUG("Setting overlay hook delay to %i seconds", (int)val); - } - } - - { - auto val = ini.GetLongValue("overlay::general", "renderer_detector_timeout_sec", -1); - if (val > 0) { - settings_client->overlay_renderer_detector_timeout_sec = val; - settings_server->overlay_renderer_detector_timeout_sec = val; - PRINT_DEBUG("Setting overlay renderer detector timeout to %i seconds", (int)val); - } - } - - settings_client->disable_overlay_achievement_notification = ini.GetBoolValue("overlay::general", "disable_achievement_notification", settings_client->disable_overlay_achievement_notification); - settings_server->disable_overlay_achievement_notification = ini.GetBoolValue("overlay::general", "disable_achievement_notification", settings_server->disable_overlay_achievement_notification); - - settings_client->disable_overlay_friend_notification = ini.GetBoolValue("overlay::general", "disable_friend_notification", settings_client->disable_overlay_friend_notification); - settings_server->disable_overlay_friend_notification = ini.GetBoolValue("overlay::general", "disable_friend_notification", settings_server->disable_overlay_friend_notification); - - settings_client->disable_overlay_achievement_progress = ini.GetBoolValue("overlay::general", "disable_achievement_progress", settings_client->disable_overlay_achievement_progress); - settings_server->disable_overlay_achievement_progress = ini.GetBoolValue("overlay::general", "disable_achievement_progress", settings_server->disable_overlay_achievement_progress); - - settings_client->disable_overlay_warning_any = ini.GetBoolValue("overlay::general", "disable_warning_any", settings_client->disable_overlay_warning_any); - settings_server->disable_overlay_warning_any = ini.GetBoolValue("overlay::general", "disable_warning_any", settings_server->disable_overlay_warning_any); - - settings_client->disable_overlay_warning_bad_appid = ini.GetBoolValue("overlay::general", "disable_warning_bad_appid", settings_client->disable_overlay_warning_bad_appid); - settings_server->disable_overlay_warning_bad_appid = ini.GetBoolValue("overlay::general", "disable_warning_bad_appid", settings_server->disable_overlay_warning_bad_appid); - - settings_client->disable_overlay_warning_local_save = ini.GetBoolValue("overlay::general", "disable_warning_local_save", settings_client->disable_overlay_warning_local_save); - settings_server->disable_overlay_warning_local_save = ini.GetBoolValue("overlay::general", "disable_warning_local_save", settings_server->disable_overlay_warning_local_save); - - settings_client->overlay_upload_achs_icons_to_gpu = ini.GetBoolValue("overlay::general", "upload_achievements_icons_to_gpu", settings_client->overlay_upload_achs_icons_to_gpu); - settings_server->overlay_upload_achs_icons_to_gpu = ini.GetBoolValue("overlay::general", "upload_achievements_icons_to_gpu", settings_server->overlay_upload_achs_icons_to_gpu); - - settings_client->overlay_always_show_user_info = ini.GetBoolValue("overlay::general", "overlay_always_show_user_info", settings_client->overlay_always_show_user_info); - settings_server->overlay_always_show_user_info = ini.GetBoolValue("overlay::general", "overlay_always_show_user_info", settings_server->overlay_always_show_user_info); - - settings_client->overlay_always_show_fps = ini.GetBoolValue("overlay::general", "overlay_always_show_fps", settings_client->overlay_always_show_fps); - settings_server->overlay_always_show_fps = ini.GetBoolValue("overlay::general", "overlay_always_show_fps", settings_server->overlay_always_show_fps); - - settings_client->overlay_always_show_frametime = ini.GetBoolValue("overlay::general", "overlay_always_show_frametime", settings_client->overlay_always_show_frametime); - settings_server->overlay_always_show_frametime = ini.GetBoolValue("overlay::general", "overlay_always_show_frametime", settings_server->overlay_always_show_frametime); - - settings_client->overlay_always_show_playtime = ini.GetBoolValue("overlay::general", "overlay_always_show_playtime", settings_client->overlay_always_show_playtime); - settings_server->overlay_always_show_playtime = ini.GetBoolValue("overlay::general", "overlay_always_show_playtime", settings_server->overlay_always_show_playtime); - - { - auto val = ini.GetLongValue("overlay::general", "fps_averaging_window", settings_client->overlay_fps_avg_window); - if (val > 0) { - settings_client->overlay_fps_avg_window = val; - } - } - - { - auto val = ini.GetLongValue("overlay::general", "fps_averaging_window", settings_server->overlay_fps_avg_window); - if (val > 0) { - settings_server->overlay_fps_avg_window = val; - } - } - -} - -// main::misc::steam_game_stats_reports_dir -static void parse_steam_game_stats_reports_dir(class Settings *settings_client, class Settings *settings_server) -{ - std::string line(common_helpers::string_strip(ini.GetValue("main::misc", "steam_game_stats_reports_dir", ""))); - if (line.size()) { - auto folder = common_helpers::to_absolute(line, get_full_program_path()); - if (folder.size()) { - PRINT_DEBUG("ISteamGameStats reports will be saved to '%s'", folder.c_str()); - settings_client->steam_game_stats_reports_dir = folder; - settings_server->steam_game_stats_reports_dir = folder; - } - } -} - -// mainly enable/disable features -static void parse_simple_features(class Settings *settings_client, class Settings *settings_server) -{ - // [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); - - settings_client->use_gc_token = ini.GetBoolValue("main::general", "gc_token", settings_client->use_gc_token); - settings_server->use_gc_token = ini.GetBoolValue("main::general", "gc_token", settings_server->use_gc_token); - - settings_client->disable_account_avatar = !ini.GetBoolValue("main::general", "enable_account_avatar", !settings_client->disable_account_avatar); - settings_server->disable_account_avatar = !ini.GetBoolValue("main::general", "enable_account_avatar", !settings_server->disable_account_avatar); - - settings_client->steam_deck = ini.GetBoolValue("main::general", "steam_deck", settings_client->steam_deck); - settings_server->steam_deck = ini.GetBoolValue("main::general", "steam_deck", settings_server->steam_deck); - - settings_client->immediate_gameserver_stats = ini.GetBoolValue("main::general", "immediate_gameserver_stats", settings_client->immediate_gameserver_stats); - settings_server->immediate_gameserver_stats = ini.GetBoolValue("main::general", "immediate_gameserver_stats", settings_server->immediate_gameserver_stats); - - settings_client->matchmaking_server_details_via_source_query = ini.GetBoolValue("main::general", "matchmaking_server_details_via_source_query", settings_client->matchmaking_server_details_via_source_query); - settings_server->matchmaking_server_details_via_source_query = ini.GetBoolValue("main::general", "matchmaking_server_details_via_source_query", settings_server->matchmaking_server_details_via_source_query); - - settings_client->matchmaking_server_list_always_lan_type = !ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", !settings_client->matchmaking_server_list_always_lan_type); - settings_server->matchmaking_server_list_always_lan_type = !ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", !settings_server->matchmaking_server_list_always_lan_type); - - - // [main::connectivity] - settings_client->disable_networking = ini.GetBoolValue("main::connectivity", "disable_networking", settings_client->disable_networking); - settings_server->disable_networking = ini.GetBoolValue("main::connectivity", "disable_networking", settings_server->disable_networking); - - settings_client->disable_sharing_stats_with_gameserver = ini.GetBoolValue("main::connectivity", "disable_sharing_stats_with_gameserver", settings_client->disable_sharing_stats_with_gameserver); - settings_server->disable_sharing_stats_with_gameserver = ini.GetBoolValue("main::connectivity", "disable_sharing_stats_with_gameserver", settings_server->disable_sharing_stats_with_gameserver); - - settings_client->disable_source_query = ini.GetBoolValue("main::connectivity", "disable_source_query", settings_client->disable_source_query); - settings_server->disable_source_query = ini.GetBoolValue("main::connectivity", "disable_source_query", settings_server->disable_source_query); - - settings_client->share_leaderboards_over_network = ini.GetBoolValue("main::connectivity", "share_leaderboards_over_network", settings_client->share_leaderboards_over_network); - settings_server->share_leaderboards_over_network = ini.GetBoolValue("main::connectivity", "share_leaderboards_over_network", settings_server->share_leaderboards_over_network); - - settings_client->disable_lobby_creation = ini.GetBoolValue("main::connectivity", "disable_lobby_creation", settings_client->disable_lobby_creation); - settings_server->disable_lobby_creation = ini.GetBoolValue("main::connectivity", "disable_lobby_creation", settings_server->disable_lobby_creation); - - settings_client->download_steamhttp_requests = ini.GetBoolValue("main::connectivity", "download_steamhttp_requests", settings_client->download_steamhttp_requests); - settings_server->download_steamhttp_requests = ini.GetBoolValue("main::connectivity", "download_steamhttp_requests", settings_server->download_steamhttp_requests); - - - // [main::misc] - settings_client->achievement_bypass = ini.GetBoolValue("main::misc", "achievements_bypass", settings_client->achievement_bypass); - settings_server->achievement_bypass = ini.GetBoolValue("main::misc", "achievements_bypass", settings_server->achievement_bypass); - - settings_client->force_steamhttp_success = ini.GetBoolValue("main::misc", "force_steamhttp_success", settings_client->force_steamhttp_success); - settings_server->force_steamhttp_success = ini.GetBoolValue("main::misc", "force_steamhttp_success", settings_server->force_steamhttp_success); - - settings_client->disable_steamoverlaygameid_env_var = ini.GetBoolValue("main::misc", "disable_steamoverlaygameid_env_var", settings_client->disable_steamoverlaygameid_env_var); - settings_server->disable_steamoverlaygameid_env_var = ini.GetBoolValue("main::misc", "disable_steamoverlaygameid_env_var", settings_server->disable_steamoverlaygameid_env_var); - - settings_client->enable_builtin_preowned_ids = ini.GetBoolValue("main::misc", "enable_steam_preowned_ids", settings_client->enable_builtin_preowned_ids); - settings_server->enable_builtin_preowned_ids = ini.GetBoolValue("main::misc", "enable_steam_preowned_ids", settings_server->enable_builtin_preowned_ids); -} - -// [main::stats] -static void parse_stats_features(class Settings *settings_client, class Settings *settings_server) -{ - settings_client->disable_leaderboards_create_unknown = ini.GetBoolValue("main::stats", "disable_leaderboards_create_unknown", settings_client->disable_leaderboards_create_unknown); - settings_server->disable_leaderboards_create_unknown = ini.GetBoolValue("main::stats", "disable_leaderboards_create_unknown", settings_server->disable_leaderboards_create_unknown); - - settings_client->allow_unknown_stats = ini.GetBoolValue("main::stats", "allow_unknown_stats", settings_client->allow_unknown_stats); - settings_server->allow_unknown_stats = ini.GetBoolValue("main::stats", "allow_unknown_stats", settings_server->allow_unknown_stats); - - settings_client->stat_achievement_progress_functionality = ini.GetBoolValue("main::stats", "stat_achievement_progress_functionality", settings_client->stat_achievement_progress_functionality); - settings_server->stat_achievement_progress_functionality = ini.GetBoolValue("main::stats", "stat_achievement_progress_functionality", settings_server->stat_achievement_progress_functionality); - - settings_client->save_only_higher_stat_achievement_progress = ini.GetBoolValue("main::stats", "save_only_higher_stat_achievement_progress", settings_client->save_only_higher_stat_achievement_progress); - settings_server->save_only_higher_stat_achievement_progress = ini.GetBoolValue("main::stats", "save_only_higher_stat_achievement_progress", settings_server->save_only_higher_stat_achievement_progress); - - { - long val_client = ini.GetLongValue("main::stats", "paginated_achievements_icons", settings_client->paginated_achievements_icons); - settings_client->paginated_achievements_icons = static_cast(val_client); - - long val_server = ini.GetLongValue("main::stats", "paginated_achievements_icons", settings_server->paginated_achievements_icons); - settings_server->paginated_achievements_icons = static_cast(val_server); - } -} - - - -static std::map old_itfs_map{}; - -static bool try_parse_old_steam_interfaces_file(std::string interfaces_path) -{ - std::ifstream input( std::filesystem::u8path(interfaces_path) ); - if (!input.is_open()) return false; - - PRINT_DEBUG("Trying to parse old steam interfaces from '%s'", interfaces_path.c_str()); - for( std::string line; std::getline( input, line ); ) { - line.erase(std::remove(line.begin(), line.end(), ' '), line.end()); - line.erase(std::remove(line.begin(), line.end(), '\n'), line.end()); - line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); - line.erase(std::remove(line.begin(), line.end(), '\t'), line.end()); - PRINT_DEBUG(" valid line: |%s|", line.c_str()); - -#define OLD_ITF_LINE(istr, itype) { \ - if (line.find(istr) != std::string::npos) { \ - old_itfs_map[itype] = line; \ - continue; \ - } \ -} - - OLD_ITF_LINE("SteamClient", SettingsItf::CLIENT); - - // NOTE: you must try to read the one with the most characters first - OLD_ITF_LINE("SteamGameServerStats", SettingsItf::GAMESERVER_STATS); - OLD_ITF_LINE("SteamGameServer", SettingsItf::GAMESERVER); - - // NOTE: you must try to read the one with the most characters first - OLD_ITF_LINE("SteamMatchMakingServers", SettingsItf::MATCHMAKING_SERVERS); - OLD_ITF_LINE("SteamMatchMaking", SettingsItf::MATCHMAKING); - - OLD_ITF_LINE("SteamUser", SettingsItf::USER); - OLD_ITF_LINE("SteamFriends", SettingsItf::FRIENDS); - OLD_ITF_LINE("SteamUtils", SettingsItf::UTILS); - OLD_ITF_LINE("STEAMUSERSTATS_INTERFACE_VERSION", SettingsItf::USER_STATS); - OLD_ITF_LINE("STEAMAPPS_INTERFACE_VERSION", SettingsItf::APPS); - OLD_ITF_LINE("SteamNetworking", SettingsItf::NETWORKING); - OLD_ITF_LINE("STEAMREMOTESTORAGE_INTERFACE_VERSION", SettingsItf::REMOTE_STORAGE); - OLD_ITF_LINE("STEAMSCREENSHOTS_INTERFACE_VERSION", SettingsItf::SCREENSHOTS); - OLD_ITF_LINE("STEAMHTTP_INTERFACE_VERSION", SettingsItf::HTTP); - OLD_ITF_LINE("STEAMUNIFIEDMESSAGES_INTERFACE_VERSION", SettingsItf::UNIFIED_MESSAGES); - - OLD_ITF_LINE("STEAMCONTROLLER_INTERFACE_VERSION", SettingsItf::CONTROLLER); - OLD_ITF_LINE("SteamController", SettingsItf::CONTROLLER); - - OLD_ITF_LINE("STEAMUGC_INTERFACE_VERSION", SettingsItf::UGC); - OLD_ITF_LINE("STEAMAPPLIST_INTERFACE_VERSION", SettingsItf::APPLIST); - OLD_ITF_LINE("STEAMMUSIC_INTERFACE_VERSION", SettingsItf::MUSIC); - OLD_ITF_LINE("STEAMMUSICREMOTE_INTERFACE_VERSION", SettingsItf::MUSIC_REMOTE); - OLD_ITF_LINE("STEAMHTMLSURFACE_INTERFACE_VERSION", SettingsItf::HTML_SURFACE); - OLD_ITF_LINE("STEAMINVENTORY_INTERFACE", SettingsItf::INVENTORY); - OLD_ITF_LINE("STEAMVIDEO_INTERFACE", SettingsItf::VIDEO); - OLD_ITF_LINE("SteamMasterServerUpdater", SettingsItf::MASTERSERVER_UPDATER); - -#undef OLD_ITF_LINE - - PRINT_DEBUG(" NOT REPLACED |%s|", line.c_str()); - } - - return true; -} - -static void parse_old_steam_interfaces() -{ - if (!try_parse_old_steam_interfaces_file(Local_Storage::get_game_settings_path() + "steam_interfaces.txt") && - !try_parse_old_steam_interfaces_file(Local_Storage::get_program_path() + "steam_interfaces.txt")) { - PRINT_DEBUG("Couldn't load steam_interfaces.txt"); - } -} - -static void load_all_config_settings() -{ - static std::recursive_mutex ini_mtx{}; - static bool loaded = false; - - std::lock_guard lck(ini_mtx); - if (loaded) return; - loaded = true; - - constexpr const static char* config_files[] = { - config_ini_app, - config_ini_main, - config_ini_overlay, - config_ini_user, - }; - - ini.SetUnicode(); - ini.SetSpaces(false); - - // we have to load the local one first, since it might change base saves_folder_name - { - CSimpleIniA local_ini{}; - local_ini.SetUnicode(); - - for (const auto &config_file : config_files) { - std::ifstream local_ini_file( std::filesystem::u8path(Local_Storage::get_game_settings_path() + config_file), std::ios::binary | std::ios::in); - if (!local_ini_file.is_open()) continue; - - auto err = local_ini.LoadData(local_ini_file); - local_ini_file.close(); - PRINT_DEBUG("result of parsing ini in local settings '%s' %i (success == 0)", config_file, (int)err); - if (err == SI_OK) { - merge_ini(local_ini); - } - } - - std::string saves_folder_name(common_helpers::string_strip(Settings::sanitize(local_ini.GetValue("user::saves", "saves_folder_name", "")))); - if (saves_folder_name.size()) { - Local_Storage::set_saves_folder_name(saves_folder_name); - PRINT_DEBUG("changed base folder for save data to '%s'", saves_folder_name.c_str()); - } - } - - std::string local_save_folder{}; - if (parse_local_save(local_save_folder) && local_save_folder.size()) { - CSimpleIniA local_ini{}; - local_ini.SetUnicode(); - - for (const auto &config_file : config_files) { - std::ifstream local_ini_file( std::filesystem::u8path(local_save_folder + Local_Storage::settings_storage_folder + PATH_SEPARATOR + config_file), std::ios::binary | std::ios::in); - if (!local_ini_file.is_open()) continue; - - auto err = local_ini.LoadData(local_ini_file); - local_ini_file.close(); - PRINT_DEBUG("result of parsing ini in local save '%s' %i (success == 0)", config_file, (int)err); - if (err == SI_OK) { - merge_ini(local_ini, true); - } - } - - std::string saves_folder_name(common_helpers::string_strip(Settings::sanitize(local_ini.GetValue("user::saves", "saves_folder_name", "")))); - if (saves_folder_name.size()) { - Local_Storage::set_saves_folder_name(saves_folder_name); - PRINT_DEBUG("changed base folder for save data to '%s'", saves_folder_name.c_str()); - } - - PRINT_DEBUG("global settings will be ignored since local save is being used"); - - } else { // only read global folder if we're not using local save - CSimpleIniA global_ini{}; - global_ini.SetUnicode(); - - // now we can access get_user_appdata_path() which might have been changed by the above code - for (const auto &config_file : config_files) { - std::ifstream ini_file( std::filesystem::u8path(Local_Storage::get_user_appdata_path() + Local_Storage::settings_storage_folder + PATH_SEPARATOR + config_file), std::ios::binary | std::ios::in); - if (!ini_file.is_open()) continue; - - auto err = global_ini.LoadData(ini_file); - ini_file.close(); - PRINT_DEBUG("result of parsing global ini '%s' %i (success == 0)", config_file, (int)err); - - if (err == SI_OK) { - merge_ini(global_ini); - } - } - } - - parse_old_steam_interfaces(); - -#ifndef EMU_RELEASE_BUILD - // dump the final ini file - { - PRINT_DEBUG("final ini start ---------"); - std::list sections{}; - ini.GetAllSections(sections); - for (auto const &sec : sections) { - PRINT_DEBUG("[%s]", sec.pItem); - std::list keys{}; - ini.GetAllKeys(sec.pItem, keys); - for (auto const &key : keys) { - std::list vals{}; - ini.GetAllValues(sec.pItem, key.pItem, vals); - for (const auto &val : vals) { - PRINT_DEBUG("%s=%s", key.pItem, val.pItem); - } - } - PRINT_DEBUG(""); - } - PRINT_DEBUG("final ini end *********"); - } -#endif // EMU_RELEASE_BUILD - - reset_LastError(); - -} - - -uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out) -{ - PRINT_DEBUG("start ----------"); - - load_all_config_settings(); - -#if defined(EMU_BUILD_STRING) - PRINT_DEBUG("emu build '%s'", EXPAND_AS_STR(EMU_BUILD_STRING)); -#else - PRINT_DEBUG(""); -#endif - - parse_crash_printer_location(); - - const std::string program_path(Local_Storage::get_program_path()); - const std::string steam_settings_path(Local_Storage::get_game_settings_path()); - - std::string save_path(Local_Storage::get_user_appdata_path()); - bool local_save = parse_local_save(save_path); - PRINT_DEBUG("program path: '%s', base path for saves: '%s'", program_path.c_str(), save_path.c_str()); - - Local_Storage *local_storage = new Local_Storage(save_path); - PRINT_DEBUG("global settings path: '%s'", local_storage->get_global_settings_path().c_str()); - uint32 appid = parse_steam_app_id(program_path); - local_storage->setAppId(appid); - - // Custom broadcasts - std::set custom_broadcasts{}; - load_custom_broadcasts(local_storage->get_global_settings_path(), custom_broadcasts); - load_custom_broadcasts(steam_settings_path, custom_broadcasts); - - // Listen port - uint16 port = parse_listen_port(local_storage); - // Acount name - std::string name(parse_account_name(local_storage)); - // Steam ID - CSteamID user_id = parse_user_steam_id(local_storage); - // Language - std::string language(parse_current_language(local_storage)); - // Supported languages, this will change the current language if needed - std::set supported_languages(parse_supported_languages(local_storage, language)); - - bool steam_offline_mode = ini.GetBoolValue("main::connectivity", "offline", false); - if (steam_offline_mode) { - PRINT_DEBUG("setting emu to offline mode"); - } - Settings *settings_client = new Settings(user_id, CGameID(appid), name, language, steam_offline_mode); - Settings *settings_server = new Settings(generate_steam_id_server(), CGameID(appid), name, language, steam_offline_mode); - - // listen port - settings_client->set_port(port); - settings_server->set_port(port); - // broadcasts list - settings_client->custom_broadcasts = custom_broadcasts; - settings_server->custom_broadcasts = custom_broadcasts; - // overlay warning for local save - settings_client->overlay_warn_local_save = local_save; - settings_server->overlay_warn_local_save = local_save; - // supported languages list - settings_client->set_supported_languages(supported_languages); - settings_server->set_supported_languages(supported_languages); - - parse_simple_features(settings_client, settings_server); - parse_stats_features(settings_client, settings_server); - - parse_dlc(settings_client, settings_server); - parse_installed_app_Ids(settings_client, settings_server); - parse_app_paths(settings_client, settings_server, program_path); - - parse_leaderboards(settings_client, settings_server); - parse_stats(settings_client, settings_server, local_storage); - parse_depots(settings_client, settings_server); - parse_subscribed_groups(settings_client, settings_server); - load_subscribed_groups_clans(local_storage->get_global_settings_path(), settings_client, settings_server); - load_subscribed_groups_clans(steam_settings_path, settings_client, settings_server); - - parse_mods_folder(settings_client, settings_server, local_storage); - 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); - parse_steam_game_stats_reports_dir(settings_client, settings_server); - - *settings_client_out = settings_client; - *settings_server_out = settings_server; - *local_storage_out = local_storage; - - PRINT_DEBUG("end *********"); - reset_LastError(); - return appid; -} - -void save_global_settings(class Local_Storage *local_storage, const char *name, const char *language) -{ - save_global_ini_value( - local_storage, - config_ini_user, - "user::general", "account_name", IniValue(name), - "user account name" - ); - - save_global_ini_value( - local_storage, - config_ini_user, - "user::general", "language", IniValue(language), - "the language reported to the game, default is 'english', check 'API language code' in https://partner.steamgames.com/doc/store/localization/languages" - ); -} - -bool settings_disable_lan_only() -{ - load_all_config_settings(); - return ini.GetBoolValue("main::connectivity", "disable_lan_only", false); -} - -const std::map& settings_old_interfaces() -{ - load_all_config_settings(); - return old_itfs_map; + settings_client->inner_deadzone = std::min(static_cast(ini.GetLongValue("main::gamepad", "inner_deadzone", settings_client->inner_deadzone)), static_cast(JOYSTICK_MAX)); + settings_server->inner_deadzone = std::min(static_cast(ini.GetLongValue("main::gamepad", "inner_deadzone", settings_server->inner_deadzone)), static_cast(JOYSTICK_MAX)); + settings_client->outer_deadzone = std::min(static_cast(ini.GetLongValue("main::gamepad", "outer_deadzone", settings_client->outer_deadzone)), static_cast(JOYSTICK_MAX)); + settings_server->outer_deadzone = std::min(static_cast(ini.GetLongValue("main::gamepad", "outer_deadzone", settings_server->outer_deadzone)), static_cast(JOYSTICK_MAX)); } + +// [main::stats] +static void parse_stats_features(class Settings *settings_client, class Settings *settings_server) +{ + settings_client->disable_leaderboards_create_unknown = ini.GetBoolValue("main::stats", "disable_leaderboards_create_unknown", settings_client->disable_leaderboards_create_unknown); + settings_server->disable_leaderboards_create_unknown = ini.GetBoolValue("main::stats", "disable_leaderboards_create_unknown", settings_server->disable_leaderboards_create_unknown); + + settings_client->allow_unknown_stats = ini.GetBoolValue("main::stats", "allow_unknown_stats", settings_client->allow_unknown_stats); + settings_server->allow_unknown_stats = ini.GetBoolValue("main::stats", "allow_unknown_stats", settings_server->allow_unknown_stats); + + settings_client->stat_achievement_progress_functionality = ini.GetBoolValue("main::stats", "stat_achievement_progress_functionality", settings_client->stat_achievement_progress_functionality); + settings_server->stat_achievement_progress_functionality = ini.GetBoolValue("main::stats", "stat_achievement_progress_functionality", settings_server->stat_achievement_progress_functionality); + + settings_client->save_only_higher_stat_achievement_progress = ini.GetBoolValue("main::stats", "save_only_higher_stat_achievement_progress", settings_client->save_only_higher_stat_achievement_progress); + settings_server->save_only_higher_stat_achievement_progress = ini.GetBoolValue("main::stats", "save_only_higher_stat_achievement_progress", settings_server->save_only_higher_stat_achievement_progress); + + { + long val_client = ini.GetLongValue("main::stats", "paginated_achievements_icons", settings_client->paginated_achievements_icons); + settings_client->paginated_achievements_icons = static_cast(val_client); + + long val_server = ini.GetLongValue("main::stats", "paginated_achievements_icons", settings_server->paginated_achievements_icons); + settings_server->paginated_achievements_icons = static_cast(val_server); + } +} + + + +static std::map old_itfs_map{}; + +static bool try_parse_old_steam_interfaces_file(std::string interfaces_path) +{ + std::ifstream input( std::filesystem::u8path(interfaces_path) ); + if (!input.is_open()) return false; + + PRINT_DEBUG("Trying to parse old steam interfaces from '%s'", interfaces_path.c_str()); + for( std::string line; std::getline( input, line ); ) { + line.erase(std::remove(line.begin(), line.end(), ' '), line.end()); + line.erase(std::remove(line.begin(), line.end(), '\n'), line.end()); + line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); + line.erase(std::remove(line.begin(), line.end(), '\t'), line.end()); + PRINT_DEBUG(" valid line: |%s|", line.c_str()); + +#define OLD_ITF_LINE(istr, itype) { \ + if (line.find(istr) != std::string::npos) { \ + old_itfs_map[itype] = line; \ + continue; \ + } \ +} + + OLD_ITF_LINE("SteamClient", SettingsItf::CLIENT); + + // NOTE: you must try to read the one with the most characters first + OLD_ITF_LINE("SteamGameServerStats", SettingsItf::GAMESERVER_STATS); + OLD_ITF_LINE("SteamGameServer", SettingsItf::GAMESERVER); + + // NOTE: you must try to read the one with the most characters first + OLD_ITF_LINE("SteamMatchMakingServers", SettingsItf::MATCHMAKING_SERVERS); + OLD_ITF_LINE("SteamMatchMaking", SettingsItf::MATCHMAKING); + + OLD_ITF_LINE("SteamUser", SettingsItf::USER); + OLD_ITF_LINE("SteamFriends", SettingsItf::FRIENDS); + OLD_ITF_LINE("SteamUtils", SettingsItf::UTILS); + OLD_ITF_LINE("STEAMUSERSTATS_INTERFACE_VERSION", SettingsItf::USER_STATS); + OLD_ITF_LINE("STEAMAPPS_INTERFACE_VERSION", SettingsItf::APPS); + OLD_ITF_LINE("SteamNetworking", SettingsItf::NETWORKING); + OLD_ITF_LINE("STEAMREMOTESTORAGE_INTERFACE_VERSION", SettingsItf::REMOTE_STORAGE); + OLD_ITF_LINE("STEAMSCREENSHOTS_INTERFACE_VERSION", SettingsItf::SCREENSHOTS); + OLD_ITF_LINE("STEAMHTTP_INTERFACE_VERSION", SettingsItf::HTTP); + OLD_ITF_LINE("STEAMUNIFIEDMESSAGES_INTERFACE_VERSION", SettingsItf::UNIFIED_MESSAGES); + + OLD_ITF_LINE("STEAMCONTROLLER_INTERFACE_VERSION", SettingsItf::CONTROLLER); + OLD_ITF_LINE("SteamController", SettingsItf::CONTROLLER); + + OLD_ITF_LINE("STEAMUGC_INTERFACE_VERSION", SettingsItf::UGC); + OLD_ITF_LINE("STEAMAPPLIST_INTERFACE_VERSION", SettingsItf::APPLIST); + OLD_ITF_LINE("STEAMMUSIC_INTERFACE_VERSION", SettingsItf::MUSIC); + OLD_ITF_LINE("STEAMMUSICREMOTE_INTERFACE_VERSION", SettingsItf::MUSIC_REMOTE); + OLD_ITF_LINE("STEAMHTMLSURFACE_INTERFACE_VERSION", SettingsItf::HTML_SURFACE); + OLD_ITF_LINE("STEAMINVENTORY_INTERFACE", SettingsItf::INVENTORY); + OLD_ITF_LINE("STEAMVIDEO_INTERFACE", SettingsItf::VIDEO); + OLD_ITF_LINE("SteamMasterServerUpdater", SettingsItf::MASTERSERVER_UPDATER); + +#undef OLD_ITF_LINE + + PRINT_DEBUG(" NOT REPLACED |%s|", line.c_str()); + } + + return true; +} + +static void parse_old_steam_interfaces() +{ + if (!try_parse_old_steam_interfaces_file(Local_Storage::get_game_settings_path() + "steam_interfaces.txt") && + !try_parse_old_steam_interfaces_file(Local_Storage::get_program_path() + "steam_interfaces.txt")) { + PRINT_DEBUG("Couldn't load steam_interfaces.txt"); + } +} + +static void load_all_config_settings() +{ + static std::recursive_mutex ini_mtx{}; + static bool loaded = false; + + std::lock_guard lck(ini_mtx); + if (loaded) return; + loaded = true; + + constexpr const static char* config_files[] = { + config_ini_app, + config_ini_main, + config_ini_overlay, + config_ini_user, + }; + + ini.SetUnicode(); + ini.SetSpaces(false); + + // we have to load the local one first, since it might change base saves_folder_name + { + CSimpleIniA local_ini{}; + local_ini.SetUnicode(); + + for (const auto &config_file : config_files) { + std::ifstream local_ini_file( std::filesystem::u8path(Local_Storage::get_game_settings_path() + config_file), std::ios::binary | std::ios::in); + if (!local_ini_file.is_open()) continue; + + auto err = local_ini.LoadData(local_ini_file); + local_ini_file.close(); + PRINT_DEBUG("result of parsing ini in local settings '%s' %i (success == 0)", config_file, (int)err); + if (err == SI_OK) { + merge_ini(local_ini); + } + } + + std::string saves_folder_name(common_helpers::string_strip(Settings::sanitize(local_ini.GetValue("user::saves", "saves_folder_name", "")))); + if (saves_folder_name.size()) { + Local_Storage::set_saves_folder_name(saves_folder_name); + PRINT_DEBUG("changed base folder for save data to '%s'", saves_folder_name.c_str()); + } + } + + std::string local_save_folder{}; + if (parse_local_save(local_save_folder) && local_save_folder.size()) { + CSimpleIniA local_ini{}; + local_ini.SetUnicode(); + + for (const auto &config_file : config_files) { + std::ifstream local_ini_file( std::filesystem::u8path(local_save_folder + Local_Storage::settings_storage_folder + PATH_SEPARATOR + config_file), std::ios::binary | std::ios::in); + if (!local_ini_file.is_open()) continue; + + auto err = local_ini.LoadData(local_ini_file); + local_ini_file.close(); + PRINT_DEBUG("result of parsing ini in local save '%s' %i (success == 0)", config_file, (int)err); + if (err == SI_OK) { + merge_ini(local_ini, true); + } + } + + std::string saves_folder_name(common_helpers::string_strip(Settings::sanitize(local_ini.GetValue("user::saves", "saves_folder_name", "")))); + if (saves_folder_name.size()) { + Local_Storage::set_saves_folder_name(saves_folder_name); + PRINT_DEBUG("changed base folder for save data to '%s'", saves_folder_name.c_str()); + } + + PRINT_DEBUG("global settings will be ignored since local save is being used"); + + } else { // only read global folder if we're not using local save + CSimpleIniA global_ini{}; + global_ini.SetUnicode(); + + // now we can access get_user_appdata_path() which might have been changed by the above code + for (const auto &config_file : config_files) { + std::ifstream ini_file( std::filesystem::u8path(Local_Storage::get_user_appdata_path() + Local_Storage::settings_storage_folder + PATH_SEPARATOR + config_file), std::ios::binary | std::ios::in); + if (!ini_file.is_open()) continue; + + auto err = global_ini.LoadData(ini_file); + ini_file.close(); + PRINT_DEBUG("result of parsing global ini '%s' %i (success == 0)", config_file, (int)err); + + if (err == SI_OK) { + merge_ini(global_ini); + } + } + } + + parse_old_steam_interfaces(); + +#ifndef EMU_RELEASE_BUILD + // dump the final ini file + { + PRINT_DEBUG("final ini start ---------"); + std::list sections{}; + ini.GetAllSections(sections); + for (auto const &sec : sections) { + PRINT_DEBUG("[%s]", sec.pItem); + std::list keys{}; + ini.GetAllKeys(sec.pItem, keys); + for (auto const &key : keys) { + std::list vals{}; + ini.GetAllValues(sec.pItem, key.pItem, vals); + for (const auto &val : vals) { + PRINT_DEBUG("%s=%s", key.pItem, val.pItem); + } + } + PRINT_DEBUG(""); + } + PRINT_DEBUG("final ini end *********"); + } +#endif // EMU_RELEASE_BUILD + + reset_LastError(); + +} + + +uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out) +{ + PRINT_DEBUG("start ----------"); + + load_all_config_settings(); + +#if defined(EMU_BUILD_STRING) + PRINT_DEBUG("emu build '%s'", EXPAND_AS_STR(EMU_BUILD_STRING)); +#else + PRINT_DEBUG(""); +#endif + + parse_crash_printer_location(); + + const std::string program_path(Local_Storage::get_program_path()); + const std::string steam_settings_path(Local_Storage::get_game_settings_path()); + + std::string save_path(Local_Storage::get_user_appdata_path()); + bool local_save = parse_local_save(save_path); + PRINT_DEBUG("program path: '%s', base path for saves: '%s'", program_path.c_str(), save_path.c_str()); + + Local_Storage *local_storage = new Local_Storage(save_path); + PRINT_DEBUG("global settings path: '%s'", local_storage->get_global_settings_path().c_str()); + uint32 appid = parse_steam_app_id(program_path); + local_storage->setAppId(appid); + + // Custom broadcasts + std::set custom_broadcasts{}; + load_custom_broadcasts(local_storage->get_global_settings_path(), custom_broadcasts); + load_custom_broadcasts(steam_settings_path, custom_broadcasts); + + // Listen port + uint16 port = parse_listen_port(local_storage); + // Acount name + std::string name(parse_account_name(local_storage)); + // Steam ID + CSteamID user_id = parse_user_steam_id(local_storage); + // Language + std::string language(parse_current_language(local_storage)); + // Supported languages, this will change the current language if needed + std::set supported_languages(parse_supported_languages(local_storage, language)); + + bool steam_offline_mode = ini.GetBoolValue("main::connectivity", "offline", false); + if (steam_offline_mode) { + PRINT_DEBUG("setting emu to offline mode"); + } + Settings *settings_client = new Settings(user_id, CGameID(appid), name, language, steam_offline_mode); + Settings *settings_server = new Settings(generate_steam_id_server(), CGameID(appid), name, language, steam_offline_mode); + + // listen port + settings_client->set_port(port); + settings_server->set_port(port); + // broadcasts list + settings_client->custom_broadcasts = custom_broadcasts; + settings_server->custom_broadcasts = custom_broadcasts; + // overlay warning for local save + settings_client->overlay_warn_local_save = local_save; + settings_server->overlay_warn_local_save = local_save; + // supported languages list + settings_client->set_supported_languages(supported_languages); + settings_server->set_supported_languages(supported_languages); + + parse_simple_features(settings_client, settings_server); + parse_stats_features(settings_client, settings_server); + parse_user_gamepad_settings(settings_client, settings_server); + + parse_dlc(settings_client, settings_server); + parse_installed_app_Ids(settings_client, settings_server); + parse_app_paths(settings_client, settings_server, program_path); + + parse_leaderboards(settings_client, settings_server); + parse_stats(settings_client, settings_server, local_storage); + parse_depots(settings_client, settings_server); + parse_subscribed_groups(settings_client, settings_server); + load_subscribed_groups_clans(local_storage->get_global_settings_path(), settings_client, settings_server); + load_subscribed_groups_clans(steam_settings_path, settings_client, settings_server); + + parse_mods_folder(settings_client, settings_server, local_storage); + 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); + parse_steam_game_stats_reports_dir(settings_client, settings_server); + + *settings_client_out = settings_client; + *settings_server_out = settings_server; + *local_storage_out = local_storage; + + PRINT_DEBUG("end *********"); + reset_LastError(); + return appid; +} + +void save_global_settings(class Local_Storage *local_storage, const char *name, const char *language) +{ + save_global_ini_value( + local_storage, + config_ini_user, + "user::general", "account_name", IniValue(name), + "user account name" + ); + + save_global_ini_value( + local_storage, + config_ini_user, + "user::general", "language", IniValue(language), + "the language reported to the game, default is 'english', check 'API language code' in https://partner.steamgames.com/doc/store/localization/languages" + ); +} + +bool settings_disable_lan_only() +{ + load_all_config_settings(); + return ini.GetBoolValue("main::connectivity", "disable_lan_only", false); +} + +const std::map& settings_old_interfaces() +{ + load_all_config_settings(); + return old_itfs_map; +} diff --git a/dll/steam_controller.cpp b/dll/steam_controller.cpp index 27c15e81..ce101d85 100644 --- a/dll/steam_controller.cpp +++ b/dll/steam_controller.cpp @@ -19,47 +19,63 @@ #define JOY_ID_START 10 #define STICK_DPAD 3 -#define DEADZONE_BUTTON_STICK 0.3 - - - -#if !defined(CONTROLLER_SUPPORT) - -inline void GamepadInit(void) {} -inline void GamepadShutdown(void) {} -inline void GamepadUpdate(void) {} -inline GAMEPAD_BOOL GamepadIsConnected(GAMEPAD_DEVICE device) { return GAMEPAD_FALSE; } -inline GAMEPAD_BOOL GamepadButtonDown(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button) { return GAMEPAD_FALSE; } -inline float GamepadTriggerLength(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) { return 0.0; } -inline GAMEPAD_STICKDIR GamepadStickDir(GAMEPAD_DEVICE device, GAMEPAD_STICK stick) { return STICKDIR_CENTER; } -inline void GamepadStickNormXY(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, float* outX, float* outY) {} -inline float GamepadStickLength(GAMEPAD_DEVICE device, GAMEPAD_STICK stick) { return 0.0; } -inline void GamepadSetRumble(GAMEPAD_DEVICE device, float left, float right, unsigned int rumble_length_ms) {} - -#endif - +using namespace gamepad_provider::sdl; Controller_Action::Controller_Action(ControllerHandle_t controller_handle) { this->controller_handle = controller_handle; } -void Controller_Action::activate_action_set(ControllerDigitalActionHandle_t active_set, std::map &controller_maps) { +void Controller_Action::activate_action_set(ControllerDigitalActionHandle_t active_set, std::map& controller_maps) { auto map = controller_maps.find(active_set); if (map == controller_maps.end()) return; this->active_set = active_set; this->active_map = map->second; + this->active_layers.clear(); +} + +void Controller_Action::activate_action_set_layer(ControllerActionSetHandle_t active_layer, std::map& controller_maps) { + auto map = controller_maps.find(active_layer); + auto layer = this->active_layers.find(active_layer); + if (map == controller_maps.end() || layer != this->active_layers.end()) return; + this->active_layers.insert(std::pair(active_layer, map->second)); +} + +void Controller_Action::deactivate_action_set_layer(ControllerActionSetHandle_t active_layer) { + auto layer = this->active_layers.find(active_layer); + if (layer == this->active_layers.end()) return; + this->active_layers.erase(layer); } std::set Controller_Action::button_id(ControllerDigitalActionHandle_t handle) { - auto a = active_map.active_digital.find(handle); - if (a == active_map.active_digital.end()) return {}; + Controller_Map map{}; + for (auto iter = active_layers.rbegin(); iter != active_layers.rend(); ++iter) { + map = iter->second; + auto a = map.active_digital.find(handle); + if (a == map.active_digital.end()) continue; + return a->second; + } + + // Active base action set + map = this->active_map; + auto a = map.active_digital.find(handle); + if (a == map.active_digital.end()) return {}; return a->second; } std::pair, enum EInputSourceMode> Controller_Action::analog_id(ControllerAnalogActionHandle_t handle) { - auto a = active_map.active_analog.find(handle); - if (a == active_map.active_analog.end()) return std::pair, enum EInputSourceMode>({}, k_EInputSourceMode_None); + Controller_Map map{}; + for (auto iter = active_layers.rbegin(); iter != active_layers.rend(); ++iter) { + map = iter->second; + auto a = map.active_analog.find(handle); + if (a == map.active_analog.end()) continue; + return a->second; + } + + // Active base action set + map = this->active_map; + auto a = map.active_analog.find(handle); + if (a == map.active_analog.end()) return {}; return a->second; } @@ -107,31 +123,36 @@ const std::map Steam_Controller::analog_inpu }; -void Steam_Controller::set_handles(std::map, std::string>>> action_sets) +void Steam_Controller::set_handles(std::map, std::string>>> action_sets, + std::map, std::string>>> action_set_layers) { + // TODO Load action_set_layers properly? uint64 handle_num = 1; - for (auto & set : action_sets) { + + auto storeActionSetHandle = [&](std::pair, std::string>>> set) { ControllerActionSetHandle_t action_handle_num = handle_num; ++handle_num; action_handles[set.first] = action_handle_num; - for (auto & config_key : set.second) { + for (auto& config_key : set.second) { uint64 current_handle_num = handle_num; ++handle_num; - for (auto & button_string : config_key.second.first) { + for (auto& button_string : config_key.second.first) { auto digital = button_strings.find(button_string); if (digital != button_strings.end()) { ControllerDigitalActionHandle_t digital_handle_num = current_handle_num; if (digital_action_handles.find(config_key.first) == digital_action_handles.end()) { digital_action_handles[config_key.first] = digital_handle_num; - } else { + } + else { digital_handle_num = digital_action_handles[config_key.first]; } controller_maps[action_handle_num].active_digital[digital_handle_num].insert(digital->second); - } else { + } + else { auto analog = analog_strings.find(button_string); if (analog != analog_strings.end()) { ControllerAnalogActionHandle_t analog_handle_num = current_handle_num; @@ -139,7 +160,8 @@ void Steam_Controller::set_handles(std::mapsecond == TRIGGER_LEFT || analog->second == TRIGGER_RIGHT) { source_mode = k_EInputSourceMode_Trigger; - } else { + } + else { source_mode = k_EInputSourceMode_JoystickMove; } @@ -150,26 +172,37 @@ void Steam_Controller::set_handles(std::mapsecond); controller_maps[action_handle_num].active_analog[analog_handle_num].second = source_mode; - } else { + } + else { PRINT_DEBUG("Did not recognize controller button %s", button_string.c_str()); continue; } } } } + }; + + for (auto& set : action_sets) { + storeActionSetHandle(set); + } + + for (auto& set : action_set_layers) { + storeActionSetHandle(set); } } -void Steam_Controller::background_rumble(Rumble_Thread_Data *data) +void Steam_Controller::background_rumble(Rumble_Thread_Data* data) { + // TODO Is this needed anymore? while (true) { unsigned short left, right; unsigned int rumble_length_ms; @@ -201,29 +234,30 @@ void Steam_Controller::background_rumble(Rumble_Thread_Data *data) } } - GamepadSetRumble((GAMEPAD_DEVICE)gamepad, ((float)left) / 65535.0f, ((float)right) / 65535.0f, rumble_length_ms); + // SDL_Gamepad* gamepadHandle = SDL_GetGamepadFromPlayerIndex(gamepad); + // SDL_RumbleGamepad(gamepadHandle, left, right, rumble_length_ms); } } -void Steam_Controller::steam_run_every_runcb(void *object) +void Steam_Controller::steam_run_every_runcb(void* object) { // PRINT_DEBUG_ENTRY(); - Steam_Controller *steam_controller = (Steam_Controller *)object; + Steam_Controller* steam_controller = (Steam_Controller*)object; steam_controller->RunCallbacks(); } -Steam_Controller::Steam_Controller(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb) +Steam_Controller::Steam_Controller(class Settings* settings, class SteamCallResults* callback_results, class SteamCallBacks* callbacks, class RunEveryRunCB* run_every_runcb) { this->settings = settings; this->callback_results = callback_results; this->callbacks = callbacks; this->run_every_runcb = run_every_runcb; - set_handles(settings->controller_settings.action_sets); + set_handles(settings->controller_settings.action_sets, settings->controller_settings.action_set_layers); disabled = action_handles.empty(); initialized = false; - + this->run_every_runcb->add(&Steam_Controller::steam_run_every_runcb, this); } @@ -244,29 +278,32 @@ bool Steam_Controller::Init(bool bExplicitlyCallRunFrame) return true; } - GamepadInit(); + GamepadInit(settings->combine_joycons); GamepadUpdate(); - for (int i = 1; i < 5; ++i) { - struct Controller_Action cont_action(i); + std::vector newHandles{}; + std::vector handles{}; + DetectGamepads(handles, newHandles); + + for (auto& id : handles) { + struct Controller_Action cont_action(id); //Activate the first action set. //TODO: check exactly what decides which gets activated by default if (action_handles.size() >= 1) { cont_action.activate_action_set(action_handles.begin()->second, controller_maps); } - - controllers.insert(std::pair(i, cont_action)); + controllers.insert(std::pair(id, cont_action)); } - rumble_thread_data = new Rumble_Thread_Data(); - background_rumble_thread = std::thread(background_rumble, rumble_thread_data); + //rumble_thread_data = new Rumble_Thread_Data(); + //background_rumble_thread = std::thread(background_rumble, rumble_thread_data); initialized = true; explicitly_call_run_frame = bExplicitlyCallRunFrame; return true; } -bool Steam_Controller::Init( const char *pchAbsolutePathToControllerConfigVDF ) +bool Steam_Controller::Init(const char* pchAbsolutePathToControllerConfigVDF) { PRINT_DEBUG("old"); return Init(); @@ -286,19 +323,22 @@ bool Steam_Controller::Shutdown() } controllers = std::map(); - rumble_thread_data->rumble_mutex.lock(); - rumble_thread_data->kill_rumble_thread = true; - rumble_thread_data->rumble_mutex.unlock(); - rumble_thread_data->rumble_thread_cv.notify_one(); - background_rumble_thread.join(); - delete rumble_thread_data; - rumble_thread_data = nullptr; + + // rumble_thread_data->rumble_mutex.lock(); + // rumble_thread_data->kill_rumble_thread = true; + // rumble_thread_data->rumble_mutex.unlock(); + // rumble_thread_data->rumble_thread_cv.notify_one(); + // background_rumble_thread.join(); + // delete rumble_thread_data; + // rumble_thread_data = nullptr; + GamepadShutdown(); + initialized = false; return true; } -void Steam_Controller::SetOverrideMode( const char *pchMode ) +void Steam_Controller::SetOverrideMode(const char* pchMode) { PRINT_DEBUG_TODO(); } @@ -306,14 +346,14 @@ void Steam_Controller::SetOverrideMode( const char *pchMode ) // Set the absolute path to the Input Action Manifest file containing the in-game actions // and file paths to the official configurations. Used in games that bundle Steam Input // configurations inside of the game depot instead of using the Steam Workshop -bool Steam_Controller::SetInputActionManifestFilePath( const char *pchInputActionManifestAbsolutePath ) +bool Steam_Controller::SetInputActionManifestFilePath(const char* pchInputActionManifestAbsolutePath) { PRINT_DEBUG_TODO(); //TODO SteamInput005 return false; } -bool Steam_Controller::BWaitForData( bool bWaitForever, uint32 unTimeout ) +bool Steam_Controller::BWaitForData(bool bWaitForever, uint32 unTimeout) { PRINT_DEBUG_TODO(); //TODO SteamInput005 @@ -343,7 +383,7 @@ void Steam_Controller::EnableDeviceCallbacks() // Enable SteamInputActionEvent_t callbacks. Directly calls your callback function // for lower latency than standard Steam callbacks. Supports one callback at a time. // Note: this is called within either SteamInput()->RunFrame or by SteamAPI_RunCallbacks -void Steam_Controller::EnableActionEventCallbacks( SteamInputActionEventCallbackPointer pCallback ) +void Steam_Controller::EnableActionEventCallbacks(SteamInputActionEventCallbackPointer pCallback) { PRINT_DEBUG_TODO(); //TODO SteamInput005 @@ -368,7 +408,7 @@ void Steam_Controller::RunFrame() RunFrame(true); } -bool Steam_Controller::GetControllerState( uint32 unControllerIndex, SteamControllerState001_t *pState ) +bool Steam_Controller::GetControllerState(uint32 unControllerIndex, SteamControllerState001_t* pState) { PRINT_DEBUG_TODO(); return false; @@ -377,7 +417,7 @@ bool Steam_Controller::GetControllerState( uint32 unControllerIndex, SteamContro // Enumerate currently connected controllers // handlesOut should point to a STEAM_CONTROLLER_MAX_COUNT sized array of ControllerHandle_t handles // Returns the number of handles written to handlesOut -int Steam_Controller::GetConnectedControllers( ControllerHandle_t *handlesOut ) +int Steam_Controller::GetConnectedControllers(ControllerHandle_t* handlesOut) { PRINT_DEBUG_ENTRY(); if (!handlesOut) return 0; @@ -385,11 +425,24 @@ int Steam_Controller::GetConnectedControllers( ControllerHandle_t *handlesOut ) return 0; } - int count = 0; - if (GamepadIsConnected(GAMEPAD_0)) {*handlesOut = GAMEPAD_0 + 1; ++handlesOut; ++count;}; - if (GamepadIsConnected(GAMEPAD_1)) {*handlesOut = GAMEPAD_1 + 1; ++handlesOut; ++count;}; - if (GamepadIsConnected(GAMEPAD_2)) {*handlesOut = GAMEPAD_2 + 1; ++handlesOut; ++count;}; - if (GamepadIsConnected(GAMEPAD_3)) {*handlesOut = GAMEPAD_3 + 1; ++handlesOut; ++count;}; + std::vector newHandles{}; + std::vector handles{}; + int count = DetectGamepads(handles, newHandles); + + for (const auto& id : newHandles) { + PRINT_DEBUG("creating new controller with id %i", id); + struct Controller_Action cont_action(id); + //Activate the first action set. + //TODO: check exactly what decides which gets activated by default + if (action_handles.size() >= 1) { + cont_action.activate_action_set(action_handles.begin()->second, controller_maps); + } + controllers.insert(std::pair(id, cont_action)); + } + + std::copy(handles.begin(), handles.end(), handlesOut); + + PRINT_DEBUG("first controller handle is %i", handlesOut[0]); PRINT_DEBUG("returned %i connected controllers", count); return count; @@ -398,7 +451,7 @@ int Steam_Controller::GetConnectedControllers( ControllerHandle_t *handlesOut ) // Invokes the Steam overlay and brings up the binding screen // Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode -bool Steam_Controller::ShowBindingPanel( ControllerHandle_t controllerHandle ) +bool Steam_Controller::ShowBindingPanel(ControllerHandle_t controllerHandle) { PRINT_DEBUG_TODO(); return false; @@ -407,12 +460,12 @@ bool Steam_Controller::ShowBindingPanel( ControllerHandle_t controllerHandle ) // ACTION SETS // Lookup the handle for an Action Set. Best to do this once on startup, and store the handles for all future API calls. -ControllerActionSetHandle_t Steam_Controller::GetActionSetHandle( const char *pszActionSetName ) +ControllerActionSetHandle_t Steam_Controller::GetActionSetHandle(const char* pszActionSetName) { PRINT_DEBUG("%s", pszActionSetName); if (!pszActionSetName) return 0; std::string upper_action_name(pszActionSetName); - std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); }); + std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(), [](unsigned char c) { return std::toupper(c); }); auto set_handle = action_handles.find(upper_action_name); if (set_handle == action_handles.end()) return 0; @@ -425,11 +478,11 @@ ControllerActionSetHandle_t Steam_Controller::GetActionSetHandle( const char *ps // Reconfigure the controller to use the specified action set (ie 'Menu', 'Walk' or 'Drive') // This is cheap, and can be safely called repeatedly. It's often easier to repeatedly call it in // your state loops, instead of trying to place it in all of your state transitions. -void Steam_Controller::ActivateActionSet( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle ) +void Steam_Controller::ActivateActionSet(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle) { PRINT_DEBUG("%llu %llu", controllerHandle, actionSetHandle); if (controllerHandle == STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS) { - for (auto & c: controllers) { + for (auto& c : controllers) { c.second.activate_action_set(actionSetHandle, controller_maps); } } @@ -440,7 +493,7 @@ void Steam_Controller::ActivateActionSet( ControllerHandle_t controllerHandle, C controller->second.activate_action_set(actionSetHandle, controller_maps); } -ControllerActionSetHandle_t Steam_Controller::GetCurrentActionSet( ControllerHandle_t controllerHandle ) +ControllerActionSetHandle_t Steam_Controller::GetCurrentActionSet(ControllerHandle_t controllerHandle) { //TODO: should return zero if no action set specifically activated with ActivateActionSet PRINT_DEBUG("%llu", controllerHandle); @@ -451,37 +504,54 @@ ControllerActionSetHandle_t Steam_Controller::GetCurrentActionSet( ControllerHan } -void Steam_Controller::ActivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle ) +void Steam_Controller::ActivateActionSetLayer(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle) { - PRINT_DEBUG_TODO(); + PRINT_DEBUG_ENTRY(); + auto controller = controllers.find(controllerHandle); + if (controller == controllers.end()) return; + controller->second.activate_action_set_layer(actionSetLayerHandle, controller_maps); } -void Steam_Controller::DeactivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle ) +void Steam_Controller::DeactivateActionSetLayer(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle) { - PRINT_DEBUG_TODO(); + PRINT_DEBUG_ENTRY(); + auto controller = controllers.find(controllerHandle); + if (controller == controllers.end()) return; + controller->second.deactivate_action_set_layer(actionSetLayerHandle); } -void Steam_Controller::DeactivateAllActionSetLayers( ControllerHandle_t controllerHandle ) +void Steam_Controller::DeactivateAllActionSetLayers(ControllerHandle_t controllerHandle) { - PRINT_DEBUG_TODO(); + PRINT_DEBUG_ENTRY(); + auto controller = controllers.find(controllerHandle); + if (controller == controllers.end()) return; + controller->second.active_layers.clear(); } -int Steam_Controller::GetActiveActionSetLayers( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t *handlesOut ) +int Steam_Controller::GetActiveActionSetLayers(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t* handlesOut) { - PRINT_DEBUG_TODO(); - return 0; + PRINT_DEBUG_ENTRY(); + auto controller = controllers.find(controllerHandle); + if (controller == controllers.end()) return 0; + int count = 0; + for (auto const& handle : controller->second.active_layers) { + *handlesOut = handle.first; + ++handlesOut; + count++; + } + return count; } // ACTIONS // Lookup the handle for a digital action. Best to do this once on startup, and store the handles for all future API calls. -ControllerDigitalActionHandle_t Steam_Controller::GetDigitalActionHandle( const char *pszActionName ) +ControllerDigitalActionHandle_t Steam_Controller::GetDigitalActionHandle(const char* pszActionName) { PRINT_DEBUG("%s", pszActionName); if (!pszActionName) return 0; std::string upper_action_name(pszActionName); - std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); }); + std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(), [](unsigned char c) { return std::toupper(c); }); auto handle = digital_action_handles.find(upper_action_name); if (handle == digital_action_handles.end()) { @@ -496,7 +566,7 @@ ControllerDigitalActionHandle_t Steam_Controller::GetDigitalActionHandle( const // Returns the current state of the supplied digital game action -ControllerDigitalActionData_t Steam_Controller::GetDigitalActionData( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle ) +ControllerDigitalActionData_t Steam_Controller::GetDigitalActionData(ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle) { PRINT_DEBUG("%llu %llu", controllerHandle, digitalActionHandle); ControllerDigitalActionData_t digitalData; @@ -510,50 +580,36 @@ ControllerDigitalActionData_t Steam_Controller::GetDigitalActionData( Controller if (!buttons.size()) return digitalData; digitalData.bActive = true; - GAMEPAD_DEVICE device = (GAMEPAD_DEVICE)(controllerHandle - 1); for (auto button : buttons) { bool pressed = false; if (button < BUTTON_COUNT) { - pressed = GamepadButtonDown(device, (GAMEPAD_BUTTON)button); - } else { + pressed = GamepadButtonDown(controllerHandle, (GAMEPAD_BUTTON)button); + } + else { switch (button) { - case BUTTON_LTRIGGER: - pressed = GamepadTriggerLength(device, TRIGGER_LEFT) > 0.8; - break; - case BUTTON_RTRIGGER: - pressed = GamepadTriggerLength(device, TRIGGER_RIGHT) > 0.8; - break; - case BUTTON_STICK_LEFT_UP: - case BUTTON_STICK_LEFT_DOWN: - case BUTTON_STICK_LEFT_LEFT: - case BUTTON_STICK_LEFT_RIGHT: { - float x = 0, y = 0, len = GamepadStickLength(device, STICK_LEFT); - GamepadStickNormXY(device, STICK_LEFT, &x, &y); - x *= len; - y *= len; - if (button == BUTTON_STICK_LEFT_UP) pressed = y > DEADZONE_BUTTON_STICK; - if (button == BUTTON_STICK_LEFT_DOWN) pressed = y < -DEADZONE_BUTTON_STICK; - if (button == BUTTON_STICK_LEFT_RIGHT) pressed = x > DEADZONE_BUTTON_STICK; - if (button == BUTTON_STICK_LEFT_LEFT) pressed = x < -DEADZONE_BUTTON_STICK; - break; - } - case BUTTON_STICK_RIGHT_UP: - case BUTTON_STICK_RIGHT_DOWN: - case BUTTON_STICK_RIGHT_LEFT: - case BUTTON_STICK_RIGHT_RIGHT: { - float x = 0, y = 0, len = GamepadStickLength(device, STICK_RIGHT); - GamepadStickNormXY(device, STICK_RIGHT, &x, &y); - x *= len; - y *= len; - if (button == BUTTON_STICK_RIGHT_UP) pressed = y > DEADZONE_BUTTON_STICK; - if (button == BUTTON_STICK_RIGHT_DOWN) pressed = y < -DEADZONE_BUTTON_STICK; - if (button == BUTTON_STICK_RIGHT_RIGHT) pressed = x > DEADZONE_BUTTON_STICK; - if (button == BUTTON_STICK_RIGHT_LEFT) pressed = x < -DEADZONE_BUTTON_STICK; - break; - } - default: - break; + case BUTTON_STICK_LEFT_UP: + case BUTTON_STICK_LEFT_DOWN: + case BUTTON_STICK_LEFT_LEFT: + case BUTTON_STICK_LEFT_RIGHT: { + float x = 0, y = 0; + GamepadStickNormXY(controllerHandle, STICK_LEFT, &x, &y, this->settings->inner_deadzone, this->settings->outer_deadzone); + if (button == BUTTON_STICK_LEFT_DOWN || button == BUTTON_STICK_LEFT_UP) pressed = y != 0.0f; + if (button == BUTTON_STICK_LEFT_RIGHT || button == BUTTON_STICK_LEFT_LEFT) pressed = x != 0.0f; + break; + } + case BUTTON_STICK_RIGHT_UP: + case BUTTON_STICK_RIGHT_DOWN: + case BUTTON_STICK_RIGHT_LEFT: + case BUTTON_STICK_RIGHT_RIGHT: { + float x = 0, y = 0; + GamepadStickNormXY(controllerHandle, STICK_LEFT, &x, &y, this->settings->inner_deadzone, this->settings->outer_deadzone); + if (button == BUTTON_STICK_RIGHT_DOWN || button == BUTTON_STICK_RIGHT_UP) pressed = y != 0.0f; + if (button == BUTTON_STICK_RIGHT_RIGHT || button == BUTTON_STICK_RIGHT_LEFT) pressed = x != 0.0f; + break; + } + default: + break; } } @@ -569,19 +625,39 @@ ControllerDigitalActionData_t Steam_Controller::GetDigitalActionData( Controller // Get the origin(s) for a digital action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action. // originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles -int Steam_Controller::GetDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, EControllerActionOrigin *originsOut ) +int Steam_Controller::GetDigitalActionOrigins(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, EControllerActionOrigin* originsOut) { PRINT_DEBUG_ENTRY(); EInputActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS]; - int ret = GetDigitalActionOrigins(controllerHandle, actionSetHandle, digitalActionHandle, origins ); + int ret = GetDigitalActionOrigins(controllerHandle, actionSetHandle, digitalActionHandle, origins); for (int i = 0; i < ret; ++i) { - originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_XBox360_A - (long)k_EControllerActionOrigin_XBox360_A)); + switch (GamepadGetType(controllerHandle)) { + case k_ESteamInputType_PS3Controller: + case k_ESteamInputType_PS4Controller: + originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_PS4_X - (long)k_EControllerActionOrigin_PS4_X)); + break; + case k_ESteamInputType_PS5Controller: + originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_PS5_X - (long)k_EControllerActionOrigin_PS5_X)); + break; + case k_ESteamInputType_SwitchJoyConSingle: + case k_ESteamInputType_SwitchJoyConPair: + case k_ESteamInputType_SwitchProController: + originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_Switch_A - (long)k_EControllerActionOrigin_Switch_A)); + break; + case k_ESteamInputType_XBoxOneController: + originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_XBoxOne_A - (long)k_EControllerActionOrigin_XBoxOne_A)); + break; + case k_ESteamInputType_XBox360Controller: + default: + originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_XBox360_A - (long)k_EControllerActionOrigin_XBox360_A)); + break; + } } return ret; } -int Steam_Controller::GetDigitalActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputDigitalActionHandle_t digitalActionHandle, EInputActionOrigin *originsOut ) +int Steam_Controller::GetDigitalActionOrigins(InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputDigitalActionHandle_t digitalActionHandle, EInputActionOrigin* originsOut) { PRINT_DEBUG_ENTRY(); auto controller = controllers.find(inputHandle); @@ -593,88 +669,338 @@ int Steam_Controller::GetDigitalActionOrigins( InputHandle_t inputHandle, InputA auto a = map->second.active_digital.find(digitalActionHandle); if (a == map->second.active_digital.end()) return 0; + // TODO Different controller layouts int count = 0; - for (auto button: a->second) { + ESteamInputType type = GamepadGetType(controller->first); + for (auto button : a->second) { + originsOut[count] = k_EInputActionOrigin_None; switch (button) { - case BUTTON_A: + case BUTTON_A: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_X; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_X; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_A; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) { + if (this->settings->flip_nintendo_layout) + originsOut[count] = k_EInputActionOrigin_Switch_B; + else + originsOut[count] = k_EInputActionOrigin_Switch_A; + } + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_A; - break; - case BUTTON_B: + break; + + case BUTTON_B: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_Circle; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_Circle; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_B; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) { + if (this->settings->flip_nintendo_layout) + originsOut[count] = k_EInputActionOrigin_Switch_A; + else + originsOut[count] = k_EInputActionOrigin_Switch_B; + } + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_B; - break; - case BUTTON_X: + break; + + case BUTTON_X: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_Square; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_Square; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_X; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) { + if (this->settings->flip_nintendo_layout) + originsOut[count] = k_EInputActionOrigin_Switch_Y; + else + originsOut[count] = k_EInputActionOrigin_Switch_X; + } + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_X; - break; - case BUTTON_Y: + break; + + case BUTTON_Y: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_Triangle; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_Triangle; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_Y; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) { + if (this->settings->flip_nintendo_layout) + originsOut[count] = k_EInputActionOrigin_Switch_X; + else + originsOut[count] = k_EInputActionOrigin_Switch_Y; + } + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_Y; - break; - case BUTTON_LEFT_SHOULDER: + break; + + case BUTTON_LEFT_SHOULDER: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_LeftBumper; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_LeftBumper; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_LeftBumper; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_LeftBumper; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_LeftBumper; - break; - case BUTTON_RIGHT_SHOULDER: + break; + + case BUTTON_RIGHT_SHOULDER: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_RightBumper; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_RightBumper; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_RightBumper; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_RightBumper; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_RightBumper; - break; - case BUTTON_START: + break; + + case BUTTON_START: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_Options; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_Option; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_Menu; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_Plus; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_Start; - break; - case BUTTON_BACK: + break; + + case BUTTON_BACK: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_Share; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_Create; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_View; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_Minus; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_Back; - break; - case BUTTON_LTRIGGER: + break; + + case BUTTON_LTRIGGER: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_LeftTrigger_Click; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_LeftTrigger_Click; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_LeftTrigger_Click; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_LeftTrigger_Click; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_LeftTrigger_Click; - break; - case BUTTON_RTRIGGER: + break; + + case BUTTON_RTRIGGER: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_RightTrigger_Click; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_RightTrigger_Click; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_RightTrigger_Click; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_RightTrigger_Click; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_RightTrigger_Click; - break; - case BUTTON_LEFT_THUMB: + break; + + case BUTTON_LEFT_THUMB: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_LeftStick_Click; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_LeftStick_Click; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_LeftStick_Click; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_LeftStick_Click; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_Click; - break; - case BUTTON_RIGHT_THUMB: + break; + + case BUTTON_RIGHT_THUMB: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_RightStick_Click; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_RightStick_Click; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_RightStick_Click; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_RightStick_Click; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_Click; - break; + break; - case BUTTON_STICK_LEFT_UP: + case BUTTON_STICK_LEFT_UP: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_LeftStick_DPadNorth; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_LeftStick_DPadNorth; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_LeftStick_DPadNorth; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_LeftStick_DPadNorth; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadNorth; - break; - case BUTTON_STICK_LEFT_DOWN: + break; + + case BUTTON_STICK_LEFT_DOWN: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_LeftStick_DPadSouth; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_LeftStick_DPadSouth; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_LeftStick_DPadSouth; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_LeftStick_DPadSouth; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadSouth; - break; - case BUTTON_STICK_LEFT_LEFT: + break; + + case BUTTON_STICK_LEFT_LEFT: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_LeftStick_DPadWest; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_LeftStick_DPadWest; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_LeftStick_DPadWest; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_LeftStick_DPadWest; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadWest; - break; - case BUTTON_STICK_LEFT_RIGHT: + break; + + case BUTTON_STICK_LEFT_RIGHT: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_LeftStick_DPadEast; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_LeftStick_DPadEast; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_LeftStick_DPadEast; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_LeftStick_DPadEast; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadEast; - break; + break; - case BUTTON_STICK_RIGHT_UP: + case BUTTON_STICK_RIGHT_UP: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_RightStick_DPadNorth; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_RightStick_DPadNorth; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_RightStick_DPadNorth; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_RightStick_DPadNorth; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadNorth; - break; - case BUTTON_STICK_RIGHT_DOWN: + break; + + case BUTTON_STICK_RIGHT_DOWN: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_RightStick_DPadSouth; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_RightStick_DPadSouth; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_RightStick_DPadSouth; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_RightStick_DPadSouth; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadSouth; - break; - case BUTTON_STICK_RIGHT_LEFT: + break; + + case BUTTON_STICK_RIGHT_LEFT: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_RightStick_DPadWest; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_RightStick_DPadWest; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_RightStick_DPadWest; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_RightStick_DPadWest; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadWest; - break; - case BUTTON_STICK_RIGHT_RIGHT: + break; + + case BUTTON_STICK_RIGHT_RIGHT: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_RightStick_DPadEast; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_RightStick_DPadEast; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_RightStick_DPadEast; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_RightStick_DPadEast; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadEast; - break; + break; - case BUTTON_DPAD_UP: + case BUTTON_DPAD_UP: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_DPad_North; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_DPad_North; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_DPad_North; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_DPad_North; + else if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_DPad_North; - break; - case BUTTON_DPAD_DOWN: - originsOut[count] = k_EInputActionOrigin_XBox360_DPad_South; - break; - case BUTTON_DPAD_LEFT: - originsOut[count] = k_EInputActionOrigin_XBox360_DPad_West; - break; - case BUTTON_DPAD_RIGHT: - originsOut[count] = k_EInputActionOrigin_XBox360_DPad_East; - break; + break; - default: - originsOut[count] = k_EInputActionOrigin_None; - break; + case BUTTON_DPAD_DOWN: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_DPad_South; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_DPad_South; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_DPad_South; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_DPad_South; + else if (type == k_ESteamInputType_XBox360Controller) + originsOut[count] = k_EInputActionOrigin_XBox360_DPad_South; + break; + + case BUTTON_DPAD_LEFT: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_DPad_West; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_DPad_West; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_DPad_West; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_DPad_West; + else if (type == k_ESteamInputType_XBox360Controller) + originsOut[count] = k_EInputActionOrigin_XBox360_DPad_West; + break; + case BUTTON_DPAD_RIGHT: + if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) + originsOut[count] = k_EInputActionOrigin_PS4_DPad_East; + else if (type == k_ESteamInputType_PS5Controller) + originsOut[count] = k_EInputActionOrigin_PS5_DPad_East; + else if (type == k_ESteamInputType_XBoxOneController) + originsOut[count] = k_EInputActionOrigin_XBoxOne_DPad_East; + else if (type == k_ESteamInputType_SwitchJoyConSingle || type == k_ESteamInputType_SwitchJoyConPair || type == k_ESteamInputType_SwitchProController) + originsOut[count] = k_EInputActionOrigin_Switch_DPad_East; + else if (type == k_ESteamInputType_XBox360Controller) + originsOut[count] = k_EInputActionOrigin_XBox360_DPad_East; + break; } ++count; @@ -687,7 +1013,7 @@ int Steam_Controller::GetDigitalActionOrigins( InputHandle_t inputHandle, InputA } // Returns a localized string (from Steam's language setting) for the user-facing action name corresponding to the specified handle -const char* Steam_Controller::GetStringForDigitalActionName( InputDigitalActionHandle_t eActionHandle ) +const char* Steam_Controller::GetStringForDigitalActionName(InputDigitalActionHandle_t eActionHandle) { PRINT_DEBUG_TODO(); //TODO SteamInput005 @@ -695,12 +1021,12 @@ const char* Steam_Controller::GetStringForDigitalActionName( InputDigitalActionH } // Lookup the handle for an analog action. Best to do this once on startup, and store the handles for all future API calls. -ControllerAnalogActionHandle_t Steam_Controller::GetAnalogActionHandle( const char *pszActionName ) +ControllerAnalogActionHandle_t Steam_Controller::GetAnalogActionHandle(const char* pszActionName) { PRINT_DEBUG("%s", pszActionName); if (!pszActionName) return 0; std::string upper_action_name(pszActionName); - std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); }); + std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(), [](unsigned char c) { return std::toupper(c); }); auto handle = analog_action_handles.find(upper_action_name); if (handle == analog_action_handles.end()) return 0; @@ -710,10 +1036,9 @@ ControllerAnalogActionHandle_t Steam_Controller::GetAnalogActionHandle( const ch // Returns the current state of these supplied analog game action -ControllerAnalogActionData_t Steam_Controller::GetAnalogActionData( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle ) +ControllerAnalogActionData_t Steam_Controller::GetAnalogActionData(ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle) { PRINT_DEBUG("%llu %llu", controllerHandle, analogActionHandle); - GAMEPAD_DEVICE device = (GAMEPAD_DEVICE)(controllerHandle - 1); ControllerAnalogActionData_t data; data.eMode = k_EInputSourceMode_None; @@ -733,8 +1058,8 @@ ControllerAnalogActionData_t Steam_Controller::GetAnalogActionData( ControllerHa if (a >= JOY_ID_START) { int joystick_id = a - JOY_ID_START; if (joystick_id == STICK_DPAD) { - int mov_y = (int)GamepadButtonDown(device, BUTTON_DPAD_UP) - (int)GamepadButtonDown(device, BUTTON_DPAD_DOWN); - int mov_x = (int)GamepadButtonDown(device, BUTTON_DPAD_RIGHT) - (int)GamepadButtonDown(device, BUTTON_DPAD_LEFT); + int mov_y = (int)GamepadButtonDown(controllerHandle, BUTTON_DPAD_UP) - (int)GamepadButtonDown(controllerHandle, BUTTON_DPAD_DOWN); + int mov_x = (int)GamepadButtonDown(controllerHandle, BUTTON_DPAD_RIGHT) - (int)GamepadButtonDown(controllerHandle, BUTTON_DPAD_LEFT); if (mov_y || mov_x) { data.x = static_cast(mov_x); data.y = static_cast(mov_y); @@ -742,14 +1067,13 @@ ControllerAnalogActionData_t Steam_Controller::GetAnalogActionData( ControllerHa data.x = data.x * length; data.y = data.y * length; } - } else { - GamepadStickNormXY(device, (GAMEPAD_STICK) joystick_id, &data.x, &data.y); - float length = GamepadStickLength(device, (GAMEPAD_STICK) joystick_id); - data.x = data.x * length; - data.y = data.y * length; } - } else { - data.x = GamepadTriggerLength(device, (GAMEPAD_TRIGGER) a); + else { + GamepadStickNormXY(controllerHandle, (GAMEPAD_STICK)joystick_id, &data.x, &data.y, this->settings->inner_deadzone, this->settings->outer_deadzone); + } + } + else { + data.x = GamepadTriggerLength(controllerHandle, (GAMEPAD_TRIGGER)a); } if (data.x || data.y) { @@ -763,19 +1087,39 @@ ControllerAnalogActionData_t Steam_Controller::GetAnalogActionData( ControllerHa // Get the origin(s) for an analog action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action. // originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles -int Steam_Controller::GetAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, EControllerActionOrigin *originsOut ) +int Steam_Controller::GetAnalogActionOrigins(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, EControllerActionOrigin* originsOut) { PRINT_DEBUG_ENTRY(); EInputActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS]; - int ret = GetAnalogActionOrigins(controllerHandle, actionSetHandle, analogActionHandle, origins ); + int ret = GetAnalogActionOrigins(controllerHandle, actionSetHandle, analogActionHandle, origins); for (int i = 0; i < ret; ++i) { - originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_XBox360_A - (long)k_EControllerActionOrigin_XBox360_A)); + switch (GamepadGetType(controllerHandle)) { + case k_ESteamInputType_PS3Controller: + case k_ESteamInputType_PS4Controller: + originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_PS4_X - (long)k_EControllerActionOrigin_PS4_X)); + break; + case k_ESteamInputType_PS5Controller: + originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_PS5_X - (long)k_EControllerActionOrigin_PS5_X)); + break; + case k_ESteamInputType_SwitchJoyConSingle: + case k_ESteamInputType_SwitchJoyConPair: + case k_ESteamInputType_SwitchProController: + originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_Switch_A - (long)k_EControllerActionOrigin_Switch_A)); + break; + case k_ESteamInputType_XBoxOneController: + originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_XBoxOne_A - (long)k_EControllerActionOrigin_XBoxOne_A)); + break; + case k_ESteamInputType_XBox360Controller: + default: + originsOut[i] = (EControllerActionOrigin)(origins[i] - ((long)k_EInputActionOrigin_XBox360_A - (long)k_EControllerActionOrigin_XBox360_A)); + break; + } } return ret; } -int Steam_Controller::GetAnalogActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputAnalogActionHandle_t analogActionHandle, EInputActionOrigin *originsOut ) +int Steam_Controller::GetAnalogActionOrigins(InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputAnalogActionHandle_t analogActionHandle, EInputActionOrigin* originsOut) { PRINT_DEBUG_ENTRY(); auto controller = controllers.find(inputHandle); @@ -788,26 +1132,52 @@ int Steam_Controller::GetAnalogActionOrigins( InputHandle_t inputHandle, InputAc if (a == map->second.active_analog.end()) return 0; int count = 0; - for (auto b: a->second.first) { + ESteamInputType type = GamepadGetType(inputHandle); + for (auto b : a->second.first) { + originsOut[count] = k_EInputActionOrigin_None; switch (b) { - case TRIGGER_LEFT: - originsOut[count] = k_EInputActionOrigin_XBox360_LeftTrigger_Pull; - break; - case TRIGGER_RIGHT: - originsOut[count] = k_EInputActionOrigin_XBox360_RightTrigger_Pull; - break; - case STICK_LEFT + JOY_ID_START: - originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_Move; - break; - case STICK_RIGHT + JOY_ID_START: - originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_Move; - break; - case STICK_DPAD + JOY_ID_START: - originsOut[count] = k_EInputActionOrigin_XBox360_DPad_Move; - break; - default: - originsOut[count] = k_EInputActionOrigin_None; - break; + case TRIGGER_LEFT: + if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_LeftTrigger_Pull; + else if (type == k_ESteamInputType_XBoxOneController) originsOut[count] = k_EInputActionOrigin_XBoxOne_LeftTrigger_Pull; + else if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) originsOut[count] = k_EInputActionOrigin_PS4_LeftTrigger_Pull; + else if (type == k_ESteamInputType_PS5Controller) originsOut[count] = k_EInputActionOrigin_PS5_LeftTrigger_Pull; + else if (type == k_ESteamInputType_SwitchProController || type == k_ESteamInputType_SwitchJoyConPair + || type == k_ESteamInputType_SwitchJoyConSingle) originsOut[count] = k_EInputActionOrigin_Switch_LeftTrigger_Pull; + break; + case TRIGGER_RIGHT: + if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_RightTrigger_Pull; + else if (type == k_ESteamInputType_XBoxOneController) originsOut[count] = k_EInputActionOrigin_XBoxOne_RightTrigger_Pull; + else if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) originsOut[count] = k_EInputActionOrigin_PS4_RightTrigger_Pull; + else if (type == k_ESteamInputType_PS5Controller) originsOut[count] = k_EInputActionOrigin_PS5_RightTrigger_Pull; + else if (type == k_ESteamInputType_SwitchProController || type == k_ESteamInputType_SwitchJoyConPair + || type == k_ESteamInputType_SwitchJoyConSingle) originsOut[count] = k_EInputActionOrigin_Switch_RightTrigger_Pull; + break; + case STICK_LEFT + JOY_ID_START: + if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_Move; + else if (type == k_ESteamInputType_XBoxOneController) originsOut[count] = k_EInputActionOrigin_XBoxOne_LeftStick_Move; + else if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) originsOut[count] = k_EInputActionOrigin_PS4_LeftStick_Move; + else if (type == k_ESteamInputType_PS5Controller) originsOut[count] = k_EInputActionOrigin_PS5_LeftStick_Move; + else if (type == k_ESteamInputType_SwitchProController || type == k_ESteamInputType_SwitchJoyConPair + || type == k_ESteamInputType_SwitchJoyConSingle) originsOut[count] = k_EInputActionOrigin_Switch_LeftStick_Move; + break; + case STICK_RIGHT + JOY_ID_START: + if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_Move; + else if (type == k_ESteamInputType_XBoxOneController) originsOut[count] = k_EInputActionOrigin_XBoxOne_RightStick_Move; + else if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) originsOut[count] = k_EInputActionOrigin_PS4_RightStick_Move; + else if (type == k_ESteamInputType_PS5Controller) originsOut[count] = k_EInputActionOrigin_PS5_RightStick_Move; + else if (type == k_ESteamInputType_SwitchProController || type == k_ESteamInputType_SwitchJoyConPair + || type == k_ESteamInputType_SwitchJoyConSingle) originsOut[count] = k_EInputActionOrigin_Switch_RightStick_Move; + break; + case STICK_DPAD + JOY_ID_START: + if (type == k_ESteamInputType_XBox360Controller) originsOut[count] = k_EInputActionOrigin_XBox360_DPad_Move; + else if (type == k_ESteamInputType_XBoxOneController) originsOut[count] = k_EInputActionOrigin_XBoxOne_DPad_Move; + else if (type == k_ESteamInputType_PS3Controller || type == k_ESteamInputType_PS4Controller) originsOut[count] = k_EInputActionOrigin_PS4_DPad_Move; + else if (type == k_ESteamInputType_PS5Controller) originsOut[count] = k_EInputActionOrigin_PS5_DPad_Move; + else if (type == k_ESteamInputType_SwitchProController || type == k_ESteamInputType_SwitchJoyConPair + || type == k_ESteamInputType_SwitchJoyConSingle) originsOut[count] = k_EInputActionOrigin_Switch_DPad_Move; + break; + default: + break; } ++count; @@ -819,40 +1189,40 @@ int Steam_Controller::GetAnalogActionOrigins( InputHandle_t inputHandle, InputAc return count; } - -void Steam_Controller::StopAnalogActionMomentum( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction ) + +void Steam_Controller::StopAnalogActionMomentum(ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction) { PRINT_DEBUG("%llu %llu", controllerHandle, eAction); } // Trigger a haptic pulse on a controller -void Steam_Controller::TriggerHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec ) +void Steam_Controller::TriggerHapticPulse(ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec) { PRINT_DEBUG_TODO(); } // Trigger a haptic pulse on a controller -void Steam_Controller::Legacy_TriggerHapticPulse( InputHandle_t inputHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec ) +void Steam_Controller::Legacy_TriggerHapticPulse(InputHandle_t inputHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec) { PRINT_DEBUG_TODO(); - TriggerHapticPulse(inputHandle, eTargetPad, usDurationMicroSec ); + TriggerHapticPulse(inputHandle, eTargetPad, usDurationMicroSec); } -void Steam_Controller::TriggerHapticPulse( uint32 unControllerIndex, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec ) +void Steam_Controller::TriggerHapticPulse(uint32 unControllerIndex, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec) { PRINT_DEBUG("old"); - TriggerHapticPulse(unControllerIndex, eTargetPad, usDurationMicroSec ); + TriggerHapticPulse(unControllerIndex, eTargetPad, usDurationMicroSec); } // Trigger a pulse with a duty cycle of usDurationMicroSec / usOffMicroSec, unRepeat times. // nFlags is currently unused and reserved for future use. -void Steam_Controller::TriggerRepeatedHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags ) +void Steam_Controller::TriggerRepeatedHapticPulse(ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags) { PRINT_DEBUG_TODO(); } -void Steam_Controller::Legacy_TriggerRepeatedHapticPulse( InputHandle_t inputHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags ) +void Steam_Controller::Legacy_TriggerRepeatedHapticPulse(InputHandle_t inputHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags) { PRINT_DEBUG_TODO(); TriggerRepeatedHapticPulse(inputHandle, eTargetPad, usDurationMicroSec, usOffMicroSec, unRepeat, nFlags); @@ -860,13 +1230,13 @@ void Steam_Controller::Legacy_TriggerRepeatedHapticPulse( InputHandle_t inputHan // Send a haptic pulse, works on Steam Deck and Steam Controller devices -void Steam_Controller::TriggerSimpleHapticEvent( InputHandle_t inputHandle, EControllerHapticLocation eHapticLocation, uint8 nIntensity, char nGainDB, uint8 nOtherIntensity, char nOtherGainDB ) +void Steam_Controller::TriggerSimpleHapticEvent(InputHandle_t inputHandle, EControllerHapticLocation eHapticLocation, uint8 nIntensity, char nGainDB, uint8 nOtherIntensity, char nOtherGainDB) { PRINT_DEBUG_TODO(); } // Tigger a vibration event on supported controllers. -void Steam_Controller::TriggerVibration( ControllerHandle_t controllerHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed ) +void Steam_Controller::TriggerVibration(ControllerHandle_t controllerHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed) { PRINT_DEBUG("%hu %hu", usLeftSpeed, usRightSpeed); auto controller = controllers.find(controllerHandle); @@ -879,56 +1249,74 @@ void Steam_Controller::TriggerVibration( ControllerHandle_t controllerHandle, un rumble_length_ms = 100; #endif - unsigned gamepad_device = static_cast(controllerHandle - 1); - if (gamepad_device > GAMEPAD_COUNT) return; - rumble_thread_data->rumble_mutex.lock(); - rumble_thread_data->data[gamepad_device].new_data = true; - rumble_thread_data->data[gamepad_device].left = usLeftSpeed; - rumble_thread_data->data[gamepad_device].right = usRightSpeed; - rumble_thread_data->data[gamepad_device].rumble_length_ms = rumble_length_ms; - rumble_thread_data->rumble_mutex.unlock(); - rumble_thread_data->rumble_thread_cv.notify_one(); + GamepadSetRumble(controllerHandle, usLeftSpeed, usRightSpeed, rumble_length_ms); + + //unsigned gamepad_device = static_cast(controllerHandle - 1); + //if (gamepad_device > GAMEPAD_COUNT) return; + //rumble_thread_data->rumble_mutex.lock(); + //rumble_thread_data->data[gamepad_device].new_data = true; + //rumble_thread_data->data[gamepad_device].left = usLeftSpeed; + //rumble_thread_data->data[gamepad_device].right = usRightSpeed; + //rumble_thread_data->data[gamepad_device].rumble_length_ms = rumble_length_ms; + //rumble_thread_data->rumble_mutex.unlock(); + //rumble_thread_data->rumble_thread_cv.notify_one(); } // Trigger a vibration event on supported controllers including Xbox trigger impulse rumble - Steam will translate these commands into haptic pulses for Steam Controllers -void Steam_Controller::TriggerVibrationExtended( InputHandle_t inputHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed, unsigned short usLeftTriggerSpeed, unsigned short usRightTriggerSpeed ) +void Steam_Controller::TriggerVibrationExtended(InputHandle_t inputHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed, unsigned short usLeftTriggerSpeed, unsigned short usRightTriggerSpeed) { - PRINT_DEBUG_TODO(); - TriggerVibration(inputHandle, usLeftSpeed, usRightSpeed); - //TODO trigger impulse rumbles + PRINT_DEBUG("%hu %hu %hu %hu", usLeftSpeed, usRightSpeed, usLeftTriggerSpeed, usRightTriggerSpeed); + auto controller = controllers.find(inputHandle); + if (controller == controllers.end()) return; + + unsigned int rumble_length_ms = 0; + +#if defined(__linux__) + //FIXME: shadow of the tomb raider on linux doesn't seem to turn off the rumble so I made it expire after 100ms. Need to check if this is how linux steam actually behaves. + rumble_length_ms = 100; +#endif + + GamepadSetTriggersRumble(inputHandle, usLeftSpeed, usRightSpeed, rumble_length_ms); } // Set the controller LED color on supported controllers. -void Steam_Controller::SetLEDColor( ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nColorG, uint8 nColorB, unsigned int nFlags ) +void Steam_Controller::SetLEDColor(ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nColorG, uint8 nColorB, unsigned int nFlags) { PRINT_DEBUG_TODO(); } // Returns the associated gamepad index for the specified controller, if emulating a gamepad -int Steam_Controller::GetGamepadIndexForController( ControllerHandle_t ulControllerHandle ) +int Steam_Controller::GetGamepadIndexForController(ControllerHandle_t ulControllerHandle) { PRINT_DEBUG_ENTRY(); auto controller = controllers.find(ulControllerHandle); if (controller == controllers.end()) return -1; - return static_cast(ulControllerHandle) - 1; + ESteamInputType type = GamepadGetType(controller->first); + if (type == k_ESteamInputType_XBox360Controller || type == k_ESteamInputType_XBoxOneController) return 0; // 0 means Xbox! + + return static_cast(std::distance(controllers.begin(), controller)); } // Returns the associated controller handle for the specified emulated gamepad -ControllerHandle_t Steam_Controller::GetControllerForGamepadIndex( int nIndex ) +ControllerHandle_t Steam_Controller::GetControllerForGamepadIndex(int nIndex) { PRINT_DEBUG("%i", nIndex); - ControllerHandle_t out = nIndex + 1; - auto controller = controllers.find(out); - if (controller == controllers.end()) return 0; - return out; + if (nIndex > controllers.size()) + return 0; + auto it = controllers.begin(); + std::advance(it, nIndex); + if (it == controllers.end()) return -1; // return some garbage value + ESteamInputType type = GamepadGetType(it->first); + if (type == k_ESteamInputType_XBox360Controller || type == k_ESteamInputType_XBoxOneController) return 0; // 0 means Xbox! + return it->first; } // Returns raw motion data from the specified controller -ControllerMotionData_t Steam_Controller::GetMotionData( ControllerHandle_t controllerHandle ) +ControllerMotionData_t Steam_Controller::GetMotionData(ControllerHandle_t controllerHandle) { PRINT_DEBUG_TODO(); ControllerMotionData_t data = {}; @@ -938,13 +1326,13 @@ ControllerMotionData_t Steam_Controller::GetMotionData( ControllerHandle_t contr // Attempt to display origins of given action in the controller HUD, for the currently active action set // Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode -bool Steam_Controller::ShowDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle, float flScale, float flXPosition, float flYPosition ) +bool Steam_Controller::ShowDigitalActionOrigins(ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle, float flScale, float flXPosition, float flYPosition) { PRINT_DEBUG_TODO(); return true; } -bool Steam_Controller::ShowAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle, float flScale, float flXPosition, float flYPosition ) +bool Steam_Controller::ShowAnalogActionOrigins(ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle, float flScale, float flXPosition, float flYPosition) { PRINT_DEBUG_TODO(); return true; @@ -952,20 +1340,20 @@ bool Steam_Controller::ShowAnalogActionOrigins( ControllerHandle_t controllerHan // Returns a localized string (from Steam's language setting) for the specified origin -const char* Steam_Controller::GetStringForActionOrigin( EControllerActionOrigin eOrigin ) +const char* Steam_Controller::GetStringForActionOrigin(EControllerActionOrigin eOrigin) { PRINT_DEBUG_TODO(); return "Button String"; } -const char* Steam_Controller::GetStringForActionOrigin( EInputActionOrigin eOrigin ) +const char* Steam_Controller::GetStringForActionOrigin(EInputActionOrigin eOrigin) { PRINT_DEBUG_TODO(); return "Button String"; } // Returns a localized string (from Steam's language setting) for the user-facing action name corresponding to the specified handle -const char* Steam_Controller::GetStringForAnalogActionName( InputAnalogActionHandle_t eActionHandle ) +const char* Steam_Controller::GetStringForAnalogActionName(InputAnalogActionHandle_t eActionHandle) { PRINT_DEBUG_TODO(); //TODO SteamInput005 @@ -973,7 +1361,7 @@ const char* Steam_Controller::GetStringForAnalogActionName( InputAnalogActionHan } // Get a local path to art for on-screen glyph for a particular origin -const char* Steam_Controller::GetGlyphForActionOrigin( EControllerActionOrigin eOrigin ) +const char* Steam_Controller::GetGlyphForActionOrigin(EControllerActionOrigin eOrigin) { PRINT_DEBUG("%i", eOrigin); @@ -1015,11 +1403,15 @@ const char* Steam_Controller::GetGlyphForActionOrigin( EControllerActionOrigin e return glyph->second.c_str(); } -const char* Steam_Controller::GetGlyphForActionOrigin( EInputActionOrigin eOrigin ) + + +const char* Steam_Controller::GetGlyphForActionOrigin(EInputActionOrigin eOrigin) { PRINT_DEBUG("steaminput %i", eOrigin); if (steaminput_glyphs.empty()) { + //std::string base_dir = settings->glyphs_directory; std::string dir = settings->glyphs_directory; + //dir = base_dir + (PATH_SEPARATOR "XBox360" PATH_SEPARATOR); steaminput_glyphs[k_EInputActionOrigin_XBox360_A] = dir + "button_a.png"; steaminput_glyphs[k_EInputActionOrigin_XBox360_B] = dir + "button_b.png"; steaminput_glyphs[k_EInputActionOrigin_XBox360_X] = dir + "button_x.png"; @@ -1058,7 +1450,7 @@ const char* Steam_Controller::GetGlyphForActionOrigin( EInputActionOrigin eOrigi } // Get a local path to a PNG file for the provided origin's glyph. -const char* Steam_Controller::GetGlyphPNGForActionOrigin( EInputActionOrigin eOrigin, ESteamInputGlyphSize eSize, uint32 unFlags ) +const char* Steam_Controller::GetGlyphPNGForActionOrigin(EInputActionOrigin eOrigin, ESteamInputGlyphSize eSize, uint32 unFlags) { PRINT_DEBUG_TODO(); //TODO SteamInput005 @@ -1066,7 +1458,7 @@ const char* Steam_Controller::GetGlyphPNGForActionOrigin( EInputActionOrigin eOr } // Get a local path to a SVG file for the provided origin's glyph. -const char* Steam_Controller::GetGlyphSVGForActionOrigin( EInputActionOrigin eOrigin, uint32 unFlags ) +const char* Steam_Controller::GetGlyphSVGForActionOrigin(EInputActionOrigin eOrigin, uint32 unFlags) { PRINT_DEBUG_TODO(); //TODO SteamInput005 @@ -1074,74 +1466,394 @@ const char* Steam_Controller::GetGlyphSVGForActionOrigin( EInputActionOrigin eOr } // Get a local path to an older, Big Picture Mode-style PNG file for a particular origin -const char* Steam_Controller::GetGlyphForActionOrigin_Legacy( EInputActionOrigin eOrigin ) +const char* Steam_Controller::GetGlyphForActionOrigin_Legacy(EInputActionOrigin eOrigin) { PRINT_DEBUG_ENTRY(); return GetGlyphForActionOrigin(eOrigin); } // Returns the input type for a particular handle -ESteamInputType Steam_Controller::GetInputTypeForHandle( ControllerHandle_t controllerHandle ) +ESteamInputType Steam_Controller::GetInputTypeForHandle(ControllerHandle_t controllerHandle) { - PRINT_DEBUG("%llu", controllerHandle); auto controller = controllers.find(controllerHandle); - if (controller == controllers.end()) return k_ESteamInputType_Unknown; - return k_ESteamInputType_XBox360Controller; + if (controller == controllers.end()) { + // Hack: Why does Elden Ring query controllerHandle 0? Are we returning empty handles somewhere else? + if (controllers.size() != 0 && controllerHandle == 0) + controller = controllers.begin(); + else { + PRINT_DEBUG("%llu ret %i", controllerHandle, 0); + return k_ESteamInputType_Unknown; + } + }; + + ESteamInputType type = GamepadGetType(controller->first); + PRINT_DEBUG("%llu ret %i", controllerHandle, static_cast(type)); + return type; } -const char* Steam_Controller::GetStringForXboxOrigin( EXboxOrigin eOrigin ) +const char* Steam_Controller::GetStringForXboxOrigin(EXboxOrigin eOrigin) { PRINT_DEBUG_TODO(); return ""; } -const char* Steam_Controller::GetGlyphForXboxOrigin( EXboxOrigin eOrigin ) +const char* Steam_Controller::GetGlyphForXboxOrigin(EXboxOrigin eOrigin) { PRINT_DEBUG_TODO(); return ""; } -EControllerActionOrigin Steam_Controller::GetActionOriginFromXboxOrigin_( ControllerHandle_t controllerHandle, EXboxOrigin eOrigin ) +EControllerActionOrigin Steam_Controller::GetActionOriginFromXboxOrigin_(ControllerHandle_t controllerHandle, EXboxOrigin eOrigin) +{ + PRINT_DEBUG_ENTRY(); + EInputActionOrigin ret = GetActionOriginFromXboxOrigin(controllerHandle, eOrigin); + switch (GamepadGetType(controllerHandle)) { + case k_ESteamInputType_PS3Controller: + case k_ESteamInputType_PS4Controller: + return (EControllerActionOrigin)(ret - ((long)k_EInputActionOrigin_PS4_X - (long)k_EControllerActionOrigin_PS4_X)); + case k_ESteamInputType_PS5Controller: + return (EControllerActionOrigin)(ret - ((long)k_EInputActionOrigin_PS5_X - (long)k_EControllerActionOrigin_PS5_X)); + case k_ESteamInputType_SwitchProController: + return (EControllerActionOrigin)(ret - ((long)k_EInputActionOrigin_Switch_A - (long)k_EControllerActionOrigin_Switch_A)); + case k_ESteamInputType_XBoxOneController: + return (EControllerActionOrigin)(ret - ((long)k_EInputActionOrigin_XBoxOne_A - (long)k_EControllerActionOrigin_XBoxOne_A)); + case k_ESteamInputType_XBox360Controller: + default: + return (EControllerActionOrigin)(ret - ((long)k_EInputActionOrigin_XBox360_A - (long)k_EControllerActionOrigin_XBox360_A)); + } +} + +EInputActionOrigin Steam_Controller::GetActionOriginFromXboxOrigin(InputHandle_t inputHandle, EXboxOrigin eOrigin) +{ + PRINT_DEBUG_ENTRY(); + auto controller = controllers.find(inputHandle); + if (controller == controllers.end()) return k_EInputActionOrigin_None; + ESteamInputType controllerType = GamepadGetType(inputHandle); + switch (eOrigin) { + case k_EXboxOrigin_A: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_X; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_X; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_A; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) { + if (this->settings->flip_nintendo_layout) + return k_EInputActionOrigin_Switch_B; + else + return k_EInputActionOrigin_Switch_A; + } + return k_EInputActionOrigin_None; + + case k_EXboxOrigin_B: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_Circle; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_Circle; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_B; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) { + if (this->settings->flip_nintendo_layout) + return k_EInputActionOrigin_Switch_A; + else + return k_EInputActionOrigin_Switch_B; + } + return k_EInputActionOrigin_None; + + case k_EXboxOrigin_X: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_Square; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_Square; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_X; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) { + if (this->settings->flip_nintendo_layout) + return k_EInputActionOrigin_Switch_Y; + else + return k_EInputActionOrigin_Switch_X; + } + return k_EInputActionOrigin_None; + + case k_EXboxOrigin_Y: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_Triangle; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_Triangle; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_Y; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) { + if (this->settings->flip_nintendo_layout) + return k_EInputActionOrigin_Switch_X; + else + return k_EInputActionOrigin_Switch_Y; + } + return k_EInputActionOrigin_None; + + // Bumpers + case k_EXboxOrigin_LeftBumper: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_LeftBumper; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_LeftBumper; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_LeftBumper; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_LeftBumper; + return k_EInputActionOrigin_XBox360_LeftBumper; + + case k_EXboxOrigin_RightBumper: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_RightBumper; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_RightBumper; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_RightBumper; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_RightBumper; + return k_EInputActionOrigin_XBox360_RightBumper; + + // Menu buttons + case k_EXboxOrigin_Menu: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_Options; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_Option; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_Menu; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_Plus; + return k_EInputActionOrigin_XBox360_Start; + + case k_EXboxOrigin_View: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_Share; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_Create; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_View; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_Minus; + + return k_EInputActionOrigin_XBox360_Back; + + // Triggers + case k_EXboxOrigin_LeftTrigger_Click: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_LeftTrigger_Click; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_LeftTrigger_Click; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_LeftTrigger_Click; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_LeftTrigger_Click; + return k_EInputActionOrigin_XBox360_LeftTrigger_Click; + + case k_EXboxOrigin_RightTrigger_Click: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_RightTrigger_Click; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_RightTrigger_Click; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_RightTrigger_Click; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_RightTrigger_Click; + return k_EInputActionOrigin_XBox360_RightTrigger_Click; + + // Stick clicks + case k_EXboxOrigin_LeftStick_Click: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_LeftStick_Click; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_LeftStick_Click; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_LeftStick_Click; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_LeftStick_Click; + return k_EInputActionOrigin_XBox360_LeftStick_Click; + + case k_EXboxOrigin_RightStick_Click: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_RightStick_Click; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_RightStick_Click; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_RightStick_Click; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_RightStick_Click; + return k_EInputActionOrigin_XBox360_RightStick_Click; + + // Left stick directions + case k_EXboxOrigin_LeftStick_DPadNorth: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_LeftStick_DPadNorth; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_LeftStick_DPadNorth; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_LeftStick_DPadNorth; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_LeftStick_DPadNorth; + return k_EInputActionOrigin_XBox360_LeftStick_DPadNorth; + + case k_EXboxOrigin_LeftStick_DPadSouth: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_LeftStick_DPadSouth; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_LeftStick_DPadSouth; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_LeftStick_DPadSouth; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_LeftStick_DPadSouth; + return k_EInputActionOrigin_XBox360_LeftStick_DPadSouth; + + case k_EXboxOrigin_LeftStick_DPadWest: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_LeftStick_DPadWest; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_LeftStick_DPadWest; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_LeftStick_DPadWest; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_LeftStick_DPadWest; + return k_EInputActionOrigin_XBox360_LeftStick_DPadWest; + + case k_EXboxOrigin_LeftStick_DPadEast: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_LeftStick_DPadEast; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_LeftStick_DPadEast; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_LeftStick_DPadEast; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_LeftStick_DPadEast; + return k_EInputActionOrigin_XBox360_LeftStick_DPadEast; + + // Right stick directions + case k_EXboxOrigin_RightStick_DPadNorth: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_RightStick_DPadNorth; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_RightStick_DPadNorth; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_RightStick_DPadNorth; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_RightStick_DPadNorth; + return k_EInputActionOrigin_XBox360_RightStick_DPadNorth; + + case k_EXboxOrigin_RightStick_DPadSouth: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_RightStick_DPadSouth; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_RightStick_DPadSouth; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_RightStick_DPadSouth; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_RightStick_DPadSouth; + + return k_EInputActionOrigin_XBox360_RightStick_DPadSouth; + + case k_EXboxOrigin_RightStick_DPadWest: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_RightStick_DPadWest; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_RightStick_DPadWest; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_RightStick_DPadWest; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_RightStick_DPadWest; + return k_EInputActionOrigin_XBox360_RightStick_DPadWest; + + case k_EXboxOrigin_RightStick_DPadEast: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_RightStick_DPadEast; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_RightStick_DPadEast; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_RightStick_DPadEast; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_RightStick_DPadEast; + return k_EInputActionOrigin_XBox360_RightStick_DPadEast; + + // DPad buttons + case k_EXboxOrigin_DPad_North: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_DPad_North; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_DPad_North; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_DPad_North; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_DPad_North; + return k_EInputActionOrigin_XBox360_DPad_North; + + case k_EXboxOrigin_DPad_South: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_DPad_South; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_DPad_South; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_DPad_South; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_DPad_South; + return k_EInputActionOrigin_XBox360_DPad_South; + + case k_EXboxOrigin_DPad_West: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_DPad_West; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_DPad_West; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_DPad_West; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_DPad_West; + return k_EInputActionOrigin_XBox360_DPad_West; + + case k_EXboxOrigin_DPad_East: + if (controllerType == k_ESteamInputType_PS3Controller || controllerType == k_ESteamInputType_PS4Controller) + return k_EInputActionOrigin_PS4_DPad_East; + if (controllerType == k_ESteamInputType_PS5Controller) + return k_EInputActionOrigin_PS5_DPad_East; + if (controllerType == k_ESteamInputType_XBoxOneController) + return k_EInputActionOrigin_XBoxOne_DPad_East; + if (controllerType == k_ESteamInputType_SwitchJoyConSingle || controllerType == k_ESteamInputType_SwitchJoyConPair || controllerType == k_ESteamInputType_SwitchProController) + return k_EInputActionOrigin_Switch_DPad_East; + return k_EInputActionOrigin_XBox360_DPad_East; + + default: + return k_EInputActionOrigin_None; + } + +} + +EControllerActionOrigin Steam_Controller::TranslateActionOrigin(ESteamInputType eDestinationInputType, EControllerActionOrigin eSourceOrigin) { PRINT_DEBUG_TODO(); return k_EControllerActionOrigin_None; } -EInputActionOrigin Steam_Controller::GetActionOriginFromXboxOrigin( InputHandle_t inputHandle, EXboxOrigin eOrigin ) +EInputActionOrigin Steam_Controller::TranslateActionOrigin(ESteamInputType eDestinationInputType, EInputActionOrigin eSourceOrigin) { - PRINT_DEBUG_TODO(); - return k_EInputActionOrigin_None; -} + PRINT_DEBUG("steaminput destinationinputtype %d sourceorigin %d", eDestinationInputType, eSourceOrigin); -EControllerActionOrigin Steam_Controller::TranslateActionOrigin( ESteamInputType eDestinationInputType, EControllerActionOrigin eSourceOrigin ) -{ - PRINT_DEBUG_TODO(); - return k_EControllerActionOrigin_None; -} - -EInputActionOrigin Steam_Controller::TranslateActionOrigin( ESteamInputType eDestinationInputType, EInputActionOrigin eSourceOrigin ) -{ - PRINT_DEBUG("steaminput destinationinputtype %d sourceorigin %d", eDestinationInputType, eSourceOrigin ); - if (eDestinationInputType == k_ESteamInputType_XBox360Controller) return eSourceOrigin; - + return k_EInputActionOrigin_None; } -bool Steam_Controller::GetControllerBindingRevision( ControllerHandle_t controllerHandle, int *pMajor, int *pMinor ) +bool Steam_Controller::GetControllerBindingRevision(ControllerHandle_t controllerHandle, int* pMajor, int* pMinor) { PRINT_DEBUG_TODO(); return false; } -bool Steam_Controller::GetDeviceBindingRevision( InputHandle_t inputHandle, int *pMajor, int *pMinor ) +bool Steam_Controller::GetDeviceBindingRevision(InputHandle_t inputHandle, int* pMajor, int* pMinor) { PRINT_DEBUG_TODO(); return false; } -uint32 Steam_Controller::GetRemotePlaySessionID( InputHandle_t inputHandle ) +uint32 Steam_Controller::GetRemotePlaySessionID(InputHandle_t inputHandle) { PRINT_DEBUG_TODO(); return 0; @@ -1156,7 +1868,7 @@ uint16 Steam_Controller::GetSessionInputConfigurationSettings() } // Set the trigger effect for a DualSense controller -void Steam_Controller::SetDualSenseTriggerEffect( InputHandle_t inputHandle, const ScePadTriggerEffectParam *pParam ) +void Steam_Controller::SetDualSenseTriggerEffect(InputHandle_t inputHandle, const ScePadTriggerEffectParam* pParam) { PRINT_DEBUG_TODO(); } diff --git a/helpers/gamepad_provider.cpp b/helpers/gamepad_provider.cpp new file mode 100644 index 00000000..01da58ae --- /dev/null +++ b/helpers/gamepad_provider.cpp @@ -0,0 +1,250 @@ +#include "gamepad_provider/gamepad_provider.hpp" +#include +#include +#include +#define GAMEPAD_COUNT 16 + +#if !defined(CONTROLLER_SUPPORT) +int gamepad_provider::sdl::DetectGamepads(std::vector& handlesOut, std::vector& newHandlesOut) { return 0; } +bool gamepad_provider::sdl::GamepadInit(bool combine_joycons) { return false; }; +void gamepad_provider::sdl::GamepadShutdown(void) {}; +void gamepad_provider::sdl::GamepadUpdate(void) {}; +bool gamepad_provider::sdl::GamepadButtonDown(ControllerHandle_t id, GAMEPAD_BUTTON button) { return false; }; +float gamepad_provider::sdl::GamepadTriggerLength(ControllerHandle_t id, GAMEPAD_TRIGGER trigger) { return 0.0f; }; +void gamepad_provider::sdl::GamepadStickXY(ControllerHandle_t id, GAMEPAD_STICK stick, float* out_x, float* out_y) {}; +void gamepad_provider::sdl::GamepadStickNormXY(ControllerHandle_t id, GAMEPAD_STICK stick, float* out_x, float* out_y, int inner_deadzone, int outer_deadzone) {}; +void gamepad_provider::sdl::GamepadSetRumble(ControllerHandle_t id, unsigned short left, unsigned short right, unsigned int rumble_length_ms) {}; +ESteamInputType gamepad_provider::sdl::GamepadGetType(ControllerHandle_t id) { return k_ESteamInputType_Unknown; }; + +#else + +namespace gamepad_provider { + namespace sdl { + namespace { + static std::map sdl_controller_map{}; + static std::map> guid_controller_map{}; + static ControllerHandle_t current_controller_index = 1; // Must start from 1 + + inline SDL_Gamepad* GamepadGetFromHandle(ControllerHandle_t controller) { + auto sdl_controller = sdl_controller_map.find(controller); + if (sdl_controller == sdl_controller_map.end()) return nullptr; + return SDL_GetGamepadFromID(sdl_controller->second); + } + } + } +} + +bool gamepad_provider::sdl::GamepadInit(bool combine_joycons) { + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1"); + + SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1"); + + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, combine_joycons ? "1" : "0"); // Joycons are combined by default + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STADIA, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_LUNA, "1"); + + if (!SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC | SDL_INIT_EVENTS)) + return false; + + SDL_SetGamepadEventsEnabled(false); + + return true; +} + +void gamepad_provider::sdl::GamepadShutdown(void) { + for (auto& c : sdl_controller_map) { + SDL_Gamepad* gamepad = SDL_GetGamepadFromID(c.second); + if (gamepad) + SDL_CloseGamepad(gamepad); + } + + SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC | SDL_INIT_EVENTS); +} + +void gamepad_provider::sdl::GamepadUpdate(void) { + SDL_UpdateGamepads(); +} + +/* Detects gamepad connect/disconnects. */ +int gamepad_provider::sdl::DetectGamepads(std::vector &handlesOut, std::vector &newHandlesOut) { + SDL_UpdateGamepads(); + int count = 0; + SDL_JoystickID* gamepads = SDL_GetGamepads(&count); + + for (int i = 0; i < count && i < GAMEPAD_COUNT; ++i) { + bool newHandleCreated = false; + SDL_JoystickID instance_id = gamepads[i]; + SDL_GUID guid = SDL_GetGamepadGUIDForID(instance_id); + char guid_string[33]; + SDL_GUIDToString(guid, guid_string, 33); + auto guid_handles = guid_controller_map.find(guid_string); + + // Always open gamepad, since a disconnected controller could be in the map + SDL_Gamepad* gamepadHandle = SDL_OpenGamepad(instance_id); + ControllerHandle_t curr_index = 0; + + if (guid_handles == guid_controller_map.end()) { + newHandlesOut.push_back(current_controller_index); + char guid_string[33]; + SDL_GUIDToString(guid, guid_string, 33); + sdl_controller_map.insert(std::pair(current_controller_index, instance_id)); + guid_controller_map.insert(std::pair>(guid_string, std::vector{current_controller_index})); + curr_index = current_controller_index; + newHandleCreated = true; + ++current_controller_index; + } + else { + bool replaced = false; + // GUID already exists + for (auto& c : guid_handles->second) { + SDL_Gamepad* gamepadHandle = GamepadGetFromHandle(c); + if (!gamepadHandle || !SDL_GamepadConnected(gamepadHandle)) { + // Gamepad is not connected, replace this index + if (gamepadHandle) + SDL_CloseGamepad(gamepadHandle); + gamepadHandle = SDL_OpenGamepad(instance_id); + sdl_controller_map[c] = instance_id; + replaced = true; + curr_index = c; + break; + } + if (SDL_GetGamepadID(gamepadHandle) == instance_id) { + // Gamepad is connected and already in controller_map + replaced = true; + curr_index = c; + break; + } + } + + if (!replaced) { + sdl_controller_map.insert(std::pair(current_controller_index, instance_id)); + guid_handles->second.push_back(current_controller_index); + curr_index = current_controller_index; + newHandleCreated = true; + ++current_controller_index; + } + } + + handlesOut.push_back(curr_index); + + if (newHandleCreated) { + newHandlesOut.push_back(curr_index); + } + } + + SDL_free(gamepads); + + return count; +} + +bool gamepad_provider::sdl::GamepadButtonDown(ControllerHandle_t id, GAMEPAD_BUTTON button) { + SDL_Gamepad* gamepad = GamepadGetFromHandle(id); + if (!gamepad) + return false; + return SDL_GetGamepadButton(gamepad, (SDL_GamepadButton)button); +} + +float gamepad_provider::sdl::GamepadTriggerLength(ControllerHandle_t id, GAMEPAD_TRIGGER trigger) { + SDL_Gamepad* gamepad = GamepadGetFromHandle(id); + if (!gamepad) + return 0.0f; + if (trigger == TRIGGER_LEFT) + return static_cast(SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER)); + else if (trigger == TRIGGER_RIGHT) + return static_cast(SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)); + return 0.0f; +} + +void gamepad_provider::sdl::GamepadStickNormXY(ControllerHandle_t id, GAMEPAD_STICK stick, float* out_x, float* out_y, int inner_deadzone, int outer_deadzone) { + float x = 0.0f, y = 0.0f; + GamepadStickXY(id, stick, &x, &y); + + // normalize X and Y + float length = static_cast(sqrt(x * x + y * y)); + if (length > inner_deadzone) { + // clamp length to maximum value + length = std::min(length, 32767.f); + + // normalized X and Y values + x /= (JOYSTICK_MAX - outer_deadzone); + y /= (JOYSTICK_MAX - outer_deadzone); + + //fix special case + x = std::clamp(x, -1.0f, 1.0f); + y = std::clamp(y, -1.0f, 1.0f); + } + else { + x = 0.0f; + y = 0.0f; + } + + *out_x = x; + *out_y = y; +} + +void gamepad_provider::sdl::GamepadStickXY(ControllerHandle_t id, GAMEPAD_STICK stick, float* out_x, float* out_y) { + SDL_Gamepad* gamepad = GamepadGetFromHandle(id); + if (!gamepad) + return; + + if (stick == STICK_LEFT) { + *out_x = static_cast(SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX)); + *out_y = static_cast(-SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY)); + } + else if (stick == STICK_RIGHT) { + *out_x = static_cast(SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX)); + *out_y = static_cast(-SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY)); + } +} + +void gamepad_provider::sdl::GamepadSetRumble(ControllerHandle_t id, unsigned short left, unsigned short right, unsigned int rumble_length_ms) { + SDL_Gamepad* gamepad = GamepadGetFromHandle(id); + if (!gamepad) + return; + SDL_RumbleGamepad(gamepad, left, right, rumble_length_ms); +} + +void gamepad_provider::sdl::GamepadSetTriggersRumble(ControllerHandle_t id, unsigned short left, unsigned short right, unsigned int rumble_length_ms) { + SDL_Gamepad* gamepad = GamepadGetFromHandle(id); + if (!gamepad) + return; + SDL_RumbleGamepadTriggers(gamepad, left, right, rumble_length_ms); +} + +ESteamInputType gamepad_provider::sdl::GamepadGetType(ControllerHandle_t id) { + SDL_Gamepad* gamepad = GamepadGetFromHandle(id); + if (!gamepad) + return k_ESteamInputType_Unknown; + switch (SDL_GetGamepadType(gamepad)) { + case SDL_GAMEPAD_TYPE_XBOX360: + return k_ESteamInputType_XBox360Controller; + case SDL_GAMEPAD_TYPE_XBOXONE: + return k_ESteamInputType_XBoxOneController; + case SDL_GAMEPAD_TYPE_PS3: + return k_ESteamInputType_PS3Controller; + case SDL_GAMEPAD_TYPE_PS4: + return k_ESteamInputType_PS4Controller; + case SDL_GAMEPAD_TYPE_PS5: + return k_ESteamInputType_PS5Controller; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT: + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT: + return k_ESteamInputType_SwitchJoyConSingle; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR: + return k_ESteamInputType_SwitchJoyConPair; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO: + return k_ESteamInputType_SwitchProController; + case SDL_GAMEPAD_TYPE_STANDARD: + return k_ESteamInputType_GenericGamepad; + default: + return k_ESteamInputType_Unknown; + } +} + +#endif diff --git a/helpers/gamepad_provider/gamepad_provider.hpp b/helpers/gamepad_provider/gamepad_provider.hpp new file mode 100644 index 00000000..890561a3 --- /dev/null +++ b/helpers/gamepad_provider/gamepad_provider.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include +#include + +#include +#include + +#if !defined(CONTROLLER_SUPPORT) + +#define JOYSTICK_MAX 32767 + +enum GAMEPAD_BUTTON { + BUTTON_DPAD_UP, /* UP on the direction pad */ + BUTTON_DPAD_DOWN, /**< DOWN on the direction pad */ + BUTTON_DPAD_LEFT, /**< LEFT on the direction pad */ + BUTTON_DPAD_RIGHT, /**< RIGHT on the direction pad */ + BUTTON_START, /**< START button */ + BUTTON_BACK, /**< BACK button */ + BUTTON_LEFT_THUMB, /**< Left analog stick button */ + BUTTON_RIGHT_THUMB, /**< Right analog stick button */ + BUTTON_LEFT_SHOULDER, /**< Left bumper button */ + BUTTON_RIGHT_SHOULDER, /**< Right bumper button */ + BUTTON_A, /**< A button */ + BUTTON_B, /**< B button */ + BUTTON_X, /**< X button */ + BUTTON_Y, /**< Y button */ + BUTTON_COUNT = 16 +}; +#else + +#include +#include + +#define JOYSTICK_MAX SDL_JOYSTICK_AXIS_MAX + +enum GAMEPAD_BUTTON { + BUTTON_DPAD_UP = SDL_GAMEPAD_BUTTON_DPAD_UP, /**< UP on the direction pad */ + BUTTON_DPAD_DOWN = SDL_GAMEPAD_BUTTON_DPAD_DOWN, /**< DOWN on the direction pad */ + BUTTON_DPAD_LEFT = SDL_GAMEPAD_BUTTON_DPAD_LEFT, /**< LEFT on the direction pad */ + BUTTON_DPAD_RIGHT = SDL_GAMEPAD_BUTTON_DPAD_RIGHT, /**< RIGHT on the direction pad */ + BUTTON_START = SDL_GAMEPAD_BUTTON_START, /**< START button */ + BUTTON_BACK = SDL_GAMEPAD_BUTTON_BACK, /**< BACK button */ + BUTTON_LEFT_THUMB = SDL_GAMEPAD_BUTTON_LEFT_STICK, /**< Left analog stick button */ + BUTTON_RIGHT_THUMB = SDL_GAMEPAD_BUTTON_RIGHT_STICK, /**< Right analog stick button */ + BUTTON_LEFT_SHOULDER = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, /**< Left bumper button */ + BUTTON_RIGHT_SHOULDER = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, /**< Right bumper button */ + BUTTON_A = SDL_GAMEPAD_BUTTON_SOUTH, /**< A button */ + BUTTON_B = SDL_GAMEPAD_BUTTON_EAST, /**< B button */ + BUTTON_X = SDL_GAMEPAD_BUTTON_WEST, /**< X button */ + BUTTON_Y = SDL_GAMEPAD_BUTTON_NORTH, /**< Y button */ + BUTTON_COUNT = 16 +}; + +#endif + +/** + * Enumeration of the possible pressure/trigger buttons. + */ +enum GAMEPAD_TRIGGER { + TRIGGER_LEFT = 0, /**< Left trigger */ + TRIGGER_RIGHT = 1, /**< Right trigger */ + TRIGGER_COUNT /**< Number of triggers */ +}; + +/** + * Enumeration of the analog sticks. + */ +enum GAMEPAD_STICK { + STICK_LEFT = 0, /**< Left stick */ + STICK_RIGHT = 1, /**< Right stick */ + STICK_COUNT /**< Number of analog sticks */ +}; + +namespace gamepad_provider { + namespace sdl + { + int DetectGamepads(std::vector &handlesOut, std::vector& newHandlesOut); + bool GamepadInit(bool combine_joycons); + void GamepadShutdown(void); + void GamepadUpdate(void); + bool GamepadButtonDown(ControllerHandle_t id, GAMEPAD_BUTTON button); + float GamepadTriggerLength(ControllerHandle_t id, GAMEPAD_TRIGGER trigger); + void GamepadStickXY(ControllerHandle_t id, GAMEPAD_STICK stick, float* out_x, float* out_y); + void GamepadStickNormXY(ControllerHandle_t id, GAMEPAD_STICK stick, float* out_x, float* out_y, int inner_deadzone, int outer_deadzone); + void GamepadSetRumble(ControllerHandle_t id, unsigned short left, unsigned short right, unsigned int rumble_length_ms); + void GamepadSetTriggersRumble(ControllerHandle_t id, unsigned short left, unsigned short right, unsigned int rumble_length_ms); + ESteamInputType GamepadGetType(ControllerHandle_t id); + } +} diff --git a/premake5-deps.lua b/premake5-deps.lua index cab9a16b..b5b154b9 100644 --- a/premake5-deps.lua +++ b/premake5-deps.lua @@ -93,6 +93,11 @@ newoption { trigger = "ext-ingame_overlay", description = "Extract ingame_overlay", } +newoption { + category = "extract", + trigger = "ext-sdl3", + description = "Extract sdl3", +} -- build newoption { @@ -146,6 +151,11 @@ newoption { trigger = "build-ingame_overlay", description = "Build ingame_overlay", } +newoption { + category = "build", + trigger = "build-sdl3", + description = "Build sdl3", +} local function merge_list(src, dest) @@ -185,7 +195,9 @@ else mycmake = mycmake .. '.exe' end if not os.isfile(mycmake) then - error('cmake is missing from third-party dir, you can specify custom cmake location, run the script with --help. cmake: ' .. mycmake) + error( + 'cmake is missing from third-party dir, you can specify custom cmake location, run the script with --help. cmake: ' .. + mycmake) end end @@ -226,10 +238,10 @@ local function cmake_build(dep_folder, is_32, extra_cmd_defs, c_flags_init, cxx_ end print('\n\nbuilding dep: "' .. dep_base .. '"') - + local build_dir = path.getabsolute(path.join(dep_base, 'build' .. arch_iden)) local install_dir = path.join(dep_base, 'install' .. arch_iden) - + -- clean if required if _OPTIONS["clean"] then print('cleaning dir: ' .. build_dir) @@ -243,7 +255,8 @@ local function cmake_build(dep_folder, is_32, extra_cmd_defs, c_flags_init, cxx_ return end - local cmake_common_defs_str = '-D' .. table.concat(cmake_common_defs, ' -D') .. ' -DCMAKE_INSTALL_PREFIX="' .. install_dir .. '"' + local cmake_common_defs_str = '-D' .. + table.concat(cmake_common_defs, ' -D') .. ' -DCMAKE_INSTALL_PREFIX="' .. install_dir .. '"' local cmd_gen = mycmake .. ' -S "' .. dep_base .. '" -B "' .. build_dir .. '" ' .. cmake_common_defs_str local all_cflags_init = {} @@ -275,7 +288,7 @@ local function cmake_build(dep_folder, is_32, extra_cmd_defs, c_flags_init, cxx_ error("unsupported action for cmake build: " .. _ACTION) return end - + -- add c/cxx extra init flags if c_flags_init then if type(c_flags_init) ~= 'table' then @@ -308,7 +321,7 @@ local function cmake_build(dep_folder, is_32, extra_cmd_defs, c_flags_init, cxx_ -- write toolchain file local toolchain_file_content = '' if _OPTIONS["cmake-toolchain"] then - toolchain_file_content='include(' .. _OPTIONS["cmake-toolchain"] .. ')\n\n' + toolchain_file_content = 'include(' .. _OPTIONS["cmake-toolchain"] .. ')\n\n' end if #cflags_init_str > 0 then toolchain_file_content = toolchain_file_content .. 'set(CMAKE_C_FLAGS_INIT "' .. cflags_init_str .. '" )\n' @@ -317,12 +330,15 @@ local function cmake_build(dep_folder, is_32, extra_cmd_defs, c_flags_init, cxx_ toolchain_file_content = toolchain_file_content .. 'set(CMAKE_CXX_FLAGS_INIT "' .. cxxflags_init_str .. '" )\n' end if string.match(_ACTION, 'vs.+') then -- because libssq doesn't care about CMAKE_C/XX_FLAGS_INIT - toolchain_file_content = toolchain_file_content .. 'set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT /D_MT" ) \n' - toolchain_file_content = toolchain_file_content .. 'set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT /D_MT" ) \n' + toolchain_file_content = toolchain_file_content .. + 'set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT /D_MT" ) \n' + toolchain_file_content = toolchain_file_content .. + 'set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT /D_MT" ) \n' end - + if #toolchain_file_content > 0 then - local toolchain_file = path.join(dep_base, 'toolchain_' .. tostring(is_32) .. '_' .. _ACTION .. '_' .. os_iden .. '.precmt') + local toolchain_file = path.join(dep_base, + 'toolchain_' .. tostring(is_32) .. '_' .. _ACTION .. '_' .. os_iden .. '.precmt') if not io.writefile(toolchain_file, toolchain_file_content) then error("failed to write cmake toolchain") return @@ -365,7 +381,8 @@ local function cmake_build(dep_folder, is_32, extra_cmd_defs, c_flags_init, cxx_ if _OPTIONS['j'] then parallel_str = parallel_str .. ' ' .. _OPTIONS['j'] end - local ok = os.execute(mycmake .. ' --build "' .. build_dir .. '" --config Release' .. parallel_str .. verbose_build_str) + local ok = os.execute(mycmake .. + ' --build "' .. build_dir .. '" --config Release' .. parallel_str .. verbose_build_str) if not ok then error("failed to build") return @@ -377,7 +394,7 @@ local function cmake_build(dep_folder, is_32, extra_cmd_defs, c_flags_init, cxx_ return end - local cmd_install = mycmake.. ' --install "' .. build_dir .. '" --prefix "' .. install_dir .. '"' + local cmd_install = mycmake .. ' --install "' .. build_dir .. '" --prefix "' .. install_dir .. '"' print(cmd_install) local ok = os.execute(cmd_install) if not ok then @@ -427,6 +444,9 @@ end if _OPTIONS["ext-ingame_overlay"] or _OPTIONS["all-ext"] then table.insert(deps_to_extract, { 'ingame_overlay/ingame_overlay.tar.gz', 'ingame_overlay' }) end +if _OPTIONS["ext-sdl3"] or _OPTIONS["all-ext"] then + table.insert(deps_to_extract, { 'sdl3/sdl3.tar.gz', 'sdl3' }) +end -- start extraction for _, dep in pairs(deps_to_extract) do @@ -460,7 +480,9 @@ for _, dep in pairs(deps_to_extract) do local ext = string.lower(string.sub(archive_file, -7)) -- ".tar.gz" local ok_cmd = false if ext == ".tar.gz" then - ok_cmd = os.execute(extractor .. ' -bso0 -bse2 x "' .. archive_file .. '" -so | "' .. extractor .. '" -bso0 -bse2 x -si -ttar -y -aoa -o"' .. deps_dir .. '"') + ok_cmd = os.execute(extractor .. + ' -bso0 -bse2 x "' .. + archive_file .. '" -so | "' .. extractor .. '" -bso0 -bse2 x -si -ttar -y -aoa -o"' .. deps_dir .. '"') else ok_cmd = os.execute(extractor .. ' -bso0 -bse2 x "' .. archive_file .. '" -y -aoa -o"' .. out_folder .. '"') end @@ -479,7 +501,6 @@ for _, dep in pairs(deps_to_extract) do -- end -- os.rmdir(inner_folder) -- end - end @@ -509,7 +530,7 @@ end -- if(ZLIB_FOUND) -- set(HAVE_LIBZ ON) -- set(USE_ZLIB ON) --- +-- -- # Depend on ZLIB via imported targets if supported by the running -- # version of CMake. This allows our dependents to get our dependencies -- # transitively. @@ -622,7 +643,7 @@ if _OPTIONS["build-curl"] or _OPTIONS["all-build"] then "CURL_USE_OPENSSL=OFF", "CURL_ZLIB=ON", - + "CURL_USE_MBEDTLS=ON", -- "CURL_USE_SCHANNEL=ON", "CURL_CA_FALLBACK=ON", @@ -737,3 +758,15 @@ if _OPTIONS["build-ingame_overlay"] or _OPTIONS["all-build"] then cmake_build('ingame_overlay', false, ingame_overlay_common_defs, nil, ingame_overlay_fixes) end end + +if _OPTIONS["build-sdl3"] or _OPTIONS["all-build"] then + local sdl3_common_defs = { + } + + if _OPTIONS["32-build"] then + cmake_build('sdl3', true, sdl3_common_defs) + end + if _OPTIONS["64-build"] then + cmake_build('sdl3', false, sdl3_common_defs) + end +end diff --git a/premake5.lua b/premake5.lua index 2a0684d8..43a07734 100644 --- a/premake5.lua +++ b/premake5.lua @@ -10,8 +10,8 @@ premake.override(premake.tools.gcc, "getlinks", function(originalFn, cfg, system -- https://github.com/premake/premake-core/blob/d842e671c7bc7e09f2eeaafd199fd01e48b87ee7/src/tools/gcc.lua#L568C15-L568C22 local result = originalFn(cfg, systemonly, nogroups) - local whole_syslibs = {"-Wl,--whole-archive"} - local static_whole_syslibs = {"-Wl,--whole-archive -Wl,-Bstatic"} + local whole_syslibs = { "-Wl,--whole-archive" } + local static_whole_syslibs = { "-Wl,--whole-archive -Wl,-Bstatic" } local endswith = function(s, ptrn) return ptrn == string.sub(s, -string.len(ptrn)) @@ -157,25 +157,23 @@ newoption { -- windows options if os.target() == 'windows' then + newoption { + category = "build", + trigger = "dosstub", + description = "Change the DOS stub of the Windows builds", + } -newoption { - category = "build", - trigger = "dosstub", - description = "Change the DOS stub of the Windows builds", -} - -newoption { - category = "build", - trigger = "winsign", - description = "Sign Windows builds with a fake certificate", -} - -newoption { - category = "build", - trigger = "winrsrc", - description = "Add resources to Windows builds", -} + newoption { + category = "build", + trigger = "winsign", + description = "Sign Windows builds with a fake certificate", + } + newoption { + category = "build", + trigger = "winrsrc", + description = "Add resources to Windows builds", + } end -- End windows options @@ -205,6 +203,7 @@ local x32_deps_include = { path.join(deps_dir, "protobuf/install32/include"), path.join(deps_dir, "zlib/install32/include"), path.join(deps_dir, "mbedtls/install32/include"), + path.join(deps_dir, "sdl3/install32/include"), } local x32_deps_overlay_include = { @@ -219,6 +218,7 @@ local x64_deps_include = { path.join(deps_dir, "protobuf/install64/include"), path.join(deps_dir, "zlib/install64/include"), path.join(deps_dir, "mbedtls/install64/include"), + path.join(deps_dir, "sdl3/install64/include"), } local x64_deps_overlay_include = { @@ -243,6 +243,8 @@ local common_files = { "helpers/common_helpers.cpp", "helpers/common_helpers/**", -- helpers/dbg_log "helpers/dbg_log.cpp", "helpers/dbg_log/**", + -- helpers/gamepad_provider + "helpers/gamepad_provider.cpp", "helpers/gamepad_provider/**", } local overlay_files = { @@ -273,119 +275,133 @@ if os.target() == 'windows' then end local deps_link = { - "ssq" .. static_postfix, - zlib_archive_name .. static_postfix, + "ssq" .. static_postfix, + zlib_archive_name .. static_postfix, lib_prefix .. "curl" .. static_postfix, - "mbedcrypto" .. static_postfix, - "mbedtls" .. static_postfix, - "mbedx509" .. static_postfix, + "mbedcrypto" .. static_postfix, + "mbedtls" .. static_postfix, + "mbedx509" .. static_postfix, + "SDL3-static" .. static_postfix, } -- add protobuf libs table_append(deps_link, { - lib_prefix .. "protobuf-lite" .. static_postfix, - "absl_bad_any_cast_impl" .. static_postfix, - "absl_bad_optional_access" .. static_postfix, - "absl_bad_variant_access" .. static_postfix, - "absl_base" .. static_postfix, - "absl_city" .. static_postfix, - "absl_civil_time" .. static_postfix, - "absl_cord" .. static_postfix, - "absl_cordz_functions" .. static_postfix, - "absl_cordz_handle" .. static_postfix, - "absl_cordz_info" .. static_postfix, - "absl_cordz_sample_token" .. static_postfix, - "absl_cord_internal" .. static_postfix, - "absl_crc32c" .. static_postfix, - "absl_crc_cord_state" .. static_postfix, - "absl_crc_cpu_detect" .. static_postfix, - "absl_crc_internal" .. static_postfix, - "absl_debugging_internal" .. static_postfix, - "absl_demangle_internal" .. static_postfix, - "absl_die_if_null" .. static_postfix, - "absl_examine_stack" .. static_postfix, - "absl_exponential_biased" .. static_postfix, - "absl_failure_signal_handler" .. static_postfix, - "absl_flags_commandlineflag" .. static_postfix, - "absl_flags_commandlineflag_internal" .. static_postfix, - "absl_flags_config" .. static_postfix, - "absl_flags_internal" .. static_postfix, - "absl_flags_marshalling" .. static_postfix, - "absl_flags_parse" .. static_postfix, - "absl_flags_private_handle_accessor" .. static_postfix, - "absl_flags_program_name" .. static_postfix, - "absl_flags_reflection" .. static_postfix, - "absl_flags_usage" .. static_postfix, - "absl_flags_usage_internal" .. static_postfix, - "absl_graphcycles_internal" .. static_postfix, - "absl_hash" .. static_postfix, - "absl_hashtablez_sampler" .. static_postfix, - "absl_int128" .. static_postfix, - "absl_kernel_timeout_internal" .. static_postfix, - "absl_leak_check" .. static_postfix, - "absl_log_entry" .. static_postfix, - "absl_log_flags" .. static_postfix, - "absl_log_globals" .. static_postfix, - "absl_log_initialize" .. static_postfix, - "absl_log_internal_check_op" .. static_postfix, - "absl_log_internal_conditions" .. static_postfix, - "absl_log_internal_fnmatch" .. static_postfix, - "absl_log_internal_format" .. static_postfix, - "absl_log_internal_globals" .. static_postfix, - "absl_log_internal_log_sink_set" .. static_postfix, - "absl_log_internal_message" .. static_postfix, - "absl_log_internal_nullguard" .. static_postfix, - "absl_log_internal_proto" .. static_postfix, - "absl_log_severity" .. static_postfix, - "absl_log_sink" .. static_postfix, - "absl_low_level_hash" .. static_postfix, - "absl_malloc_internal" .. static_postfix, - "absl_periodic_sampler" .. static_postfix, - "absl_random_distributions" .. static_postfix, + lib_prefix .. "protobuf-lite" .. static_postfix, + "absl_bad_any_cast_impl" .. static_postfix, + "absl_bad_optional_access" .. static_postfix, + "absl_bad_variant_access" .. static_postfix, + "absl_base" .. static_postfix, + "absl_city" .. static_postfix, + "absl_civil_time" .. static_postfix, + "absl_cord" .. static_postfix, + "absl_cordz_functions" .. static_postfix, + "absl_cordz_handle" .. static_postfix, + "absl_cordz_info" .. static_postfix, + "absl_cordz_sample_token" .. static_postfix, + "absl_cord_internal" .. static_postfix, + "absl_crc32c" .. static_postfix, + "absl_crc_cord_state" .. static_postfix, + "absl_crc_cpu_detect" .. static_postfix, + "absl_crc_internal" .. static_postfix, + "absl_debugging_internal" .. static_postfix, + "absl_demangle_internal" .. static_postfix, + "absl_die_if_null" .. static_postfix, + "absl_examine_stack" .. static_postfix, + "absl_exponential_biased" .. static_postfix, + "absl_failure_signal_handler" .. static_postfix, + "absl_flags_commandlineflag" .. static_postfix, + "absl_flags_commandlineflag_internal" .. static_postfix, + "absl_flags_config" .. static_postfix, + "absl_flags_internal" .. static_postfix, + "absl_flags_marshalling" .. static_postfix, + "absl_flags_parse" .. static_postfix, + "absl_flags_private_handle_accessor" .. static_postfix, + "absl_flags_program_name" .. static_postfix, + "absl_flags_reflection" .. static_postfix, + "absl_flags_usage" .. static_postfix, + "absl_flags_usage_internal" .. static_postfix, + "absl_graphcycles_internal" .. static_postfix, + "absl_hash" .. static_postfix, + "absl_hashtablez_sampler" .. static_postfix, + "absl_int128" .. static_postfix, + "absl_kernel_timeout_internal" .. static_postfix, + "absl_leak_check" .. static_postfix, + "absl_log_entry" .. static_postfix, + "absl_log_flags" .. static_postfix, + "absl_log_globals" .. static_postfix, + "absl_log_initialize" .. static_postfix, + "absl_log_internal_check_op" .. static_postfix, + "absl_log_internal_conditions" .. static_postfix, + "absl_log_internal_fnmatch" .. static_postfix, + "absl_log_internal_format" .. static_postfix, + "absl_log_internal_globals" .. static_postfix, + "absl_log_internal_log_sink_set" .. static_postfix, + "absl_log_internal_message" .. static_postfix, + "absl_log_internal_nullguard" .. static_postfix, + "absl_log_internal_proto" .. static_postfix, + "absl_log_severity" .. static_postfix, + "absl_log_sink" .. static_postfix, + "absl_low_level_hash" .. static_postfix, + "absl_malloc_internal" .. static_postfix, + "absl_periodic_sampler" .. static_postfix, + "absl_random_distributions" .. static_postfix, "absl_random_internal_distribution_test_util" .. static_postfix, - "absl_random_internal_platform" .. static_postfix, - "absl_random_internal_pool_urbg" .. static_postfix, - "absl_random_internal_randen" .. static_postfix, - "absl_random_internal_randen_hwaes" .. static_postfix, - "absl_random_internal_randen_hwaes_impl" .. static_postfix, - "absl_random_internal_randen_slow" .. static_postfix, - "absl_random_internal_seed_material" .. static_postfix, - "absl_random_seed_gen_exception" .. static_postfix, - "absl_random_seed_sequences" .. static_postfix, - "absl_raw_hash_set" .. static_postfix, - "absl_raw_logging_internal" .. static_postfix, - "absl_scoped_set_env" .. static_postfix, - "absl_spinlock_wait" .. static_postfix, - "absl_stacktrace" .. static_postfix, - "absl_status" .. static_postfix, - "absl_statusor" .. static_postfix, - "absl_strerror" .. static_postfix, - "absl_strings" .. static_postfix, - "absl_strings_internal" .. static_postfix, - "absl_string_view" .. static_postfix, - "absl_str_format_internal" .. static_postfix, - "absl_symbolize" .. static_postfix, - "absl_synchronization" .. static_postfix, - "absl_throw_delegate" .. static_postfix, - "absl_time" .. static_postfix, - "absl_time_zone" .. static_postfix, - "absl_vlog_config_internal" .. static_postfix, - "utf8_range" .. static_postfix, - "utf8_validity" .. static_postfix, + "absl_random_internal_platform" .. static_postfix, + "absl_random_internal_pool_urbg" .. static_postfix, + "absl_random_internal_randen" .. static_postfix, + "absl_random_internal_randen_hwaes" .. static_postfix, + "absl_random_internal_randen_hwaes_impl" .. static_postfix, + "absl_random_internal_randen_slow" .. static_postfix, + "absl_random_internal_seed_material" .. static_postfix, + "absl_random_seed_gen_exception" .. static_postfix, + "absl_random_seed_sequences" .. static_postfix, + "absl_raw_hash_set" .. static_postfix, + "absl_raw_logging_internal" .. static_postfix, + "absl_scoped_set_env" .. static_postfix, + "absl_spinlock_wait" .. static_postfix, + "absl_stacktrace" .. static_postfix, + "absl_status" .. static_postfix, + "absl_statusor" .. static_postfix, + "absl_strerror" .. static_postfix, + "absl_strings" .. static_postfix, + "absl_strings_internal" .. static_postfix, + "absl_string_view" .. static_postfix, + "absl_str_format_internal" .. static_postfix, + "absl_symbolize" .. static_postfix, + "absl_synchronization" .. static_postfix, + "absl_throw_delegate" .. static_postfix, + "absl_time" .. static_postfix, + "absl_time_zone" .. static_postfix, + "absl_vlog_config_internal" .. static_postfix, + "utf8_range" .. static_postfix, + "utf8_validity" .. static_postfix, }) local common_link_win = { -- os specific - "Ws2_32" .. static_postfix, + "Ws2_32" .. static_postfix, "Iphlpapi" .. static_postfix, - "Wldap32" .. static_postfix, - "Winmm" .. static_postfix, - "Bcrypt" .. static_postfix, - "Dbghelp" .. static_postfix, + "Wldap32" .. static_postfix, + "Winmm" .. static_postfix, + "Bcrypt" .. static_postfix, + "Dbghelp" .. static_postfix, -- gamepad - "Xinput" .. static_postfix, + "Xinput" .. static_postfix, + -- sdl3 + "imagehlp" .. static_postfix, + "kernel32" .. static_postfix, + "user32" .. static_postfix, + "gdi32" .. static_postfix, + "imm32" .. static_postfix, + "ole32" .. static_postfix, + "oleaut32" .. static_postfix, + "shell32" .. static_postfix, + "version" .. static_postfix, + "uuid" .. static_postfix, + "advapi32" .. static_postfix, + "setupapi" .. static_postfix, -- imgui / overlay - "Gdi32" .. static_postfix, - "Dwmapi" .. static_postfix, + "Gdi32" .. static_postfix, + "Dwmapi" .. static_postfix, } -- add deps to win table_append(common_link_win, deps_link) @@ -400,7 +416,7 @@ table_append(common_link_linux, deps_link) -- overlay libs local overlay_link = { "ingame_overlay", - "system", -- ingame_overlay dependency + "system", -- ingame_overlay dependency "mini_detour", -- ingame_overlay dependency } -- we add them later when needed @@ -423,6 +439,7 @@ local x32_deps_libdir = { path.join(deps_dir, "protobuf/install32/lib"), path.join(deps_dir, "zlib/install32/lib"), path.join(deps_dir, "mbedtls/install32/lib"), + path.join(deps_dir, "sdl3/install32/lib"), } local x32_deps_overlay_libdir = { @@ -438,6 +455,7 @@ local x64_deps_libdir = { path.join(deps_dir, "zlib/install64/lib"), path.join(deps_dir, "mbedtls/install64/lib"), path.join(deps_dir, "ingame_overlay/install64/lib"), + path.join(deps_dir, "sdl3/install64/lib"), } local x64_deps_overlay_libdir = { @@ -481,23 +499,23 @@ platforms { "x64", "x32", } language "C++" cppdialect "C++17" cdialect "C17" -filter { "system:not windows", "action:gmake*" , } - cdialect("gnu17") -- gamepad.c relies on some linux-specific functions like strdup() and MAX_PATH -filter {} -- reset the filter and remove all active keywords +filter { "system:not windows", "action:gmake*", } +cdialect("gnu17") -- gamepad.c relies on some linux-specific functions like strdup() and MAX_PATH +filter {} -- reset the filter and remove all active keywords characterset "Unicode" -staticruntime "on" -- /MT or /MTd -runtime "Release" -- ensure we never link with /MTd, otherwise deps linking will fail +staticruntime "on" -- /MT or /MTd +runtime "Release" -- ensure we never link with /MTd, otherwise deps linking will fail flags { - "NoPCH", -- no precompiled header on Windows + "NoPCH", -- no precompiled header on Windows "MultiProcessorCompile", -- /MP "Enable Visual Studio to use multiple compiler processes when building" "RelativeLinks", } -targetprefix "" -- prevent adding the prefix libxxx on linux -visibility "Hidden" -- hide all symbols by default on GCC (unless they are marked visible) -linkgroups "On" -- turn off the awful order dependent linking on gcc/clang, causes the linker to go back and forth to find missing symbols +targetprefix "" -- prevent adding the prefix libxxx on linux +visibility "Hidden" -- hide all symbols by default on GCC (unless they are marked visible) +linkgroups "On" -- turn off the awful order dependent linking on gcc/clang, causes the linker to go back and forth to find missing symbols exceptionhandling "On" -- "Enable exception handling. ... although it does not affect execution." -stringpooling "On" -- cache similar strings -vpaths { -- just for visual niceness, see: https://premake.github.io/docs/vpaths/ +stringpooling "On" -- cache similar strings +vpaths { -- just for visual niceness, see: https://premake.github.io/docs/vpaths/ ["headers/*"] = { "**.h", "**.hxx", "**.hpp", }, @@ -523,9 +541,9 @@ vpaths { -- just for visual niceness, see: https://premake.github.io/docs/vpaths -- arch --------- filter { "platforms:x32", } - architecture "x86" +architecture "x86" filter { "platforms:x64", } - architecture "x86_64" +architecture "x86_64" -- debug/optimization flags @@ -533,40 +551,40 @@ filter { "platforms:x64", } filter {} -- reset the filter and remove all active keywords intrinsics "On" filter { "configurations:*debug", } - symbols "On" - optimize "Off" +symbols "On" +optimize "Off" filter { "configurations:*release", } - symbols "Off" - optimize "On" +symbols "Off" +optimize "On" --- common compiler/linker options --------- -- Visual Studio common compiler/linker options filter { "action:vs*", } - buildoptions { - "/permissive-", "/DYNAMICBASE", "/bigobj", - "/utf-8", "/Zc:char8_t-", "/EHsc", "/GL-" - } - linkoptions { - -- source of emittoolversioninfo: https://developercommunity.visualstudio.com/t/add-linker-option-to-strip-rich-stamp-from-exe-hea/740443 - "/NOLOGO", "/emittoolversioninfo:no" - } +buildoptions { + "/permissive-", "/DYNAMICBASE", "/bigobj", + "/utf-8", "/Zc:char8_t-", "/EHsc", "/GL-" +} +linkoptions { + -- source of emittoolversioninfo: https://developercommunity.visualstudio.com/t/add-linker-option-to-strip-rich-stamp-from-exe-hea/740443 + "/NOLOGO", "/emittoolversioninfo:no" +} -- GNU make common compiler/linker options filter { "action:gmake*", } - buildoptions { - -- https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html - "-fno-jump-tables" , "-Wno-switch", - } - linkoptions { - "-Wl,--exclude-libs,ALL", - } +buildoptions { + -- https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html + "-fno-jump-tables", "-Wno-switch", +} +linkoptions { + "-Wl,--exclude-libs,ALL", +} -- this is made separate because GCC complains but not CLANG -filter { "action:gmake*" , "files:*.cpp or *.cxx or *.cc or *.hpp or *.hxx", } - buildoptions { - "-fno-char8_t", -- GCC gives a warning when a .c file is compiled with this - } -filter {} -- reset the filter and remove all active keywords +filter { "action:gmake*", "files:*.cpp or *.cxx or *.cc or *.hpp or *.hxx", } +buildoptions { + "-fno-char8_t", -- GCC gives a warning when a .c file is compiled with this +} +filter {} -- reset the filter and remove all active keywords -- defines @@ -577,79 +595,79 @@ defines { -- added to all filters, later defines will be appended } -- release mode defines filter { "configurations:*release" } - defines { - "NDEBUG", "EMU_RELEASE_BUILD" - } +defines { + "NDEBUG", "EMU_RELEASE_BUILD" +} -- debug mode defines filter { "configurations:*debug" } - defines { - "DEBUG", - } +defines { + "DEBUG", +} -- Windows defines filter { "system:windows", } - defines { - "_CRT_SECURE_NO_WARNINGS", - } +defines { + "_CRT_SECURE_NO_WARNINGS", +} -- Linux defines filter { "system:not windows" } - defines { - "GNUC", - } +defines { + "GNUC", +} -- MinGw on Windows -- common compiler/linker options: source: https://gcc.gnu.org/onlinedocs/gcc/Cygwin-and-MinGW-Options.html --------- filter { "system:windows", "action:gmake*", } - -- MinGw on Windows common defines - -- MinGw on Windows doesn't have a definition for '_S_IFDIR' which is microsoft specific: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions - -- this is used in 'base.cpp' -> if ( buffer.st_mode & _S_IFDIR) - -- instead microsoft has an alternative but only enabled when _CRT_DECLARE_NONSTDC_NAMES is defined - -- https://learn.microsoft.com/en-us/cpp/c-runtime-library/compatibility - defines { - -- '_CRT_NONSTDC_NO_WARNINGS', - '_CRT_DECLARE_NONSTDC_NAMES', - } - linkoptions { - -- I don't know why but if libgcc/libstdc++ as well as pthreads are not statically linked - -- none of the output binary .dlls will reach their DllMain() in x64dbg - -- even when they're force-loaded in any process they immediately unload - -- '-static-libgcc' ,'-static-libstdc++', - '-static', - } +-- MinGw on Windows common defines +-- MinGw on Windows doesn't have a definition for '_S_IFDIR' which is microsoft specific: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions +-- this is used in 'base.cpp' -> if ( buffer.st_mode & _S_IFDIR) +-- instead microsoft has an alternative but only enabled when _CRT_DECLARE_NONSTDC_NAMES is defined +-- https://learn.microsoft.com/en-us/cpp/c-runtime-library/compatibility +defines { + -- '_CRT_NONSTDC_NO_WARNINGS', + '_CRT_DECLARE_NONSTDC_NAMES', +} +linkoptions { + -- I don't know why but if libgcc/libstdc++ as well as pthreads are not statically linked + -- none of the output binary .dlls will reach their DllMain() in x64dbg + -- even when they're force-loaded in any process they immediately unload + -- '-static-libgcc' ,'-static-libstdc++', + '-static', +} -- MinGw on Windows cannot compile 'creatwth.cpp' from Detours lib (error: 'DWordMult' was not declared in this scope) -- because intsafe.h isn't included by default filter { "system:windows", "action:gmake*", "files:**/detours/creatwth.cpp" } - buildoptions { - "-include intsafe.h", - } +buildoptions { + "-include intsafe.h", +} -- add extra files for clearance filter {} -- reset the filter and remove all active keywords -- post build docs filter { 'options:incexamples', } - files { - 'post_build/**', - } +files { + 'post_build/**', +} filter { 'options:incexamples', 'system:not windows', } - removefiles { - 'post_build/win/**' - } +removefiles { + 'post_build/win/**' +} -- deps filter { 'options:incdeps', "platforms:x32", } - files { - table_postfix_items(x32_deps_include, '/**.h'), - table_postfix_items(x32_deps_include, '/**.hxx'), - table_postfix_items(x32_deps_include, '/**.hpp'), - } +files { + table_postfix_items(x32_deps_include, '/**.h'), + table_postfix_items(x32_deps_include, '/**.hxx'), + table_postfix_items(x32_deps_include, '/**.hpp'), +} filter { 'options:incdeps', "platforms:x64", } - files { - table_postfix_items(x64_deps_include, '/**.h'), - table_postfix_items(x64_deps_include, '/**.hxx'), - table_postfix_items(x64_deps_include, '/**.hpp'), - } +files { + table_postfix_items(x64_deps_include, '/**.h'), + table_postfix_items(x64_deps_include, '/**.hxx'), + table_postfix_items(x64_deps_include, '/**.hpp'), +} filter {} -- reset the filter and remove all active keywords @@ -664,7 +682,7 @@ includedirs { -- disable warnings for external libraries/deps filter { 'files:proto_gen/** or libs/** or build/deps/**' } - warnings 'Off' +warnings 'Off' filter {} -- reset the filter and remove all active keywords @@ -672,501 +690,502 @@ filter {} -- reset the filter and remove all active keywords -- post build change DOS stub + sign --------- if os.target() == "windows" then - --- token expansion like '%{cfg.platform}' happens later during project build -local dos_stub_exe = path.translate(path.getabsolute('resources/win/file_dos_stub/file_dos_stub_%{cfg.platform}.exe', _MAIN_SCRIPT_DIR), '\\') -local signer_tool = path.translate(path.getabsolute('third-party/build/win/cert/sign_helper.bat', _MAIN_SCRIPT_DIR), '\\') --- change dos stub -filter { "system:windows", "options:dosstub", } + -- token expansion like '%{cfg.platform}' happens later during project build + local dos_stub_exe = path.translate( + path.getabsolute('resources/win/file_dos_stub/file_dos_stub_%{cfg.platform}.exe', _MAIN_SCRIPT_DIR), '\\') + local signer_tool = path.translate(path.getabsolute('third-party/build/win/cert/sign_helper.bat', _MAIN_SCRIPT_DIR), + '\\') + -- change dos stub + filter { "system:windows", "options:dosstub", } postbuildcommands { '"' .. dos_stub_exe .. '" %[%{!cfg.buildtarget.abspath}]', } --- sign -filter { "system:windows", "options:winsign", } + -- sign + filter { "system:windows", "options:winsign", } postbuildcommands { '"' .. signer_tool .. '" %[%{!cfg.buildtarget.abspath}]', } -filter {} -- reset the filter and remove all active keywords + filter {} -- reset the filter and remove all active keywords end workspace "gbe" - location("build/project/%{_ACTION}/" .. os_iden) +location("build/project/%{_ACTION}/" .. os_iden) -- Project api_regular --------- project "api_regular" - kind "SharedLib" - location "%{wks.location}/%{prj.name}" - targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/regular/%{cfg.platform}") +kind "SharedLib" +location "%{wks.location}/%{prj.name}" +targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/regular/%{cfg.platform}") - -- name - --------- - filter { "system:windows", "platforms:x32", } - targetname "steam_api" - filter { "system:windows", "platforms:x64", } - targetname "steam_api64" - filter { "system:not windows", } - targetname "libsteam_api" +-- name +--------- +filter { "system:windows", "platforms:x32", } +targetname "steam_api" +filter { "system:windows", "platforms:x64", } +targetname "steam_api64" +filter { "system:not windows", } +targetname "libsteam_api" - -- x32 include dir - filter { "platforms:x32", } - includedirs { - x32_deps_include, - } +-- x32 include dir +filter { "platforms:x32", } +includedirs { + x32_deps_include, +} - -- x64 include dir - filter { "platforms:x64", } - includedirs { - x64_deps_include, - } +-- x64 include dir +filter { "platforms:x64", } +includedirs { + x64_deps_include, +} - -- common source & header files - --------- - filter {} -- reset the filter and remove all active keywords - files { -- added to all filters, later defines will be appended - common_files, - } - removefiles { - detours_files, - } - -- Windows common source files - filter { "system:windows", } - removefiles { - "dll/wrap.cpp" - } - -- Windows x32 common source files - filter { "system:windows", "platforms:x32", "options:winrsrc", } - files { - "resources/win/api/32/resources.rc" - } - -- Windows x64 common source files - filter { "system:windows", "platforms:x64", "options:winrsrc", } - files { - "resources/win/api/64/resources.rc" - } +-- common source & header files +--------- +filter {} -- reset the filter and remove all active keywords +files { -- added to all filters, later defines will be appended + common_files, +} +removefiles { + detours_files, +} +-- Windows common source files +filter { "system:windows", } +removefiles { + "dll/wrap.cpp" +} +-- Windows x32 common source files +filter { "system:windows", "platforms:x32", "options:winrsrc", } +files { + "resources/win/api/32/resources.rc" +} +-- Windows x64 common source files +filter { "system:windows", "platforms:x64", "options:winrsrc", } +files { + "resources/win/api/64/resources.rc" +} - -- libs to link - --------- - -- Windows libs to link - filter { "system:windows", } - links { - common_link_win, - } +-- libs to link +--------- +-- Windows libs to link +filter { "system:windows", } +links { + common_link_win, +} - -- Linux libs to link - filter { "system:not windows", } - links { - common_link_linux, - } +-- Linux libs to link +filter { "system:not windows", } +links { + common_link_linux, +} - -- libs search dir - --------- - -- x32 libs search dir - filter { "platforms:x32", } - libdirs { - x32_deps_libdir, - } - -- x64 libs search dir - filter { "platforms:x64", } - libdirs { - x64_deps_libdir, - } +-- libs search dir +--------- +-- x32 libs search dir +filter { "platforms:x32", } +libdirs { + x32_deps_libdir, +} +-- x64 libs search dir +filter { "platforms:x64", } +libdirs { + x64_deps_libdir, +} -- End api_regular -- Project api_experimental --------- project "api_experimental" - kind "SharedLib" - location "%{wks.location}/%{prj.name}" - targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/experimental/%{cfg.platform}") +kind "SharedLib" +location "%{wks.location}/%{prj.name}" +targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/experimental/%{cfg.platform}") - -- name - --------- - filter { "system:windows", "platforms:x32", } - targetname "steam_api" - filter { "system:windows", "platforms:x64", } - targetname "steam_api64" - filter { "system:not windows", } - targetname "libsteam_api" +-- name +--------- +filter { "system:windows", "platforms:x32", } +targetname "steam_api" +filter { "system:windows", "platforms:x64", } +targetname "steam_api64" +filter { "system:not windows", } +targetname "libsteam_api" - -- defines - --------- - filter {} -- reset the filter and remove all active keywords - defines { -- added to all filters, later defines will be appended - "EMU_OVERLAY", "ImTextureID=ImU64", - } - -- Windows defines - filter { "system:windows" } - defines { - "EMU_EXPERIMENTAL_BUILD", - } +-- defines +--------- +filter {} -- reset the filter and remove all active keywords +defines { -- added to all filters, later defines will be appended + "EMU_OVERLAY", "ImTextureID=ImU64", +} +-- Windows defines +filter { "system:windows" } +defines { + "EMU_EXPERIMENTAL_BUILD", +} - -- include dir - --------- - -- x32 include dir - filter { "platforms:x32", } - includedirs { - x32_deps_include, - x32_deps_overlay_include, - } - -- x64 include dir - filter { "platforms:x64", } - includedirs { - x64_deps_include, - x64_deps_overlay_include, - } +-- include dir +--------- +-- x32 include dir +filter { "platforms:x32", } +includedirs { + x32_deps_include, + x32_deps_overlay_include, +} +-- x64 include dir +filter { "platforms:x64", } +includedirs { + x64_deps_include, + x64_deps_overlay_include, +} - -- common source & header files - --------- - filter {} -- reset the filter and remove all active keywords - files { -- added to all filters, later defines will be appended - common_files, - overlay_files, - } - removefiles { - 'libs/detours/uimports.cc', - } - -- deps - filter { 'options:incdeps', "platforms:x32", } - files { - table_postfix_items(x32_deps_overlay_include, '/**.h'), - table_postfix_items(x32_deps_overlay_include, '/**.hxx'), - table_postfix_items(x32_deps_overlay_include, '/**.hpp'), - } - filter { 'options:incdeps', "platforms:x64", } - files { - table_postfix_items(x64_deps_overlay_include, '/**.h'), - table_postfix_items(x64_deps_overlay_include, '/**.hxx'), - table_postfix_items(x64_deps_overlay_include, '/**.hpp'), - } - -- Windows common source files - filter { "system:windows", } - removefiles { - "dll/wrap.cpp" - } - -- Windows x32 common source files - filter { "system:windows", "platforms:x32", "options:winrsrc", } - files { - "resources/win/api/32/resources.rc" - } - -- Windows x64 common source files - filter { "system:windows", "platforms:x64", "options:winrsrc", } - files { - "resources/win/api/64/resources.rc" - } - -- Linux common source files - filter { "system:not windows", } - removefiles { - detours_files, - } +-- common source & header files +--------- +filter {} -- reset the filter and remove all active keywords +files { -- added to all filters, later defines will be appended + common_files, + overlay_files, +} +removefiles { + 'libs/detours/uimports.cc', +} +-- deps +filter { 'options:incdeps', "platforms:x32", } +files { + table_postfix_items(x32_deps_overlay_include, '/**.h'), + table_postfix_items(x32_deps_overlay_include, '/**.hxx'), + table_postfix_items(x32_deps_overlay_include, '/**.hpp'), +} +filter { 'options:incdeps', "platforms:x64", } +files { + table_postfix_items(x64_deps_overlay_include, '/**.h'), + table_postfix_items(x64_deps_overlay_include, '/**.hxx'), + table_postfix_items(x64_deps_overlay_include, '/**.hpp'), +} +-- Windows common source files +filter { "system:windows", } +removefiles { + "dll/wrap.cpp" +} +-- Windows x32 common source files +filter { "system:windows", "platforms:x32", "options:winrsrc", } +files { + "resources/win/api/32/resources.rc" +} +-- Windows x64 common source files +filter { "system:windows", "platforms:x64", "options:winrsrc", } +files { + "resources/win/api/64/resources.rc" +} +-- Linux common source files +filter { "system:not windows", } +removefiles { + detours_files, +} - -- libs to link - --------- - filter {} -- reset the filter and remove all active keywords - links { - overlay_link, - } - -- Windows libs to link - filter { "system:windows", } - links { - common_link_win, - } +-- libs to link +--------- +filter {} -- reset the filter and remove all active keywords +links { + overlay_link, +} +-- Windows libs to link +filter { "system:windows", } +links { + common_link_win, +} - -- Linux libs to link - filter { "system:not windows", } - links { - common_link_linux, - "X11" - } +-- Linux libs to link +filter { "system:not windows", } +links { + common_link_linux, + "X11" +} - -- libs search dir - --------- - -- x32 libs search dir - filter { "platforms:x32", } - libdirs { - x32_deps_libdir, - x32_deps_overlay_libdir, - } - -- x64 libs search dir - filter { "platforms:x64", } - libdirs { - x64_deps_libdir, - x64_deps_overlay_libdir, - } +-- libs search dir +--------- +-- x32 libs search dir +filter { "platforms:x32", } +libdirs { + x32_deps_libdir, + x32_deps_overlay_libdir, +} +-- x64 libs search dir +filter { "platforms:x64", } +libdirs { + x64_deps_libdir, + x64_deps_overlay_libdir, +} -- End api_experimental -- Project steamclient_experimental --------- project "steamclient_experimental" - kind "SharedLib" - location "%{wks.location}/%{prj.name}" - - -- targetdir - --------- - filter { "system:windows", } - targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/steamclient_experimental") - filter { "system:not windows", } - targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/experimental/%{cfg.platform}") +kind "SharedLib" +location "%{wks.location}/%{prj.name}" + +-- targetdir +--------- +filter { "system:windows", } +targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/steamclient_experimental") +filter { "system:not windows", } +targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/experimental/%{cfg.platform}") - -- name - --------- - filter { "system:windows", "platforms:x32", } - targetname "steamclient" - filter { "system:windows", "platforms:x64", } - targetname "steamclient64" - filter { "system:not windows", } - targetname "steamclient" - - - -- defines - --------- - filter {} -- reset the filter and remove all active keywords - defines { -- added to all filters, later defines will be appended - "STEAMCLIENT_DLL", "EMU_OVERLAY", "ImTextureID=ImU64", - } - -- Windows defines - filter { "system:windows" } - defines { - "EMU_EXPERIMENTAL_BUILD", - } +-- name +--------- +filter { "system:windows", "platforms:x32", } +targetname "steamclient" +filter { "system:windows", "platforms:x64", } +targetname "steamclient64" +filter { "system:not windows", } +targetname "steamclient" - -- include dir - --------- - -- x32 include dir - filter { "platforms:x32", } - includedirs { - x32_deps_include, - x32_deps_overlay_include, - } - - -- x64 include dir - filter { "platforms:x64", } - includedirs { - x64_deps_include, - x64_deps_overlay_include, - } +-- defines +--------- +filter {} -- reset the filter and remove all active keywords +defines { -- added to all filters, later defines will be appended + "STEAMCLIENT_DLL", "EMU_OVERLAY", "ImTextureID=ImU64", +} +-- Windows defines +filter { "system:windows" } +defines { + "EMU_EXPERIMENTAL_BUILD", +} - -- common source & header files - --------- - filter {} -- reset the filter and remove all active keywords - files { -- added to all filters, later defines will be appended - common_files, - overlay_files, - } - removefiles { - 'libs/detours/uimports.cc', - } - -- deps - filter { 'options:incdeps', "platforms:x32", } - files { - table_postfix_items(x32_deps_overlay_include, '/**.h'), - table_postfix_items(x32_deps_overlay_include, '/**.hxx'), - table_postfix_items(x32_deps_overlay_include, '/**.hpp'), - } - filter { 'options:incdeps', "platforms:x64", } - files { - table_postfix_items(x64_deps_overlay_include, '/**.h'), - table_postfix_items(x64_deps_overlay_include, '/**.hxx'), - table_postfix_items(x64_deps_overlay_include, '/**.hpp'), - } - -- Windows common source files - filter { "system:windows", } - removefiles { - "dll/wrap.cpp" - } - -- Windows x32 common source files - filter { "system:windows", "platforms:x32", "options:winrsrc", } - files { - "resources/win/client/32/resources.rc" - } - -- Windows x64 common source files - filter { "system:windows", "platforms:x64", "options:winrsrc", } - files { - "resources/win/client/64/resources.rc" - } - -- Linux common source files - filter { "system:not windows", } - removefiles { - detours_files, - } +-- include dir +--------- +-- x32 include dir +filter { "platforms:x32", } +includedirs { + x32_deps_include, + x32_deps_overlay_include, +} + +-- x64 include dir +filter { "platforms:x64", } +includedirs { + x64_deps_include, + x64_deps_overlay_include, +} - -- libs to link - --------- - filter {} -- reset the filter and remove all active keywords - links { - overlay_link, - } - -- Windows libs to link - filter { "system:windows", } - links { - common_link_win, - } - - -- Linux libs to link - filter { "system:not windows", } - links { - common_link_linux, - } +-- common source & header files +--------- +filter {} -- reset the filter and remove all active keywords +files { -- added to all filters, later defines will be appended + common_files, + overlay_files, +} +removefiles { + 'libs/detours/uimports.cc', +} +-- deps +filter { 'options:incdeps', "platforms:x32", } +files { + table_postfix_items(x32_deps_overlay_include, '/**.h'), + table_postfix_items(x32_deps_overlay_include, '/**.hxx'), + table_postfix_items(x32_deps_overlay_include, '/**.hpp'), +} +filter { 'options:incdeps', "platforms:x64", } +files { + table_postfix_items(x64_deps_overlay_include, '/**.h'), + table_postfix_items(x64_deps_overlay_include, '/**.hxx'), + table_postfix_items(x64_deps_overlay_include, '/**.hpp'), +} +-- Windows common source files +filter { "system:windows", } +removefiles { + "dll/wrap.cpp" +} +-- Windows x32 common source files +filter { "system:windows", "platforms:x32", "options:winrsrc", } +files { + "resources/win/client/32/resources.rc" +} +-- Windows x64 common source files +filter { "system:windows", "platforms:x64", "options:winrsrc", } +files { + "resources/win/client/64/resources.rc" +} +-- Linux common source files +filter { "system:not windows", } +removefiles { + detours_files, +} - -- libs search dir - --------- - -- x32 libs search dir - filter { "platforms:x32", } - libdirs { - x32_deps_libdir, - x32_deps_overlay_libdir, - } - -- x64 libs search dir - filter { "platforms:x64", } - libdirs { - x64_deps_libdir, - x64_deps_overlay_libdir, - } +-- libs to link +--------- +filter {} -- reset the filter and remove all active keywords +links { + overlay_link, +} +-- Windows libs to link +filter { "system:windows", } +links { + common_link_win, +} + +-- Linux libs to link +filter { "system:not windows", } +links { + common_link_linux, +} + + +-- libs search dir +--------- +-- x32 libs search dir +filter { "platforms:x32", } +libdirs { + x32_deps_libdir, + x32_deps_overlay_libdir, +} +-- x64 libs search dir +filter { "platforms:x64", } +libdirs { + x64_deps_libdir, + x64_deps_overlay_libdir, +} -- End steamclient_experimental -- Project tool_lobby_connect --------- project "tool_lobby_connect" - kind "ConsoleApp" - location "%{wks.location}/%{prj.name}" - targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/tools/lobby_connect") - targetname "lobby_connect_%{cfg.platform}" +kind "ConsoleApp" +location "%{wks.location}/%{prj.name}" +targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/tools/lobby_connect") +targetname "lobby_connect_%{cfg.platform}" - -- defines - --------- - filter {} -- reset the filter and remove all active keywords - defines { -- added to all filters, later defines will be appended - "NO_DISK_WRITES", "LOBBY_CONNECT", - } - removedefines { - "CONTROLLER_SUPPORT", - } +-- defines +--------- +filter {} -- reset the filter and remove all active keywords +defines { -- added to all filters, later defines will be appended + "NO_DISK_WRITES", "LOBBY_CONNECT", +} +removedefines { + "CONTROLLER_SUPPORT", +} - -- include dir - --------- - -- common include dir - -- x32 include dir - filter { "platforms:x32", } - includedirs { - x32_deps_include, - } +-- include dir +--------- +-- common include dir +-- x32 include dir +filter { "platforms:x32", } +includedirs { + x32_deps_include, +} - -- x64 include dir - filter { "platforms:x64", } - includedirs { - x64_deps_include, - } +-- x64 include dir +filter { "platforms:x64", } +includedirs { + x64_deps_include, +} - -- common source & header files - --------- - filter {} -- reset the filter and remove all active keywords - files { -- added to all filters, later defines will be appended - common_files, - 'tools/lobby_connect/lobby_connect.cpp' - } - removefiles { - "libs/gamepad/**", - detours_files, - } - -- Windows x32 common source files - filter { "system:windows", "platforms:x32", "options:winrsrc", } - files { - "resources/win/launcher/32/resources.rc" - } - -- Windows x64 common source files - filter { "system:windows", "platforms:x64", "options:winrsrc", } - files { - "resources/win/launcher/64/resources.rc" - } +-- common source & header files +--------- +filter {} -- reset the filter and remove all active keywords +files { -- added to all filters, later defines will be appended + common_files, + 'tools/lobby_connect/lobby_connect.cpp' +} +removefiles { + "libs/gamepad/**", + detours_files, +} +-- Windows x32 common source files +filter { "system:windows", "platforms:x32", "options:winrsrc", } +files { + "resources/win/launcher/32/resources.rc" +} +-- Windows x64 common source files +filter { "system:windows", "platforms:x64", "options:winrsrc", } +files { + "resources/win/launcher/64/resources.rc" +} - -- libs to link - --------- - -- Windows libs to link - filter { "system:windows", } - links { - common_link_win, - 'Comdlg32', - } +-- libs to link +--------- +-- Windows libs to link +filter { "system:windows", } +links { + common_link_win, + 'Comdlg32', +} - -- Linux libs to link - filter { "system:not windows", } - links { - common_link_linux, - } +-- Linux libs to link +filter { "system:not windows", } +links { + common_link_linux, +} - -- libs search dir - --------- - -- x32 libs search dir - filter { "platforms:x32", } - libdirs { - x32_deps_libdir, - } - -- x64 libs search dir - filter { "platforms:x64", } - libdirs { - x64_deps_libdir, - } +-- libs search dir +--------- +-- x32 libs search dir +filter { "platforms:x32", } +libdirs { + x32_deps_libdir, +} +-- x64 libs search dir +filter { "platforms:x64", } +libdirs { + x64_deps_libdir, +} -- End tool_lobby_connect -- Project tool_generate_interfaces project "tool_generate_interfaces" - kind "ConsoleApp" - location "%{wks.location}/%{prj.name}" - targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/tools/generate_interfaces") - targetname "generate_interfaces_%{cfg.platform}" +kind "ConsoleApp" +location "%{wks.location}/%{prj.name}" +targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/tools/generate_interfaces") +targetname "generate_interfaces_%{cfg.platform}" - -- common source & header files - --------- - files { - "tools/generate_interfaces/generate_interfaces.cpp" - } +-- common source & header files +--------- +files { + "tools/generate_interfaces/generate_interfaces.cpp" +} -- End tool_generate_interfaces -- Project lib_steamnetworkingsockets START project "lib_steamnetworkingsockets" - kind "SharedLib" - location "%{wks.location}/%{prj.name}" - targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/steamnetworkingsockets/%{cfg.platform}") - targetname "steamnetworkingsockets" +kind "SharedLib" +location "%{wks.location}/%{prj.name}" +targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/steamnetworkingsockets/%{cfg.platform}") +targetname "steamnetworkingsockets" - -- common source & header files - --------- - files { - "networking_sockets_lib/**", - "helpers/dbg_log.cpp", "helpers/dbg_log/**", - 'helpers/common_helpers.cpp', 'helpers/common_helpers/**', - } +-- common source & header files +--------- +files { + "networking_sockets_lib/**", + "helpers/dbg_log.cpp", "helpers/dbg_log/**", + 'helpers/common_helpers.cpp', 'helpers/common_helpers/**', +} -- End lib_steamnetworkingsockets @@ -1174,70 +1193,68 @@ project "lib_steamnetworkingsockets" -- Project lib_game_overlay_renderer project "lib_game_overlay_renderer" - kind "SharedLib" - location "%{wks.location}/%{prj.name}" +kind "SharedLib" +location "%{wks.location}/%{prj.name}" - -- targetdir - --------- - filter { "system:windows", } - targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/steamclient_experimental") - filter { "system:not windows", } - targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/gameoverlayrenderer/%{cfg.platform}") +-- targetdir +--------- +filter { "system:windows", } +targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/steamclient_experimental") +filter { "system:not windows", } +targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/gameoverlayrenderer/%{cfg.platform}") - -- name - --------- - filter { "system:windows", "platforms:x32", } - targetname "GameOverlayRenderer" - filter { "system:windows", "platforms:x64", } - targetname "GameOverlayRenderer64" - filter { "system:not windows", } - targetname "gameoverlayrenderer" - - - -- include dir - --------- - -- x32 include dir - filter { "platforms:x32", } - includedirs { - x32_deps_include, - } - - -- x64 include dir - filter { "platforms:x64", } - includedirs { - x64_deps_include, - } +-- name +--------- +filter { "system:windows", "platforms:x32", } +targetname "GameOverlayRenderer" +filter { "system:windows", "platforms:x64", } +targetname "GameOverlayRenderer64" +filter { "system:not windows", } +targetname "gameoverlayrenderer" - -- common source & header files - --------- - filter {} -- reset the filter and remove all active keywords - files { - "game_overlay_renderer_lib/**" - } - -- x32 common source files - filter { "system:windows", "platforms:x32", "options:winrsrc", } - files { - "resources/win/game_overlay_renderer/32/resources.rc" - } - -- x64 common source files - filter { "system:windows", "platforms:x64", "options:winrsrc", } - files { - "resources/win/game_overlay_renderer/64/resources.rc" - } +-- include dir +--------- +-- x32 include dir +filter { "platforms:x32", } +includedirs { + x32_deps_include, +} + +-- x64 include dir +filter { "platforms:x64", } +includedirs { + x64_deps_include, +} + + +-- common source & header files +--------- +filter {} -- reset the filter and remove all active keywords +files { + "game_overlay_renderer_lib/**" +} +-- x32 common source files +filter { "system:windows", "platforms:x32", "options:winrsrc", } +files { + "resources/win/game_overlay_renderer/32/resources.rc" +} +-- x64 common source files +filter { "system:windows", "platforms:x64", "options:winrsrc", } +files { + "resources/win/game_overlay_renderer/64/resources.rc" +} -- End lib_game_overlay_renderer -- WINDOWS ONLY TARGETS START if os.target() == "windows" then - - --- Project steamclient_experimental_stub ---------- -project "steamclient_experimental_stub" + -- Project steamclient_experimental_stub + --------- + project "steamclient_experimental_stub" -- https://stackoverflow.com/a/63228027 kind "SharedLib" location "%{wks.location}/%{prj.name}" @@ -1247,32 +1264,32 @@ project "steamclient_experimental_stub" -- name --------- filter { "platforms:x32", } - targetname "steamclient" + targetname "steamclient" filter { "platforms:x64", } - targetname "steamclient64" + targetname "steamclient64" -- common source & header files --------- filter {} -- reset the filter and remove all active keywords - files { -- added to all filters, later defines will be appended + files { -- added to all filters, later defines will be appended "steamclient/steamclient.cpp", } -- x32 common source files filter { "platforms:x32", "options:winrsrc", } - files { - "resources/win/client/32/resources.rc" - } + files { + "resources/win/client/32/resources.rc" + } -- x64 common source files filter { "platforms:x64", "options:winrsrc", } - files { - "resources/win/client/64/resources.rc" - } --- End steamclient_experimental_stub + files { + "resources/win/client/64/resources.rc" + } + -- End steamclient_experimental_stub --- Project steamclient_experimental_extra -project "steamclient_experimental_extra" + -- Project steamclient_experimental_extra + project "steamclient_experimental_extra" kind "SharedLib" location "%{wks.location}/%{prj.name}" targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/steamclient_experimental/extra_dlls") @@ -1283,14 +1300,14 @@ project "steamclient_experimental_extra" --------- -- x32 include dir filter { "platforms:x32", } - includedirs { - x32_deps_include, - } + includedirs { + x32_deps_include, + } -- x64 include dir filter { "platforms:x64", } - includedirs { - x64_deps_include, - } + includedirs { + x64_deps_include, + } -- common source & header files @@ -1308,19 +1325,19 @@ project "steamclient_experimental_extra" } -- x32 common source files filter { "platforms:x32", "options:winrsrc", } - files { - "resources/win/client/32/resources.rc" - } + files { + "resources/win/client/32/resources.rc" + } -- x64 common source files filter { "platforms:x64", "options:winrsrc", } - files { - "resources/win/client/64/resources.rc" - } --- End steamclient_experimental_extra + files { + "resources/win/client/64/resources.rc" + } + -- End steamclient_experimental_extra --- Project steamclient_experimental_loader -project "steamclient_experimental_loader" + -- Project steamclient_experimental_loader + project "steamclient_experimental_loader" kind "WindowedApp" location "%{wks.location}/%{prj.name}" targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/steamclient_experimental") @@ -1331,16 +1348,16 @@ project "steamclient_experimental_loader" --------- -- MinGW on Windows filter { "action:gmake*", } - -- source: https://gcc.gnu.org/onlinedocs/gcc/Cygwin-and-MinGW-Options.html - linkoptions { - -- MinGW on Windows cannot link wWinMain by default - "-municode", - } + -- source: https://gcc.gnu.org/onlinedocs/gcc/Cygwin-and-MinGW-Options.html + linkoptions { + -- MinGW on Windows cannot link wWinMain by default + "-municode", + } -- common source & header files --------- - filter {} -- reset the filter and remove all active keywords + filter {} -- reset the filter and remove all active keywords files { "tools/steamclient_loader/win/*", -- we want the .ini too "helpers/pe_helpers.cpp", "helpers/pe_helpers/**", @@ -1350,14 +1367,14 @@ project "steamclient_experimental_loader" } -- x32 common source files filter { "platforms:x32", "options:winrsrc", } - files { - "resources/win/launcher/32/resources.rc" - } + files { + "resources/win/launcher/32/resources.rc" + } -- x64 common source files filter { "platforms:x64", "options:winrsrc", } - files { - "resources/win/launcher/64/resources.rc" - } + files { + "resources/win/launcher/64/resources.rc" + } -- libs to link @@ -1367,11 +1384,11 @@ project "steamclient_experimental_loader" -- common_link_win, 'user32', } --- End steamclient_experimental_loader + -- End steamclient_experimental_loader --- Project tool_file_dos_stub_changer -project "tool_file_dos_stub_changer" + -- Project tool_file_dos_stub_changer + project "tool_file_dos_stub_changer" kind "ConsoleApp" location "%{wks.location}/%{prj.name}" targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/file_dos_stub_changer") @@ -1386,12 +1403,12 @@ project "tool_file_dos_stub_changer" "helpers/pe_helpers.cpp", "helpers/pe_helpers/**", "helpers/common_helpers.cpp", "helpers/common_helpers/**", } --- End tool_file_dos_stub_changer + -- End tool_file_dos_stub_changer --- Project test_crash_printer ---------- -project "test_crash_printer" + -- Project test_crash_printer + --------- + project "test_crash_printer" kind "ConsoleApp" location "%{wks.location}/%{prj.name}" targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/tests/crash_printer") @@ -1401,7 +1418,7 @@ project "test_crash_printer" -- common source & header files --------- filter {} -- reset the filter and remove all active keywords - files { -- added to all filters, later defines will be appended + files { -- added to all filters, later defines will be appended 'crash_printer/' .. os_iden .. '.cpp', 'crash_printer/crash_printer/' .. os_iden .. '.hpp', -- helpers 'helpers/common_helpers.cpp', 'helpers/common_helpers/**', @@ -1429,8 +1446,7 @@ project "test_crash_printer" postbuildcommands { '%[%{!cfg.buildtarget.abspath}]', } --- End test_crash_printer - + -- End test_crash_printer end -- End WINDOWS ONLY TARGETS @@ -1438,10 +1454,9 @@ end -- LINUX ONLY TARGETS START if os.target() == "linux" then - --- Project steamclient_regular ---------- -project "steamclient_regular" + -- Project steamclient_regular + --------- + project "steamclient_regular" kind "SharedLib" location "%{wks.location}/%{prj.name}" targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/regular/%{cfg.platform}") @@ -1460,20 +1475,20 @@ project "steamclient_regular" --------- -- x32 include dir filter { "platforms:x32", } - includedirs { - x32_deps_include, - } + includedirs { + x32_deps_include, + } -- x64 include dir filter { "platforms:x64", } - includedirs { - x64_deps_include, - } + includedirs { + x64_deps_include, + } -- common source & header files --------- filter {} -- reset the filter and remove all active keywords - files { -- added to all filters, later defines will be appended + files { -- added to all filters, later defines will be appended common_files, } removefiles { @@ -1484,7 +1499,7 @@ project "steamclient_regular" -- libs to link --------- filter {} -- reset the filter and remove all active keywords - links { -- added to all filters, later defines will be appended + links { -- added to all filters, later defines will be appended common_link_linux, } @@ -1492,20 +1507,20 @@ project "steamclient_regular" --------- -- x32 libs search dir filter { "platforms:x32", } - libdirs { - x32_deps_libdir, - } + libdirs { + x32_deps_libdir, + } -- x64 libs search dir filter { "platforms:x64", } - libdirs { - x64_deps_libdir, - } --- End steamclient_regular + libdirs { + x64_deps_libdir, + } + -- End steamclient_regular --- Project test_crash_printer_sa_handler ---------- -project "test_crash_printer_sa_handler" + -- Project test_crash_printer_sa_handler + --------- + project "test_crash_printer_sa_handler" kind "ConsoleApp" location "%{wks.location}/%{prj.name}" targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/tests/crash_printer") @@ -1515,7 +1530,7 @@ project "test_crash_printer_sa_handler" -- common source & header files --------- filter {} -- reset the filter and remove all active keywords - files { -- added to all filters, later defines will be appended + files { -- added to all filters, later defines will be appended 'crash_printer/' .. os_iden .. '.cpp', 'crash_printer/crash_printer/' .. os_iden .. '.hpp', -- helpers 'helpers/common_helpers.cpp', 'helpers/common_helpers/**', @@ -1535,12 +1550,12 @@ project "test_crash_printer_sa_handler" '%[%{!cfg.buildtarget.abspath}]', } --- End test_crash_printer_sa_handler + -- End test_crash_printer_sa_handler --- Project test_crash_printer_sa_sigaction ---------- -project "test_crash_printer_sa_sigaction" + -- Project test_crash_printer_sa_sigaction + --------- + project "test_crash_printer_sa_sigaction" kind "ConsoleApp" location "%{wks.location}/%{prj.name}" targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/tests/crash_printer") @@ -1550,7 +1565,7 @@ project "test_crash_printer_sa_sigaction" -- common source & header files --------- filter {} -- reset the filter and remove all active keywords - files { -- added to all filters, later defines will be appended + files { -- added to all filters, later defines will be appended 'crash_printer/' .. os_iden .. '.cpp', 'crash_printer/crash_printer/' .. os_iden .. '.hpp', -- helpers 'helpers/common_helpers.cpp', 'helpers/common_helpers/**', @@ -1569,8 +1584,7 @@ project "test_crash_printer_sa_sigaction" postbuildcommands { '%[%{!cfg.buildtarget.abspath}]', } --- End test_crash_printer_sa_sigaction - + -- End test_crash_printer_sa_sigaction end -- End LINUX ONLY TARGETS