use std map pairs instead of string for cli usage

Plus enable individual memory shared settings and lock data file path hash whenever in used.
This commit is contained in:
RadWolfie 2019-10-10 15:20:46 -05:00
parent 11c046e0aa
commit 0f38209e4b
18 changed files with 710 additions and 180 deletions

View File

@ -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"

View File

@ -33,6 +33,8 @@
#include <filesystem>
#include "common\input\InputManager.h"
#include "common\input\layout_xbox_controller.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
@ -45,8 +47,6 @@ static_assert(false, "Please implement support for cross-platform's user profile
#include <QStandardPaths> // 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) {

View File

@ -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 {

View File

@ -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, "\"");
}

View File

@ -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 <typename E>

View File

@ -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 <chrono>
#include <unordered_map>
#include "cliConverter.hpp"
#include "cliConfig.hpp"
std::unordered_map<std::string, std::string> 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;
}
}

View File

@ -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 <string>
// 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);
}

View File

@ -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<std::string, std::string> 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<std::string, std::string>& 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<std::string, std::string> 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;
}

View File

@ -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 <string>
#include <unordered_map>
std::string cliMapPairsToString(std::unordered_map<std::string, std::string>& array_map_out);
std::unordered_map<std::string, std::string> cliToMapPairs(char** argv, int argc);

View File

@ -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;

View File

@ -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

View File

@ -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<LPSTR>(szProcArgsBuffer.c_str()), nullptr, nullptr, false, 0, nullptr, nullptr, &startupInfo, &processInfo) == 0) {
return 0;
}
delete[] szArgsBufferOutput;
CloseHandle(processInfo.hThread);
if (requestHandleProcess) {

View File

@ -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 <algorithm> // for std::replace
#include <locale>
@ -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;

View File

@ -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 <clocale>
#include <process.h>
@ -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;

View File

@ -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();

View File

@ -33,6 +33,8 @@
#include "EmuShared.h"
#include "core\kernel\init\CxbxKrnl.h" // For HandleFirstLaunch() and LaunchEmulation()
//#include <commctrl.h>
#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();

View File

@ -34,6 +34,8 @@
#include "EmuShared.h"
#include "common\Settings.hpp"
#include <commctrl.h>
#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());
}

View File

@ -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);