From 47a9b2bc8b63a79c7d6b24dc8b74a34f3dcd7777 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 28 Apr 2026 08:52:18 +0100 Subject: [PATCH 01/11] Copy across settings from previous version if required. --- bin/languageLauncher-en.txt | 2 + bin/languageLauncher-es.txt | 2 + bin/languageLauncher-fr.txt | 2 + src/Constants.hpp | 1 + src/Utilities.cpp | 12 +++++ src/Utilities.hpp | 1 + src/launcher/main.cpp | 97 ++++++++++++++++++++++++++++++++++++- 7 files changed, 115 insertions(+), 2 deletions(-) diff --git a/bin/languageLauncher-en.txt b/bin/languageLauncher-en.txt index 3c2567fa..8906bdfd 100644 --- a/bin/languageLauncher-en.txt +++ b/bin/languageLauncher-en.txt @@ -15,3 +15,5 @@ startINIMH = "Settings: Multiplayer Hub " startDOC = "Documentation " user = "Open user folder " leave = "Exit " +copy = "Copy" +copyUserFolder = "Do you want to copy\nscenarios and settings\nfrom the previous version?" diff --git a/bin/languageLauncher-es.txt b/bin/languageLauncher-es.txt index eac65db0..6a0596df 100644 --- a/bin/languageLauncher-es.txt +++ b/bin/languageLauncher-es.txt @@ -15,3 +15,5 @@ startINIMH = "Configuración: Hub multijugador " startDOC = "Manuales " user = "Carpeta del usuario " leave = "Salir " +copy = "Copiar" +copyUserFolder = "¿Desea copiar los escenario\ny la configuración de la\nversión anterior?" \ No newline at end of file diff --git a/bin/languageLauncher-fr.txt b/bin/languageLauncher-fr.txt index 49ec96d3..6a45ffe9 100644 --- a/bin/languageLauncher-fr.txt +++ b/bin/languageLauncher-fr.txt @@ -15,3 +15,5 @@ startINIMH = "Paramètres : Hub multijoueurs " startDOC = "Documentation " user = "Ouvrir le répertoire utilisateur " leave = "Quitter " +copy = "Copier" +copyUserFolder = "Souhaitez-vous copier les\nscénarios et les paramètres\ndel la version précédente?" diff --git a/src/Constants.hpp b/src/Constants.hpp index f7bd009b..ec87a855 100644 --- a/src/Constants.hpp +++ b/src/Constants.hpp @@ -45,6 +45,7 @@ const irr::f32 RAD_PER_S_IN_DEG_PER_MINUTE = 180.0/PI * 60 ; //general definitions const std::string LONGNAME = "Bridge Command 5.11.0-alpha.1"; +const std::string PREV_VERSION = "5.10"; const std::string VERSION = "5.11"; const std::string LONGVERSION = "5.11.0-alpha.1"; #endif diff --git a/src/Utilities.cpp b/src/Utilities.cpp index 79c5c93a..13e8cc84 100644 --- a/src/Utilities.cpp +++ b/src/Utilities.cpp @@ -225,6 +225,18 @@ namespace Utilities return userFolder; } + std::string getPrevUserDir() { + std::string userFolder = getUserDirBase(); + + if (userFolder.length() > 0) { + userFolder.append(PREV_VERSION); + userFolder.append("/"); + } + + return userFolder; + } + + bool pathExists(std::string filePath) { if (filePath.empty()) { diff --git a/src/Utilities.hpp b/src/Utilities.hpp index f322bd13..202cb1fb 100644 --- a/src/Utilities.hpp +++ b/src/Utilities.hpp @@ -47,6 +47,7 @@ namespace Utilities std::vector split(const std::string &inputString, char delim); std::string getUserDirBase(); //Returns the directory path (absolute, with trailing slash) for a user read/writable directory, the first level folder in the user's filesystem (eg %appdata%/Bridge Command/ on windows) std::string getUserDir(); //Returns the directory path (absolute, with trailing slash) for a user read/writable directory (eg %appdata%/Bridge Command/VERSIONUMBER/ on windows) + std::string getPrevUserDir(); // Returns the same a getUserDir, but for the previous program version bool pathExists(std::string filePath); int copyDir(std::string source, std::string dest); ScenarioData getScenarioDataFromFile(std::string scenarioPath, std::string scenarioName); //Read a scenario from ini files diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 3bd375c2..b0f3cca5 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -56,8 +56,55 @@ const irr::s32 INI_MH_BUTTON = 9; const irr::s32 DOC_BUTTON = 10; const irr::s32 USER_BUTTON = 11; const irr::s32 EXIT_BUTTON = 12; +const irr::s32 COPY_USER = 13; std::string userFolder; +std::string prevUserFolder; + +irr::IrrlichtDevice* device; + +// Get list of all scenarios (actually just folders) within a folder +std::vector getScenarioList(std::string scenarioPath) { + + // Start with an empty list + std::vector scenarioList; + + irr::io::IFileSystem* fileSystem = device->getFileSystem(); + if (fileSystem == 0) { + return scenarioList; + } + //store current dir + irr::io::path cwd = fileSystem->getWorkingDirectory(); + + //change to scenario dir + if (!fileSystem->changeWorkingDirectoryTo(scenarioPath.c_str())) { + return scenarioList; + } + + irr::io::IFileList* fileList = fileSystem->createFileList(); + if (fileList == 0) { + return scenarioList; + } + + //List here + for (irr::u32 i = 0; i < fileList->getFileCount(); i++) { + if (fileList->isDirectory(i)) { + const irr::io::path& fileName = fileList->getFileName(i); + if (fileName.findFirst('.') != 0) { //Check it doesn't start with '.' (., .., or hidden) + //Add scenario to the list + scenarioList.push_back(fileName.c_str()); + } + } + } + + //change back + fileSystem->changeWorkingDirectoryTo(cwd); + + // clean up + fileList->drop(); + + return scenarioList; +} //Event receiver: This does the actual launching class Receiver : public irr::IEventReceiver @@ -68,8 +115,8 @@ class Receiver : public irr::IEventReceiver virtual bool OnEvent(const irr::SEvent& event) { if (event.EventType == irr::EET_GUI_EVENT) { + irr::s32 id = event.GUIEvent.Caller->getID(); if (event.GUIEvent.EventType == irr::gui::EGET_BUTTON_CLICKED ) { - irr::s32 id = event.GUIEvent.Caller->getID(); if (id == EXIT_BUTTON) { exit(EXIT_SUCCESS); @@ -250,6 +297,41 @@ class Receiver : public irr::IEventReceiver #endif } } + + if (event.GUIEvent.EventType == irr::gui::EGET_MESSAGEBOX_OK) { + if (id == COPY_USER) { + // Copy folder here + Utilities::copyDir(prevUserFolder, userFolder); + + // Copy any new scenarios that exist in the (new) installation but not the user folder + std::string userScenarioFolder = userFolder + "Scenarios/"; + std::string installScenarioFolder = "Scenarios/"; // Relative to installation folder, which should be current working dir + std::vector userScenarios = getScenarioList(userScenarioFolder); + std::vector installScenarios = getScenarioList(installScenarioFolder); + // Find scenarios not in user folder that are in the install folder + std::vector scenariosToCopy; + for (std::vector::iterator itInstall = installScenarios.begin(); itInstall != installScenarios.end(); ++itInstall) { + bool scenarioFound = false; + for (std::vector::iterator itUser = userScenarios.begin(); itUser != userScenarios.end(); ++itUser) { + if (*itUser == *itInstall) { + scenarioFound = true; + break; + } + } + if (!scenarioFound) { + scenariosToCopy.push_back(*itInstall); + } + } + + // Copy these scenarios (from installation to user folder) + for (std::vector::iterator it = scenariosToCopy.begin(); it != scenariosToCopy.end(); ++it) { + Utilities::copyDir(installScenarioFolder + *it + "/", userScenarioFolder + *it + "/"); + } + + // TODO: Update ini settings to include any new settings from the (new) installation + + } + } } if (event.EventType == irr::EET_KEY_INPUT_EVENT) { if (event.KeyInput.Key == irr::KEY_ESCAPE ) { @@ -294,6 +376,7 @@ int main (int argc, char ** argv) //User read/write location - look in here first and the exe folder second for files userFolder = Utilities::getUserDir(); + prevUserFolder = Utilities::getPrevUserDir(); //Read basic ini settings std::string iniFilename = "bc5.ini"; @@ -301,6 +384,10 @@ int main (int argc, char ** argv) if (Utilities::pathExists(userFolder + iniFilename)) { iniFilename = userFolder + iniFilename; } + else if (Utilities::pathExists(prevUserFolder + iniFilename)) { + // Fall back to previous version ini file if it exists (just used for language and font) + iniFilename = prevUserFolder + iniFilename; + } std::string modifier = IniFile::iniFileToString(iniFilename, "lang"); if (modifier.length()==0) { @@ -328,7 +415,7 @@ int main (int argc, char ** argv) irr::u32 graphicsDepth = 32; bool fullScreen = false; - irr::IrrlichtDevice* device = irr::createDevice(irr::video::EDT_OPENGL, irr::core::dimension2d(graphicsWidth,graphicsHeight),graphicsDepth,fullScreen,false,false,0); + device = irr::createDevice(irr::video::EDT_OPENGL, irr::core::dimension2d(graphicsWidth,graphicsHeight),graphicsDepth,fullScreen,false,false,0); irr::video::IVideoDriver* driver = device->getVideoDriver(); irr::video::ITexture* imgTexture = driver->getTexture("media/logo.png"); @@ -430,6 +517,12 @@ int main (int argc, char ** argv) chdir("/usr/bin"); #endif // FOR_DEB + // Check if previous user dir exists, but current one doesn't (so probably a new installation) + if (!Utilities::pathExists(userFolder) && Utilities::pathExists(prevUserFolder)) { + // Ask user if we want to copy user folder across + device->getGUIEnvironment()->addMessageBox(language.translate("copy").c_str(), language.translate("copyUserFolder").c_str(), true, irr::gui::EMBF_OK | irr::gui::EMBF_CANCEL, 0, COPY_USER); + } + while (device->run()) { driver->beginScene(irr::video::ECBF_COLOR | irr::video::ECBF_DEPTH, irr::video::SColor(0, 200, 200, 200)); device->getGUIEnvironment()->drawAll(); From 79b2783c79a5dc1bc0b4fdff9ecd3e7a3f098ca7 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 28 Apr 2026 18:23:03 +0100 Subject: [PATCH 02/11] Add auto mode for ini editor, to automatically combine new global ini parameters into user ini files. --- src/iniEditor/main.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/iniEditor/main.cpp b/src/iniEditor/main.cpp index f3bb10ed..02784c5b 100644 --- a/src/iniEditor/main.cpp +++ b/src/iniEditor/main.cpp @@ -216,6 +216,12 @@ int main (int argc, char ** argv) iniFilename = "repeater.ini"; } + bool autoMode = false; + if (((argc > 1) && (strcmp(argv[1], "-auto") == 0)) || + ((argc > 2) && (strcmp(argv[2], "-auto") == 0))) { + autoMode = true; + } + //Mac OS: //Find starting folder #ifdef __APPLE__ @@ -525,10 +531,16 @@ int main (int argc, char ** argv) Receiver receiver(device, environment, tabbedPane, iniFilename); device->setEventReceiver(&receiver); - while (device->run()) { - driver->beginScene(); - device->getGUIEnvironment()->drawAll(); - driver->endScene(); + if (autoMode) { + // Automatically save and close. This mode is used to ensure we have user settings file updated with any new global ini settings + saveFile(device, iniFilename, tabbedPane); + } + else { + while (device->run()) { + driver->beginScene(); + device->getGUIEnvironment()->drawAll(); + driver->endScene(); + } } return(0); } From 09f424d92892f57fbffce29c0ef2ebaf639c3917 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 28 Apr 2026 18:23:31 +0100 Subject: [PATCH 03/11] Tweak launcher transparency. --- src/launcher/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index b0f3cca5..8320033b 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -437,6 +437,11 @@ int main (int argc, char ** argv) irr::gui::IGUISkin* newskin = device->getGUIEnvironment()->createSkin(irr::gui::EGST_WINDOWS_CLASSIC); device->getGUIEnvironment()->setSkin(newskin); + //Set gui skin less transparent + irr::video::SColor col = device->getGUIEnvironment()->getSkin()->getColor(irr::gui::EGDC_3D_FACE); + col.setAlpha(200); + device->getGUIEnvironment()->getSkin()->setColor(irr::gui::EGDC_3D_FACE, col); + std::string fontName = IniFile::iniFileToString(iniFilename, "font"); std::string fontPath = "media/fonts/" + fontName + "/" + fontName + "-" + std::to_string(fontSize) + ".xml"; irr::gui::IGUIFont *font = device->getGUIEnvironment()->getFont(fontPath.c_str()); From f219535076a6da4004ecd4114c5a694ca51f0648 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 28 Apr 2026 18:34:17 +0100 Subject: [PATCH 04/11] Use ini editor to automatically update local ini file with new global settings. --- src/launcher/main.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 8320033b..1933c977 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -328,7 +328,28 @@ class Receiver : public irr::IEventReceiver Utilities::copyDir(installScenarioFolder + *it + "/", userScenarioFolder + *it + "/"); } - // TODO: Update ini settings to include any new settings from the (new) installation + // Update ini settings to include any new settings from the (new) installation. Use the INI editor to do this + #ifdef _WIN32 + ShellExecute(NULL, NULL, "bridgecommand-ini.exe", "-auto", NULL, SW_SHOW); + ShellExecute(NULL, NULL, "bridgecommand-ini.exe", "-M -auto", NULL, SW_SHOW); + ShellExecute(NULL, NULL, "bridgecommand-ini.exe", "-H -auto", NULL, SW_SHOW); + ShellExecute(NULL, NULL, "bridgecommand-ini.exe", "-R -auto", NULL, SW_SHOW); + #else + #ifdef __APPLE__ + //APPLE + execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-auto", NULL); + execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-M -auto", NULL); + execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-H -auto", NULL); + execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-R -auto", NULL); + #else + //Other (assumed posix) + execl("./bridgecommand-ini", "bridgecommand-ini", "-auto", NULL); + execl("./bridgecommand-ini", "bridgecommand-ini", "-M -auto", NULL); + execl("./bridgecommand-ini", "bridgecommand-ini", "-H -auto", NULL); + execl("./bridgecommand-ini", "bridgecommand-ini", "-R -auto", NULL); + #endif + #endif + } } From 608f607e3e24ed57dd3045950e4b4f10f5deeefe Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 28 Apr 2026 19:01:14 +0100 Subject: [PATCH 05/11] Improve copying new settings into ini file, creating new tab if needed. --- src/iniEditor/main.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/iniEditor/main.cpp b/src/iniEditor/main.cpp index 02784c5b..349a4dde 100644 --- a/src/iniEditor/main.cpp +++ b/src/iniEditor/main.cpp @@ -394,16 +394,25 @@ int main (int argc, char ** argv) } } - //If not, find the corresponding tab, or fall back to the first tab + //If not, find the corresponding tab, or add a new tab and add there if (!found) { //Add to corresponding tab - int whichTab = 0; + int whichTab = -1; for (int i = 0; i < iniFileStructure.size(); i++) { if (currentTabName.compare(iniFileStructure.at(i).tabName) == 0) { whichTab = i; } } - iniFileStructure.at(whichTab).settings.push_back(thisEntry); + if (whichTab < 0) { + // Tab not found, create a new one + IniFileTab newTab; + newTab.tabName = currentTabName; + newTab.settings.push_back(thisEntry); + iniFileStructure.push_back(newTab); + } else { + // Found existing tab, use this + iniFileStructure.at(whichTab).settings.push_back(thisEntry); + } } } From 8d08589356a4b327eac11f9ba2cb21a5b2767c68 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 28 Apr 2026 19:18:31 +0100 Subject: [PATCH 06/11] Correct execl behaviour on posix (ensure we fork before execl) --- src/launcher/main.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 1933c977..315fb518 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -337,16 +337,16 @@ class Receiver : public irr::IEventReceiver #else #ifdef __APPLE__ //APPLE - execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-auto", NULL); - execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-M -auto", NULL); - execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-H -auto", NULL); - execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-R -auto", NULL); + int pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-auto", NULL);} + int pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-M -auto", NULL);} + int pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-H -auto", NULL);} + int pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-R -auto", NULL);} #else //Other (assumed posix) - execl("./bridgecommand-ini", "bridgecommand-ini", "-auto", NULL); - execl("./bridgecommand-ini", "bridgecommand-ini", "-M -auto", NULL); - execl("./bridgecommand-ini", "bridgecommand-ini", "-H -auto", NULL); - execl("./bridgecommand-ini", "bridgecommand-ini", "-R -auto", NULL); + int pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-auto", NULL);} + int pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-M -auto", NULL);} + int pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-H -auto", NULL);} + int pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-R -auto", NULL);} #endif #endif From 30a34f4912526d28019f23e93df8f729815e6a99 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 28 Apr 2026 19:20:31 +0100 Subject: [PATCH 07/11] Fix (accidentally declared pid multiple times) --- src/launcher/main.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 315fb518..47b9c2a6 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -335,18 +335,19 @@ class Receiver : public irr::IEventReceiver ShellExecute(NULL, NULL, "bridgecommand-ini.exe", "-H -auto", NULL, SW_SHOW); ShellExecute(NULL, NULL, "bridgecommand-ini.exe", "-R -auto", NULL, SW_SHOW); #else + int pid; #ifdef __APPLE__ //APPLE - int pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-auto", NULL);} - int pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-M -auto", NULL);} - int pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-H -auto", NULL);} - int pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-R -auto", NULL);} + pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-auto", NULL);} + pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-M -auto", NULL);} + pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-H -auto", NULL);} + pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-R -auto", NULL);} #else //Other (assumed posix) - int pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-auto", NULL);} - int pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-M -auto", NULL);} - int pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-H -auto", NULL);} - int pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-R -auto", NULL);} + pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-auto", NULL);} + pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-M -auto", NULL);} + pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-H -auto", NULL);} + pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-R -auto", NULL);} #endif #endif From 8580c8a2091fa4ccc1b708f9192b490d6c28853a Mon Sep 17 00:00:00 2001 From: James Packer Date: Fri, 1 May 2026 21:54:02 +0100 Subject: [PATCH 08/11] Fixes for posix. --- src/Utilities.cpp | 5 +++++ src/launcher/main.cpp | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Utilities.cpp b/src/Utilities.cpp index 13e8cc84..b49d37b6 100644 --- a/src/Utilities.cpp +++ b/src/Utilities.cpp @@ -277,6 +277,11 @@ namespace Utilities return SHFileOperation(&fileOp); #else + // Try to make dest dir if it doesn't exist. + // Won't help if the parent doesn't exist, but will help in many cases + if (!pathExists(dest)) { + mkdir(dest.c_str(), 0755); + } #ifdef __APPLE__ //Apple version: Requires that dest dir exists copyfile_state_t s; diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 47b9c2a6..a08c1eec 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -339,15 +339,15 @@ class Receiver : public irr::IEventReceiver #ifdef __APPLE__ //APPLE pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-auto", NULL);} - pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-M -auto", NULL);} - pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-H -auto", NULL);} - pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-R -auto", NULL);} + pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-M", "-auto", NULL);} + pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-H", "-auto", NULL);} + pid = fork(); if (pid == 0) { execl("../Helpers/ini.app/Contents/MacOS/ini", "ini", "-R", "-auto", NULL);} #else //Other (assumed posix) pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-auto", NULL);} - pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-M -auto", NULL);} - pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-H -auto", NULL);} - pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-R -auto", NULL);} + pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-M", "-auto", NULL);} + pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-H", "-auto", NULL);} + pid = fork(); if (pid == 0) { execl("./bridgecommand-ini", "bridgecommand-ini", "-R", "-auto", NULL);} #endif #endif From bdb20804b884d2fe0e1c9b3792079fad0c918f08 Mon Sep 17 00:00:00 2001 From: James Packer Date: Mon, 4 May 2026 19:42:51 +0100 Subject: [PATCH 09/11] Updates to fix folder copying on Linux --- src/Utilities.cpp | 29 ++++++++++++++++++++--------- src/launcher/main.cpp | 4 ++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Utilities.cpp b/src/Utilities.cpp index b49d37b6..72df8750 100644 --- a/src/Utilities.cpp +++ b/src/Utilities.cpp @@ -261,6 +261,10 @@ namespace Utilities int copyDir(std::string source, std::string dest) { + if (source.empty() || dest.empty()) { + return -1; + } + //Copy contents of source dir into dest dir #ifdef _WIN32 @@ -277,6 +281,14 @@ namespace Utilities return SHFileOperation(&fileOp); #else + // Strip trailing slash if present + if (dest.back() == '/') { + dest.pop_back(); + } + if (source.back() == '/') { + source.pop_back(); + } + // Try to make dest dir if it doesn't exist. // Won't help if the parent doesn't exist, but will help in many cases if (!pathExists(dest)) { @@ -294,20 +306,20 @@ namespace Utilities //Other posix //Note: Not implemented yet for other posix: need to implement recursive directory copy. //Requires that dest dir exists - //std::cout << "Copying from:" << source << " to:" << dest << std::endl; + std::cout << "Copying dir from:" << source << " to:" << dest << std::endl; if (!Utilities::pathExists(dest)) { return -1; } //For each folder at root level, create new folder in dest, and call copyDir on this DIR* dir = opendir(source.c_str()); - if (!dir) { return -1; } + if (!dir) { return -2; } struct dirent* entry = readdir(dir); while (entry != NULL) { if (entry->d_type == DT_DIR && entry->d_name[0] != '.') { std::string newDir = dest; - newDir.append(source); + //newDir.append(source); newDir.append("/"); newDir.append(entry->d_name); //newDir.append("/"); @@ -320,19 +332,17 @@ namespace Utilities fromDir.append("/"); fromDir.append(entry->d_name); - std::string toDir = dest; - - copyDir(fromDir, toDir); + copyDir(fromDir, newDir); } else { - return -1; + return -3; } } else if (entry->d_type == DT_REG) { //Copy file //entry->d_name; std::string newFile = dest; - newFile.append(source); + //newFile.append(source); newFile.append("/"); newFile.append(entry->d_name); @@ -344,6 +354,7 @@ namespace Utilities std::ifstream fromStream(fromFile.c_str(), std::ios::binary); std::ofstream destStream(newFile.c_str(), std::ios::binary); + std::cout << "Copying from " << fromFile << " to " << newFile << std::endl; if (fromStream && destStream) { destStream << fromStream.rdbuf(); } @@ -359,7 +370,7 @@ namespace Utilities #endif // __APPLE__ #endif // _WIN32 - return -1; + return 0; } ScenarioData getScenarioDataFromFile(std::string scenarioPath, std::string scenarioName) //Read a scenario from ini files diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index a08c1eec..b681442b 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -301,7 +301,8 @@ class Receiver : public irr::IEventReceiver if (event.GUIEvent.EventType == irr::gui::EGET_MESSAGEBOX_OK) { if (id == COPY_USER) { // Copy folder here - Utilities::copyDir(prevUserFolder, userFolder); + std::cout << "Copying from:" << prevUserFolder << " to:" << userFolder << std::endl; + std::cout << Utilities::copyDir(prevUserFolder, userFolder) << std::endl; // Copy any new scenarios that exist in the (new) installation but not the user folder std::string userScenarioFolder = userFolder + "Scenarios/"; @@ -351,7 +352,6 @@ class Receiver : public irr::IEventReceiver #endif #endif - } } } From 42532246b8b4375f3199a86a60ed77110ae752d7 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 5 May 2026 08:16:43 +0100 Subject: [PATCH 10/11] Simplify so same copyDir code used for macOS and other posix. --- src/Utilities.cpp | 23 +++-------------------- src/editor/main.cpp | 7 ++----- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/Utilities.cpp b/src/Utilities.cpp index 72df8750..bd60f7bd 100644 --- a/src/Utilities.cpp +++ b/src/Utilities.cpp @@ -45,14 +45,9 @@ #include #include #else // _WIN32 -#ifdef __APPLE__ -#include -#include -#else #include #include #include -#endif #endif // __APPLE__ namespace Utilities @@ -294,17 +289,7 @@ namespace Utilities if (!pathExists(dest)) { mkdir(dest.c_str(), 0755); } -#ifdef __APPLE__ - //Apple version: Requires that dest dir exists - copyfile_state_t s; - s = copyfile_state_alloc(); - //use copyfile here to do recursive copy - int returnValue = copyfile(source.c_str(), dest.c_str(), s, COPYFILE_DATA | COPYFILE_RECURSIVE); - copyfile_state_free(s); - return returnValue; -#else // __APPLE__ - //Other posix - //Note: Not implemented yet for other posix: need to implement recursive directory copy. + //Requires that dest dir exists std::cout << "Copying dir from:" << source << " to:" << dest << std::endl; if (!Utilities::pathExists(dest)) { @@ -313,7 +298,7 @@ namespace Utilities //For each folder at root level, create new folder in dest, and call copyDir on this DIR* dir = opendir(source.c_str()); - if (!dir) { return -2; } + if (!dir) { return -1; } struct dirent* entry = readdir(dir); while (entry != NULL) { if (entry->d_type == DT_DIR && entry->d_name[0] != '.') { @@ -335,7 +320,7 @@ namespace Utilities copyDir(fromDir, newDir); } else { - return -3; + return -1; } } else if (entry->d_type == DT_REG) { @@ -366,8 +351,6 @@ namespace Utilities //For each file at root level, create the file and copy contents - -#endif // __APPLE__ #endif // _WIN32 return 0; diff --git a/src/editor/main.cpp b/src/editor/main.cpp index 586fabde..7d2233bb 100644 --- a/src/editor/main.cpp +++ b/src/editor/main.cpp @@ -262,8 +262,8 @@ void checkUserScenarioDir(void) if (!Utilities::pathExists(userFolder + scenarioPath)) { - #ifdef _WIN32 std::cout << "Copying scenario files into " << userFolder + scenarioPath << std::endl; + #ifdef _WIN32 Utilities::copyDir("Scenarios", userFolder + scenarioPath); #else //Make sure destination folder for scenarios exists. Not needed on windows as the copy method creates the output folder and directories above it. @@ -281,11 +281,8 @@ void checkUserScenarioDir(void) std::string pathToMake = Utilities::getUserDir() + "Scenarios"; mkdir(pathToMake.c_str(),0755); } - std::cout << "Copying scenario files into " << userFolder << std::endl; - Utilities::copyDir("Scenarios", userFolder); + Utilities::copyDir("Scenarios", userFolder + scenarioPath); #endif // __APPLE__ - - } } From ce643ff4acc9401a3390f5d6f267747cdd08b681 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 5 May 2026 08:17:40 +0100 Subject: [PATCH 11/11] Change dialog message to Yes/No instead of OK/Cancel --- src/launcher/main.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index b681442b..8545f0f4 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -298,11 +298,12 @@ class Receiver : public irr::IEventReceiver } } - if (event.GUIEvent.EventType == irr::gui::EGET_MESSAGEBOX_OK) { + if (event.GUIEvent.EventType == irr::gui::EGET_MESSAGEBOX_YES) { if (id == COPY_USER) { // Copy folder here std::cout << "Copying from:" << prevUserFolder << " to:" << userFolder << std::endl; - std::cout << Utilities::copyDir(prevUserFolder, userFolder) << std::endl; + int copyStatus = Utilities::copyDir(prevUserFolder, userFolder); + std::cout << "Copy status value: " << copyStatus << std::endl; // Copy any new scenarios that exist in the (new) installation but not the user folder std::string userScenarioFolder = userFolder + "Scenarios/"; @@ -547,7 +548,7 @@ int main (int argc, char ** argv) // Check if previous user dir exists, but current one doesn't (so probably a new installation) if (!Utilities::pathExists(userFolder) && Utilities::pathExists(prevUserFolder)) { // Ask user if we want to copy user folder across - device->getGUIEnvironment()->addMessageBox(language.translate("copy").c_str(), language.translate("copyUserFolder").c_str(), true, irr::gui::EMBF_OK | irr::gui::EMBF_CANCEL, 0, COPY_USER); + device->getGUIEnvironment()->addMessageBox(language.translate("copy").c_str(), language.translate("copyUserFolder").c_str(), true, irr::gui::EMBF_YES | irr::gui::EMBF_NO, 0, COPY_USER); } while (device->run()) {