Added proper emulator launch function for Windows and added logic for the new %EMUPATH% variable.

This commit is contained in:
Leon Styhre 2020-07-07 21:25:15 +02:00
parent 76aa239855
commit 2bea3021c3
8 changed files with 187 additions and 15 deletions

View file

@ -22,6 +22,7 @@
#include "SystemData.h" #include "SystemData.h"
#include "VolumeControl.h" #include "VolumeControl.h"
#include "Window.h" #include "Window.h"
#include <assert.h> #include <assert.h>
FileData::FileData( FileData::FileData(
@ -452,18 +453,84 @@ void FileData::launchGame(Window* window)
else else
command = mEnvData->mLaunchCommand; command = mEnvData->mLaunchCommand;
std::string commandRaw = command;
const std::string rom = Utils::FileSystem::getEscapedPath(getPath()); const std::string rom = Utils::FileSystem::getEscapedPath(getPath());
const std::string basename = Utils::FileSystem::getStem(getPath()); const std::string basename = Utils::FileSystem::getStem(getPath());
const std::string rom_raw = Utils::FileSystem::getPreferredPath(getPath()); const std::string rom_raw = Utils::FileSystem::getPreferredPath(getPath());
const std::string emupath = Utils::FileSystem::getExePath();
command = Utils::String::replace(command, "%ROM%", rom); command = Utils::String::replace(command, "%ROM%", rom);
command = Utils::String::replace(command, "%BASENAME%", basename); command = Utils::String::replace(command, "%BASENAME%", basename);
command = Utils::String::replace(command, "%ROM_RAW%", rom_raw); command = Utils::String::replace(command, "%ROM_RAW%", rom_raw);
Scripting::fireEvent("game-start", rom, basename); #ifdef _WIN64
std::wstring commandWide = Utils::String::charToWideChar(command);
#endif
LOG(LogInfo) << " " << command; Scripting::fireEvent("game-start", rom, basename);
int exitCode = runSystemCommand(command); int exitCode = 0;
if (command.find("%EMUPATH%") != std::string::npos) {
// Extract the emulator executable from the launch command string. This could either be
// just the program name, assuming the binary is in the PATH variable of the operating
// system, or it could be an absolute path to the emulator. (In the latter case, if
// there is a space in the the path, it needs to be enclosed by quotation marks in
// es_systems.cfg.)
std::string emuExecutable;
// If the first character is a quotation mark, then we need to extract up to the
// next quotation mark, otherwise we'll extract up to the first space character.
if (command.front() == '\"') {
std::string emuTemp = command.substr(1, std::string::npos);
emuExecutable = emuTemp.substr(0, emuTemp.find('"'));
}
else {
emuExecutable = command.substr(0, command.find(' '));
}
// For Windows, we need to handle UTF-16 encoding.
#ifdef _WIN64
std::wstring emuExecutableWide;
std::wstring emuPathWide;
emuExecutableWide = Utils::String::charToWideChar(emuExecutable);
// Search for the emulator using the PATH environmental variable.
DWORD size = SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", 0, nullptr, nullptr);
if (size) {
std::vector<wchar_t> pathBuffer(static_cast<size_t>(size) + 1 );
wchar_t* fileName = nullptr;
SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", size + 1 ,
pathBuffer.data(), &fileName);
std::wstring pathString = pathBuffer.data();
if (pathString.length()) {
emuPathWide = pathString.substr(0, pathString.size() -
std::wstring(fileName).size());
emuPathWide.pop_back();
auto stringPos = commandWide.find(L"%EMUPATH%");
commandWide = commandWide.replace(stringPos, 9, emuPathWide);
}
}
#else
// TODO for Unix.
#endif
}
LOG(LogInfo) << "Raw emulator launch command:";
LOG(LogInfo) << commandRaw;
LOG(LogInfo) << "Expanded emulator launch command:";
#ifdef _WIN64
LOG(LogInfo) << Utils::String::wideCharToChar(commandWide);
exitCode = launchEmulatorWindows(commandWide);
#else
LOG(LogInfo) << command;
exitCode = launchEmulatorUnix(command);
#endif
if (exitCode != 0) { if (exitCode != 0) {
LOG(LogWarning) << "...launch terminated with nonzero exit code " << exitCode << "!"; LOG(LogWarning) << "...launch terminated with nonzero exit code " << exitCode << "!";

View file

@ -235,7 +235,11 @@ bool SystemData::loadConfig()
for (pugi::xml_node system = systemList.child("system"); system; for (pugi::xml_node system = systemList.child("system"); system;
system = system.next_sibling("system")) { system = system.next_sibling("system")) {
std::string name, fullname, path, cmd, themeFolder; std::string name;
std::string fullname;
std::string path;
std::string cmd;
std::string themeFolder;
name = system.child("name").text().get(); name = system.child("name").text().get();
fullname = system.child("fullname").text().get(); fullname = system.child("fullname").text().get();

View file

@ -312,8 +312,8 @@ int VolumeControl::getVolume() const
float floatVolume = 0.0f; // 0-1 float floatVolume = 0.0f; // 0-1
if (endpointVolume->GetMasterVolumeLevelScalar(&floatVolume) == S_OK) { if (endpointVolume->GetMasterVolumeLevelScalar(&floatVolume) == S_OK) {
volume = (int)Math::round(floatVolume * 100.0f); volume = (int)Math::round(floatVolume * 100.0f);
LOG(LogInfo) << " getting volume as " << volume << LOG(LogInfo) << "System audio volume is " << volume <<
" ( from float " << floatVolume << ")"; " (floating point value " << floatVolume << ")";
} }
else { else {
LOG(LogError) << "VolumeControl::getVolume() - Failed to get master volume!"; LOG(LogError) << "VolumeControl::getVolume() - Failed to get master volume!";

View file

@ -5,6 +5,7 @@
// //
#include "Platform.h" #include "Platform.h"
#include "utils/StringUtil.h"
#if defined(__linux__) || defined(_WIN64) #if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL_events.h> #include <SDL2/SDL_events.h>
@ -13,6 +14,7 @@
#endif #endif
#ifdef _WIN64 #ifdef _WIN64
#include <windows.h>
#include <codecvt> #include <codecvt>
#include <locale> #include <locale>
#else #else
@ -42,16 +44,89 @@ int runPoweroffCommand()
int runSystemCommand(const std::string& cmd_utf8) int runSystemCommand(const std::string& cmd_utf8)
{ {
#ifdef _WIN64 #ifdef _WIN64
// On Windows we use _wsystem to support non-ASCII paths // On Windows we use _wsystem to support non-ASCII paths
// which requires converting from UTF8 to a wstring. // which requires converting from UTF-8 to a wstring.
typedef std::codecvt_utf8<wchar_t> convert_type; std::wstring wchar_str = Utils::String::charToWideChar(cmd_utf8);
std::wstring_convert<convert_type, wchar_t> converter;
std::wstring wchar_str = converter.from_bytes(cmd_utf8);
return _wsystem(wchar_str.c_str()); return _wsystem(wchar_str.c_str());
#else #else
return system(cmd_utf8.c_str()); return system(cmd_utf8.c_str());
#endif #endif
}
int runSystemCommand(const std::wstring& cmd_utf16)
{
#ifdef _WIN64
return _wsystem(cmd_utf16.c_str());
#else
return 0;
#endif
}
int launchEmulatorUnix(const std::string& cmd_utf8)
{
#ifdef __unix__
return system(cmd_utf8.c_str());
#else
return 0;
#endif
}
int launchEmulatorWindows(const std::wstring& cmd_utf16)
{
#ifdef _WIN64
STARTUPINFOW si {};
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
bool processReturnValue = true;
DWORD errorCode = 0;
processReturnValue = CreateProcessW(
nullptr, // No application name (use command line).
(wchar_t*) cmd_utf16.c_str(), // Command line.
nullptr, // Process attributes.
nullptr, // Thread attributes.
FALSE, // Handles inheritance.
0, // Creation flags.
nullptr, // Use parent's environment block.
nullptr, // Use parent's starting directory.
&si, // Pointer to the STARTUPINFOW structure.
&pi ); // Pointer to the PROCESS_INFORMATION structure.
// Wait for the child process to exit.
WaitForSingleObject(pi.hThread, INFINITE);
WaitForSingleObject(pi.hProcess, INFINITE);
// If the return value is false, then something failed.
if (!processReturnValue) {
LPWSTR pBuffer = nullptr;
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&pBuffer, 0, nullptr);
errorCode = GetLastError();
std::string errorMessage = Utils::String::wideCharToChar(pBuffer);
// Remove trailing newline from the error message.
if (errorMessage.back() == '\n');
errorMessage.pop_back();
if (errorMessage.back() == '\r');
errorMessage.pop_back();
LOG(LogError) << "Error - launchEmulatorWindows - system error code " <<
errorCode << ": " << errorMessage;
}
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return errorCode;
#else // _WIN64
return 0;
#endif
} }
QuitMode quitMode = QuitMode::QUIT; QuitMode quitMode = QuitMode::QUIT;

View file

@ -21,8 +21,14 @@ enum QuitMode {
POWEROFF = 2 POWEROFF = 2
}; };
// Run UTF-8 encoded in the shell (requires wstring conversion on Windows). // Uses UTF-8 for Unix and does a UTF-16/wstring conversion for Windows.
int runSystemCommand(const std::string& cmd_utf8); int runSystemCommand(const std::string& cmd_utf8);
// Windows specific UTF-16/wstring function. (FOR FUTURE USE)
int runSystemCommand(const std::wstring& cmd_utf16);
int launchEmulatorUnix(const std::string& cmd_utf8);
int launchEmulatorWindows(const std::wstring& cmd_utf16);
int quitES(QuitMode mode = QuitMode::QUIT); int quitES(QuitMode mode = QuitMode::QUIT);
void processQuitMode(); void processQuitMode();

View file

@ -27,7 +27,7 @@ namespace Utils
std::string getCWDPath(); std::string getCWDPath();
void setExePath(const std::string& _path); void setExePath(const std::string& _path);
std::string getExePath(); std::string getExePath();
std::string getProgramDataPath (); std::string getProgramDataPath();
std::string getPreferredPath(const std::string& _path); std::string getPreferredPath(const std::string& _path);
std::string getGenericPath(const std::string& _path); std::string getGenericPath(const std::string& _path);
std::string getEscapedPath(const std::string& _path); std::string getEscapedPath(const std::string& _path);

View file

@ -8,6 +8,8 @@
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
#include <algorithm> #include <algorithm>
#include <codecvt>
#include <locale>
#include <stdarg.h> #include <stdarg.h>
namespace Utils namespace Utils
@ -185,6 +187,22 @@ namespace Utils
return string; return string;
} }
std::wstring charToWideChar(const std::string& _string)
{
typedef std::codecvt_utf8<wchar_t> convert_type;
std::wstring_convert<convert_type, wchar_t> stringConverter;
return stringConverter.from_bytes(_string);
}
std::string wideCharToChar(const std::wstring& _string)
{
typedef std::codecvt_utf8<wchar_t> convert_type;
std::wstring_convert<convert_type, wchar_t> stringConverter;
return stringConverter.to_bytes(_string);
}
bool startsWith(const std::string& _string, const std::string& _start) bool startsWith(const std::string& _string, const std::string& _start)
{ {
return (_string.find(_start) == 0); return (_string.find(_start) == 0);

View file

@ -28,6 +28,8 @@ namespace Utils
std::string trim(const std::string& _string); std::string trim(const std::string& _string);
std::string replace(const std::string& _string, const std::string& _replace, std::string replace(const std::string& _string, const std::string& _replace,
const std::string& _with); const std::string& _with);
std::wstring charToWideChar(const std::string& _string);
std::string wideCharToChar(const std::wstring& _string);
bool startsWith(const std::string& _string, const std::string& _start); bool startsWith(const std::string& _string, const std::string& _start);
bool endsWith(const std::string& _string, const std::string& _end); bool endsWith(const std::string& _string, const std::string& _end);
std::string removeParenthesis(const std::string& _string); std::string removeParenthesis(const std::string& _string);