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..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 @@ -225,6 +220,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()) { @@ -249,6 +256,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 @@ -265,19 +276,22 @@ namespace Utilities return SHFileOperation(&fileOp); #else -#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. + // 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)) { + mkdir(dest.c_str(), 0755); + } + //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; } @@ -290,7 +304,7 @@ namespace Utilities 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("/"); @@ -303,9 +317,7 @@ namespace Utilities fromDir.append("/"); fromDir.append(entry->d_name); - std::string toDir = dest; - - copyDir(fromDir, toDir); + copyDir(fromDir, newDir); } else { return -1; @@ -315,7 +327,7 @@ namespace Utilities //Copy file //entry->d_name; std::string newFile = dest; - newFile.append(source); + //newFile.append(source); newFile.append("/"); newFile.append(entry->d_name); @@ -327,6 +339,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(); } @@ -338,11 +351,9 @@ namespace Utilities //For each file at root level, create the file and copy contents - -#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/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/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__ - - } } diff --git a/src/iniEditor/main.cpp b/src/iniEditor/main.cpp index f3bb10ed..349a4dde 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__ @@ -388,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); + } } } @@ -525,10 +540,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); } diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 3bd375c2..8545f0f4 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,64 @@ class Receiver : public irr::IEventReceiver #endif } } + + 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; + 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/"; + 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 + "/"); + } + + // 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 + int pid; + #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);} + #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);} + #endif + #endif + + } + } } if (event.EventType == irr::EET_KEY_INPUT_EVENT) { if (event.KeyInput.Key == irr::KEY_ESCAPE ) { @@ -294,6 +399,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 +407,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 +438,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"); @@ -350,6 +460,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()); @@ -430,6 +545,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_YES | irr::gui::EMBF_NO, 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();