From 267a47fdbd3c132d3fc541ba7dad1782455d2708 Mon Sep 17 00:00:00 2001 From: Leon Styhre <leon@leonstyhre.com> Date: Sun, 10 Jan 2021 22:55:17 +0100 Subject: [PATCH] Expanded the usability of the %COREPATH% variable. --- es-app/src/FileData.cpp | 226 ++++++++++++++++++++++++++-------------- es-app/src/FileData.h | 5 + 2 files changed, 150 insertions(+), 81 deletions(-) diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index d2bc730bf..946018c09 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -760,23 +760,53 @@ void FileData::launchGame(Window* window) std::string commandRaw = command; - const std::string rom = Utils::FileSystem::getEscapedPath(getPath()); - const std::string basename = Utils::FileSystem::getStem(getPath()); - const std::string romraw = Utils::FileSystem::getPreferredPath(getPath()); - const std::string emupath = Utils::FileSystem::getExePath(); + const std::string romPath = Utils::FileSystem::getEscapedPath(getPath()); + const std::string baseName = Utils::FileSystem::getStem(getPath()); + const std::string romRaw = Utils::FileSystem::getPreferredPath(getPath()); + const std::string esPath = Utils::FileSystem::getExePath(); const std::string emulatorCorePath = Settings::getInstance()->getString("EmulatorCorePath"); - command = Utils::String::replace(command, "%ROM%", rom); - command = Utils::String::replace(command, "%BASENAME%", basename); - command = Utils::String::replace(command, "%ROMRAW%", romraw); - command = Utils::String::replace(command, "%ESPATH%", emupath); + command = Utils::String::replace(command, "%ROM%", romPath); + command = Utils::String::replace(command, "%BASENAME%", baseName); + command = Utils::String::replace(command, "%ROMRAW%", romRaw); + command = Utils::String::replace(command, "%ESPATH%", esPath); // Expand home path if ~ is used. command = Utils::FileSystem::expandHomePath(command); + // If there's a quotation mark before the %COREPATH% variable, then remove it. + // The closing quotation mark will be removed later below. + command = Utils::String::replace(command, "\"%COREPATH%", "%COREPATH%"); + // Get the emulator path, which will be used to check that this binary actually exists, + // and to expand the %EMUPATH% variable. #if defined(_WIN64) - std::wstring commandWide = Utils::String::stringToWideString(command); + std::string commandTemp = Utils::String::replace(command, "/", "\\"); + std::wstring commandWide = Utils::String::stringToWideString(commandTemp); + std::wstring emuPathWide = findEmulatorPath(commandWide); + if (!emuPathWide.empty()) { + auto stringPos = commandWide.find(L"%EMUPATH%"); + if (stringPos != std::wstring::npos) + commandWide = commandWide.replace(stringPos, 9, emuPathWide); + } + else { + #else + std::string emuPath = findEmulatorPath(command); + if (!emuPath.empty()) { + auto stringPos = command.find("%EMUPATH%"); + if (stringPos != std::string::npos) + command = command.replace(stringPos, 9, emuPath); + } + else { #endif + LOG(LogError) << "Couldn't launch game, emulator binary not found"; + LOG(LogError) << "Raw emulator launch command:"; + LOG(LogError) << commandRaw; + + GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: COULDN'T FIND EMULATOR, HAS IT " \ + "BEEN PROPERLY INSTALLED?", 6000); + window->setInfoPopup(s); + return; + } // If %COREPATH% is used in es_systems.cfg for this system, try to find the emulator // core using the core paths defined in the setting EmulatorCorePath. @@ -800,23 +830,41 @@ void FileData::launchGame(Window* window) if (spacePos != std::string::npos) { coreName = command.substr(corePos + 10, spacePos - corePos - 10); for (auto path : corePaths) { - bool pathHasSpaces = false; - if (path.find(" ") != std::string::npos) - pathHasSpaces = true; std::string coreFile = Utils::FileSystem::expandHomePath(path + coreName); + // Expand %EMUPATH% if it has been used in the %COREPATH% variable. + auto stringPos = coreFile.find("%EMUPATH%"); + if (stringPos != std::string::npos) { + #if defined (_WIN64) + coreFile = coreFile.replace(stringPos, 9, + Utils::String::wideStringToString(emuPathWide)); + #else + coreFile = coreFile.replace(stringPos, 9, emuPath); + #endif + } + else { + // Expand %ESPATH% if it has been used in the %COREPATH% variable. + stringPos = coreFile.find("%ESPATH%"); + if (stringPos != std::string::npos) { + coreFile = coreFile.replace(stringPos, 8, esPath); + #if defined(_WIN64) + coreFile = Utils::String::replace(coreFile, "/", "\\"); + + #endif + } + } + // Remove any quotation mark as it will make the file functions fail. + if (coreFile.find(" ") != std::string::npos) + coreFile = Utils::String::replace(coreFile, "\"", ""); if (Utils::FileSystem::isRegularFile(coreFile) || Utils::FileSystem::isSymlink(coreFile)) { foundCoreFile = true; - if (pathHasSpaces) + // Escape any blankspaces. + if (coreFile.find(" ") != std::string::npos) + coreFile = Utils::FileSystem::getEscapedPath(coreFile); #if defined(_WIN64) - commandWide.replace(corePos, spacePos - corePos, L"\"" + - Utils::String::stringToWideString(coreFile) + L"\""); - else commandWide.replace(corePos, spacePos - corePos, Utils::String::stringToWideString(coreFile)); #else - command.replace(corePos, spacePos - corePos, "\"" + coreFile + "\""); - else command.replace(corePos, spacePos - corePos, coreFile); #endif break; @@ -824,7 +872,7 @@ void FileData::launchGame(Window* window) } } else { - LOG(LogError) << "Invalid entry in systems configuration file es_systems.cfg."; + LOG(LogError) << "Invalid entry in systems configuration file es_systems.cfg"; LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << commandRaw; @@ -834,10 +882,12 @@ void FileData::launchGame(Window* window) return; } if (!foundCoreFile && coreName.size() > 0) { - LOG(LogError) << "Couldn't launch game, emulator core file '" << - coreName.substr(1, coreName.size() - 1) << "' does not exist."; + LOG(LogError) << "Couldn't launch game, emulator core file \"" << + coreName.substr(1, coreName.size() - 1) << "\" does not exist"; LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << commandRaw; + LOG(LogError) << "Configured core path:"; + LOG(LogError) << emulatorCorePath; GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: COULDN'T FIND EMULATOR CORE FILE '" + Utils::String::toUpper(coreName.substr(1, coreName.size() - 1) + "'"), 6000); @@ -847,72 +897,16 @@ void FileData::launchGame(Window* window) } else if (corePos != std::string::npos) { LOG(LogError) << "The variable %COREPATH% is used in es_systems.cfg but " \ - "no paths are defined in the setting EmulatorCorePath."; + "no paths are defined using the setting EmulatorCorePath"; GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: NO CORE PATHS CONFIGURED, " \ "CAN'T LOCATE EMULATOR CORE", 6000); window->setInfoPopup(s); return; } - Scripting::fireEvent("game-start", rom, getSourceFileData()->metadata.get("name")); + Scripting::fireEvent("game-start", romPath, getSourceFileData()->metadata.get("name")); int returnValue = 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. - #if defined(_WIN64) - std::wstring emuExecutableWide; - std::wstring emuPathWide; - - emuExecutableWide = Utils::String::stringToWideString(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 - std::string exePath; - if (Utils::FileSystem::isRegularFile(emuExecutable) || - Utils::FileSystem::isSymlink(emuExecutable)) - exePath = Utils::FileSystem::getParent(emuExecutable); - else - exePath = Utils::FileSystem::getPathToBinary(emuExecutable); - - command = Utils::String::replace(command, "%EMUPATH%", exePath); - #endif - } - LOG(LogDebug) << "Raw emulator launch command:"; LOG(LogDebug) << commandRaw; LOG(LogInfo) << "Expanded emulator launch command:"; @@ -927,7 +921,7 @@ void FileData::launchGame(Window* window) // Notify the user in case of a failed game launch using a popup window. if (returnValue != 0) { - LOG(LogWarning) << "...launch terminated with nonzero return value " << returnValue << "!"; + LOG(LogWarning) << "...launch terminated with nonzero return value " << returnValue; GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR LAUNCHING GAME '" + Utils::String::toUpper(metadata.get("name")) + "' (ERROR CODE " + @@ -954,7 +948,7 @@ void FileData::launchGame(Window* window) #endif } - Scripting::fireEvent("game-end", rom, getSourceFileData()->metadata.get("name")); + Scripting::fireEvent("game-end", romPath, getSourceFileData()->metadata.get("name")); // Re-enable the text scrolling that was disabled in ViewController on game launch. window->setAllowTextScrolling(true); @@ -981,6 +975,76 @@ void FileData::launchGame(Window* window) gameToUpdate->mSystem->onMetaDataSavePoint(); } +#if defined(_WIN64) +std::wstring FileData::findEmulatorPath(const std::wstring& command) +{ + // 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::wstring emuExecutable; + std::wstring exePath; + + // If the first character is a quotation mark, then we need to extract up to the + // next quotation mark, otherwise we'll only extract up to the first space character. + if (command.front() == '\"') { + std::wstring emuTemp = command.substr(1, std::string::npos); + emuExecutable = emuTemp.substr(0, emuTemp.find('"')); + } + else { + emuExecutable = command.substr(0, command.find(' ')); + } + + // Search for the emulator using the PATH environmental variable. + DWORD size = SearchPathW(nullptr, emuExecutable.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, emuExecutable.c_str(), L".exe", size + 1 , + pathBuffer.data(), &fileName); + std::wstring pathString = pathBuffer.data(); + + if (pathString.length()) { + exePath = pathString.substr(0, pathString.size() - + std::wstring(fileName).size()); + exePath.pop_back(); + } + } + return exePath; +} +#else +std::string FileData::findEmulatorPath(const std::string& command) +{ + // 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; + std::string exePath; + + // If the first character is a quotation mark, then we need to extract up to the + // next quotation mark, otherwise we'll only 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(' ')); + } + + if (Utils::FileSystem::isRegularFile(emuExecutable) || + Utils::FileSystem::isSymlink(emuExecutable)) + exePath = Utils::FileSystem::getParent(emuExecutable); + else + exePath = Utils::FileSystem::getPathToBinary(emuExecutable); + return exePath; +} +#endif + CollectionFileData::CollectionFileData(FileData* file, SystemData* system) : FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(), file->getSourceFileData()->getSystemEnvData(), system) diff --git a/es-app/src/FileData.h b/es-app/src/FileData.h index 6b3133f5c..bb0627eaa 100644 --- a/es-app/src/FileData.h +++ b/es-app/src/FileData.h @@ -97,6 +97,11 @@ public: std::string getCleanName() const; void launchGame(Window* window); + #if defined(_WIN64) + std::wstring findEmulatorPath(const std::wstring& command); + #else + std::string findEmulatorPath(const std::string& command); + #endif typedef bool ComparisonFunction(const FileData* a, const FileData* b); struct SortType {