diff --git a/README.md b/README.md index 1275063c..db181ed0 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,6 @@ The following flags can be passed to `cmake`: - `-DWITH_SERVER=1` Whether to build the server (default 0 = no). - `-DWITH_CLIENT=0` Whether to build the client (default 1 = yes). - `-DWITH_ORACLE=0` Whether to build oracle (default 1 = yes). -- `-DPORTABLE=1` Build portable versions of client & oracle (default 0 = no). - `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings (default `Release`). - `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files (default 0 = no). - `-DTEST=1` Enable regression tests (default 0 = no). Note: needs googletest, will be downloaded on the fly if unavailable. To run tests: ```make test```. diff --git a/cmake/NSIS.template.in b/cmake/NSIS.template.in index 5fdb3aab..2e1b971d 100644 --- a/cmake/NSIS.template.in +++ b/cmake/NSIS.template.in @@ -1,14 +1,24 @@ !include ..\..\..\NSIS.definitions.nsh -!include "MUI2.nsh" -!include "FileFunc.nsh" - Name "@CPACK_PACKAGE_NAME@" BrandingText "@CPACK_PACKAGE_FILE_NAME@" OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" -SetCompressor /SOLID lzma -InstallDir "$PROGRAMFILES\Cockatrice" + +!define UNINSTKEY "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" +!define DEFAULTNORMALDESTINATON "$ProgramFiles\Cockatrice" +!define DEFAULTPORTABLEDESTINATON "$Desktop\CockatricePortable" !define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" +RequestExecutionlevel highest +SetCompressor LZMA + +Var NormalDestDir +Var PortableDestDir +Var PortableMode + +!include LogicLib.nsh +!include FileFunc.nsh +!include MUI2.nsh + !define MUI_ABORTWARNING !define MUI_WELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp" !define MUI_UNWELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp" @@ -21,6 +31,8 @@ InstallDir "$PROGRAMFILES\Cockatrice" !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "${NSIS_SOURCE_PATH}\COPYING" +Page Custom PortableModePageCreate PortableModePageLeave +!define MUI_PAGE_CUSTOMFUNCTION_PRE componentsPagePre !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES @@ -31,15 +43,150 @@ InstallDir "$PROGRAMFILES\Cockatrice" !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH -!insertmacro MUI_LANGUAGE "English" +!insertmacro MUI_LANGUAGE English + +Function .onInit +StrCpy $NormalDestDir "${DEFAULTNORMALDESTINATON}" +StrCpy $PortableDestDir "${DEFAULTPORTABLEDESTINATON}" + +${GetParameters} $9 + +ClearErrors +${GetOptions} $9 "/?" $8 +${IfNot} ${Errors} + MessageBox MB_ICONINFORMATION|MB_SETFOREGROUND "\ + /PORTABLE : Install in portable mode$\n\ + /S : Silent install$\n\ + /D=%directory% : Specify destination directory$\n" + Quit +${EndIf} + +ClearErrors +${GetOptions} $9 "/PORTABLE" $8 +${IfNot} ${Errors} + StrCpy $PortableMode 1 + StrCpy $0 $PortableDestDir +${Else} + StrCpy $PortableMode 0 + StrCpy $0 $NormalDestDir + ${If} ${Silent} + Call RequireAdmin + ${EndIf} +${EndIf} + +${If} $InstDir == "" + ; User did not use /D to specify a directory, + ; we need to set a default based on the install mode + StrCpy $InstDir $0 +${EndIf} +Call SetModeDestinationFromInstdir + +FunctionEnd + + +Function RequireAdmin +UserInfo::GetAccountType +Pop $8 +${If} $8 != "admin" + MessageBox MB_ICONSTOP "You need administrator rights to install Cockatrice" + SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED + Abort +${EndIf} +FunctionEnd + + +Function SetModeDestinationFromInstdir +${If} $PortableMode = 0 + StrCpy $NormalDestDir $InstDir +${Else} + StrCpy $PortableDestDir $InstDir +${EndIf} +FunctionEnd + + +Function PortableModePageCreate +Call SetModeDestinationFromInstdir ; If the user clicks BACK on the directory page we will remember their mode specific directory +!insertmacro MUI_HEADER_TEXT "Install Mode" "Choose how you want to install Cockatrice." +nsDialogs::Create 1018 +Pop $0 +${NSD_CreateLabel} 0 10u 100% 24u "Select install mode:" +Pop $0 +${NSD_CreateRadioButton} 30u 50u -30u 8u "Normal installation" +Pop $1 +${NSD_CreateRadioButton} 30u 70u -30u 8u "Portable mode (all files in a single folder)" +Pop $2 +${If} $PortableMode = 0 + SendMessage $1 ${BM_SETCHECK} ${BST_CHECKED} 0 +${Else} + SendMessage $2 ${BM_SETCHECK} ${BST_CHECKED} 0 +${EndIf} +nsDialogs::Show +FunctionEnd + +Function PortableModePageLeave +${NSD_GetState} $1 $0 +${If} $0 <> ${BST_UNCHECKED} + StrCpy $PortableMode 0 + StrCpy $InstDir $NormalDestDir + Call RequireAdmin +${Else} + StrCpy $PortableMode 1 + StrCpy $InstDir $PortableDestDir +${EndIf} +FunctionEnd + +Function componentsPagePre +${If} $PortableMode = 0 + SetShellVarContext all + ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" + StrCmp $R0 "" done + + MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst + Abort + + uninst: + ClearErrors + ExecWait "$R0" + + done: +${Else} + Abort +${EndIf} +FunctionEnd Section "Application" SecApplication - SetShellVarContext all - SetOutPath "$INSTDIR" - @CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS@ - @CPACK_NSIS_FULL_INSTALL@ +SetShellVarContext all +SetOutPath "$INSTDIR" +${If} $PortableMode = 1 +${AndIf} ${FileExists} "$INSTDIR\portable.dat" + ; upgrade portable mode + RMDir /r "$INSTDIR\plugins" + RMDir /r "$INSTDIR\sounds" + RMDir /r "$INSTDIR\themes" + RMDir /r "$INSTDIR\translations" + Delete "$INSTDIR\uninstall.exe" + Delete "$INSTDIR\cockatrice.exe" + Delete "$INSTDIR\oracle.exe" + Delete "$INSTDIR\servatrice.exe" + Delete "$INSTDIR\Qt*.dll" + Delete "$INSTDIR\libmysql.dll" + Delete "$INSTDIR\icu*.dll" + Delete "$INSTDIR\libeay32.dll" + Delete "$INSTDIR\ssleay32.dll" + Delete "$INSTDIR\qt.conf" + Delete "$INSTDIR\qdebug.txt" + Delete "$INSTDIR\servatrice.sql" + Delete "$INSTDIR\servatrice.ini.example" + Delete "$INSTDIR\zlib*.dll" + RMDir "$INSTDIR" +${EndIf} + +@CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS@ +@CPACK_NSIS_FULL_INSTALL@ + +${If} $PortableMode = 0 WriteUninstaller "$INSTDIR\uninstall.exe" ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 IntFmt $0 "0x%08X" $0 @@ -49,24 +196,22 @@ Section "Application" SecApplication WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "InstallLocation" "$INSTDIR" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayIcon" "$INSTDIR\cockatrice.exe" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "EstimatedSize" "$0" -SectionEnd - -Section "Update configuration" SecUpdateConfig - SetShellVarContext current - WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "carddatabase" "$LOCALAPPDATA\Cockatrice\cards.xml" - WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "tokendatabase" "$LOCALAPPDATA\Cockatrice\tokens.xml" - WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "decks" "$LOCALAPPDATA\Cockatrice\decks" - WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "pics" "$LOCALAPPDATA\Cockatrice\pics" - WriteRegStr HKCU "Software\Cockatrice\Cockatrice\replays" "pics" "$LOCALAPPDATA\Cockatrice\replays" - WriteRegStr HKCU "Software\Cockatrice\Cockatrice\sound" "path" "$INSTDIR\sounds" +${Else} + ; Create the file the application uses to detect portable mode + FileOpen $0 "$INSTDIR\portable.dat" w + FileWrite $0 "PORTABLE" + FileClose $0 +${EndIf} SectionEnd Section "Start menu item" SecStartMenu +${If} $PortableMode = 0 SetShellVarContext all createDirectory "$SMPROGRAMS\Cockatrice" createShortCut "$SMPROGRAMS\Cockatrice\Cockatrice.lnk" "$INSTDIR\cockatrice.exe" createShortCut "$SMPROGRAMS\Cockatrice\Oracle.lnk" "$INSTDIR\oracle.exe" createShortCut "$SMPROGRAMS\Cockatrice\Servatrice.lnk" "$INSTDIR\servatrice.exe" +${EndIf} SectionEnd Section "un.Application" UnSecApplication @@ -105,11 +250,9 @@ Section /o "un.Configurations, decks, cards, pics" UnSecConfiguration SectionEnd LangString DESC_SecApplication ${LANG_ENGLISH} "Cockatrice program files" -LangString DESC_SecUpdateConfig ${LANG_ENGLISH} "Update the paths in the application settings according to the installation paths." LangString DESC_SecStartMenu ${LANG_ENGLISH} "Create start menu items for Cockatrice and Oracle." !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${SecApplication} $(DESC_SecApplication) - !insertmacro MUI_DESCRIPTION_TEXT ${SecUpdateConfig} $(DESC_SecUpdateConfig) !insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} $(DESC_SecStartMenu) !insertmacro MUI_FUNCTION_DESCRIPTION_END @@ -118,21 +261,4 @@ LangString DESC_UnSecConfiguration ${LANG_ENGLISH} "Configurations, decks, card !insertmacro MUI_UNFUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${UnSecApplication} $(DESC_UnSecApplication) !insertmacro MUI_DESCRIPTION_TEXT ${UnSecConfiguration} $(DESC_UnSecConfiguration) -!insertmacro MUI_UNFUNCTION_DESCRIPTION_END - - -Function .onInit - SetShellVarContext all - ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" - StrCmp $R0 "" done - - MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst - Abort - -uninst: - ClearErrors - ExecWait "$R0" - -done: - -FunctionEnd \ No newline at end of file +!insertmacro MUI_UNFUNCTION_DESCRIPTION_END \ No newline at end of file diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index f969d612..8834d92b 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -308,8 +308,3 @@ Data = Resources\") install(FILES ${WIN32SSLRUNTIME_LIBRARIES} DESTINATION ./) endif() endif() -#Compile a portable version, default off -option(PORTABLE "portable build" OFF) -IF(PORTABLE) -add_definitions(-DPORTABLE_BUILD) -endif() \ No newline at end of file diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 75d8f30e..a87c3cb2 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -130,6 +130,21 @@ GeneralSettingsPage::GeneralSettingsPage() QPushButton *tokenDatabasePathButton = new QPushButton("..."); connect(tokenDatabasePathButton, SIGNAL(clicked()), this, SLOT(tokenDatabasePathButtonClicked())); + if(settingsCache->getIsPortableBuild()) + { + deckPathEdit->setEnabled(false); + replaysPathEdit->setEnabled(false); + picsPathEdit->setEnabled(false); + cardDatabasePathEdit->setEnabled(false); + tokenDatabasePathEdit->setEnabled(false); + + deckPathButton->setVisible(false); + replaysPathButton->setVisible(false); + picsPathButton->setVisible(false); + cardDatabasePathButton->setVisible(false); + tokenDatabasePathButton->setVisible(false); + } + QGridLayout *pathsGrid = new QGridLayout; pathsGrid->addWidget(&deckPathLabel, 0, 0); pathsGrid->addWidget(deckPathEdit, 0, 1); @@ -273,7 +288,14 @@ void GeneralSettingsPage::retranslateUi() personalGroupBox->setTitle(tr("Personal settings")); languageLabel.setText(tr("Language:")); picDownloadCheckBox.setText(tr("Download card pictures on the fly")); - pathsGroupBox->setTitle(tr("Paths")); + + if(settingsCache->getIsPortableBuild()) + { + pathsGroupBox->setTitle(tr("Paths (editing disabled in portable mode)")); + } else { + pathsGroupBox->setTitle(tr("Paths")); + } + deckPathLabel.setText(tr("Decks directory:")); replaysPathLabel.setText(tr("Replays directory:")); picsPathLabel.setText(tr("Pictures directory:")); diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index dd2b91f7..ffbc8f4f 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -10,12 +10,10 @@ QString SettingsCache::getDataPath() { - return -#ifdef PORTABLE_BUILD - qApp->applicationDirPath() + "/data/"; -#else - QStandardPaths::writableLocation(QStandardPaths::DataLocation); -#endif + if(isPortableBuild) + return qApp->applicationDirPath() + "/data/"; + else + return QStandardPaths::writableLocation(QStandardPaths::DataLocation); } QString SettingsCache::getSettingsPath() @@ -25,9 +23,8 @@ QString SettingsCache::getSettingsPath() void SettingsCache::translateLegacySettings() { -#ifdef PORTABLE_BUILD - return; -#endif + if(isPortableBuild) + return; //Layouts QFile layoutFile(getSettingsPath()+"layouts/deckLayout.ini"); @@ -148,6 +145,11 @@ QString SettingsCache::getSafeConfigFilePath(QString configEntry, QString defaul } SettingsCache::SettingsCache() { + // first, figure out if we are running in portable mode + isPortableBuild = QFile::exists(qApp->applicationDirPath() + "/portable.dat"); + if(isPortableBuild) + qDebug() << "Portable mode enabled"; + // define a dummy context that will be used where needed QString dummy = QT_TRANSLATE_NOOP("i18n", "English"); diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index 6d6029b8..4969097b 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -115,6 +115,7 @@ private: QString getSafeConfigFilePath(QString configEntry, QString defaultPath) const; bool rememberGameSettings; QList releaseChannels; + bool isPortableBuild; public: SettingsCache(); @@ -198,6 +199,7 @@ public: MessageSettings& messages() const { return *messageSettings; } GameFiltersSettings& gameFilters() const { return *gameFiltersSettings; } LayoutsSettings& layouts() const { return *layoutsSettings; } + bool getIsPortableBuild() const { return isPortableBuild; } public slots: void setMainWindowGeometry(const QByteArray &_mainWindowGeometry); void setTokenDialogGeometry(const QByteArray &_tokenDialog); diff --git a/cockatrice/src/soundengine.cpp b/cockatrice/src/soundengine.cpp index a1569238..05aeb0be 100644 --- a/cockatrice/src/soundengine.cpp +++ b/cockatrice/src/soundengine.cpp @@ -106,13 +106,8 @@ QStringMap & SoundEngine::getAvailableThemes() availableThemes.clear(); // load themes from user profile dir - dir = -#ifdef PORTABLE_BUILD - qApp->applicationDirPath() + -#else - QStandardPaths::standardLocations(QStandardPaths::DataLocation).first() + -#endif - "/sounds"; + + dir = settingsCache->getDataPath() + "/sounds"; foreach(QString themeName, dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) { diff --git a/cockatrice/src/thememanager.cpp b/cockatrice/src/thememanager.cpp index 4f99674d..2d5ef71a 100644 --- a/cockatrice/src/thememanager.cpp +++ b/cockatrice/src/thememanager.cpp @@ -38,13 +38,7 @@ QStringMap & ThemeManager::getAvailableThemes() availableThemes.clear(); // load themes from user profile dir - dir = -#ifdef PORTABLE_BUILD - qApp->applicationDirPath() + -#else - QStandardPaths::standardLocations(QStandardPaths::DataLocation).first() + -#endif - "/themes"; + dir = settingsCache->getDataPath() + "/themes"; foreach(QString themeName, dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) { diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 4a53111a..3f0eec16 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -207,8 +207,3 @@ Translations = Resources/translations\") fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Oracle.exe\" \"\${QTPLUGINS}\" \"${libSearchDirs}\") " COMPONENT Runtime) endif() -#Compile a portable version, default off -option(PORTABLE "portable build" OFF) -IF(PORTABLE) -add_definitions(-DPORTABLE_BUILD) -endif() diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index 47805850..091bf9ed 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -490,9 +490,6 @@ void SaveSetsPage::retranslateUi() "Press \"Save\" to save the imported cards to the Cockatrice database.")); defaultPathCheckBox->setText(tr("Save to the default path (recommended)")); - #ifdef PORTABLE_BUILD - defaultPathCheckBox->setEnabled(false); - #endif } void SaveSetsPage::updateTotalProgress(int cardsImported, int /* setIndex */, const QString &setName) diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index 805966fc..2e2b562f 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -195,8 +195,3 @@ Translations = Resources/translations\") fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Servatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") " COMPONENT Runtime) endif() -#Compile a portable version, default off -option(PORTABLE "portable build" OFF) -IF(PORTABLE) -add_definitions(-DPORTABLE_BUILD) -endif() diff --git a/servatrice/src/settingscache.cpp b/servatrice/src/settingscache.cpp index 5009365b..a759b672 100644 --- a/servatrice/src/settingscache.cpp +++ b/servatrice/src/settingscache.cpp @@ -2,10 +2,14 @@ #include #include #include +#include SettingsCache::SettingsCache(const QString & fileName, QSettings::Format format, QObject * parent) :QSettings(fileName, format, parent) { + // first, figure out if we are running in portable mode + isPortableBuild = QFile::exists(qApp->applicationDirPath() + "/portable.dat"); + QStringList disallowedRegExpStr = value("users/disallowedregexp", "").toString().split(",", QString::SkipEmptyParts); disallowedRegExpStr.removeDuplicates(); for (const QString ®ExpStr : disallowedRegExpStr) { @@ -16,9 +20,12 @@ SettingsCache::SettingsCache(const QString & fileName, QSettings::Format format, QString SettingsCache::guessConfigurationPath(QString & specificPath) { const QString fileName="servatrice.ini"; - #ifdef PORTABLE_BUILD - return fileName; - #endif + if(QFile::exists(qApp->applicationDirPath() + "/portable.dat")) + { + qDebug() << "Portable mode enabled"; + return fileName; + } + QString guessFileName; // specific path if(!specificPath.isEmpty() && QFile::exists(specificPath)) diff --git a/servatrice/src/settingscache.h b/servatrice/src/settingscache.h index 75e0a86f..d952ee06 100644 --- a/servatrice/src/settingscache.h +++ b/servatrice/src/settingscache.h @@ -10,10 +10,12 @@ class SettingsCache : public QSettings { Q_OBJECT private: QSettings *settings; + bool isPortableBuild; public: SettingsCache(const QString & fileName="servatrice.ini", QSettings::Format format=QSettings::IniFormat, QObject * parent = 0); static QString guessConfigurationPath(QString & specificPath); QList disallowedRegExp; + bool getIsPortableBuild() const { return isPortableBuild; } }; extern SettingsCache *settingsCache;