diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cc0e8a55..a2ef5aed4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,8 @@ file (GLOB CXBXR_HEADER_COMMON "${CXBXR_ROOT_DIR}/src/common/ReservedMemory.h" "${CXBXR_ROOT_DIR}/src/common/Settings.hpp" "${CXBXR_ROOT_DIR}/src/common/Timer.h" + "${CXBXR_ROOT_DIR}/src/common/util/cliConfig.hpp" + "${CXBXR_ROOT_DIR}/src/common/util/cliConverter.hpp" "${CXBXR_ROOT_DIR}/src/common/util/CPUID.h" "${CXBXR_ROOT_DIR}/src/common/util/crc32c.h" "${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.h" @@ -218,6 +220,8 @@ file (GLOB CXBXR_SOURCE_COMMON "${CXBXR_ROOT_DIR}/src/common/Logging.cpp" "${CXBXR_ROOT_DIR}/src/common/Settings.cpp" "${CXBXR_ROOT_DIR}/src/common/Timer.cpp" + "${CXBXR_ROOT_DIR}/src/common/util/cliConfig.cpp" + "${CXBXR_ROOT_DIR}/src/common/util/cliConverter.cpp" "${CXBXR_ROOT_DIR}/src/common/util/crc32c.cpp" "${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.cpp" "${CXBXR_ROOT_DIR}/src/common/util/hasher.cpp" diff --git a/src/common/Settings.cpp b/src/common/Settings.cpp index c759aa774..7ae1b4afb 100644 --- a/src/common/Settings.cpp +++ b/src/common/Settings.cpp @@ -33,6 +33,8 @@ #include #include "common\input\InputManager.h" #include "common\input\layout_xbox_controller.h" +#include +#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 @@ -45,8 +47,6 @@ static_assert(false, "Please implement support for cross-platform's user profile #include // for cross-platform's user profile support #endif -std::string g_exec_filepath; - // Individual library version uint16_t g_LibVersion_D3D8 = 0; uint16_t g_LibVersion_DSOUND = 0; @@ -144,7 +144,9 @@ static struct { std::string GenerateExecDirectoryStr() { - return g_exec_filepath.substr(0, g_exec_filepath.find_last_of("\\/")); + 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. @@ -203,41 +205,12 @@ bool Settings::Init() // Enter setup installer process if (!bRet) { - std::string saveFile; -#ifdef RETRO_API_VERSION // TODO: Change me to #ifndef QT_VERSION - // Can only have one option without Qt. - saveFile = GenerateExecDirectoryStr(); + std::string setupFile; + m_gui.DataStorageToggle = SetupFile(setupFile); -#else // Only support for Qt compile build. - int iRet = MessageBox(nullptr, szSettings_save_user_option_message, "Cxbx-Reloaded", MB_YESNOCANCEL | MB_ICONQUESTION); - - if (iRet == IDYES) { - saveFile = GenerateExecDirectoryStr(); - m_gui.DataStorageToggle = CXBX_DATA_EXECDIR; - } - else if (iRet == IDNO){ - saveFile = GenerateUserProfileDirectoryStr(); - m_gui.DataStorageToggle = CXBX_DATA_APPDATA; - if (saveFile.size() == 0) { - return false; - } - - // Check if data directory exists. - bRet = std::filesystem::exists(saveFile); - if (!bRet) { - // Then try create data directory. - bRet = std::filesystem::create_directory(saveFile); - if (!bRet) { - // Unable to create a data directory - return false; - } - } - } - else { + if (m_gui.DataStorageToggle == CXBX_DATA_INVALID) { return false; } -#endif - saveFile.append(szSettings_settings_file); // Call LoadConfig, this will load the config, applying defaults for any missing fields bRet = LoadConfig(); @@ -247,30 +220,18 @@ bool Settings::Init() return false; } - bRet = Save(saveFile); + bRet = Save(setupFile); } return bRet; } bool Settings::LoadUserConfig() { - std::string fileSearch = GenerateExecDirectoryStr(); + std::string fileSearch; + m_gui.DataStorageToggle = FindSettingsLocation(fileSearch); - 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 false; - } - fileSearch.append(szSettings_settings_file); - - // Check if the user profile directory settings file exists. - if (std::filesystem::exists(fileSearch) == false) { - return false; - } + if (m_gui.DataStorageToggle == CXBX_DATA_INVALID) { + return false; } return LoadFile(fileSearch); @@ -820,6 +781,82 @@ std::string Settings::GetDataLocation() 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. + int iRet = MessageBox(nullptr, szSettings_save_user_option_message, "Cxbx-Reloaded", MB_YESNOCANCEL | MB_ICONQUESTION); + + if (iRet == IDYES) { + setupFile = GenerateExecDirectoryStr(); + data_ret = CXBX_DATA_EXECDIR; + } + else if (iRet == IDNO) { + 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) { + MessageBox(nullptr, szSettings_setup_error, "Cxbx-Reloaded", MB_OK); + } + 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) { switch (CurrentRevision) { diff --git a/src/common/Settings.hpp b/src/common/Settings.hpp index f46ac389a..14bf70811 100644 --- a/src/common/Settings.hpp +++ b/src/common/Settings.hpp @@ -44,6 +44,7 @@ extern uint16_t g_LibVersion_DSOUND; // Cxbx-Reloaded's data storage location. typedef enum _CXBX_DATA { + CXBX_DATA_INVALID = -1, CXBX_DATA_APPDATA = 0, CXBX_DATA_EXECDIR = 1, CXBX_DATA_CUSTOM = 2, @@ -74,6 +75,8 @@ public: void SyncToEmulator(); void Verify(); std::string GetDataLocation(); + static CXBX_DATA FindSettingsLocation(std::string& file_path_out); + static CXBX_DATA SetupFile(std::string& file_path_out); // GUI settings struct s_gui { diff --git a/src/common/util/CxbxUtil.cpp b/src/common/util/CxbxUtil.cpp index 09caf894d..e42d0beaa 100644 --- a/src/common/util/CxbxUtil.cpp +++ b/src/common/util/CxbxUtil.cpp @@ -258,15 +258,25 @@ void unix2dos(std::string& string) // Refer to the license.txt file of Dolphin at https://github.com/dolphin-emu/dolphin/blob/master/license.txt. // Source: StringUtil.cpp of Dolphin emulator -/* Turns " hello " into "hello". Also handles tabs */ -std::string StripSpaces(const std::string& str) +std::string StripChars(const std::string& str, const char* strip_chars) { - const size_t s = str.find_first_not_of(" \t\r\n"); + const size_t s = str.find_first_not_of(strip_chars); if (str.npos != s) { - return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1); + return str.substr(s, str.find_last_not_of(strip_chars) - s + 1); } else { return ""; } } + +/* Turns " hello " into "hello". Also handles tabs */ +std::string StripSpaces(const std::string& str) +{ + return StripChars(str, " \t\r\n"); +} + +std::string StripQuotes(const std::string& str) +{ + return StripChars(str, "\""); +} diff --git a/src/common/util/CxbxUtil.h b/src/common/util/CxbxUtil.h index 0da5beda8..fe1496c08 100644 --- a/src/common/util/CxbxUtil.h +++ b/src/common/util/CxbxUtil.h @@ -64,6 +64,7 @@ bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite); void unix2dos(std::string& string); std::string StripSpaces(const std::string& str); +std::string StripQuotes(const std::string& str); // Retrieves the underlying integer value of a scoped enumerator. It allows to avoid using static_cast every time template diff --git a/src/common/util/cliConfig.cpp b/src/common/util/cliConfig.cpp new file mode 100644 index 000000000..1a06f2585 --- /dev/null +++ b/src/common/util/cliConfig.cpp @@ -0,0 +1,166 @@ +// ****************************************************************** +// * +// * 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, Boston, MA 02111-1307, USA. +// * +// * (c) 2019 RadWolfie +// * +// * All rights reserved +// * +// ****************************************************************** + +#include +#include + +#include "cliConverter.hpp" +#include "cliConfig.hpp" + +std::unordered_map g_cli_configs; + +namespace cli_config { + +bool GenConfig(char** argv, int argc) +{ + g_cli_configs = cliToMapPairs(argv, argc); + return (g_cli_configs.size() != 0); +} + +size_t ConfigSize() +{ + return g_cli_configs.size(); +} + +bool GenCMD(std::string& cmd_line_out) +{ + cmd_line_out = cliMapPairsToString(g_cli_configs); + return (cmd_line_out.length() != 0); +} + +// Generic check if key exist +bool hasKey(std::string key) +{ + auto found = g_cli_configs.find(key); + if (found != g_cli_configs.end()) { + return true; + } + return false; +} + +// Generic getter +bool GetValue(const std::string key, std::string* value) +{ + auto found = g_cli_configs.find(key); + if (found != g_cli_configs.end() && found->second.length() != 0) { + if (value != nullptr) { + *value = found->second; + } + return true; + } + + if (value != nullptr) { + *value = ""; + } + return false; +} +bool GetValue(const std::string key, int* value) +{ + auto found = g_cli_configs.find(key); + if (found != g_cli_configs.end() && found->second.length() != 0) { + if (value != nullptr) { + *value = std::stoi(found->second, nullptr, 10); + } + return true; + } + + if (value != nullptr) { + *value = 0; + } + return false; +} +bool GetValue(const std::string key, long long* value) +{ + auto found = g_cli_configs.find(key); + if (found != g_cli_configs.end() && found->second.length() != 0) { + if (value != nullptr) { + *value = std::stoll(found->second, nullptr, 10); + } + return true; + } + + if (value != nullptr) { + *value = 0; + } + return false; +} + +// Generic setter (allow modification from gui/cli, emulation does not need it.) +void SetValue(const std::string key, const std::string value) +{ + auto found = g_cli_configs.find(key); + if (found != g_cli_configs.end()) { + found->second = value; + } + else { + g_cli_configs[key] = value; + } +} +void SetValue(const std::string key, const char* value) +{ + SetValue(key, std::string(value)); +} +void SetValue(const std::string key, const void* value) +{ + SetValue(key, std::to_string((size_t)value)); +} +void SetValue(const std::string key, int value) +{ + SetValue(key, std::to_string(value)); +} +void SetValue(const std::string key, long long value) +{ + SetValue(key, std::to_string(value)); +} + +// Custom setter for emulation accessible. +void SetLoad(const std::string value) +{ + SetValue(cli_config::load, value); +} + +void SetSID(long long value) +{ + // If sid key exist, then do not replace old or new one. + if (!hasKey(cli_config::sid)) { + SetValue(cli_config::sid, value); + } +} + +long long GetSessionID() +{ + long long sessionID = 0; + + // Check if previous session ID had been set then use it. + if (!GetValue(cli_config::sid, &sessionID)) { + sessionID = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + // From now and the future will continue to use the same sessionID until all processes end. + SetSID(sessionID); + } + + return sessionID; +} + +} diff --git a/src/common/util/cliConfig.hpp b/src/common/util/cliConfig.hpp new file mode 100644 index 000000000..51a5b080c --- /dev/null +++ b/src/common/util/cliConfig.hpp @@ -0,0 +1,56 @@ +// ****************************************************************** +// * +// * 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, Boston, MA 02111-1307, USA. +// * +// * (c) 2019 RadWolfie +// * +// * All rights reserved +// * +// ****************************************************************** +#pragma once + +#include + +// Command Line Interface functions +// NOTE - Reason only provide functions is to prevent misuse "exec" + +namespace cli_config { + +static constexpr char exec[] = "exec"; +static constexpr char arg1[] = "arg1"; +static constexpr char load[] = "load"; +static constexpr char hwnd[] = "hwnd"; +static constexpr char debug_mode[] = "dm"; +static constexpr char debug_file[] = "df"; +static constexpr char sid[] = "sid"; + +bool GenConfig(char** argv, int argc); +size_t ConfigSize(); +bool GenCMD(std::string& cmd_line_out); + +// Generic check if key exist +bool hasKey(const std::string key); +// Generic getter +bool GetValue(const std::string key, std::string* value); + +long long GetSessionID(); + +// Change xbe path to launch. +void SetLoad(const std::string value); + +} diff --git a/src/common/util/cliConverter.cpp b/src/common/util/cliConverter.cpp new file mode 100644 index 000000000..4ecfd8900 --- /dev/null +++ b/src/common/util/cliConverter.cpp @@ -0,0 +1,152 @@ +// ****************************************************************** +// * +// * 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, Boston, MA 02111-1307, USA. +// * +// * (c) 2019 RadWolfie +// * +// * All rights reserved +// * +// ****************************************************************** + +#include "CxbxUtil.h" // for StripQuotes +#include "cliConverter.hpp" +#include "cliConfig.hpp" + +typedef std::unordered_map unordered_map_strings; + +constexpr char str_quote[] = "\""; +constexpr char str_quote_space[] = "\" "; +constexpr char str_space_quote[] = " \""; +constexpr char str_space[] = " "; +constexpr char str_slash_forward[] = "/"; + +std::string cliMapPairsToString(std::unordered_map& map_pairs_out) +{ + std::string to_string; + + unordered_map_strings::iterator i = map_pairs_out.begin(); + + // If map pairs are empty, return empty string. + // Or "exec" is not in first iterator by requirement, then return empty string. + if (i == map_pairs_out.end() || i->first.compare(cli_config::exec)) { + return to_string; + } + + to_string += str_quote + i->second + str_quote_space; + + i++; + + for (i; i != map_pairs_out.end();) { + // If argument 1 was input, ignore it since key is reserved from user first input. + if (!i->first.compare(cli_config::arg1)) { + i++; + continue; + } + // If argument has space inside, return as empty. + size_t found = i->first.find(str_space); + if (found != std::string::npos) { + return std::string(); + } + // If argument has quote inside, return as empty. + found = i->first.find(str_quote); + if (found != std::string::npos) { + return std::string(); + } + to_string += str_slash_forward + i->first; + + if (i->second.length() != 0) { + found = i->second.find(str_space); + // If found space inside, then escape with quote. + if (found != std::string::npos) { + // If argument has quote inside, return as empty. + found = i->second.find(str_quote); + if (found != std::string::npos) { + return std::string(); + } + + to_string += str_space_quote + i->second + str_quote; + } + else { + to_string += str_space + i->second; + } + } + + // If there are more, then add space + i++; + if (i != map_pairs_out.end()) { + to_string += str_space; + } + else { + break; + } + } + + return to_string; +} + +std::unordered_map cliToMapPairs(char** argv, int argc) +{ + unordered_map_strings map_pairs; + + // Always set first since first argument is the path to executable file. + map_pairs[cli_config::exec] = argv[0]; + + for (int i = 1; i < argc; i++) { + // Check for forward slash to trigger pair bind. + std::string first = StripQuotes(argv[i]); + if (first.at(0) == str_slash_forward[0]) { + first = first.substr(1); + // Do not allow overwrite argv[0]. + if (!first.compare(cli_config::exec)) { + continue; + } + // And do not allow overwrite argv[1]. + if (!first.compare(cli_config::arg1)) { + continue; + } + + // If the count is at maximum, then simply do empty pair. + if (i + 1 == argc) { + map_pairs[first] = ""; + continue; + } + // Check for forward slash to bind pair. + else if (i + 1 < argc) { + std::string second = StripQuotes(argv[i + 1]); + // If next arg has a slash, then do a empty pair. + if (second.at(0) == str_slash_forward[0]) { + map_pairs[first] = ""; + } + // Otherwise, do the input pair. + else { + map_pairs[first] = second; + i++; + } + continue; + } + } + // Check if 1st argument exist then allow forward it. + else if (i == 1) { + map_pairs[cli_config::arg1] = first; + continue; + } + // Otherwise, let's mark as invalid input + return unordered_map_strings(); + } + return map_pairs; +} diff --git a/src/common/util/cliConverter.hpp b/src/common/util/cliConverter.hpp new file mode 100644 index 000000000..025338849 --- /dev/null +++ b/src/common/util/cliConverter.hpp @@ -0,0 +1,32 @@ +// ****************************************************************** +// * +// * 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, Boston, MA 02111-1307, USA. +// * +// * (c) 2019 RadWolfie +// * +// * All rights reserved +// * +// ****************************************************************** +#pragma once + +#include +#include + +std::string cliMapPairsToString(std::unordered_map& array_map_out); + +std::unordered_map cliToMapPairs(char** argv, int argc); diff --git a/src/common/win32/EmuShared.cpp b/src/common/win32/EmuShared.cpp index 53ca21198..cca37b254 100644 --- a/src/common/win32/EmuShared.cpp +++ b/src/common/win32/EmuShared.cpp @@ -48,7 +48,7 @@ HMODULE hActiveModule = NULL; // ****************************************************************** // * func: EmuShared::EmuSharedInit // ****************************************************************** -bool EmuShared::Init(DWORD guiProcessID) +bool EmuShared::Init(long long sessionID) { // ****************************************************************** // * Ensure initialization only occurs once @@ -58,15 +58,22 @@ bool EmuShared::Init(DWORD guiProcessID) // ****************************************************************** // * Prevent multiple initializations // ****************************************************************** - if(hMapObject != NULL) + if (hMapObject != NULL) { return true; + } + + // ****************************************************************** + // * Prevent invalid session process id + // ****************************************************************** + if (sessionID == 0) { + return false; + } // ****************************************************************** // * Create the shared memory "file" // ****************************************************************** { - // NOTE: guiProcessID support is not available due to 2+ emulation is causing problem with graphic screen. - std::string emuSharedStr = "Local\\EmuShared-s" + std::to_string(settings_version);// +"-p" + std::to_string(guiProcessID); + std::string emuSharedStr = "Local\\EmuShared-s" + std::to_string(sessionID); hMapObject = CreateFileMapping ( INVALID_HANDLE_VALUE, // Paging file @@ -97,8 +104,10 @@ bool EmuShared::Init(DWORD guiProcessID) 0 // default: map entire file ); - if(g_EmuShared == nullptr) + if (g_EmuShared == nullptr) { + CloseHandle(hMapObject); return false; // CxbxKrnlCleanupEx(CXBXR_MODULE::INIT, "Could not map view of shared memory!"); + } } // ****************************************************************** @@ -109,6 +118,12 @@ bool EmuShared::Init(DWORD guiProcessID) } g_EmuShared->m_RefCount++; + + if (g_EmuShared->m_size != sizeof(EmuShared)) { + EmuShared::Cleanup(); + return false; + } + return true; } @@ -131,6 +146,7 @@ void EmuShared::Cleanup() // ****************************************************************** EmuShared::EmuShared() { + m_size = sizeof(EmuShared); // m_bMultiXbe = false; // m_LaunchDataPAddress = NULL; m_bDebugging = false; diff --git a/src/common/win32/EmuShared.h b/src/common/win32/EmuShared.h index e4eed7ddf..9503e4c11 100644 --- a/src/common/win32/EmuShared.h +++ b/src/common/win32/EmuShared.h @@ -60,10 +60,15 @@ class EmuShared : public Mutex public: int m_RefCount; + // ****************************************************************** + // * Fixed memory allocation size + // ****************************************************************** + unsigned int m_size; + // ****************************************************************** // * Each process needs to call this to initialize shared memory // ****************************************************************** - static bool Init(DWORD guiProcessID); + static bool Init(long long sessionID); // ****************************************************************** // * Each process needs to call this to cleanup shared memory diff --git a/src/common/win32/InlineFunc.cpp b/src/common/win32/InlineFunc.cpp index bcbaf9885..ff870a329 100644 --- a/src/common/win32/InlineFunc.cpp +++ b/src/common/win32/InlineFunc.cpp @@ -28,6 +28,7 @@ #if defined(_WIN32) || defined(WIN32) #include "core\kernel\init\CxbxKrnl.h" +#include "common/util/cliConfig.hpp" // Source: https://stackoverflow.com/questions/8046097/how-to-check-if-a-process-has-the-administrative-rights bool CxbxIsElevated() { @@ -46,15 +47,19 @@ bool CxbxIsElevated() { return fRet; } -bool CxbxExec(std::string &execCommand, HANDLE* hProcess, bool requestHandleProcess) { +bool CxbxExec(bool useDebugger, HANDLE* hProcess, bool requestHandleProcess) { STARTUPINFO startupInfo = { 0 }; PROCESS_INFORMATION processInfo = { 0 }; - size_t szSize = execCommand.size(); - char* szArgsBufferOutput = new char[szSize + 1]; - - strncpy(szArgsBufferOutput, execCommand.c_str(), szSize); - szArgsBufferOutput[szSize] = '\0'; + std::string szProcArgsBuffer; + if (!cli_config::GenCMD(szProcArgsBuffer)) { + return false; + } + + // TODO: Set a configuration variable for this. For now it will be within the same folder as Cxbx.exe + if (useDebugger) { + szProcArgsBuffer = "cxbxr-debugger.exe " + szProcArgsBuffer; + } /* NOTE: CreateProcess's 2nd parameter (lpCommandLine) is char*, not const char*. Plus it has ability to change the input buffer data. Source: https://msdn.microsoft.com/en-us/library/ms682425.aspx @@ -63,11 +68,9 @@ bool CxbxExec(std::string &execCommand, HANDLE* hProcess, bool requestHandleProc Plus ShellExecute is high level whilst CreateProcess is low level. We want to use official low level functions as possible to reduce cpu load cycles to get the task done. */ - if (CreateProcess(nullptr, szArgsBufferOutput, nullptr, nullptr, false, 0, nullptr, nullptr, &startupInfo, &processInfo) == 0) { - delete[] szArgsBufferOutput; + if (CreateProcess(nullptr, const_cast(szProcArgsBuffer.c_str()), nullptr, nullptr, false, 0, nullptr, nullptr, &startupInfo, &processInfo) == 0) { return 0; } - delete[] szArgsBufferOutput; CloseHandle(processInfo.hThread); if (requestHandleProcess) { diff --git a/src/core/kernel/exports/EmuKrnlHal.cpp b/src/core/kernel/exports/EmuKrnlHal.cpp index af1cd94f6..0dfbc9e1f 100644 --- a/src/core/kernel/exports/EmuKrnlHal.cpp +++ b/src/core/kernel/exports/EmuKrnlHal.cpp @@ -38,7 +38,7 @@ namespace xboxkrnl #include "Logging.h" // For LOG_FUNC() #include "EmuKrnl.h" // For InitializeListHead(), etc. #include "EmuKrnlLogging.h" -#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlCleanup, CxbxConvertArgToString, and CxbxExec +#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlCleanup, and CxbxExec #include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, ) #include "EmuKrnl.h" #include "devices\x86\EmuX86.h" // HalReadWritePciSpace needs this @@ -47,8 +47,9 @@ namespace xboxkrnl #include "common\EmuEEPROM.h" // For EEPROM #include "devices\Xbox.h" // For g_SMBus, SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER #include "devices\SMCDevice.h" // For SMC_COMMAND_SCRATCH -#include "common/util/strConverter.hpp" // for utf16_to_ascii +#include "common/util/strConverter.hpp" // for utf16_to_ascii #include "core\kernel\memory-manager\VMManager.h" +#include "common/util/cliConfig.hpp" #include // for std::replace #include @@ -572,10 +573,8 @@ XBSYSAPI EXPORTNUM(49) xboxkrnl::VOID DECLSPEC_NORETURN NTAPI xboxkrnl::HalRetur // Some titles (Xbox Dashboard and retail/demo discs) use ";" as a current directory path seperator // This process is handled during initialization. No speical handling here required. - std::string szProcArgsBuffer; - CxbxConvertArgToString(szProcArgsBuffer, szFilePath_CxbxReloaded_Exe, XbePath.c_str(), CxbxKrnl_hEmuParent, CxbxKrnl_DebugMode, CxbxKrnl_DebugFileName.c_str()); - - if (!CxbxExec(szProcArgsBuffer, nullptr, false)) { + cli_config::SetLoad(XbePath); + if (!CxbxExec(false, nullptr, false)) { CxbxKrnlCleanup("Could not launch %s", XbePath.c_str()); } } @@ -593,10 +592,8 @@ XBSYSAPI EXPORTNUM(49) xboxkrnl::VOID DECLSPEC_NORETURN NTAPI xboxkrnl::HalRetur g_VMManager.SavePersistentMemory(); - std::string szProcArgsBuffer; - CxbxConvertArgToString(szProcArgsBuffer, szFilePath_CxbxReloaded_Exe, szFilePath_Xbe, CxbxKrnl_hEmuParent, CxbxKrnl_DebugMode, CxbxKrnl_DebugFileName.c_str()); - - if (!CxbxExec(szProcArgsBuffer, nullptr, false)) { + cli_config::SetLoad(szFilePath_Xbe); + if (!CxbxExec(false, nullptr, false)) { CxbxKrnlCleanup("Could not launch %s", szFilePath_Xbe); } break; diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 839c5670b..718bc192f 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -52,6 +52,8 @@ namespace xboxkrnl #include "ReservedMemory.h" // For virtual_memory_placeholder #include "core\kernel\memory-manager\VMManager.h" #include "CxbxDebugger.h" +#include "common/util/cliConfig.hpp" +#include "common/util/xxhash.h" #include #include @@ -679,33 +681,6 @@ void ImportLibraries(XbeImportEntry *pImportDirectory) } } -bool CheckLoadArgument(int argc, char* argv[], DWORD *pguiProcessID) -{ - bool bHasLoadArgument; - - if (argc >= 2 && std::strcmp(argv[1], "/load") == 0 && std::strlen(argv[2]) > 0) { - HWND hWnd = nullptr; - bHasLoadArgument = true; - // Perform check if command line contain gui's hWnd value. - if (argc > 3) { - hWnd = (HWND)std::stoi(argv[3], nullptr, 10); - hWnd = IsWindow(hWnd) ? hWnd : nullptr; - if (hWnd != nullptr) { - // We don't need thread ID from window handle. - GetWindowThreadProcessId(hWnd, pguiProcessID); - } - } - } - else { - bHasLoadArgument = false; - *pguiProcessID = GetCurrentProcessId(); - } - - g_exec_filepath = argv[0]; // NOTE: Workaround solution until simulated "main" function is made. - - return bHasLoadArgument; -} - bool CreateSettings() { g_Settings = new Settings(); @@ -749,8 +724,10 @@ bool HandleFirstLaunch() return true; } -void CxbxKrnlMain(int argc, char* argv[], uint32_t blocks_reserved[384]) +void CxbxKrnlEmulate(uint32_t blocks_reserved[384]) { + std::string tempStr; + // NOTE: This is designated for standalone kernel mode launch without GUI if (g_Settings != nullptr) { @@ -775,29 +752,39 @@ void CxbxKrnlMain(int argc, char* argv[], uint32_t blocks_reserved[384]) /* Initialize Cxbx File Paths */ CxbxInitFilePaths(); + /* Must be called after CxbxInitFilePaths */ + if (!CxbxLockFilePath()) { + return; + } + // Skip '/load' switch // Get XBE Name : - std::string xbePath = std::filesystem::absolute(std::filesystem::path(argv[2])).string(); + std::string xbePath; + cli_config::GetValue(cli_config::load, &xbePath); + xbePath = std::filesystem::absolute(std::filesystem::path(xbePath)).string(); // Get DCHandle : - HWND hWnd = 0; - if (argc > 3) { - hWnd = (HWND)std::atoi(argv[3]); + // We must save this handle now to keep the child window working in the case we need to display the UEM + HWND hWnd = nullptr; + if (cli_config::GetValue(cli_config::hwnd, &tempStr)) { + hWnd = (HWND)std::atoi(tempStr.c_str()); } + CxbxKrnl_hEmuParent = IsWindow(hWnd) ? hWnd : nullptr; // Get KernelDebugMode : DebugMode DbgMode = DebugMode::DM_NONE; - if (argc > 4) { - DbgMode = (DebugMode)std::atoi(argv[4]); + if (cli_config::GetValue(cli_config::debug_mode, &tempStr)) { + DbgMode = (DebugMode)std::atoi(tempStr.c_str()); } // Get KernelDebugFileName : std::string DebugFileName = ""; - if (argc > 5) { - DebugFileName = argv[5]; + if (cli_config::GetValue(cli_config::debug_file, &tempStr)) { + DebugFileName = tempStr; } int BootFlags; + FILE* krnlLog = nullptr; g_EmuShared->GetBootFlags(&BootFlags); // debug console allocation (if configured) @@ -811,8 +798,8 @@ void CxbxKrnlMain(int argc, char* argv[], uint32_t blocks_reserved[384]) GetConsoleScreenBufferInfo(StdHandle, &coninfo); coninfo.dwSize.Y = SHRT_MAX - 1; // = 32767-1 = 32766 = maximum value that works SetConsoleScreenBufferSize(StdHandle, coninfo.dwSize); - freopen("CONOUT$", "wt", stdout); - freopen("CONIN$", "rt", stdin); + (void)freopen("CONOUT$", "wt", stdout); + (void)freopen("CONIN$", "rt", stdin); SetConsoleTitle("Cxbx-Reloaded : Kernel Debug Console"); SetConsoleTextAttribute(StdHandle, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); } @@ -822,7 +809,7 @@ void CxbxKrnlMain(int argc, char* argv[], uint32_t blocks_reserved[384]) FreeConsole(); if (DbgMode == DM_FILE) { // Peform clean write to kernel log for first boot. Unless multi-xbe boot occur then perform append to existing log. - freopen(DebugFileName.c_str(), ((BootFlags == DebugMode::DM_NONE) ? "wt" : "at"), stdout); + krnlLog = freopen(DebugFileName.c_str(), ((BootFlags == DebugMode::DM_NONE) ? "wt" : "at"), stdout); // Append separator for better readability after reboot. if (BootFlags != DebugMode::DM_NONE) { std::cout << "\n------REBOOT------REBOOT------REBOOT------REBOOT------REBOOT------\n" << std::endl; @@ -831,13 +818,10 @@ void CxbxKrnlMain(int argc, char* argv[], uint32_t blocks_reserved[384]) else { char buffer[16]; if (GetConsoleTitle(buffer, 16) != NULL) - freopen("nul", "w", stdout); + (void)freopen("nul", "w", stdout); } } - // We must save this handle now to keep the child window working in the case we need to display the UEM - CxbxKrnl_hEmuParent = IsWindow(hWnd) ? hWnd : NULL; - g_CurrentProcessHandle = GetCurrentProcess(); // OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); // Set up the logging variables for the kernel process during initialization. @@ -891,7 +875,7 @@ void CxbxKrnlMain(int argc, char* argv[], uint32_t blocks_reserved[384]) } } - if (dwExitCode != EXIT_SUCCESS) {// StopEmulation + if (dwExitCode != EXIT_SUCCESS) {// Stop emulation CxbxKrnlShutDown(); } @@ -1202,6 +1186,10 @@ void CxbxKrnlMain(int argc, char* argv[], uint32_t blocks_reserved[384]) BootFlags ); } + + if (!krnlLog) { + (void)fclose(krnlLog); + } } #pragma optimize("", on) @@ -1559,13 +1547,21 @@ __declspec(noreturn) void CxbxKrnlInit EmuLogInit(LOG_LEVEL::DEBUG, "XBE entry point returned"); fflush(stdout); + CxbxUnlockFilePath(); + // EmuShared::Cleanup(); FIXME: commenting this line is a bad workaround for issue #617 (https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/617) CxbxKrnlTerminateThread(); } void CxbxInitFilePaths() { - g_EmuShared->GetStorageLocation(szFolder_CxbxReloadedData); + if (g_Settings) { + std::string dataLoc = g_Settings->GetDataLocation(); + std::strncpy(szFolder_CxbxReloadedData, dataLoc.c_str(), dataLoc.length() + 1); + } + else { + g_EmuShared->GetStorageLocation(szFolder_CxbxReloadedData); + } // Make sure our data folder exists : bool result = std::filesystem::exists(szFolder_CxbxReloadedData); @@ -1585,6 +1581,50 @@ void CxbxInitFilePaths() GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH); } +HANDLE hMapDataHash = nullptr; + +bool CxbxLockFilePath() +{ + std::stringstream filePathHash("Local\\"); + uint64_t hashValue = XXH3_64bits(szFolder_CxbxReloadedData, strlen(szFolder_CxbxReloadedData) + 1); + if (!hashValue) { + CxbxKrnlCleanup("%s : Couldn't generate Cxbx-Reloaded's data folder hash!", __func__); + } + + filePathHash << std::hex << hashValue; + + hMapDataHash = CreateFileMapping + ( + INVALID_HANDLE_VALUE, // Paging file + nullptr, // default security attributes + PAGE_READONLY, // readonly access + 0, // size: high 32 bits + /*Dummy size*/4, // size: low 32 bits + filePathHash.str().c_str() // name of map object + ); + + if (hMapDataHash == nullptr) { + return false; + } + + if (GetLastError() == ERROR_ALREADY_EXISTS) { + CxbxShowError("Data path directory is currently in used.\nUse different data path directory or stop emulation from another process."); + CloseHandle(hMapDataHash); + return false; + } + + return true; +} + +void CxbxUnlockFilePath() +{ + // Close opened file path lockdown shared memory. + if (hMapDataHash) { + CloseHandle(hMapDataHash); + hMapDataHash = nullptr; + } +} + // REMARK: the following is useless, but PatrickvL has asked to keep it for documentation purposes /*xboxkrnl::LAUNCH_DATA_PAGE DefaultLaunchDataPage = { @@ -1730,8 +1770,11 @@ void CxbxKrnlShutDown() // Shutdown the input device manager g_InputDeviceManager.Shutdown(); - if (CxbxKrnl_hEmuParent != NULL) + CxbxUnlockFilePath(); + + if (CxbxKrnl_hEmuParent != NULL) { SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, WM_DESTROY, 0); + } EmuShared::Cleanup(); TerminateProcess(g_CurrentProcessHandle, 0); @@ -1828,21 +1871,6 @@ void CxbxKrnlPanic() CxbxKrnlCleanup("Kernel Panic!"); } -void CxbxConvertArgToString(std::string &dest, const char* krnlExe, const char* xbeFile, HWND hwndParent, DebugMode krnlDebug, const char* krnlDebugFile) { - - std::stringstream szArgsStream; - - // The format is: "krnlExe" /load "xbeFile" hwndParent krnlDebug "krnlDebugFile" - szArgsStream << - "\"" << krnlExe << "\"" - " /load \"" << xbeFile << "\"" - " " << std::dec << (int)hwndParent << - " " << std::dec << (int)krnlDebug << - " \"" << krnlDebugFile << "\""; - - dest = szArgsStream.str(); -} - static clock_t g_DeltaTime = 0; // Used for benchmarking/fps count static unsigned int g_Frames = 0; diff --git a/src/core/kernel/init/CxbxKrnl.h b/src/core/kernel/init/CxbxKrnl.h index d84f977bd..5d4533336 100644 --- a/src/core/kernel/init/CxbxKrnl.h +++ b/src/core/kernel/init/CxbxKrnl.h @@ -231,15 +231,13 @@ extern Xbe::Certificate *g_pCertificate; bool CxbxKrnlVerifyVersion(const char *szVersion); extern bool g_bIsDebugKernel; - -bool CheckLoadArgument(int argc, char* argv[], DWORD *pguiProcessID); bool CreateSettings(); bool HandleFirstLaunch(); /*! Cxbx Kernel Entry Point */ -void CxbxKrnlMain(int argc, char* argv[], uint32_t blocks_reserved[384]); +void CxbxKrnlEmulate(uint32_t blocks_reserved[384]); /*! initialize emulation */ __declspec(noreturn) void CxbxKrnlInit(void *pTLSData, Xbe::TLS *pTLS, Xbe::LibraryVersion *LibraryVersion, DebugMode DbgMode, const char *szDebugFilename, Xbe::Header *XbeHeader, uint32_t XbeHeaderSize, void (*Entry)(), int BootFlags); @@ -279,11 +277,12 @@ void CxbxKrnlNoFunc(); void CxbxInitPerformanceCounters(); // Implemented in EmuKrnlKe.cpp void CxbxInitFilePaths(); + +// For emulation usage only +bool CxbxLockFilePath(); +void CxbxUnlockFilePath(); -/*! Generate a standard arg format string */ -void CxbxConvertArgToString(std::string &dest, const char* krnlExe, const char* xbeFile, HWND hwndParent, DebugMode krnlDebug, const char* krnlDebugFile); - -bool CxbxExec(std::string &execCommand, HANDLE* hProcess, bool requestHandleProcess); +bool CxbxExec(bool useDebugger, HANDLE* hProcess, bool requestHandleProcess); bool CxbxIsElevated(); diff --git a/src/emulator/cxbxr-emu.cpp b/src/emulator/cxbxr-emu.cpp index 6a028bc2d..12303a1df 100644 --- a/src/emulator/cxbxr-emu.cpp +++ b/src/emulator/cxbxr-emu.cpp @@ -33,6 +33,8 @@ #include "EmuShared.h" #include "core\kernel\init\CxbxKrnl.h" // For HandleFirstLaunch() and LaunchEmulation() //#include +#include "common/util/cliConverter.hpp" +#include "common/util/cliConfig.hpp" PCHAR* CommandLineToArgvA( @@ -143,17 +145,22 @@ DWORD WINAPI Emulate(int system, uint32_t blocks_reserved[384]) return EXIT_FAILURE; } - DWORD guiProcessID = 0; - bool bHasLoadArgument = CheckLoadArgument(argc, argv, &guiProcessID); - if (!bHasLoadArgument) { - CxbxShowError("No /load argument on command line!"); + if (!cli_config::GenConfig(argv, argc)) { + CxbxShowError("Couldn't convert parsed command line!"); + LocalFree(argv); + return EXIT_FAILURE; + } + LocalFree(argv); + + /*! verify load argument is included */ + if (!cli_config::hasKey("load")) { + CxbxShowError("No /load argument in command line!"); return EXIT_FAILURE; } /*! initialize shared memory */ - if (!EmuShared::Init(guiProcessID)) { + if (!EmuShared::Init(cli_config::GetSessionID())) { CxbxShowError("Could not map shared memory!"); - LocalFree(argv); return EXIT_FAILURE; } @@ -163,9 +170,7 @@ DWORD WINAPI Emulate(int system, uint32_t blocks_reserved[384]) return EXIT_FAILURE; } - CxbxKrnlMain(argc, argv, blocks_reserved); - - LocalFree(argv); + CxbxKrnlEmulate(blocks_reserved); /*! cleanup shared memory */ EmuShared::Cleanup(); diff --git a/src/gui/WinMain.cpp b/src/gui/WinMain.cpp index b2f73d703..cb6605cde 100644 --- a/src/gui/WinMain.cpp +++ b/src/gui/WinMain.cpp @@ -34,6 +34,8 @@ #include "EmuShared.h" #include "common\Settings.hpp" #include +#include "common/util/cliConverter.hpp" +#include "common/util/cliConfig.hpp" // Enable Visual Styles @@ -45,6 +47,7 @@ processorArchitecture = '*' publicKeyToken = '6595b64144ccf1df' language = '*'\" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { hActiveModule = hInstance; // == GetModuleHandle(NULL); // Points to GUI (Cxbx.exe) ImageBase + std::string tempStr; // First detect if we are running on WoW64, if not, prevent Cxbx-Reloaded from starting // Cxbx-Reloaded needs access to high memory, only exposed to WoW64. @@ -61,12 +64,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine } #endif - DWORD guiProcessID = 0; - // TODO: Convert ALL __argc & __argv to use main(int argc, char** argv) method. - bool bHasLoadArgument = CheckLoadArgument(__argc, __argv, &guiProcessID); + if (!cli_config::GenConfig(__argv, __argc)) { + CxbxShowError("Couldn't convert parsed command line!"); + return EXIT_FAILURE; + } /*! initialize shared memory */ - if (!EmuShared::Init(guiProcessID)) { + if (!EmuShared::Init(cli_config::GetSessionID())) { CxbxShowError("Could not map shared memory!"); return EXIT_FAILURE; } @@ -76,18 +80,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine return EXIT_FAILURE; } - if (bHasLoadArgument) { + if (cli_config::hasKey("load")) { #ifndef CXBX_LOADER - CxbxKrnlMain(__argc, __argv, nullptr); + CxbxKrnlEmulate(nullptr); EmuShared::Cleanup(); return EXIT_SUCCESS; #else - std::string szProcArgsBuffer; - for (int i = 0; i < __argc; i++) { - szProcArgsBuffer.append(__argv[i]); - } - - if (!CxbxExec(szProcArgsBuffer, nullptr, false)) { + if (!CxbxExec(false, nullptr, false)) { CxbxShowError("Could not launch Cxbx-R loader!"); EmuShared::Cleanup(); return EXIT_FAILURE; @@ -124,9 +123,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine } /*! optionally open xbe and start emulation, if command line parameter was specified */ - if(__argc > 1 && false == MainWindow->HasError()) + if(cli_config::ConfigSize() > 1 && false == MainWindow->HasError() && cli_config::GetValue(cli_config::arg1, &tempStr)) { - MainWindow->OpenXbe(std::filesystem::absolute(std::filesystem::path(__argv[1])).string().c_str()); + tempStr = std::filesystem::absolute(std::filesystem::path(tempStr)).string(); + MainWindow->OpenXbe(tempStr.c_str()); MainWindow->StartEmulation(MainWindow->GetHwnd()); } diff --git a/src/gui/WndMain.cpp b/src/gui/WndMain.cpp index 88ed4b5df..59c993e80 100644 --- a/src/gui/WndMain.cpp +++ b/src/gui/WndMain.cpp @@ -43,8 +43,9 @@ #include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For CxbxSetPixelContainerHeader #include "core\hle\D3D8\XbConvert.h" // For EmuPC2XB_D3DFormat #include "common\Settings.hpp" +#include "common/util/cliConfig.hpp" -#include "core\kernel\init\CxbxKrnl.h" // For CxbxConvertArgToString and CxbxExec +#include "core\kernel\init\CxbxKrnl.h" // For CxbxExec #include "resource/ResCxbx.h" #include "CxbxVersion.h" #include "Shlwapi.h" @@ -2214,6 +2215,13 @@ void WndMain::SaveXbeAs() SaveXbe(ofn.lpstrFile); } +// Only grant access to GUI end. +namespace cli_config { +extern void SetValue(const std::string key, const std::string value); +extern void SetValue(const std::string key, const char* value); +extern void SetValue(const std::string key, const void* value); +extern void SetValue(const std::string key, int value); +} // start emulation void WndMain::StartEmulation(HWND hwndParent, DebuggerState LocalDebuggerState /*= debuggerOff*/) { @@ -2268,8 +2276,19 @@ void WndMain::StartEmulation(HWND hwndParent, DebuggerState LocalDebuggerState / bool AttachLocalDebugger = (LocalDebuggerState == debuggerOn); g_EmuShared->SetDebuggingFlag(&AttachLocalDebugger); - std::string szProcArgsBuffer; - CxbxConvertArgToString(szProcArgsBuffer, szExeFileName, m_XbeFilename, hwndParent, g_Settings->m_core.KrnlDebugMode, g_Settings->m_core.szKrnlDebug); + /* Main process to generate emulation command line begin. */ + // If we are adding more arguments, this is the place to do so. + cli_config::SetValue(cli_config::exec, szExeFileName); + cli_config::SetLoad(m_XbeFilename); + cli_config::SetValue(cli_config::hwnd, hwndParent); + cli_config::SetValue(cli_config::debug_mode, g_Settings->m_core.KrnlDebugMode); + if (g_Settings->m_core.KrnlDebugMode == DM_FILE) { + cli_config::SetValue(cli_config::debug_file, g_Settings->m_core.szKrnlDebug); + } + else { + cli_config::SetValue(cli_config::debug_file, ""); + } + /* Main process to generate emulation command line end. */ UnmapPersistedMemory(); @@ -2278,10 +2297,7 @@ void WndMain::StartEmulation(HWND hwndParent, DebuggerState LocalDebuggerState / // Check then close existing debugger monitor. DebuggerMonitorClose(); - // TODO: Set a configuration variable for this. For now it will be within the same folder as Cxbx.exe - std::string szProcDbgArgsBuffer = "cxbxr-debugger.exe " + szProcArgsBuffer; - - if (!CxbxExec(szProcDbgArgsBuffer, &m_hDebuggerProc, true)) { + if (!CxbxExec(true, &m_hDebuggerProc, true)) { MessageBox(m_hwnd, "Failed to start emulation with the debugger.\n\nYou will need to build CxbxDebugger manually.", "Cxbx-Reloaded", MB_ICONSTOP | MB_OK); printf("WndMain: %s debugger shell failed.\n", m_Xbe->m_szAsciiTitle); @@ -2294,7 +2310,7 @@ void WndMain::StartEmulation(HWND hwndParent, DebuggerState LocalDebuggerState / } else { - if (!CxbxExec(szProcArgsBuffer, nullptr, false)) { + if (!CxbxExec(false, nullptr, false)) { MessageBox(m_hwnd, "Emulation failed.\n\n If this message repeats, the Xbe is not supported.", "Cxbx-Reloaded", MB_ICONSTOP | MB_OK); printf("WndMain: %s shell failed.\n", m_Xbe->m_szAsciiTitle);