1050 lines
39 KiB
C++
1050 lines
39 KiB
C++
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
|
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
|
// ******************************************************************
|
|
// *
|
|
// * This file is part of the Cxbx project.
|
|
// *
|
|
// * Cxbx and Cxbe are free software; you can redistribute them
|
|
// * and/or modify them under the terms of the GNU General Public
|
|
// * License as published by the Free Software Foundation; either
|
|
// * version 2 of the license, or (at your option) any later version.
|
|
// *
|
|
// * This program 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 General Public License for more details.
|
|
// *
|
|
// * You should have recieved a copy of the GNU General Public License
|
|
// * along with this program; see the file COPYING.
|
|
// * If not, write to the Free Software Foundation, Inc.,
|
|
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
|
// *
|
|
// * (c) 2018 wutno (#/g/punk - Rizon)
|
|
// * (c) 2018 RadWolfie
|
|
// * (c) 2019 ergo720
|
|
// *
|
|
// * All rights reserved
|
|
// *
|
|
// ******************************************************************
|
|
|
|
#define LOG_PREFIX CXBXR_MODULE::CXBXR
|
|
|
|
#include "Settings.hpp"
|
|
#include "core\kernel\support\Emu.h"
|
|
#include "EmuShared.h"
|
|
#include <filesystem>
|
|
#include "common\input\layout_xbox_device.h"
|
|
#include <fstream>
|
|
#include "common/util/cliConfig.hpp"
|
|
|
|
// TODO: Implement Qt support when real CPU emulation is available.
|
|
#ifndef QT_VERSION // NOTE: Non-Qt will be using current directory for data
|
|
#include <ShlObj.h> // For SHGetSpecialFolderPath and CSIDL_APPDATA
|
|
#else
|
|
static_assert(false, "Please implement support for cross-platform's user profile data.");
|
|
|
|
#include <QDir> // for create directory
|
|
#include <QFile> // for check file existance
|
|
#include <QStandardPaths> // for cross-platform's user profile support
|
|
#endif
|
|
|
|
// Individual library version
|
|
uint16_t g_LibVersion_D3D8 = 0;
|
|
uint16_t g_LibVersion_DSOUND = 0;
|
|
|
|
// NOTE: Update settings_version when conversion to setting's structure is required.
|
|
// UPDATE: When settings are removed, use "if (use false && settings_version < {next_version}) {" statement
|
|
// until existing settings require replacement or conversion. next_version input is a hardcode number.
|
|
// Settings version 10 and later should consider as not backward compatible.
|
|
// TODO: Add read-only state when using an older build and add a notification for will not be able save to file.
|
|
// The sooner we do this, the better before version upgrade.
|
|
///////////////////////////
|
|
// * History:
|
|
// * 2: (RadWolfie), initial version
|
|
// * 3: (ergo720), added logging settings
|
|
// * 4: (LukeUsher), added network settings
|
|
// * 5: (ergo720), added new input gui settings and revision to core
|
|
// * 6: (RadWolfie), added loader executable member to core, only for clean up loader expertimental setting
|
|
// * 7: (RadWolfie), fix allowAdminPrivilege not align with other boolean members
|
|
// * 8: (ergo720), added general input settings
|
|
// * 9: (LukeUsher), replaced HardwareYUV with MaintainAspect
|
|
///////////////////////////
|
|
const unsigned int settings_version = 9;
|
|
|
|
Settings* g_Settings = nullptr;
|
|
|
|
#define szSettings_setup_error "ERROR: Either setup have a problem or do not have write permission to directory."
|
|
#define szSettings_init_error "ERROR: Unable to initialize Settings class."
|
|
#define szSettings_save_user_option_message "Use Cxbx-Reloaded in Portable Mode?" \
|
|
"\n\nClicking 'Yes' will save your setting in the current directory, rather than your User Profile."
|
|
|
|
#define szSettings_settings_file "/settings.ini"
|
|
#define szSettings_cxbx_reloaded_directory "/Cxbx-Reloaded"
|
|
|
|
static const char* section_gui = "gui";
|
|
static struct {
|
|
const char* CxbxDebugMode = "CxbxDebugMode";
|
|
const char* CxbxDebugLogFile = "CxbxDebugLogFile";
|
|
const char* RecentXbeFiles = "RecentXbeFiles";
|
|
const char* DataStorageToggle = "DataStorageToggle";
|
|
const char* DataCustomLocation = "DataCustomLocation";
|
|
const char* IgnoreInvalidXbeSig = "IgnoreInvalidXbeSig";
|
|
const char *IgnoreInvalidXbeSec = "IgnoreInvalidXbeSec";
|
|
const char* ConsoleTypeToggle = "ConsoleTypeToggle";
|
|
} sect_gui_keys;
|
|
|
|
static const char* section_core = "core";
|
|
static struct {
|
|
const char* Revision = "Revision";
|
|
const char* FlagsLLE = "FlagsLLE";
|
|
const char* KrnlDebugMode = "KrnlDebugMode";
|
|
const char* KrnlDebugLogFile = "KrnlDebugLogFile";
|
|
const char* AllowAdminPrivilege = "AllowAdminPrivilege";
|
|
const char* LoggedModules = "LoggedModules";
|
|
const char* LogLevel = "LogLevel";
|
|
const char* LogPopupTestCase = "LogPopupTestCase";
|
|
} sect_core_keys;
|
|
|
|
static const char* section_video = "video";
|
|
static struct {
|
|
const char* VideoResolution = "VideoResolution";
|
|
const char* adapter = "adapter";
|
|
const char* Direct3DDevice = "Direct3DDevice";
|
|
const char* VSync = "VSync";
|
|
const char* FullScreen = "FullScreen";
|
|
const char* MaintainAspect = "MaintainAspect";
|
|
const char* RenderResolution = "RenderResolution";
|
|
} sect_video_keys;
|
|
|
|
static const char* section_overlay = "overlay";
|
|
static struct {
|
|
const char* build_hash = "Build Hash";
|
|
const char* FPS = "FPS";
|
|
const char* hle_lle_stats = "HLE/LLE Stats";
|
|
const char* title_name = "Title Name";
|
|
const char* file_name = "File Name";
|
|
} sect_overlay_keys;
|
|
|
|
static const char* section_audio = "audio";
|
|
static struct {
|
|
const char* adapter = "adapter";
|
|
const char* adapter_value = "%08X %04X %04X %02X%02X %02X%02X%02X%02X%02X%02X";
|
|
const char* codec_pcm = "PCM";
|
|
const char* codec_xadpcm = "XADPCM";
|
|
const char* codec_unknown = "UnknownCodec";
|
|
const char* mute_on_unfocus = "MuteOnUnfocus";
|
|
} sect_audio_keys;
|
|
|
|
static const char* section_network = "network";
|
|
static struct {
|
|
const char* adapter_name = "adapter_name";
|
|
} sect_network_keys;
|
|
|
|
static const char *section_input_general = "input-general";
|
|
static struct {
|
|
const char *mo_axis_range = "MouseAxisRange";
|
|
const char *mo_wheel_range = "MouseWheelRange";
|
|
const char *ignore_kbmo_unfocus = "IgnoreKbMoUnfocus";
|
|
} sect_input_general;
|
|
|
|
static const char* section_controller_dinput = "controller-dinput";
|
|
static const char* section_controller_port = "controller-port";
|
|
|
|
static const char* section_input_port = "input-port-";
|
|
static struct {
|
|
const char* type = "Type";
|
|
const char* device = "DeviceName";
|
|
const char* config = "ProfileName";
|
|
const char *top_slot = "TopSlot";
|
|
const char *bottom_slot = "BottomSlot";
|
|
} sect_input_port;
|
|
|
|
static const char* section_input_profiles = "input-profile-";
|
|
static struct {
|
|
const char* type = "Type";
|
|
const char* config = "ProfileName";
|
|
const char* device = "DeviceName";
|
|
const char* control = "%s";
|
|
} sect_input_profiles;
|
|
|
|
static const char* section_hack = "hack";
|
|
static struct {
|
|
const char* DisablePixelShaders = "DisablePixelShaders";
|
|
const char* UseAllCores = "UseAllCores";
|
|
const char* SkipRdtscPatching = "SkipRdtscPatching";
|
|
} sect_hack_keys;
|
|
|
|
std::string GenerateExecDirectoryStr()
|
|
{
|
|
std::string exec_path;
|
|
(void)cli_config::GetValue(cli_config::exec, &exec_path);
|
|
return exec_path.substr(0, exec_path.find_last_of("\\/"));
|
|
}
|
|
|
|
// NOTE: This function will be only have Qt support, std::filesystem doesn't have generic support.
|
|
// Plus appending support for each OSes are not worthy to work on.
|
|
std::string GenerateUserProfileDirectoryStr()
|
|
{
|
|
// ========================================================
|
|
// TODO: Use QT's QDir and QFile classes for cross-platform
|
|
// with QStandardPaths::GenericDataLocation for generic User Profile location.
|
|
// NOTE: LibRetro compile build will not have user profile option support.
|
|
// ========================================================
|
|
char folderOption[MAX_PATH];
|
|
std::string genDirectory;
|
|
// TODO: Use QDir and QStandardPaths::GenericDataLocation for get user profile directory to support cross-platform
|
|
BOOL bRet = SHGetSpecialFolderPathA(NULL, folderOption, CSIDL_APPDATA, TRUE); // NOTE: Windows only support
|
|
if (!bRet) {
|
|
return "";
|
|
}
|
|
genDirectory = folderOption;
|
|
genDirectory.append(szSettings_cxbx_reloaded_directory);
|
|
|
|
return genDirectory;
|
|
}
|
|
|
|
std::string TrimQuoteFromString(const char* data)
|
|
{
|
|
// Safeguard before continue on.
|
|
if (data == nullptr) {
|
|
return "";
|
|
}
|
|
|
|
std::string trim_str = data;
|
|
size_t len = trim_str.size();
|
|
if (len > 0) {
|
|
// New method, in order to support spaces inside DeviceName value.
|
|
if (data[0] == '"') {
|
|
len--; // Let's decrement since it also can act as offset too.
|
|
if (trim_str.at(len) == '"') {
|
|
len--;
|
|
}
|
|
trim_str = trim_str.substr(1, len);
|
|
}
|
|
// Old method, don't do anything special.
|
|
}
|
|
return trim_str;
|
|
}
|
|
|
|
#define AppendQuoteToString(d) "\"" + std::string(d) + "\""
|
|
|
|
bool Settings::Init()
|
|
{
|
|
m_si.SetMultiKey(true);
|
|
|
|
bool bRet = LoadUserConfig();
|
|
|
|
// Enter setup installer process
|
|
if (!bRet) {
|
|
|
|
std::string setupFile;
|
|
m_gui.DataStorageToggle = SetupFile(setupFile);
|
|
|
|
if (m_gui.DataStorageToggle == CXBX_DATA_INVALID) {
|
|
return false;
|
|
}
|
|
|
|
// Call LoadConfig, this will load the config, applying defaults for any missing fields
|
|
bRet = LoadConfig();
|
|
|
|
if (!bRet) {
|
|
PopupError(nullptr, szSettings_setup_error);
|
|
return false;
|
|
}
|
|
|
|
bRet = Save(setupFile);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
bool Settings::LoadUserConfig()
|
|
{
|
|
std::string fileSearch;
|
|
m_gui.DataStorageToggle = FindSettingsLocation(fileSearch);
|
|
|
|
if (m_gui.DataStorageToggle == CXBX_DATA_INVALID) {
|
|
return false;
|
|
}
|
|
|
|
return LoadFile(fileSearch);
|
|
}
|
|
|
|
bool Settings::LoadFile(std::string file_path)
|
|
{
|
|
std::list<CSimpleIniA::Entry> si_list;
|
|
std::list<CSimpleIniA::Entry>::iterator si_list_iterator;
|
|
|
|
SI_Error siError = m_si.LoadFile(file_path.c_str());
|
|
|
|
if (siError != SI_OK) {
|
|
return false;
|
|
}
|
|
m_file_path = file_path;
|
|
|
|
return LoadConfig();
|
|
}
|
|
|
|
bool Settings::LoadConfig()
|
|
{
|
|
bool bRet;
|
|
const char* si_data;
|
|
int iStatus;
|
|
std::list<CSimpleIniA::Entry> si_list;
|
|
std::list<CSimpleIniA::Entry>::iterator si_list_iterator;
|
|
std::string trim_str;
|
|
|
|
// ==== GUI Begin ===========
|
|
|
|
m_gui.CxbxDebugMode = (DebugMode)m_si.GetLongValue(section_gui, sect_gui_keys.CxbxDebugMode, /*Default=*/DM_NONE);
|
|
si_data = m_si.GetValue(section_gui, sect_gui_keys.CxbxDebugLogFile, /*Default=*/nullptr);
|
|
// Fallback to null string if value is empty or contain bigger string.
|
|
if (si_data == nullptr || std::strlen(si_data) >= MAX_PATH) {
|
|
m_gui.szCxbxDebugFile = "";
|
|
}
|
|
else {
|
|
m_gui.szCxbxDebugFile = si_data;
|
|
}
|
|
|
|
m_gui.DataStorageToggle = m_si.GetLongValue(section_gui, sect_gui_keys.DataStorageToggle, /*Default=*/m_gui.DataStorageToggle);
|
|
si_data = m_si.GetValue(section_gui, sect_gui_keys.DataCustomLocation, /*Default=*/nullptr);
|
|
// Fallback to null string if value is empty or contains a bigger string.
|
|
if (si_data == nullptr || std::strlen(si_data) >= MAX_PATH) {
|
|
m_gui.szCustomLocation = "";
|
|
}
|
|
else {
|
|
m_gui.szCustomLocation = si_data;
|
|
}
|
|
|
|
// GUI - Recent xbe file paths
|
|
unsigned int index = 0;
|
|
unsigned int list_max = std::size(m_gui.szRecentXbeFiles);
|
|
bRet = m_si.GetAllValues(section_gui, sect_gui_keys.RecentXbeFiles, si_list);/*Default=empty list*/
|
|
if (bRet) {
|
|
si_list_iterator = si_list.begin();
|
|
for (si_list_iterator; si_list_iterator != si_list.end(); si_list_iterator++) {
|
|
// Exit loop when the list has reached the limit.
|
|
if (index == list_max) {
|
|
break;
|
|
}
|
|
// Do not accept any file path greater or equal to MAX_PATH
|
|
if (std::strlen(si_list_iterator->pItem) >= MAX_PATH) {
|
|
continue;
|
|
}
|
|
m_gui.szRecentXbeFiles[index] = si_list_iterator->pItem;
|
|
index++;
|
|
}
|
|
}
|
|
// Set all or remaining recent xbe file paths to null string
|
|
while (index < list_max) {
|
|
m_gui.szRecentXbeFiles[index] = "";
|
|
index++;
|
|
}
|
|
|
|
m_gui.bIgnoreInvalidXbeSig = m_si.GetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSig, /*Default=*/false);
|
|
m_gui.bIgnoreInvalidXbeSec = m_si.GetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSec, /*Default=*/false);
|
|
|
|
m_gui.ConsoleTypeToggle = (EMU_CONSOLE_TYPE)m_si.GetLongValue(section_gui, sect_gui_keys.ConsoleTypeToggle, /*Default=*/EMU_CONSOLE_TYPE_AUTO);
|
|
|
|
// ==== GUI End =============
|
|
|
|
// ==== Core Begin ==========
|
|
|
|
m_core.Revision = m_si.GetLongValue(section_core, sect_core_keys.Revision, 4);
|
|
m_core.FlagsLLE = m_si.GetLongValue(section_core, sect_core_keys.FlagsLLE, /*Default=*/LLE_NONE);
|
|
m_core.KrnlDebugMode = (DebugMode)m_si.GetLongValue(section_core, sect_core_keys.KrnlDebugMode, /*Default=*/DM_NONE);
|
|
si_data = m_si.GetValue(section_core, sect_core_keys.KrnlDebugLogFile, /*Default=*/nullptr);
|
|
// Fallback to null string if value is empty or contain bigger string.
|
|
if (si_data == nullptr || std::strlen(si_data) >= MAX_PATH) {
|
|
m_core.szKrnlDebug[0] = '\0';
|
|
}
|
|
else {
|
|
std::strncpy(m_core.szKrnlDebug, si_data, MAX_PATH);
|
|
}
|
|
|
|
m_core.allowAdminPrivilege = m_si.GetBoolValue(section_core, sect_core_keys.AllowAdminPrivilege, /*Default=*/false);
|
|
|
|
m_core.LogLevel = m_si.GetLongValue(section_core, sect_core_keys.LogLevel, 1);
|
|
si_list.clear();
|
|
index = 0;
|
|
list_max = std::size(m_core.LoggedModules);
|
|
bRet = m_si.GetAllValues(section_core, sect_core_keys.LoggedModules, si_list);
|
|
if (bRet) {
|
|
si_list_iterator = si_list.begin();
|
|
for (si_list_iterator; si_list_iterator != si_list.end(); si_list_iterator++) {
|
|
// Exit loop when the list has reached the limit.
|
|
if (index == list_max) {
|
|
break;
|
|
}
|
|
if (std::strncmp(si_list_iterator->pItem, "0x", 2) == 0) {
|
|
si_list_iterator->pItem += 2;
|
|
}
|
|
m_core.LoggedModules[index] = std::strtoul(si_list_iterator->pItem, nullptr, 16);
|
|
index++;
|
|
}
|
|
}
|
|
while (index < list_max) {
|
|
m_core.LoggedModules[index] = 0;
|
|
index++;
|
|
}
|
|
m_core.bLogPopupTestCase = m_si.GetBoolValue(section_core, sect_core_keys.LogPopupTestCase, /*Default=*/true);
|
|
|
|
// ==== Core End ============
|
|
|
|
// Delete/update legacy configs from previous revisions
|
|
RemoveLegacyConfigs(m_core.Revision);
|
|
m_core.Revision = settings_version;
|
|
|
|
// ==== Hack Begin ==========
|
|
|
|
m_hacks.DisablePixelShaders = m_si.GetBoolValue(section_hack, sect_hack_keys.DisablePixelShaders, /*Default=*/false);
|
|
m_hacks.UseAllCores = m_si.GetBoolValue(section_hack, sect_hack_keys.UseAllCores, /*Default=*/false);
|
|
m_hacks.SkipRdtscPatching = m_si.GetBoolValue(section_hack, sect_hack_keys.SkipRdtscPatching, /*Default=*/false);
|
|
|
|
// ==== Hack End ============
|
|
|
|
// ==== Video Begin =========
|
|
|
|
// Video - Resolution config
|
|
si_data = m_si.GetValue(section_video, sect_video_keys.VideoResolution, /*Default=*/nullptr);
|
|
// Fallback to null string if value is empty or contain bigger string.
|
|
if (si_data == nullptr || std::strlen(si_data) >= std::size(m_video.szVideoResolution)) {
|
|
m_video.szVideoResolution[0] = '\0';
|
|
}
|
|
else {
|
|
std::strncpy(m_video.szVideoResolution, si_data, std::size(m_video.szVideoResolution));
|
|
}
|
|
|
|
m_video.adapter = m_si.GetLongValue(section_video, sect_video_keys.adapter, /*Default=*/0);
|
|
m_video.direct3DDevice = m_si.GetLongValue(section_video, sect_video_keys.Direct3DDevice, /*Default=*/0);
|
|
m_video.bVSync = m_si.GetBoolValue(section_video, sect_video_keys.VSync, /*Default=*/false);
|
|
m_video.bFullScreen = m_si.GetBoolValue(section_video, sect_video_keys.FullScreen, /*Default=*/false);
|
|
m_video.bMaintainAspect = m_si.GetBoolValue(section_video, sect_video_keys.MaintainAspect, /*Default=*/true);
|
|
m_video.renderScaleFactor = m_si.GetLongValue(section_video, sect_video_keys.RenderResolution, /*Default=*/1);
|
|
|
|
// ==== Video End ===========
|
|
|
|
// ==== Audio Begin =========
|
|
|
|
// Audio - Adapter config
|
|
si_data = m_si.GetValue(section_audio, sect_audio_keys.adapter, /*Default=*/nullptr);
|
|
if (si_data == nullptr) {
|
|
// Default to primary audio device
|
|
m_audio.adapterGUID = { 0 };
|
|
}
|
|
else {
|
|
iStatus = std::sscanf(si_data, sect_audio_keys.adapter_value,
|
|
&m_audio.adapterGUID.Data1, &m_audio.adapterGUID.Data2, &m_audio.adapterGUID.Data3,
|
|
&m_audio.adapterGUID.Data4[0], &m_audio.adapterGUID.Data4[1], &m_audio.adapterGUID.Data4[2], &m_audio.adapterGUID.Data4[3],
|
|
&m_audio.adapterGUID.Data4[4], &m_audio.adapterGUID.Data4[5], &m_audio.adapterGUID.Data4[6], &m_audio.adapterGUID.Data4[7]);
|
|
|
|
// Fallback to primary audio device if file contain invalid value.
|
|
if (iStatus != 11 /*= total arguments*/) {
|
|
m_audio.adapterGUID = { 0 };
|
|
}
|
|
}
|
|
|
|
m_audio.codec_pcm = m_si.GetBoolValue(section_audio, sect_audio_keys.codec_pcm, /*Default=*/true, nullptr);
|
|
m_audio.codec_xadpcm = m_si.GetBoolValue(section_audio, sect_audio_keys.codec_xadpcm, /*Default=*/true, nullptr);
|
|
m_audio.codec_unknown = m_si.GetBoolValue(section_audio, sect_audio_keys.codec_unknown, /*Default=*/true, nullptr);
|
|
|
|
m_audio.mute_on_unfocus = m_si.GetBoolValue(section_audio, sect_audio_keys.mute_on_unfocus, /*Default=*/true, nullptr);
|
|
|
|
// ==== Audio End ===========
|
|
|
|
// ==== Network Begin =======
|
|
|
|
si_data = m_si.GetValue(section_network, sect_network_keys.adapter_name, /*Default=*/nullptr);
|
|
// Fallback to null string if value is empty or contains a bigger string.
|
|
if (si_data == nullptr || std::strlen(si_data) >= std::size(m_network.adapter_name)) {
|
|
m_network.adapter_name[0] = '\0';
|
|
} else {
|
|
std::strncpy(m_network.adapter_name, si_data, std::size(m_network.adapter_name));
|
|
}
|
|
|
|
// ==== Network End =========
|
|
|
|
// ==== Input General Begin ====
|
|
|
|
m_input_general.MoAxisRange = m_si.GetLongValue(section_input_general, sect_input_general.mo_axis_range, MO_AXIS_DEFAULT_RANGE);
|
|
m_input_general.MoWheelRange = m_si.GetLongValue(section_input_general, sect_input_general.mo_wheel_range, MO_WHEEL_DEFAULT_RANGE);
|
|
m_input_general.IgnoreKbMoUnfocus = m_si.GetBoolValue(section_input_general, sect_input_general.ignore_kbmo_unfocus, true);
|
|
|
|
// ==== Input General End ==============
|
|
|
|
// ==== Input Port Begin ====
|
|
|
|
for (int port_num = 0; port_num < 4; port_num++) {
|
|
for (int slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) {
|
|
m_input_port[port_num].Type = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
|
m_input_port[port_num].SlotType[slot] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
|
|
|
std::string current_section = std::string(section_input_port) + std::to_string(port_num);
|
|
int ret = m_si.GetLongValue(current_section.c_str(), sect_input_port.type, -2);
|
|
if (ret == -2) {
|
|
continue;
|
|
}
|
|
m_input_port[port_num].Type = ret;
|
|
m_input_port[port_num].DeviceName = m_si.GetValue(current_section.c_str(), sect_input_port.device);
|
|
m_input_port[port_num].ProfileName = TrimQuoteFromString(m_si.GetValue(current_section.c_str(), sect_input_port.config));
|
|
ret = m_si.GetLongValue(current_section.c_str(), slot == 0 ? sect_input_port.top_slot : sect_input_port.bottom_slot, -2);
|
|
if (ret == -2) {
|
|
continue;
|
|
}
|
|
m_input_port[port_num].SlotType[slot] = ret;
|
|
}
|
|
}
|
|
|
|
// ==== Input Port End ==============
|
|
|
|
// ==== Input Profile Begin ====
|
|
|
|
std::array<std::vector<std::string>, to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)> control_names;
|
|
for (int device = 0; device < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX); device++) {
|
|
if (dev_num_buttons[device] == 0) {
|
|
continue;
|
|
}
|
|
|
|
const auto &lambda = [&control_names, &device](int num_buttons, const char *const ctrl_names[]) {
|
|
for (int i = 0; i < num_buttons; i++) {
|
|
char control_name[XBOX_BUTTON_NAME_LENGTH];
|
|
std::sprintf(control_name, sect_input_profiles.control, ctrl_names[i]);
|
|
control_names[device].push_back(control_name);
|
|
}
|
|
};
|
|
|
|
switch (device)
|
|
{
|
|
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE):
|
|
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S):
|
|
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
|
|
lambda(dev_num_buttons[device], button_xbox_ctrl_names);
|
|
break;
|
|
|
|
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
|
|
lambda(dev_num_buttons[device], button_sbc_names);
|
|
break;
|
|
|
|
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
|
lambda(dev_num_buttons[device], button_lightgun_names);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
index = 0;
|
|
while (true) {
|
|
std::string current_section = std::string(section_input_profiles) + std::to_string(index);
|
|
if (m_si.GetSectionSize(current_section.c_str()) == -1) {
|
|
break;
|
|
}
|
|
s_input_profiles local_profile;
|
|
local_profile.Type = m_si.GetLongValue(current_section.c_str(), sect_input_profiles.type);
|
|
local_profile.ProfileName = TrimQuoteFromString(m_si.GetValue(current_section.c_str(), sect_input_profiles.config));
|
|
local_profile.DeviceName = m_si.GetValue(current_section.c_str(), sect_input_profiles.device);
|
|
for (int vec_control_index = 0; vec_control_index < dev_num_buttons[local_profile.Type]; vec_control_index++) {
|
|
local_profile.ControlList.push_back(m_si.GetValue(current_section.c_str(),
|
|
control_names[local_profile.Type][vec_control_index].c_str()));
|
|
}
|
|
m_input_profiles[local_profile.Type].push_back(std::move(local_profile));
|
|
index++;
|
|
}
|
|
|
|
// ==== Input Profile End ======
|
|
|
|
// ==== Overlay Begin =========
|
|
|
|
m_overlay.build_hash = m_si.GetBoolValue(section_overlay, sect_overlay_keys.build_hash, false);
|
|
m_overlay.fps = m_si.GetBoolValue(section_overlay, sect_overlay_keys.FPS, false);
|
|
m_overlay.hle_lle_stats = m_si.GetBoolValue(section_overlay, sect_overlay_keys.hle_lle_stats, false);
|
|
m_overlay.title_name = m_si.GetBoolValue(section_overlay, sect_overlay_keys.title_name, false);
|
|
m_overlay.file_name = m_si.GetBoolValue(section_overlay, sect_overlay_keys.file_name, false);
|
|
|
|
// ==== Overlay End ===========
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Settings::Save(std::string file_path)
|
|
{
|
|
if (m_file_path.empty() && file_path.empty()) {
|
|
return false;
|
|
}
|
|
|
|
// Minimal need is 25, 0x37 for GUID.
|
|
char si_value[64];
|
|
std::string quote_str;
|
|
|
|
// ==== GUI Begin ===========
|
|
|
|
m_si.SetLongValue(section_gui, sect_gui_keys.CxbxDebugMode, m_gui.CxbxDebugMode, nullptr, true, true);
|
|
m_si.SetValue(section_gui, sect_gui_keys.CxbxDebugLogFile, m_gui.szCxbxDebugFile.c_str(), nullptr, true);
|
|
m_si.SetLongValue(section_gui, sect_gui_keys.DataStorageToggle, m_gui.DataStorageToggle, nullptr, true, true);
|
|
m_si.SetValue(section_gui, sect_gui_keys.DataCustomLocation, m_gui.szCustomLocation.c_str(), nullptr, true);
|
|
|
|
// First force overwrite existing value
|
|
m_si.SetValue(section_gui, sect_gui_keys.RecentXbeFiles, m_gui.szRecentXbeFiles[0].c_str(), nullptr, true);
|
|
// Then append recent xbe file strings
|
|
for (unsigned int i = 1; i < 10; i++) {
|
|
m_si.SetValue(section_gui, sect_gui_keys.RecentXbeFiles, m_gui.szRecentXbeFiles[i].c_str(), nullptr, false);
|
|
}
|
|
|
|
m_si.SetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSig, m_gui.bIgnoreInvalidXbeSig, nullptr, true);
|
|
m_si.SetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSec, m_gui.bIgnoreInvalidXbeSec, nullptr, true);
|
|
|
|
m_si.SetLongValue(section_gui, sect_gui_keys.ConsoleTypeToggle, m_gui.ConsoleTypeToggle, nullptr, true, true);
|
|
|
|
// ==== GUI End =============
|
|
|
|
// ==== Core Begin ==========
|
|
|
|
m_si.SetLongValue(section_core, sect_core_keys.Revision, m_core.Revision, nullptr, false, true);
|
|
m_si.SetLongValue(section_core, sect_core_keys.FlagsLLE, m_core.FlagsLLE, nullptr, true, true);
|
|
m_si.SetLongValue(section_core, sect_core_keys.KrnlDebugMode, m_core.KrnlDebugMode, nullptr, true, true);
|
|
m_si.SetValue(section_core, sect_core_keys.KrnlDebugLogFile, m_core.szKrnlDebug, nullptr, true);
|
|
m_si.SetBoolValue(section_core, sect_core_keys.AllowAdminPrivilege, m_core.allowAdminPrivilege, nullptr, true);
|
|
m_si.SetLongValue(section_core, sect_core_keys.LogLevel, m_core.LogLevel, nullptr, false, true);
|
|
|
|
std::stringstream stream;
|
|
stream << "0x" << std::hex << m_core.LoggedModules[0];
|
|
m_si.SetValue(section_core, sect_core_keys.LoggedModules, stream.str().c_str(), nullptr, true);
|
|
for (int i = 1; i < NUM_INTEGERS_LOG; i++) {
|
|
stream.str("");
|
|
stream << "0x" << std::hex << m_core.LoggedModules[i];
|
|
m_si.SetValue(section_core, sect_core_keys.LoggedModules, stream.str().c_str(), nullptr, false);
|
|
}
|
|
m_si.SetBoolValue(section_core, sect_core_keys.LogPopupTestCase, m_core.bLogPopupTestCase, nullptr, true);
|
|
|
|
// ==== Core End ============
|
|
|
|
// ==== Video Begin =========
|
|
|
|
m_si.SetValue(section_video, sect_video_keys.VideoResolution, m_video.szVideoResolution, nullptr, true);
|
|
|
|
m_si.SetLongValue(section_video, sect_video_keys.adapter, m_video.adapter, nullptr, true, true);
|
|
m_si.SetLongValue(section_video, sect_video_keys.Direct3DDevice, m_video.direct3DDevice, nullptr, true, true);
|
|
m_si.SetBoolValue(section_video, sect_video_keys.VSync, m_video.bVSync, nullptr, true);
|
|
m_si.SetBoolValue(section_video, sect_video_keys.FullScreen, m_video.bFullScreen, nullptr, true);
|
|
m_si.SetBoolValue(section_video, sect_video_keys.MaintainAspect, m_video.bMaintainAspect, nullptr, true);
|
|
m_si.SetLongValue(section_video, sect_video_keys.RenderResolution, m_video.renderScaleFactor, nullptr, false, true);
|
|
|
|
// ==== Video End ===========
|
|
|
|
// ==== Audio Begin =========
|
|
|
|
// Audio - Adapter config
|
|
std::sprintf(si_value, sect_audio_keys.adapter_value,
|
|
m_audio.adapterGUID.Data1, m_audio.adapterGUID.Data2, m_audio.adapterGUID.Data3,
|
|
m_audio.adapterGUID.Data4[0], m_audio.adapterGUID.Data4[1], m_audio.adapterGUID.Data4[2], m_audio.adapterGUID.Data4[3],
|
|
m_audio.adapterGUID.Data4[4], m_audio.adapterGUID.Data4[5], m_audio.adapterGUID.Data4[6], m_audio.adapterGUID.Data4[7]);
|
|
|
|
m_si.SetValue(section_audio, sect_audio_keys.adapter, si_value, nullptr, true);
|
|
|
|
m_si.SetBoolValue(section_audio, sect_audio_keys.codec_pcm, m_audio.codec_pcm, nullptr, true);
|
|
m_si.SetBoolValue(section_audio, sect_audio_keys.codec_xadpcm, m_audio.codec_xadpcm, nullptr, true);
|
|
m_si.SetBoolValue(section_audio, sect_audio_keys.codec_unknown, m_audio.codec_unknown, nullptr, true);
|
|
m_si.SetBoolValue(section_audio, sect_audio_keys.mute_on_unfocus, m_audio.mute_on_unfocus, nullptr, true);
|
|
|
|
// ==== Audio End ===========
|
|
|
|
// ==== Network Begin =======
|
|
|
|
m_si.SetValue(section_network, sect_network_keys.adapter_name, m_network.adapter_name, nullptr, true);
|
|
|
|
// ==== Network End =========
|
|
|
|
// ==== Input General Begin =======
|
|
|
|
m_si.SetLongValue(section_input_general, sect_input_general.mo_axis_range, m_input_general.MoAxisRange, nullptr, false, true);
|
|
m_si.SetLongValue(section_input_general, sect_input_general.mo_wheel_range, m_input_general.MoWheelRange, nullptr, false, true);
|
|
m_si.SetBoolValue(section_input_general, sect_input_general.ignore_kbmo_unfocus, m_input_general.IgnoreKbMoUnfocus, nullptr, true);
|
|
|
|
// ==== Input General End =========
|
|
|
|
// ==== Input Port Begin ====
|
|
|
|
for (int port_num = 0; port_num < 4; port_num++) {
|
|
std::string current_section = std::string(section_input_port) + std::to_string(port_num);
|
|
std::string quoted_prf_str = m_input_port[port_num].ProfileName.insert(0, "\"");
|
|
quoted_prf_str += "\"";
|
|
m_si.SetLongValue(current_section.c_str(), sect_input_port.type, m_input_port[port_num].Type, nullptr, false, true);
|
|
m_si.SetValue(current_section.c_str(), sect_input_port.device, m_input_port[port_num].DeviceName.c_str(), nullptr, true);
|
|
m_si.SetValue(current_section.c_str(), sect_input_port.config, quoted_prf_str.c_str(), nullptr, true);
|
|
m_si.SetLongValue(current_section.c_str(), sect_input_port.top_slot, m_input_port[port_num].SlotType[SLOT_TOP], nullptr, false, true);
|
|
m_si.SetLongValue(current_section.c_str(), sect_input_port.bottom_slot, m_input_port[port_num].SlotType[SLOT_BOTTOM], nullptr, false, true);
|
|
}
|
|
|
|
// ==== Input Port End ==============
|
|
|
|
// ==== Input Profile Begin ====
|
|
|
|
std::array<std::vector<std::string>, to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)> control_names;
|
|
for (int device = 0; device < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX); device++) {
|
|
if (dev_num_buttons[device] == 0) {
|
|
continue;
|
|
}
|
|
|
|
const auto &lambda = [&control_names, &device](int num_buttons, const char *const ctrl_names[]) {
|
|
for (int i = 0; i < num_buttons; i++) {
|
|
char control_name[XBOX_BUTTON_NAME_LENGTH];
|
|
std::sprintf(control_name, sect_input_profiles.control, ctrl_names[i]);
|
|
control_names[device].push_back(control_name);
|
|
}
|
|
};
|
|
|
|
switch (device)
|
|
{
|
|
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE):
|
|
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S):
|
|
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
|
|
lambda(dev_num_buttons[device], button_xbox_ctrl_names);
|
|
break;
|
|
|
|
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
|
|
lambda(dev_num_buttons[device], button_sbc_names);
|
|
break;
|
|
|
|
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
|
lambda(dev_num_buttons[device], button_lightgun_names);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int profile_num = 0;
|
|
for (int i = 0; i < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX); i++) {
|
|
size_t vec_size = m_input_profiles[i].size();
|
|
if (vec_size == 0) {
|
|
continue;
|
|
}
|
|
for (unsigned int vec_index = 0; vec_index < vec_size; vec_index++, profile_num++) {
|
|
std::string current_section = std::string(section_input_profiles) + std::to_string(profile_num);
|
|
std::string quoted_prf_str = m_input_profiles[i][vec_index].ProfileName.insert(0, "\"");
|
|
quoted_prf_str += "\"";
|
|
m_si.SetLongValue(current_section.c_str(), sect_input_profiles.type, m_input_profiles[i][vec_index].Type, nullptr, false, true);
|
|
m_si.SetValue(current_section.c_str(), sect_input_profiles.config, quoted_prf_str.c_str(), nullptr, true);
|
|
m_si.SetValue(current_section.c_str(), sect_input_profiles.device, m_input_profiles[i][vec_index].DeviceName.c_str(), nullptr, true);
|
|
size_t vec_control_size = m_input_profiles[i][vec_index].ControlList.size();
|
|
if (vec_control_size == 0) {
|
|
continue;
|
|
}
|
|
m_si.SetValue(current_section.c_str(), control_names[i][0].c_str(), m_input_profiles[i][vec_index].ControlList[0].c_str(), nullptr, true);
|
|
for (unsigned int vec_control_index = 1; vec_control_index < vec_control_size; vec_control_index++) {
|
|
m_si.SetValue(current_section.c_str(), control_names[i][vec_control_index].c_str(),
|
|
m_input_profiles[i][vec_index].ControlList[vec_control_index].c_str(), nullptr, true);
|
|
}
|
|
}
|
|
}
|
|
while (true) {
|
|
std::string current_section = std::string(section_input_profiles) + std::to_string(profile_num);
|
|
if (m_si.GetSectionSize(current_section.c_str()) == -1) {
|
|
break;
|
|
}
|
|
m_si.Delete(current_section.c_str(), nullptr, true);
|
|
profile_num++;
|
|
}
|
|
|
|
// ==== Input Profile End ======
|
|
|
|
// ==== Overlay Begin =======
|
|
|
|
m_si.SetBoolValue(section_overlay, sect_overlay_keys.build_hash, m_overlay.build_hash, nullptr, true);
|
|
m_si.SetBoolValue(section_overlay, sect_overlay_keys.FPS, m_overlay.fps, nullptr, true);
|
|
m_si.SetBoolValue(section_overlay, sect_overlay_keys.hle_lle_stats, m_overlay.hle_lle_stats, nullptr, true);
|
|
m_si.SetBoolValue(section_overlay, sect_overlay_keys.title_name, m_overlay.title_name, nullptr, true);
|
|
m_si.SetBoolValue(section_overlay, sect_overlay_keys.file_name, m_overlay.file_name, nullptr, true);
|
|
|
|
// ==== Overlay End =========
|
|
|
|
// ==== Hack Begin ==========
|
|
|
|
m_si.SetBoolValue(section_hack, sect_hack_keys.DisablePixelShaders, m_hacks.DisablePixelShaders, nullptr, true);
|
|
m_si.SetBoolValue(section_hack, sect_hack_keys.UseAllCores, m_hacks.UseAllCores, nullptr, true);
|
|
m_si.SetBoolValue(section_hack, sect_hack_keys.SkipRdtscPatching, m_hacks.SkipRdtscPatching, nullptr, true);
|
|
|
|
// ==== Hack End ============
|
|
|
|
SI_Error siError;
|
|
if (!file_path.empty()) {
|
|
siError = m_si.SaveFile(file_path.c_str(), true);
|
|
|
|
if (siError == SI_OK) {
|
|
m_file_path = file_path;
|
|
}
|
|
}
|
|
else {
|
|
siError = m_si.SaveFile(m_file_path.c_str(), true);
|
|
}
|
|
|
|
return (siError == SI_OK);
|
|
}
|
|
|
|
void Settings::Delete()
|
|
{
|
|
std::filesystem::remove(m_file_path);
|
|
}
|
|
|
|
// Universal update to EmuShared from both standalone kernel, and GUI process.
|
|
void Settings::SyncToEmulator()
|
|
{
|
|
// register Core settings
|
|
g_EmuShared->SetCoreSettings(&m_core);
|
|
g_EmuShared->SetIsKrnlLogEnabled(m_core.KrnlDebugMode != DebugMode::DM_NONE);
|
|
|
|
// register Video settings
|
|
g_EmuShared->SetVideoSettings(&m_video);
|
|
g_EmuShared->SetOverlaySettings(&m_overlay);
|
|
|
|
// register Audio settings
|
|
g_EmuShared->SetAudioSettings(&m_audio);
|
|
|
|
// register Network settings
|
|
g_EmuShared->SetNetworkSettings(&m_network);
|
|
|
|
// register xbox device input settings
|
|
for (int i = 0; i < 4; i++) {
|
|
g_EmuShared->SetInputDevTypeSettings(&m_input_port[i].Type, i);
|
|
g_EmuShared->SetInputSlotTypeSettings(&m_input_port[i].SlotType[SLOT_TOP], i, SLOT_TOP);
|
|
g_EmuShared->SetInputSlotTypeSettings(&m_input_port[i].SlotType[SLOT_BOTTOM], i, SLOT_BOTTOM);
|
|
if (m_input_port[i].Type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
|
g_EmuShared->SetInputDevNameSettings(m_input_port[i].DeviceName.c_str(), i);
|
|
if (m_input_port[i].Type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)) {
|
|
auto it = std::find_if(m_input_profiles[m_input_port[i].Type].begin(),
|
|
m_input_profiles[m_input_port[i].Type].end(), [this, i](const auto &profile) {
|
|
if (profile.ProfileName == m_input_port[i].ProfileName) {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
if (it != m_input_profiles[m_input_port[i].Type].end()) {
|
|
char controls_name[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
|
for (int index = 0; index < dev_num_buttons[m_input_port[i].Type]; index++) {
|
|
strncpy(controls_name[index], it->ControlList[index].c_str(), 30);
|
|
}
|
|
g_EmuShared->SetInputBindingsSettings(controls_name, dev_num_buttons[m_input_port[i].Type], i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// register Input general settings
|
|
g_EmuShared->SetInputGeneralSettings(&m_input_general);
|
|
|
|
// register Hacks settings
|
|
g_EmuShared->SetHackSettings(&m_hacks);
|
|
|
|
// register data location setting
|
|
g_EmuShared->SetDataLocation(GetDataLocation().c_str());
|
|
|
|
// reset title mount path
|
|
g_EmuShared->SetTitleMountPath("");
|
|
}
|
|
|
|
void verifyDebugFilePath(DebugMode& debug_mode, std::string& file_path)
|
|
{
|
|
// Prevent using an incorrect path from the registry if the debug folders have been moved
|
|
std::string szDebugPath;
|
|
std::string szDebugName;
|
|
|
|
if (debug_mode == DM_FILE) {
|
|
|
|
if(file_path.size() == 0 || file_path.size() >= MAX_PATH) {
|
|
debug_mode = DM_NONE;
|
|
}
|
|
else {
|
|
szDebugName = file_path.substr(file_path.find_last_of("\\/"), std::string::npos);
|
|
|
|
if(file_path.size() < szDebugName.size()) {
|
|
file_path = "";
|
|
debug_mode = DM_NONE;
|
|
}
|
|
else {
|
|
szDebugPath = file_path.substr(0, file_path.size() - szDebugName.size());
|
|
|
|
if(std::filesystem::exists(szDebugPath) == false) {
|
|
file_path = "";
|
|
debug_mode = DM_NONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Settings::Verify()
|
|
{
|
|
std::string szKrnlDebug = m_core.szKrnlDebug; // Temporary placeholder until m_core.szKrnlDebug is replace to std::string.
|
|
|
|
verifyDebugFilePath(m_gui.CxbxDebugMode, m_gui.szCxbxDebugFile);
|
|
|
|
verifyDebugFilePath(m_core.KrnlDebugMode, szKrnlDebug);
|
|
|
|
// Set to null string once if contain invalid path.
|
|
if (m_core.szKrnlDebug[0] != '\0' && szKrnlDebug.size() == 0) {
|
|
std::memset(m_core.szKrnlDebug, 0, MAX_PATH);
|
|
}
|
|
}
|
|
|
|
std::string Settings::GetDataLocation()
|
|
{
|
|
|
|
// Optimization purpose for not require to re-process when toggle state has not changed.
|
|
if (m_current_DataStorageToggle == m_gui.DataStorageToggle) {
|
|
return m_current_data_location;
|
|
}
|
|
|
|
switch (m_gui.DataStorageToggle) {
|
|
default:
|
|
#ifdef RETRO_API_VERSION // TODO: Change me to #ifndef QT_VERSION
|
|
|
|
m_gui.DataStorageToggle = CXBX_DATA_EXECDIR;
|
|
|
|
#else // Only support for Qt compile build.
|
|
|
|
m_gui.DataStorageToggle = CXBX_DATA_APPDATA;
|
|
// If unknown value, default to CXBX_DATA_APPDATA (below, don't use break)
|
|
|
|
case CXBX_DATA_APPDATA:
|
|
m_current_data_location = GenerateUserProfileDirectoryStr();
|
|
break;
|
|
#endif
|
|
|
|
case CXBX_DATA_EXECDIR:
|
|
m_current_data_location = GenerateExecDirectoryStr();
|
|
break;
|
|
|
|
case CXBX_DATA_CUSTOM:
|
|
m_current_data_location = m_gui.szCustomLocation;
|
|
break;
|
|
}
|
|
m_current_DataStorageToggle = m_gui.DataStorageToggle;
|
|
|
|
return m_current_data_location;
|
|
}
|
|
|
|
// Detect where settings file is located and return default data mode.
|
|
CXBX_DATA Settings::FindSettingsLocation(std::string& file_path_out)
|
|
{
|
|
std::string fileSearch = GenerateExecDirectoryStr();
|
|
CXBX_DATA ret = CXBX_DATA_EXECDIR;
|
|
|
|
fileSearch.append(szSettings_settings_file);
|
|
|
|
// Check and see if file exists from portable, current, directory.
|
|
if (std::filesystem::exists(fileSearch) == false) {
|
|
|
|
fileSearch = GenerateUserProfileDirectoryStr();
|
|
if (fileSearch.size() == 0) {
|
|
return CXBX_DATA_INVALID;
|
|
}
|
|
CXBX_DATA ret = CXBX_DATA_APPDATA;
|
|
fileSearch.append(szSettings_settings_file);
|
|
|
|
// Check if the user profile directory settings file exists.
|
|
if (std::filesystem::exists(fileSearch) == false) {
|
|
return CXBX_DATA_INVALID;
|
|
}
|
|
}
|
|
file_path_out = fileSearch;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Enter setup installer process
|
|
CXBX_DATA Settings::SetupFile(std::string& file_path_out)
|
|
{
|
|
std::string setupFile;
|
|
CXBX_DATA data_ret = CXBX_DATA_INVALID;
|
|
#ifdef RETRO_API_VERSION // TODO: Change me to #ifndef QT_VERSION
|
|
// Can only have one option without Qt.
|
|
setupFile = GenerateExecDirectoryStr();
|
|
|
|
#else // Only support for Qt compile build.
|
|
PopupReturn eRet = PopupQuestion(nullptr, szSettings_save_user_option_message);
|
|
|
|
if (eRet == PopupReturn::Yes) {
|
|
setupFile = GenerateExecDirectoryStr();
|
|
data_ret = CXBX_DATA_EXECDIR;
|
|
}
|
|
else if (eRet == PopupReturn::No) {
|
|
setupFile = GenerateUserProfileDirectoryStr();
|
|
data_ret = CXBX_DATA_APPDATA;
|
|
if (setupFile.size() != 0) {
|
|
// Check if data directory exists.
|
|
if (!std::filesystem::exists(setupFile)) {
|
|
// Then try create data directory.
|
|
if (!std::filesystem::create_directory(setupFile)) {
|
|
// Unable to create a data directory
|
|
data_ret = CXBX_DATA_INVALID;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (data_ret == CXBX_DATA_INVALID) {
|
|
PopupError(nullptr, szSettings_setup_error);
|
|
}
|
|
else {
|
|
setupFile.append(szSettings_settings_file);
|
|
// Create the file, that's it. Load the default configuration later on;
|
|
std::ofstream createFile(setupFile);
|
|
if (createFile.is_open()) {
|
|
createFile.close();
|
|
}
|
|
file_path_out = setupFile;
|
|
}
|
|
|
|
return data_ret;
|
|
}
|
|
|
|
void Settings::RemoveLegacyConfigs(unsigned int CurrentRevision)
|
|
{
|
|
if (CurrentRevision < 5) {
|
|
m_si.Delete(section_controller_dinput, nullptr, true);
|
|
m_si.Delete(section_controller_port, nullptr, true);
|
|
}
|
|
|
|
if (CurrentRevision == 5) {
|
|
m_si.Delete(section_core, "LoaderExperiment", true);
|
|
}
|
|
|
|
|
|
if (CurrentRevision < 8) {
|
|
const std::string kb_str = "Keyboard";
|
|
|
|
for (unsigned port_num = 0; port_num < 4; ++port_num) {
|
|
std::string current_section = std::string(section_input_port) + std::to_string(port_num);
|
|
std::string device_name = m_si.GetValue(current_section.c_str(), sect_input_port.device, "");
|
|
|
|
if (device_name.ends_with(kb_str)) {
|
|
device_name += "Mouse";
|
|
m_si.SetValue(current_section.c_str(), sect_input_port.device, device_name.c_str(), nullptr, true);
|
|
}
|
|
}
|
|
|
|
for (unsigned index = 0; ; ++index) {
|
|
std::string current_section = std::string(section_input_profiles) + std::to_string(index);
|
|
if (m_si.GetSectionSize(current_section.c_str()) == -1) {
|
|
break;
|
|
}
|
|
|
|
std::string device_name = m_si.GetValue(current_section.c_str(), sect_input_profiles.device, "");
|
|
|
|
// NOTE: with C++20, this can be simplified by simply calling device_name.ends_with()
|
|
if (device_name.length() >= kb_str.length()) {
|
|
if (device_name.compare(device_name.length() - kb_str.length(), kb_str.length(), kb_str) == 0) {
|
|
device_name += "Mouse";
|
|
m_si.SetValue(current_section.c_str(), sect_input_profiles.device, device_name.c_str(), nullptr, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(CurrentRevision < 9) {
|
|
m_si.Delete(section_video, "HardwareYUV", true);
|
|
}
|
|
|
|
// see settings_version for details.
|
|
if(false && CurrentRevision < 10) {
|
|
m_si.Delete(section_core, "LoaderExecutable", true);
|
|
}
|
|
}
|