From f6ef5477e718b14af99bd22436f66dee0b9d22cd Mon Sep 17 00:00:00 2001 From: Tannin Date: Thu, 17 Jul 2014 22:02:15 +0200 Subject: normalized eol style (all files should now have windows line endings) --- src/settings.h | 626 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 313 insertions(+), 313 deletions(-) (limited to 'src/settings.h') diff --git a/src/settings.h b/src/settings.h index 0b051831..44e8088e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -1,313 +1,313 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#ifndef WORKAROUNDS_H -#define WORKAROUNDS_H - -#include "loadmechanism.h" -#include "serverinfo.h" -#include - -#include -#include -#include - - -/** - * manages the settings for Mod Organizer. The settings are not cached - * inside the class but read/written directly from/to disc - **/ -class Settings : public QObject -{ - - Q_OBJECT - -public: - - /** - * @brief constructor - **/ - Settings(); - - virtual ~Settings(); - - static Settings &instance(); - - /** - * unregister all plugins from settings - */ - void clearPlugins(); - - /** - * @brief register plugin to be configurable - * @param plugin the plugin to register - * @return true if the plugin may be registered, false if it is blacklisted - */ - void registerPlugin(MOBase::IPlugin *plugin); - - /** - * displays a SettingsDialog that allows the user to change settings. If the - * user accepts the changes, the settings are immediately written - **/ - void query(QWidget *parent); - - /** - * set up the settings for the specified plugins - **/ - void addPluginSettings(const std::vector &plugins); - - /** - * @return true if the user wants unchecked plugins (esp, esm) should be hidden from - * the virtual dat adirectory - **/ - bool hideUncheckedPlugins() const; - - /** - * @return true if files of the core game are forced-enabled so the user can't accidentally disable them - */ - bool forceEnableCoreFiles() const; - - /** - * @brief register download speed - * @param url complete download url - * @param bytesPerSecond download size in bytes per second - */ - void setDownloadSpeed(const QString &serverName, int bytesPerSecond); - - /** - * the steam appid is assigned by the steam platform to each product sold there. - * The appid may differ between different versions of a game so it may be impossible - * for Mod Organizer to automatically recognize it, though usually it does - * @return the steam appid for the game - **/ - QString getSteamAppID() const; - - /** - * retrieve the directory where downloads are stored (with native separators) - **/ - QString getDownloadDirectory() const; - - /** - * retrieve a sorted list of preferred servers - */ - std::map getPreferredServers(); - - /** - * retrieve the directory where mods are stored (with native separators) - **/ - QString getModDirectory() const; - - /** - * returns the version of nmm to impersonate when connecting to nexus - **/ - QString getNMMVersion() const; - - /** - * retrieve the directory where the web cache is stored (with native separators) - **/ - QString getCacheDirectory() const; - - /** - * @return true if the user has set up automatic login to nexus - **/ - bool automaticLoginEnabled() const; - - /** - * @brief retrieve the login information for nexus - * - * @param username (out) receives the user name for nexus - * @param password (out) received the password for nexus - * @return true if automatic login is active, false otherwise - **/ - bool getNexusLogin(QString &username, QString &password) const; - - /** - * @return true if the user disabled internet features - */ - bool offlineMode() const; - - /** - * @return true if the user chose compact downloads - */ - bool compactDownloads() const; - - /** - * @return true if the user chose meta downloads - */ - bool metaDownloads() const; - - /** - * @return the configured log level - */ - int logLevel() const; - - /** - * @brief set the nexus login information - * - * @param username username - * @param password password - */ - void setNexusLogin(QString username, QString password); - - /** - * @return the load mechanism to be used - **/ - LoadMechanism::EMechanism getLoadMechanism() const; - - /** - * @brief activate the load mechanism selected by the user - **/ - void setupLoadMechanism(); - - /** - * @return true if the user configured the use of a network proxy - */ - bool useProxy(); - - /** - * @return true if the user wants to see non-official plugins installed outside MO in his mod list - */ - bool displayForeign(); - - /** - * @brief sets the new motd hash - **/ - void setMotDHash(uint hash); - - /** - * @return hash of the last displayed message of the day - **/ - uint getMotDHash() const; - - /** - * @brief allows direct access to the wrapped QSettings object - * @return the wrapped QSettings object - */ - QSettings &directInterface() { return m_Settings; } - - /** - * @brief retrieve a setting for one of the installed plugins - * @param pluginName name of the plugin - * @param key name of the setting to retrieve - * @return the requested value as a QVariant - * @note an invalid QVariant is returned if the the plugin/setting is not declared - */ - QVariant pluginSetting(const QString &pluginName, const QString &key) const; - - /** - * @brief set a setting for one of the installed mods - * @param pluginName name of the plugin - * @param key name of the setting to change - * @param value the new value to set - * @throw an exception is thrown if pluginName is invalid - */ - void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value); - - /** - * @brief retrieve a persistent value for a plugin - * @param pluginName name of the plugin to store data for - * @param key id of the value to retrieve - * @param def default value to return if the value is not set - * @return the requested value - */ - QVariant pluginPersistent(const QString &pluginName, const QString &key, const QVariant &def) const; - - /** - * @brief set a persistent value for a plugin - * @param pluginName name of the plugin to store data for - * @param key id of the value to retrieve - * @param value value to set - * @throw an exception is thrown if pluginName is invalid - */ - void setPluginPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync); - - /** - * @return short code of the configured language (corresponding to the translation files) - */ - QString language(); - - /** - * @brief updates the list of known servers - * @param list of servers from a recent query - */ - void updateServers(const QList &servers); - - /** - * @brief add a plugin that is to be blacklisted - * @param fileName name of the plugin to blacklist - */ - void addBlacklistPlugin(const QString &fileName); - - /** - * @brief test if a plugin is blacklisted and shouldn't be loaded - * @param fileName name of the plugin - * @return true if the file is blacklisted - */ - bool pluginBlacklisted(const QString &fileName) const; - - /** - * @return all loaded MO plugins - */ - std::vector plugins() const { return m_Plugins; } - - /** - * @brief register MO as the handler for nxm links - * @param force set to true to enforce the registration dialog to show up, - * even if the user said earlier not to - */ - void registerAsNXMHandler(bool force); - -private: - - QString obfuscate(const QString &password) const; - QString deObfuscate(const QString &password) const; - - void addLanguages(QComboBox *languageBox); - void addStyles(QComboBox *styleBox); - bool isNXMHandler(bool *modifyable); - void setNXMHandlerActive(bool active, bool writable); - void readPluginBlacklist(); - void writePluginBlacklist(); - -private slots: - - void resetDialogs(); - -signals: - - void languageChanged(const QString &newLanguage); - void styleChanged(const QString &newStyle); - -private: - - static Settings *s_Instance; - - QSettings m_Settings; - - LoadMechanism m_LoadMechanism; - - std::vector m_Plugins; - - QMap > m_PluginSettings; - - QSet m_PluginBlacklist; - -}; - -#endif // WORKAROUNDS_H +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#ifndef WORKAROUNDS_H +#define WORKAROUNDS_H + +#include "loadmechanism.h" +#include "serverinfo.h" +#include + +#include +#include +#include + + +/** + * manages the settings for Mod Organizer. The settings are not cached + * inside the class but read/written directly from/to disc + **/ +class Settings : public QObject +{ + + Q_OBJECT + +public: + + /** + * @brief constructor + **/ + Settings(); + + virtual ~Settings(); + + static Settings &instance(); + + /** + * unregister all plugins from settings + */ + void clearPlugins(); + + /** + * @brief register plugin to be configurable + * @param plugin the plugin to register + * @return true if the plugin may be registered, false if it is blacklisted + */ + void registerPlugin(MOBase::IPlugin *plugin); + + /** + * displays a SettingsDialog that allows the user to change settings. If the + * user accepts the changes, the settings are immediately written + **/ + void query(QWidget *parent); + + /** + * set up the settings for the specified plugins + **/ + void addPluginSettings(const std::vector &plugins); + + /** + * @return true if the user wants unchecked plugins (esp, esm) should be hidden from + * the virtual dat adirectory + **/ + bool hideUncheckedPlugins() const; + + /** + * @return true if files of the core game are forced-enabled so the user can't accidentally disable them + */ + bool forceEnableCoreFiles() const; + + /** + * @brief register download speed + * @param url complete download url + * @param bytesPerSecond download size in bytes per second + */ + void setDownloadSpeed(const QString &serverName, int bytesPerSecond); + + /** + * the steam appid is assigned by the steam platform to each product sold there. + * The appid may differ between different versions of a game so it may be impossible + * for Mod Organizer to automatically recognize it, though usually it does + * @return the steam appid for the game + **/ + QString getSteamAppID() const; + + /** + * retrieve the directory where downloads are stored (with native separators) + **/ + QString getDownloadDirectory() const; + + /** + * retrieve a sorted list of preferred servers + */ + std::map getPreferredServers(); + + /** + * retrieve the directory where mods are stored (with native separators) + **/ + QString getModDirectory() const; + + /** + * returns the version of nmm to impersonate when connecting to nexus + **/ + QString getNMMVersion() const; + + /** + * retrieve the directory where the web cache is stored (with native separators) + **/ + QString getCacheDirectory() const; + + /** + * @return true if the user has set up automatic login to nexus + **/ + bool automaticLoginEnabled() const; + + /** + * @brief retrieve the login information for nexus + * + * @param username (out) receives the user name for nexus + * @param password (out) received the password for nexus + * @return true if automatic login is active, false otherwise + **/ + bool getNexusLogin(QString &username, QString &password) const; + + /** + * @return true if the user disabled internet features + */ + bool offlineMode() const; + + /** + * @return true if the user chose compact downloads + */ + bool compactDownloads() const; + + /** + * @return true if the user chose meta downloads + */ + bool metaDownloads() const; + + /** + * @return the configured log level + */ + int logLevel() const; + + /** + * @brief set the nexus login information + * + * @param username username + * @param password password + */ + void setNexusLogin(QString username, QString password); + + /** + * @return the load mechanism to be used + **/ + LoadMechanism::EMechanism getLoadMechanism() const; + + /** + * @brief activate the load mechanism selected by the user + **/ + void setupLoadMechanism(); + + /** + * @return true if the user configured the use of a network proxy + */ + bool useProxy(); + + /** + * @return true if the user wants to see non-official plugins installed outside MO in his mod list + */ + bool displayForeign(); + + /** + * @brief sets the new motd hash + **/ + void setMotDHash(uint hash); + + /** + * @return hash of the last displayed message of the day + **/ + uint getMotDHash() const; + + /** + * @brief allows direct access to the wrapped QSettings object + * @return the wrapped QSettings object + */ + QSettings &directInterface() { return m_Settings; } + + /** + * @brief retrieve a setting for one of the installed plugins + * @param pluginName name of the plugin + * @param key name of the setting to retrieve + * @return the requested value as a QVariant + * @note an invalid QVariant is returned if the the plugin/setting is not declared + */ + QVariant pluginSetting(const QString &pluginName, const QString &key) const; + + /** + * @brief set a setting for one of the installed mods + * @param pluginName name of the plugin + * @param key name of the setting to change + * @param value the new value to set + * @throw an exception is thrown if pluginName is invalid + */ + void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value); + + /** + * @brief retrieve a persistent value for a plugin + * @param pluginName name of the plugin to store data for + * @param key id of the value to retrieve + * @param def default value to return if the value is not set + * @return the requested value + */ + QVariant pluginPersistent(const QString &pluginName, const QString &key, const QVariant &def) const; + + /** + * @brief set a persistent value for a plugin + * @param pluginName name of the plugin to store data for + * @param key id of the value to retrieve + * @param value value to set + * @throw an exception is thrown if pluginName is invalid + */ + void setPluginPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync); + + /** + * @return short code of the configured language (corresponding to the translation files) + */ + QString language(); + + /** + * @brief updates the list of known servers + * @param list of servers from a recent query + */ + void updateServers(const QList &servers); + + /** + * @brief add a plugin that is to be blacklisted + * @param fileName name of the plugin to blacklist + */ + void addBlacklistPlugin(const QString &fileName); + + /** + * @brief test if a plugin is blacklisted and shouldn't be loaded + * @param fileName name of the plugin + * @return true if the file is blacklisted + */ + bool pluginBlacklisted(const QString &fileName) const; + + /** + * @return all loaded MO plugins + */ + std::vector plugins() const { return m_Plugins; } + + /** + * @brief register MO as the handler for nxm links + * @param force set to true to enforce the registration dialog to show up, + * even if the user said earlier not to + */ + void registerAsNXMHandler(bool force); + +private: + + QString obfuscate(const QString &password) const; + QString deObfuscate(const QString &password) const; + + void addLanguages(QComboBox *languageBox); + void addStyles(QComboBox *styleBox); + bool isNXMHandler(bool *modifyable); + void setNXMHandlerActive(bool active, bool writable); + void readPluginBlacklist(); + void writePluginBlacklist(); + +private slots: + + void resetDialogs(); + +signals: + + void languageChanged(const QString &newLanguage); + void styleChanged(const QString &newStyle); + +private: + + static Settings *s_Instance; + + QSettings m_Settings; + + LoadMechanism m_LoadMechanism; + + std::vector m_Plugins; + + QMap > m_PluginSettings; + + QSet m_PluginBlacklist; + +}; + +#endif // WORKAROUNDS_H -- cgit v1.3.1 From 5d4a55c3a67caec2811a2b997753adc116403d77 Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 1 Aug 2014 12:10:49 +0200 Subject: - performance optimizations for detection of file changes in mod directories - added a workaround for download-link handling in chrome - MO will now create a profile even if the game-launcher has never been run - bugfix: files weren't cleanly removed from vfs file register --- src/ModOrganizer.pro | 2 +- src/organizer.pro | 8 +++-- src/settings.cpp | 45 +++++--------------------- src/settings.h | 3 -- src/shared/directoryentry.cpp | 74 +++++++++++++++++++++++++++++++++++++------ src/shared/directoryentry.h | 36 +++++++++++++-------- src/shared/shared.pro | 4 +-- src/shared/skyriminfo.cpp | 4 ++- src/shared/util.cpp | 18 +++++------ 9 files changed, 115 insertions(+), 79 deletions(-) (limited to 'src/settings.h') diff --git a/src/ModOrganizer.pro b/src/ModOrganizer.pro index 9907d086..1624c5d0 100644 --- a/src/ModOrganizer.pro +++ b/src/ModOrganizer.pro @@ -12,7 +12,7 @@ SUBDIRS = bsatk \ proxydll \ nxmhandler \ BossDummy \ - pythonRunner \ +# pythonRunner \ esptk plugins.depends = pythonRunner diff --git a/src/organizer.pro b/src/organizer.pro index 994da396..76f95955 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -235,10 +235,14 @@ OTHER_FILES += \ # leak detection with vld -#INCLUDEPATH += "E:/Visual Leak Detector/include" -#LIBS += -L"E:/Visual Leak Detector/lib/Win32" +INCLUDEPATH += "E:/Visual Leak Detector/include" +LIBS += -L"E:/Visual Leak Detector/lib/Win32" #DEFINES += LEAK_CHECK_WITH_VLD +# custom leak detection +LIBS += -lDbgHelp + +# model tests #SOURCES += modeltest.cpp #HEADERS += modeltest.h #DEFINES += TEST_MODELS diff --git a/src/settings.cpp b/src/settings.cpp index 1cc168dd..6bf13ee0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -25,6 +25,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include @@ -32,6 +33,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include using namespace MOBase; @@ -107,8 +109,12 @@ void Settings::registerAsNXMHandler(bool force) std::wstring nxmPath = ToWString(QCoreApplication::applicationDirPath() + "/nxmhandler.exe"); std::wstring executable = ToWString(QCoreApplication::applicationFilePath()); std::wstring mode = force ? L"forcereg" : L"reg"; - ::ShellExecuteW(NULL, L"open", nxmPath.c_str(), - (mode + L" " + GameInfo::instance().getGameShortName() + L" \"" + executable + L"\"").c_str(), NULL, SW_SHOWNORMAL); + std::wstring parameters = mode + L" " + GameInfo::instance().getGameShortName() + L" \"" + executable + L"\""; + HINSTANCE res = ::ShellExecuteW(NULL, L"open", nxmPath.c_str(), parameters.c_str(), NULL, SW_SHOWNORMAL); + if ((int)res <= 32) { + QMessageBox::critical(NULL, tr("Failed"), + tr("Sorry, failed to start the helper application")); + } } void Settings::registerPlugin(IPlugin *plugin) @@ -460,41 +466,6 @@ void Settings::addStyles(QComboBox *styleBox) } } -bool Settings::isNXMHandler(bool *modifyable) -{ - QSettings handlerReg("HKEY_CURRENT_USER\\Software\\Classes\\nxm\\shell\\open\\command", - QSettings::NativeFormat); - - QString currentExe = handlerReg.value("Default", "").toString().toUtf8().constData(); - QString myExe = QString("\"%1\" ").arg(QDir::toNativeSeparators(QCoreApplication::applicationFilePath())).append("\"%1\""); - if (modifyable != NULL) { - handlerReg.setValue("Default", currentExe); - handlerReg.sync(); - - *modifyable = handlerReg.status() == QSettings::NoError; - // QSettings::isWritable returns wrong results... - } - return currentExe == myExe; -} - - -void Settings::setNXMHandlerActive(bool active, bool writable) -{ -// QSettings handlerReg("HKEY_CLASSES_ROOT\\nxm\\", QSettings::NativeFormat); - QSettings handlerReg("HKEY_CURRENT_USER\\Software\\Classes\\nxm\\", QSettings::NativeFormat); - - if (writable) { - QString myExe = QString("\"%1\" ").arg(QDir::toNativeSeparators(QCoreApplication::applicationFilePath())).append("\"%1\""); - handlerReg.setValue("Default", "URL:NXM Protocol"); - handlerReg.setValue("URL Protocol", ""); - handlerReg.setValue("shell/open/command/Default", active ? myExe : ""); - handlerReg.sync(); - } else { - Helper::setNXMHandler(GameInfo::instance().getOrganizerDirectory(), active); - } -} - - void Settings::resetDialogs() { m_Settings.beginGroup("DialogChoices"); diff --git a/src/settings.h b/src/settings.h index 44e8088e..40fd2c5a 100644 --- a/src/settings.h +++ b/src/settings.h @@ -272,7 +272,6 @@ public: * even if the user said earlier not to */ void registerAsNXMHandler(bool force); - private: QString obfuscate(const QString &password) const; @@ -280,8 +279,6 @@ private: void addLanguages(QComboBox *languageBox); void addStyles(QComboBox *styleBox); - bool isNXMHandler(bool *modifyable); - void setNXMHandlerActive(bool active, bool writable); void readPluginBlacklist(); void writePluginBlacklist(); diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index aead0cca..e0914870 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see . #define WIN32_LEAN_AND_MEAN #include #include +#include #include #include #include "error_report.h" @@ -28,6 +29,7 @@ along with Mod Organizer. If not, see . #include "windows_error.h" #include #include +#include #include "util.h" #include "leaktrace.h" @@ -133,13 +135,11 @@ private: // -void FilesOrigin::enable(bool enabled) +void FilesOrigin::enable(bool enabled, time_t notAfter) { if (!enabled) { std::set copy = m_Files; - for (auto iter = copy.begin(); iter != copy.end(); ++iter) { - m_FileRegister.lock()->removeOrigin(*iter, m_ID); - } + m_FileRegister.lock()->removeOriginMulti(copy, m_ID, notAfter); m_Files.clear(); } m_Disabled = !enabled; @@ -233,6 +233,7 @@ std::vector FilesOrigin::getFiles() const void FileEntry::addOrigin(int origin, FILETIME fileTime, const std::wstring &archive) { + m_LastAccessed = time(NULL); if (m_Parent != NULL) { m_Parent->propagateOrigin(origin); } @@ -324,13 +325,13 @@ static bool ByOriginPriority(DirectoryEntry *entry, int LHS, int RHS) FileEntry::FileEntry() - : m_Index(UINT_MAX), m_Name(), m_Parent(NULL) + : m_Index(UINT_MAX), m_Name(), m_Parent(NULL), m_LastAccessed(time(NULL)) { LEAK_TRACE; } FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) - : m_Index(index), m_Name(name), m_Parent(parent), m_Origin(-1), m_Archive(L"") + : m_Index(index), m_Name(name), m_Parent(parent), m_Origin(-1), m_Archive(L""), m_LastAccessed(time(NULL)) { LEAK_TRACE; } @@ -654,6 +655,18 @@ void DirectoryEntry::removeFile(FileEntry::Index index) } +void DirectoryEntry::removeFiles(const std::set &indices) +{ + for (auto iter = m_Files.begin(); iter != m_Files.end();) { + if (indices.find(iter->second) != indices.end()) { + m_Files.erase(iter++); + } else { + ++iter; + } + } +} + + int DirectoryEntry::anyOrigin() const { bool ignore; @@ -737,7 +750,7 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const if (len == std::string::npos) { // no more path components - auto iter = m_Files.find(path); + auto iter = m_Files.find(ToLower(path)); if (iter != m_Files.end()) { return m_FileRegister->getFile(iter->second); } else if (directory != NULL) { @@ -777,7 +790,7 @@ DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &pa const FileEntry::Ptr DirectoryEntry::findFile(const std::wstring &name) const { - auto iter = m_Files.find(name); + auto iter = m_Files.find(ToLower(name)); if (iter != m_Files.end()) { return m_FileRegister->getFile(iter->second); } else { @@ -865,7 +878,6 @@ FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const return FileEntry::Ptr(); } - void FileRegister::unregisterFile(FileEntry::Ptr file) { bool ignore; @@ -890,6 +902,8 @@ void FileRegister::removeFile(FileEntry::Index index) if (iter != m_Files.end()) { unregisterFile(iter->second); m_Files.erase(index); + } else { + log("invalid file index for remove: %lu", index); } } @@ -899,7 +913,49 @@ void FileRegister::removeOrigin(FileEntry::Index index, int originID) if (iter != m_Files.end()) { if (iter->second->removeOrigin(originID)) { unregisterFile(iter->second); + m_Files.erase(iter); } + } else { + log("invalid file index for remove (for origin): %lu", index); + } +} + +void FileRegister::removeOriginMulti(std::set indices, int originID, time_t notAfter) +{ + std::vector removedFiles; + for (auto iter = indices.begin(); iter != indices.end();) { + auto pos = m_Files.find(*iter); + if (pos != m_Files.end() + && (pos->second->lastAccessed() < notAfter) + && pos->second->removeOrigin(originID)) { + removedFiles.push_back(pos->second); + m_Files.erase(pos); + ++iter; + } else { + indices.erase(iter++); + } + } + + if (removedFiles.size() > 0) { + log("%d files actually removed", removedFiles.size()); + } + + // optimization: this is only called when disabling an origin and in this case we don't have + // to remove the file from the origin + + // need to remove files from their parent directories. multiple ways to go about this: + // a) for each file, search its parents file-list (preferably by name) and remove what is found + // b) gather the parent directories, go through the file list for each once and remove all files that have been removed + // the latter should be faster when there are many files in few directories. since this is called + // only when disabling an origin that is probably frequently the case + std::set parents; + BOOST_FOREACH (const FileEntry::Ptr &file, removedFiles) { + if (file->getParent() != NULL) { + parents.insert(file->getParent()); + } + } + BOOST_FOREACH (DirectoryEntry *parent, parents) { + parent->removeFiles(indices); } } diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 7b656b05..1e904311 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -34,6 +34,11 @@ along with Mod Organizer. If not, see . #include "util.h" + +#include "error_report.h" + + + namespace MOShared { @@ -60,6 +65,8 @@ public: Index getIndex() const { return m_Index; } + time_t lastAccessed() const { return m_LastAccessed; } + void addOrigin(int origin, FILETIME fileTime, const std::wstring &archive); // remove the specified origin from the list of origins that contain this file. if no origin is left, // the file is effectively deleted and true is returned. otherwise, false is returned @@ -98,6 +105,8 @@ private: DirectoryEntry *m_Parent; mutable FILETIME m_FileTime; + time_t m_LastAccessed; + friend bool operator<(const FileEntry &lhs, const FileEntry &rhs) { return _wcsicmp(lhs.m_Name.c_str(), rhs.m_Name.c_str()) < 0; } @@ -130,7 +139,7 @@ public: std::vector getFiles() const; - void enable(bool enabled); + void enable(bool enabled, time_t notAfter = LONG_MAX); bool isDisabled() const { return m_Disabled; } void addFile(FileEntry::Index index) { m_Files.insert(index); } @@ -171,8 +180,11 @@ public: FileEntry::Ptr createFile(const std::wstring &name, DirectoryEntry *parent); FileEntry::Ptr getFile(FileEntry::Index index) const; + size_t size() const { return m_Files.size(); } + void removeFile(FileEntry::Index index); void removeOrigin(FileEntry::Index index, int originID); + void removeOriginMulti(std::set indices, int originID, time_t notAfter); void sortOrigins(); @@ -258,7 +270,7 @@ public: void removeDir(const std::wstring &path); void remove(const std::wstring &fileName, int *origin) { - auto iter = m_Files.find(fileName); + auto iter = m_Files.find(ToLower(fileName)); if (iter != m_Files.end()) { if (origin != NULL) { FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); @@ -268,6 +280,8 @@ public: } } m_FileRegister->removeFile(iter->second); + } else { + log ("failed to remove %ls from %ls", fileName.c_str(), m_Name.c_str()); } } @@ -275,20 +289,23 @@ public: FilesOrigin &createOrigin(const std::wstring &originName, const std::wstring &directory, int priority); + void removeFiles(const std::set &indices); + private: DirectoryEntry(const DirectoryEntry &reference); DirectoryEntry &operator=(const DirectoryEntry &reference); void insert(const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive) { - auto iter = m_Files.find(fileName); + std::wstring fileNameLower = ToLower(fileName); + auto iter = m_Files.find(fileNameLower); FileEntry::Ptr file; if (iter != m_Files.end()) { file = m_FileRegister->getFile(iter->second); } else { file = m_FileRegister->createFile(fileName, this); // TODO this has been observed to cause a crash, no clue why - m_Files[fileName] = file->getIndex(); + m_Files[fileNameLower] = file->getIndex(); } file->addOrigin(origin.getID(), fileTime, archive); origin.addFile(file->getIndex()); @@ -305,22 +322,13 @@ private: void removeDirRecursive(); -private: - - struct WStrLess { - bool operator()(const std::wstring &LHS, const std::wstring &RHS) const { - return _wcsicmp(LHS.c_str(), RHS.c_str()) < 0; - } - }; - - private: boost::shared_ptr m_FileRegister; boost::shared_ptr m_OriginConnection; std::wstring m_Name; - std::map m_Files; + std::map m_Files; std::vector m_SubDirectories; DirectoryEntry *m_Parent; diff --git a/src/shared/shared.pro b/src/shared/shared.pro index c5925c24..84a9c274 100644 --- a/src/shared/shared.pro +++ b/src/shared/shared.pro @@ -15,8 +15,8 @@ INCLUDEPATH += ../bsatk "$(BOOSTPATH)" # only for custom leak detection -#DEFINES += TRACE_LEAKS -#LIBS += -lDbgHelp +DEFINES += TRACE_LEAKS +LIBS += -lDbgHelp CONFIG(debug, debug|release) { diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 5017da38..72452255 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -273,7 +273,9 @@ void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults) std::wostringstream source; source << getMyGamesDirectory() << L"\\Skyrim\\skyrimprefs.ini"; if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) { - if (::GetLastError() != ERROR_FILE_EXISTS) { + log("failed to copy ini file %ls", source.str().c_str()); + // create empty + if (::CreateFileW(target.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) == INVALID_HANDLE_VALUE) { std::ostringstream stream; stream << "failed to copy ini file: " << ToString(source.str(), false); throw windows_error(stream.str()); diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 22657e6c..61ca9cdc 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -86,30 +86,28 @@ std::wstring ToWString(const std::string &source, bool utf8) std::string &ToLower(std::string &text) { - std::transform(text.begin(), text.end(), text.begin(), tolower); + std::transform(text.begin(), text.end(), text.begin(), ::tolower); return text; } std::string ToLower(const std::string &text) { - std::string temp = text; - - std::transform(temp.begin(), temp.end(), temp.begin(), tolower); - return temp; + std::string result = text; + std::transform(result.begin(), result.end(), result.begin(), ::tolower); + return result; } std::wstring &ToLower(std::wstring &text) { - std::transform(text.begin(), text.end(), text.begin(), towlower); + std::transform(text.begin(), text.end(), text.begin(), ::towlower); return text; } std::wstring ToLower(const std::wstring &text) { - std::wstring temp = text; - - std::transform(temp.begin(), temp.end(), temp.begin(), towlower); - return temp; + std::wstring result = text; + std::transform(result.begin(), result.end(), result.begin(), ::towlower); + return result; } VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName) -- cgit v1.3.1 From 12a57d7f6dfbff8a5de618fc7f09066b83efa78f Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 10 Sep 2014 20:34:31 +0200 Subject: - descriptions for plugin settings are now displayed --- src/settings.cpp | 1486 ++++++++++++++++++++++++------------------------ src/settings.h | 621 ++++++++++---------- src/settingsdialog.cpp | 355 ++++++------ 3 files changed, 1238 insertions(+), 1224 deletions(-) (limited to 'src/settings.h') diff --git a/src/settings.cpp b/src/settings.cpp index 65ea3a27..04a5f279 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,741 +1,745 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#include "settings.h" - -#include "settingsdialog.h" -#include "utility.h" -#include "helper.h" -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - - -using namespace MOBase; -using namespace MOShared; - - -template -class QListWidgetItemEx : public QListWidgetItem { -public: - QListWidgetItemEx(const QString &text, int sortRole = Qt::DisplayRole, QListWidget *parent = 0, int type = Type) - : QListWidgetItem(text, parent, type), m_SortRole(sortRole) {} - - virtual bool operator< ( const QListWidgetItem & other ) const { - return this->data(m_SortRole).value() < other.data(m_SortRole).value(); - } -private: - int m_SortRole; -}; - - -static const unsigned char Key2[20] = { 0x99, 0xb8, 0x76, 0x42, 0x3e, 0xc1, 0x60, 0xa4, 0x5b, 0x01, - 0xdb, 0xf8, 0x43, 0x3a, 0xb7, 0xb6, 0x98, 0xd4, 0x7d, 0xa2 }; - -Settings *Settings::s_Instance = NULL; - - -Settings::Settings() - : m_Settings(ToQString(GameInfo::instance().getIniFilename()), QSettings::IniFormat) -{ - if (s_Instance != NULL) { - throw std::runtime_error("second instance of \"Settings\" created"); - } else { - s_Instance = this; - } -} - - -Settings::~Settings() -{ - s_Instance = NULL; -} - - -Settings &Settings::instance() -{ - if (s_Instance == NULL) { - throw std::runtime_error("no instance of \"Settings\""); - } - return *s_Instance; -} - -void Settings::clearPlugins() -{ - m_Plugins.clear(); - m_PluginSettings.clear(); - - m_PluginBlacklist.clear(); - int count = m_Settings.beginReadArray("pluginBlacklist"); - for (int i = 0; i < count; ++i) { - m_Settings.setArrayIndex(i); - m_PluginBlacklist.insert(m_Settings.value("name").toString()); - } - m_Settings.endArray(); -} - -bool Settings::pluginBlacklisted(const QString &fileName) const -{ - return m_PluginBlacklist.contains(fileName); -} - -void Settings::registerAsNXMHandler(bool force) -{ - std::wstring nxmPath = ToWString(QCoreApplication::applicationDirPath() + "/nxmhandler.exe"); - std::wstring executable = ToWString(QCoreApplication::applicationFilePath()); - std::wstring mode = force ? L"forcereg" : L"reg"; - std::wstring parameters = mode + L" " + GameInfo::instance().getGameShortName() + L" \"" + executable + L"\""; - HINSTANCE res = ::ShellExecuteW(NULL, L"open", nxmPath.c_str(), parameters.c_str(), NULL, SW_SHOWNORMAL); - if ((int)res <= 32) { - QMessageBox::critical(NULL, tr("Failed"), - tr("Sorry, failed to start the helper application")); - } -} - -void Settings::registerPlugin(IPlugin *plugin) -{ - m_Plugins.push_back(plugin); - m_PluginSettings.insert(plugin->name(), QMap()); - foreach (const PluginSetting &setting, plugin->settings()) { - QVariant temp = m_Settings.value("Plugins/" + plugin->name() + "/" + setting.key, setting.defaultValue); - if (!temp.convert(setting.defaultValue.type())) { - qWarning("failed to interpret \"%s\" as correct type for \"%s\" in plugin \"%s\", using default", - qPrintable(temp.toString()), qPrintable(setting.key), qPrintable(plugin->name())); - temp = setting.defaultValue; - } - m_PluginSettings[plugin->name()][setting.key] = temp; - } -} - -QString Settings::obfuscate(const QString &password) const -{ - QByteArray temp = password.toUtf8(); - - QByteArray buffer; - for (int i = 0; i < temp.length(); ++i) { - buffer.append(temp.at(i) ^ Key2[i % 20]); - } - return buffer.toBase64(); -} - - -QString Settings::deObfuscate(const QString &password) const -{ - QByteArray temp(QByteArray::fromBase64(password.toUtf8())); - - QByteArray buffer; - for (int i = 0; i < temp.length(); ++i) { - buffer.append(temp.at(i) ^ Key2[i % 20]); - } - return QString::fromUtf8(buffer.constData()); -} - - -bool Settings::hideUncheckedPlugins() const -{ - return m_Settings.value("Settings/hide_unchecked_plugins", false).toBool(); -} - -bool Settings::forceEnableCoreFiles() const -{ - return m_Settings.value("Settings/force_enable_core_files", true).toBool(); -} - -bool Settings::automaticLoginEnabled() const -{ - return m_Settings.value("Settings/nexus_login", false).toBool(); -} - -QString Settings::getSteamAppID() const -{ - return m_Settings.value("Settings/app_id", ToQString(GameInfo::instance().getSteamAPPId(m_Settings.value("game_edition", 0).toInt()))).toString(); -} - -QString Settings::getDownloadDirectory() const -{ - return QDir::toNativeSeparators(m_Settings.value("Settings/download_directory", ToQString(GameInfo::instance().getDownloadDir())).toString()); -} - - -void Settings::setDownloadSpeed(const QString &serverName, int bytesPerSecond) -{ - m_Settings.beginGroup("Servers"); - - foreach (const QString &serverKey, m_Settings.childKeys()) { - QVariantMap data = m_Settings.value(serverKey).toMap(); - if (serverKey == serverName) { - data["downloadCount"] = data["downloadCount"].toInt() + 1; - data["downloadSpeed"] = data["downloadSpeed"].toDouble() + static_cast(bytesPerSecond); - m_Settings.setValue(serverKey, data); - } - } - - m_Settings.endGroup(); - m_Settings.sync(); -} - -std::map Settings::getPreferredServers() -{ - std::map result; - m_Settings.beginGroup("Servers"); - - foreach (const QString &serverKey, m_Settings.childKeys()) { - QVariantMap data = m_Settings.value(serverKey).toMap(); - int preference = data["preferred"].toInt(); - if (preference > 0) { - result[serverKey] = preference; - } - } - m_Settings.endGroup(); - - return result; -} - -QString Settings::getCacheDirectory() const -{ - return QDir::toNativeSeparators(m_Settings.value("Settings/cache_directory", ToQString(GameInfo::instance().getCacheDir())).toString()); -} - -QString Settings::getModDirectory() const -{ - return QDir::toNativeSeparators(m_Settings.value("Settings/mod_directory", ToQString(GameInfo::instance().getModsDir())).toString()); -} - -QString Settings::getNMMVersion() const -{ - static const QString MIN_NMM_VERSION = "0.47.0"; - QString result = m_Settings.value("Settings/nmm_version", MIN_NMM_VERSION).toString(); - if (VersionInfo(result) < VersionInfo(MIN_NMM_VERSION)) { - result = MIN_NMM_VERSION; - } - return result; -} - -bool Settings::getNexusLogin(QString &username, QString &password) const -{ - if (m_Settings.value("Settings/nexus_login", false).toBool()) { - username = m_Settings.value("Settings/nexus_username", "").toString(); - password = deObfuscate(m_Settings.value("Settings/nexus_password", "").toString()); - return true; - } else { - return false; - } -} - -bool Settings::compactDownloads() const -{ - return m_Settings.value("Settings/compact_downloads", false).toBool(); -} - -bool Settings::metaDownloads() const -{ - return m_Settings.value("Settings/meta_downloads", false).toBool(); -} - -bool Settings::offlineMode() const -{ - return m_Settings.value("Settings/offline_mode", false).toBool(); -} - -int Settings::logLevel() const -{ - return m_Settings.value("Settings/log_level", 0).toInt(); -} - - -void Settings::setNexusLogin(QString username, QString password) -{ - m_Settings.setValue("Settings/nexus_login", true); - m_Settings.setValue("Settings/nexus_username", username); - m_Settings.setValue("Settings/nexus_password", obfuscate(password)); -} - - -LoadMechanism::EMechanism Settings::getLoadMechanism() const -{ - switch (m_Settings.value("Settings/load_mechanism").toInt()) { - case LoadMechanism::LOAD_MODORGANIZER: return LoadMechanism::LOAD_MODORGANIZER; - case LoadMechanism::LOAD_SCRIPTEXTENDER: return LoadMechanism::LOAD_SCRIPTEXTENDER; - case LoadMechanism::LOAD_PROXYDLL: return LoadMechanism::LOAD_PROXYDLL; - } - throw std::runtime_error("invalid load mechanism"); -} - - -void Settings::setupLoadMechanism() -{ - m_LoadMechanism.activate(getLoadMechanism()); -} - - -bool Settings::useProxy() -{ - return m_Settings.value("Settings/use_proxy", false).toBool(); -} - -bool Settings::displayForeign() -{ - return m_Settings.value("Settings/display_foreign", true).toBool(); -} - -void Settings::setMotDHash(uint hash) -{ - m_Settings.setValue("motd_hash", hash); -} - -uint Settings::getMotDHash() const -{ - return m_Settings.value("motd_hash", 0).toUInt(); -} - -QVariant Settings::pluginSetting(const QString &pluginName, const QString &key) const -{ - auto iterPlugin = m_PluginSettings.find(pluginName); - if (iterPlugin == m_PluginSettings.end()) { - return QVariant(); - } - auto iterSetting = iterPlugin->find(key); - if (iterSetting == iterPlugin->end()) { - return QVariant(); - } - - return *iterSetting; -} - -void Settings::setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value) -{ - auto iterPlugin = m_PluginSettings.find(pluginName); - if (iterPlugin == m_PluginSettings.end()) { - throw MyException(tr("attempt to store setting for unknown plugin \"%1\"").arg(pluginName)); - } - - // store the new setting both in memory and in the ini - m_PluginSettings[pluginName][key] = value; - m_Settings.setValue("Plugins/" + pluginName + "/" + key, value); -} - -QVariant Settings::pluginPersistent(const QString &pluginName, const QString &key, const QVariant &def) const -{ - if (!m_PluginSettings.contains(pluginName)) { - return def; - } - return m_Settings.value("PluginPersistance/" + pluginName + "/" + key, def); -} - -void Settings::setPluginPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync) -{ - if (!m_PluginSettings.contains(pluginName)) { - throw MyException(tr("attempt to store setting for unknown plugin \"%1\"").arg(pluginName)); - } - m_Settings.setValue("PluginPersistance/" + pluginName + "/" + key, value); - if (sync) { - m_Settings.sync(); - } -} - -QString Settings::language() -{ - QString result = m_Settings.value("Settings/language", "").toString(); - if (result.isEmpty()) { - QStringList languagePreferences = QLocale::system().uiLanguages(); - if (languagePreferences.length() > 0) { - // the users most favoritest language - result = languagePreferences.at(0); - } else { - // fallback system locale - result = QLocale::system().name(); - } - } - return result; -} - -void Settings::updateServers(const QList &servers) -{ - m_Settings.beginGroup("Servers"); - QStringList oldServerKeys = m_Settings.childKeys(); - - foreach (const ServerInfo &server, servers) { - if (!oldServerKeys.contains(server.name)) { - // not yet known server - QVariantMap newVal; - newVal["premium"] = server.premium; - newVal["preferred"] = server.preferred ? 1 : 0; - newVal["lastSeen"] = server.lastSeen; - newVal["downloadCount"] = 0; - newVal["downloadSpeed"] = 0.0; - - m_Settings.setValue(server.name, newVal); - } else { - QVariantMap data = m_Settings.value(server.name).toMap(); - data["lastSeen"] = server.lastSeen; - data["premium"] = server.premium; - - m_Settings.setValue(server.name, data); - } - } - - m_Settings.endGroup(); - m_Settings.sync(); -} - -void Settings::addBlacklistPlugin(const QString &fileName) -{ - m_PluginBlacklist.insert(fileName); - writePluginBlacklist(); -} - -void Settings::writePluginBlacklist() -{ - m_Settings.beginWriteArray("pluginBlacklist"); - int idx = 0; - foreach (const QString &plugin, m_PluginBlacklist) { - m_Settings.setArrayIndex(idx++); - m_Settings.setValue("name", plugin); - } - - m_Settings.endArray(); -} - -void Settings::addLanguages(QComboBox *languageBox) -{ - languageBox->addItem("English", "en_US"); - - QDirIterator langIter(QCoreApplication::applicationDirPath() + "/translations", QDir::Files); - QString pattern = ToQString(AppConfig::translationPrefix()) + "_([a-z]{2,3}(_[A-Z]{2,2})?).qm"; - QRegExp exp(pattern); - while (langIter.hasNext()) { - langIter.next(); - QString file = langIter.fileName(); - if (exp.exactMatch(file)) { - QString languageCode = exp.cap(1); - QLocale locale(languageCode); - QString languageString = QLocale::languageToString(locale.language()); - if (locale.language() == QLocale::Chinese) { - if (languageCode == "zh_TW") { - languageString = "Chinese (traditional)"; - } else { - languageString = "Chinese (simplified)"; - } - } - languageBox->addItem(QString("%1").arg(languageString), exp.cap(1)); - } - } -} - -void Settings::addStyles(QComboBox *styleBox) -{ - styleBox->addItem("None", ""); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) - styleBox->addItem("Fusion", "Fusion"); -#else - styleBox->addItem("Plastique", "Plastique"); - styleBox->addItem("Cleanlooks", "Cleanlooks"); -#endif - - QDirIterator langIter(QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::stylesheetsPath()), QStringList("*.qss"), QDir::Files); - while (langIter.hasNext()) { - langIter.next(); - QString style = langIter.fileName(); - styleBox->addItem(style, style); - } -} - -void Settings::resetDialogs() -{ - m_Settings.beginGroup("DialogChoices"); - QStringList keys = m_Settings.childKeys(); - foreach (QString key, keys) { - m_Settings.remove(key); - } - - m_Settings.endGroup(); -} - - -void Settings::query(QWidget *parent) -{ - SettingsDialog dialog(parent); - - connect(&dialog, SIGNAL(resetDialogs()), this, SLOT(resetDialogs())); - - // General Page - QComboBox *languageBox = dialog.findChild("languageBox"); - QComboBox *styleBox = dialog.findChild("styleBox"); - QComboBox *logLevelBox = dialog.findChild("logLevelBox"); - QCheckBox *compactBox = dialog.findChild("compactBox"); - QCheckBox *showMetaBox = dialog.findChild("showMetaBox"); - - QLineEdit *downloadDirEdit = dialog.findChild("downloadDirEdit"); - QLineEdit *modDirEdit = dialog.findChild("modDirEdit"); - QLineEdit *cacheDirEdit = dialog.findChild("cacheDirEdit"); - - // nexus page - QCheckBox *loginCheckBox = dialog.findChild("loginCheckBox"); - QLineEdit *usernameEdit = dialog.findChild("usernameEdit"); - QLineEdit *passwordEdit = dialog.findChild("passwordEdit"); - QCheckBox *offlineBox = dialog.findChild("offlineBox"); - QCheckBox *proxyBox = dialog.findChild("proxyBox"); - - QListWidget *knownServersList = dialog.findChild("knownServersList"); - QListWidget *preferredServersList = dialog.findChild("preferredServersList"); - - // plugis page - QListWidget *pluginsList = dialog.findChild("pluginsList"); - QListWidget *pluginBlacklistList = dialog.findChild("pluginBlacklist"); - - // workarounds page - QCheckBox *forceEnableBox = dialog.findChild("forceEnableBox"); - QComboBox *mechanismBox = dialog.findChild("mechanismBox"); - QLineEdit *appIDEdit = dialog.findChild("appIDEdit"); - QLineEdit *nmmVersionEdit = dialog.findChild("nmmVersionEdit"); - QCheckBox *hideUncheckedBox = dialog.findChild("hideUncheckedBox"); - QCheckBox *displayForeignBox = dialog.findChild("displayForeignBox"); - - - // - // set up current settings - // - LoadMechanism::EMechanism mechanismID = getLoadMechanism(); - int index = 0; - - if (m_LoadMechanism.isDirectLoadingSupported()) { - mechanismBox->addItem(QObject::tr("Mod Organizer"), LoadMechanism::LOAD_MODORGANIZER); - if (mechanismID == LoadMechanism::LOAD_MODORGANIZER) { - index = mechanismBox->count() - 1; - } - } - - if (m_LoadMechanism.isScriptExtenderSupported()) { - mechanismBox->addItem(QObject::tr("Script Extender"), LoadMechanism::LOAD_SCRIPTEXTENDER); - if (mechanismID == LoadMechanism::LOAD_SCRIPTEXTENDER) { - index = mechanismBox->count() - 1; - } - } - - if (m_LoadMechanism.isProxyDLLSupported()) { - mechanismBox->addItem(QObject::tr("Proxy DLL"), LoadMechanism::LOAD_PROXYDLL); - if (mechanismID == LoadMechanism::LOAD_PROXYDLL) { - index = mechanismBox->count() - 1; - } - } - - mechanismBox->setCurrentIndex(index); - - { - addLanguages(languageBox); - QString languageCode = language(); - int currentID = languageBox->findData(languageCode); - // I made a mess. :( Most languages are stored with only the iso country code (2 characters like "de") but chinese - // with the exact language variant (zh_TW) so I have to search for both variants - if (currentID == -1) { - currentID = languageBox->findData(languageCode.mid(0, 2)); - } - if (currentID != -1) { - languageBox->setCurrentIndex(currentID); - } - } - - { - addStyles(styleBox); - int currentID = styleBox->findData(m_Settings.value("Settings/style", "").toString()); - if (currentID != -1) { - styleBox->setCurrentIndex(currentID); - } - } - - compactBox->setChecked(compactDownloads()); - showMetaBox->setChecked(metaDownloads()); - - hideUncheckedBox->setChecked(hideUncheckedPlugins()); - displayForeignBox->setChecked(displayForeign()); - forceEnableBox->setChecked(forceEnableCoreFiles()); - - appIDEdit->setText(getSteamAppID()); - - if (automaticLoginEnabled()) { - loginCheckBox->setChecked(true); - usernameEdit->setText(m_Settings.value("Settings/nexus_username", "").toString()); - passwordEdit->setText(deObfuscate(m_Settings.value("Settings/nexus_password", "").toString())); - } - - downloadDirEdit->setText(getDownloadDirectory()); - modDirEdit->setText(getModDirectory()); - cacheDirEdit->setText(getCacheDirectory()); - offlineBox->setChecked(offlineMode()); - proxyBox->setChecked(useProxy()); - nmmVersionEdit->setText(getNMMVersion()); - logLevelBox->setCurrentIndex(logLevel()); - - // display plugin settings - foreach (IPlugin *plugin, m_Plugins) { - QListWidgetItem *listItem = new QListWidgetItem(plugin->name(), pluginsList); - listItem->setData(Qt::UserRole, QVariant::fromValue((void*)plugin)); - listItem->setData(Qt::UserRole + 1, m_PluginSettings[plugin->name()]); - pluginsList->addItem(listItem); - } - - // display plugin blacklist - foreach (const QString &pluginName, m_PluginBlacklist) { - pluginBlacklistList->addItem(pluginName); - } - - // display server preferences - m_Settings.beginGroup("Servers"); - foreach (const QString &key, m_Settings.childKeys()) { - QVariantMap val = m_Settings.value(key).toMap(); - QString type = val["premium"].toBool() ? "(premium)" : "(free)"; - - QString descriptor = key + " " + type; - if (val.contains("downloadSpeed") && val.contains("downloadCount") && (val["downloadCount"].toInt() > 0)) { - int bps = static_cast(val["downloadSpeed"].toDouble() / val["downloadCount"].toInt()); - descriptor += QString(" (%1 kbps)").arg(bps / 1024); - } - - QListWidgetItem *newItem = new QListWidgetItemEx(descriptor, Qt::UserRole + 1); - - newItem->setData(Qt::UserRole, key); - newItem->setData(Qt::UserRole + 1, val["preferred"].toInt()); - if (val["preferred"].toInt() > 0) { - preferredServersList->addItem(newItem); - } else { - knownServersList->addItem(newItem); - } - preferredServersList->sortItems(Qt::DescendingOrder); - } - m_Settings.endGroup(); - - if (dialog.exec() == QDialog::Accepted) { - // - // transfer modified settings to configuration file - // - - m_Settings.setValue("Settings/hide_unchecked_plugins", hideUncheckedBox->checkState() ? true : false); - m_Settings.setValue("Settings/force_enable_core_files", forceEnableBox->checkState() ? true : false); - m_Settings.setValue("Settings/compact_downloads", compactBox->isChecked()); - m_Settings.setValue("Settings/meta_downloads", showMetaBox->isChecked()); - m_Settings.setValue("Settings/load_mechanism", mechanismBox->itemData(mechanismBox->currentIndex()).toInt()); - - - { // advanced settings - if ((QDir::fromNativeSeparators(modDirEdit->text()) != QDir::fromNativeSeparators(getModDirectory())) && - (QMessageBox::question(NULL, tr("Confirm"), tr("Changing the mod directory affects all your profiles! " - "Mods not present (or named differently) in the new location will be disabled in all profiles. " - "There is no way to undo this unless you backed up your profiles manually. Proceed?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)) { - modDirEdit->setText(getModDirectory()); - } - - if (!QDir(downloadDirEdit->text()).exists()) { - QDir().mkpath(downloadDirEdit->text()); - } - if (!QDir(cacheDirEdit->text()).exists()) { - QDir().mkpath(cacheDirEdit->text()); - } - if (!QDir(modDirEdit->text()).exists()) { - QDir().mkpath(modDirEdit->text()); - } - - m_Settings.setValue("Settings/download_directory", QDir::toNativeSeparators(downloadDirEdit->text())); - m_Settings.setValue("Settings/cache_directory", QDir::toNativeSeparators(cacheDirEdit->text())); - m_Settings.setValue("Settings/mod_directory", QDir::toNativeSeparators(modDirEdit->text())); - } - - - QString oldLanguage = m_Settings.value("Settings/language", "en_US").toString(); - QString newLanguage = languageBox->itemData(languageBox->currentIndex()).toString(); - if (newLanguage != oldLanguage) { - m_Settings.setValue("Settings/language", newLanguage); - emit languageChanged(newLanguage); - } - - QString oldStyle = m_Settings.value("Settings/style", "").toString(); - QString newStyle = styleBox->itemData(styleBox->currentIndex()).toString(); - if (oldStyle != newStyle) { - m_Settings.setValue("Settings/style", newStyle); - emit styleChanged(newStyle); - } - - m_Settings.setValue("Settings/log_level", logLevelBox->currentIndex()); - - if (appIDEdit->text() != ToQString(GameInfo::instance().getSteamAPPId())) { - m_Settings.setValue("Settings/app_id", appIDEdit->text()); - } else { - m_Settings.remove("Settings/app_id"); - } - if (loginCheckBox->isChecked()) { - m_Settings.setValue("Settings/nexus_login", true); - m_Settings.setValue("Settings/nexus_username", usernameEdit->text()); - m_Settings.setValue("Settings/nexus_password", obfuscate(passwordEdit->text())); - } else { - m_Settings.setValue("Settings/nexus_login", false); - m_Settings.remove("Settings/nexus_username"); - m_Settings.remove("Settings/nexus_password"); - } - m_Settings.setValue("Settings/offline_mode", offlineBox->isChecked()); - m_Settings.setValue("Settings/use_proxy", proxyBox->isChecked()); - m_Settings.setValue("Settings/display_foreign", displayForeignBox->isChecked()); - - m_Settings.setValue("Settings/nmm_version", nmmVersionEdit->text()); - - // transfer plugin settings to in-memory structure - for (int i = 0; i < pluginsList->count(); ++i) { - QListWidgetItem *item = pluginsList->item(i); - m_PluginSettings[item->text()] = item->data(Qt::UserRole + 1).toMap(); - } - // store plugin settings on disc - for (auto iterPlugins = m_PluginSettings.begin(); iterPlugins != m_PluginSettings.end(); ++iterPlugins) { - for (auto iterSettings = iterPlugins->begin(); iterSettings != iterPlugins->end(); ++iterSettings) { - m_Settings.setValue("Plugins/" + iterPlugins.key() + "/" + iterSettings.key(), iterSettings.value()); - } - } - - // store plugin blacklist - m_PluginBlacklist.clear(); - foreach (QListWidgetItem *item, pluginBlacklistList->findItems("*", Qt::MatchWildcard)) { - m_PluginBlacklist.insert(item->text()); - } - writePluginBlacklist(); - - // store server preference - m_Settings.beginGroup("Servers"); - for (int i = 0; i < knownServersList->count(); ++i) { - QString key = knownServersList->item(i)->data(Qt::UserRole).toString(); - QVariantMap val = m_Settings.value(key).toMap(); - val["preferred"] = 0; - m_Settings.setValue(key, val); - } - int count = preferredServersList->count(); - for (int i = 0; i < count; ++i) { - QString key = preferredServersList->item(i)->data(Qt::UserRole).toString(); - QVariantMap val = m_Settings.value(key).toMap(); - val["preferred"] = count - i; - m_Settings.setValue(key, val); - } - m_Settings.endGroup(); - } -} +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#include "settings.h" + +#include "settingsdialog.h" +#include "utility.h" +#include "helper.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +using namespace MOBase; +using namespace MOShared; + + +template +class QListWidgetItemEx : public QListWidgetItem { +public: + QListWidgetItemEx(const QString &text, int sortRole = Qt::DisplayRole, QListWidget *parent = 0, int type = Type) + : QListWidgetItem(text, parent, type), m_SortRole(sortRole) {} + + virtual bool operator< ( const QListWidgetItem & other ) const { + return this->data(m_SortRole).value() < other.data(m_SortRole).value(); + } +private: + int m_SortRole; +}; + + +static const unsigned char Key2[20] = { 0x99, 0xb8, 0x76, 0x42, 0x3e, 0xc1, 0x60, 0xa4, 0x5b, 0x01, + 0xdb, 0xf8, 0x43, 0x3a, 0xb7, 0xb6, 0x98, 0xd4, 0x7d, 0xa2 }; + +Settings *Settings::s_Instance = NULL; + + +Settings::Settings() + : m_Settings(ToQString(GameInfo::instance().getIniFilename()), QSettings::IniFormat) +{ + if (s_Instance != NULL) { + throw std::runtime_error("second instance of \"Settings\" created"); + } else { + s_Instance = this; + } +} + + +Settings::~Settings() +{ + s_Instance = NULL; +} + + +Settings &Settings::instance() +{ + if (s_Instance == NULL) { + throw std::runtime_error("no instance of \"Settings\""); + } + return *s_Instance; +} + +void Settings::clearPlugins() +{ + m_Plugins.clear(); + m_PluginSettings.clear(); + + m_PluginBlacklist.clear(); + int count = m_Settings.beginReadArray("pluginBlacklist"); + for (int i = 0; i < count; ++i) { + m_Settings.setArrayIndex(i); + m_PluginBlacklist.insert(m_Settings.value("name").toString()); + } + m_Settings.endArray(); +} + +bool Settings::pluginBlacklisted(const QString &fileName) const +{ + return m_PluginBlacklist.contains(fileName); +} + +void Settings::registerAsNXMHandler(bool force) +{ + std::wstring nxmPath = ToWString(QCoreApplication::applicationDirPath() + "/nxmhandler.exe"); + std::wstring executable = ToWString(QCoreApplication::applicationFilePath()); + std::wstring mode = force ? L"forcereg" : L"reg"; + std::wstring parameters = mode + L" " + GameInfo::instance().getGameShortName() + L" \"" + executable + L"\""; + HINSTANCE res = ::ShellExecuteW(NULL, L"open", nxmPath.c_str(), parameters.c_str(), NULL, SW_SHOWNORMAL); + if ((int)res <= 32) { + QMessageBox::critical(NULL, tr("Failed"), + tr("Sorry, failed to start the helper application")); + } +} + +void Settings::registerPlugin(IPlugin *plugin) +{ + m_Plugins.push_back(plugin); + m_PluginSettings.insert(plugin->name(), QMap()); + m_PluginDescriptions.insert(plugin->name(), QMap()); + foreach (const PluginSetting &setting, plugin->settings()) { + QVariant temp = m_Settings.value("Plugins/" + plugin->name() + "/" + setting.key, setting.defaultValue); + if (!temp.convert(setting.defaultValue.type())) { + qWarning("failed to interpret \"%s\" as correct type for \"%s\" in plugin \"%s\", using default", + qPrintable(temp.toString()), qPrintable(setting.key), qPrintable(plugin->name())); + temp = setting.defaultValue; + } + m_PluginSettings[plugin->name()][setting.key] = temp; + m_PluginDescriptions[plugin->name()][setting.key] = QString("%1 (default: %2)").arg(setting.description).arg(setting.defaultValue.toString()); + } +} + + +QString Settings::obfuscate(const QString &password) const +{ + QByteArray temp = password.toUtf8(); + + QByteArray buffer; + for (int i = 0; i < temp.length(); ++i) { + buffer.append(temp.at(i) ^ Key2[i % 20]); + } + return buffer.toBase64(); +} + + +QString Settings::deObfuscate(const QString &password) const +{ + QByteArray temp(QByteArray::fromBase64(password.toUtf8())); + + QByteArray buffer; + for (int i = 0; i < temp.length(); ++i) { + buffer.append(temp.at(i) ^ Key2[i % 20]); + } + return QString::fromUtf8(buffer.constData()); +} + + +bool Settings::hideUncheckedPlugins() const +{ + return m_Settings.value("Settings/hide_unchecked_plugins", false).toBool(); +} + +bool Settings::forceEnableCoreFiles() const +{ + return m_Settings.value("Settings/force_enable_core_files", true).toBool(); +} + +bool Settings::automaticLoginEnabled() const +{ + return m_Settings.value("Settings/nexus_login", false).toBool(); +} + +QString Settings::getSteamAppID() const +{ + return m_Settings.value("Settings/app_id", ToQString(GameInfo::instance().getSteamAPPId(m_Settings.value("game_edition", 0).toInt()))).toString(); +} + +QString Settings::getDownloadDirectory() const +{ + return QDir::toNativeSeparators(m_Settings.value("Settings/download_directory", ToQString(GameInfo::instance().getDownloadDir())).toString()); +} + + +void Settings::setDownloadSpeed(const QString &serverName, int bytesPerSecond) +{ + m_Settings.beginGroup("Servers"); + + foreach (const QString &serverKey, m_Settings.childKeys()) { + QVariantMap data = m_Settings.value(serverKey).toMap(); + if (serverKey == serverName) { + data["downloadCount"] = data["downloadCount"].toInt() + 1; + data["downloadSpeed"] = data["downloadSpeed"].toDouble() + static_cast(bytesPerSecond); + m_Settings.setValue(serverKey, data); + } + } + + m_Settings.endGroup(); + m_Settings.sync(); +} + +std::map Settings::getPreferredServers() +{ + std::map result; + m_Settings.beginGroup("Servers"); + + foreach (const QString &serverKey, m_Settings.childKeys()) { + QVariantMap data = m_Settings.value(serverKey).toMap(); + int preference = data["preferred"].toInt(); + if (preference > 0) { + result[serverKey] = preference; + } + } + m_Settings.endGroup(); + + return result; +} + +QString Settings::getCacheDirectory() const +{ + return QDir::toNativeSeparators(m_Settings.value("Settings/cache_directory", ToQString(GameInfo::instance().getCacheDir())).toString()); +} + +QString Settings::getModDirectory() const +{ + return QDir::toNativeSeparators(m_Settings.value("Settings/mod_directory", ToQString(GameInfo::instance().getModsDir())).toString()); +} + +QString Settings::getNMMVersion() const +{ + static const QString MIN_NMM_VERSION = "0.47.0"; + QString result = m_Settings.value("Settings/nmm_version", MIN_NMM_VERSION).toString(); + if (VersionInfo(result) < VersionInfo(MIN_NMM_VERSION)) { + result = MIN_NMM_VERSION; + } + return result; +} + +bool Settings::getNexusLogin(QString &username, QString &password) const +{ + if (m_Settings.value("Settings/nexus_login", false).toBool()) { + username = m_Settings.value("Settings/nexus_username", "").toString(); + password = deObfuscate(m_Settings.value("Settings/nexus_password", "").toString()); + return true; + } else { + return false; + } +} + +bool Settings::compactDownloads() const +{ + return m_Settings.value("Settings/compact_downloads", false).toBool(); +} + +bool Settings::metaDownloads() const +{ + return m_Settings.value("Settings/meta_downloads", false).toBool(); +} + +bool Settings::offlineMode() const +{ + return m_Settings.value("Settings/offline_mode", false).toBool(); +} + +int Settings::logLevel() const +{ + return m_Settings.value("Settings/log_level", 0).toInt(); +} + + +void Settings::setNexusLogin(QString username, QString password) +{ + m_Settings.setValue("Settings/nexus_login", true); + m_Settings.setValue("Settings/nexus_username", username); + m_Settings.setValue("Settings/nexus_password", obfuscate(password)); +} + + +LoadMechanism::EMechanism Settings::getLoadMechanism() const +{ + switch (m_Settings.value("Settings/load_mechanism").toInt()) { + case LoadMechanism::LOAD_MODORGANIZER: return LoadMechanism::LOAD_MODORGANIZER; + case LoadMechanism::LOAD_SCRIPTEXTENDER: return LoadMechanism::LOAD_SCRIPTEXTENDER; + case LoadMechanism::LOAD_PROXYDLL: return LoadMechanism::LOAD_PROXYDLL; + } + throw std::runtime_error("invalid load mechanism"); +} + + +void Settings::setupLoadMechanism() +{ + m_LoadMechanism.activate(getLoadMechanism()); +} + + +bool Settings::useProxy() +{ + return m_Settings.value("Settings/use_proxy", false).toBool(); +} + +bool Settings::displayForeign() +{ + return m_Settings.value("Settings/display_foreign", true).toBool(); +} + +void Settings::setMotDHash(uint hash) +{ + m_Settings.setValue("motd_hash", hash); +} + +uint Settings::getMotDHash() const +{ + return m_Settings.value("motd_hash", 0).toUInt(); +} + +QVariant Settings::pluginSetting(const QString &pluginName, const QString &key) const +{ + auto iterPlugin = m_PluginSettings.find(pluginName); + if (iterPlugin == m_PluginSettings.end()) { + return QVariant(); + } + auto iterSetting = iterPlugin->find(key); + if (iterSetting == iterPlugin->end()) { + return QVariant(); + } + + return *iterSetting; +} + +void Settings::setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value) +{ + auto iterPlugin = m_PluginSettings.find(pluginName); + if (iterPlugin == m_PluginSettings.end()) { + throw MyException(tr("attempt to store setting for unknown plugin \"%1\"").arg(pluginName)); + } + + // store the new setting both in memory and in the ini + m_PluginSettings[pluginName][key] = value; + m_Settings.setValue("Plugins/" + pluginName + "/" + key, value); +} + +QVariant Settings::pluginPersistent(const QString &pluginName, const QString &key, const QVariant &def) const +{ + if (!m_PluginSettings.contains(pluginName)) { + return def; + } + return m_Settings.value("PluginPersistance/" + pluginName + "/" + key, def); +} + +void Settings::setPluginPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync) +{ + if (!m_PluginSettings.contains(pluginName)) { + throw MyException(tr("attempt to store setting for unknown plugin \"%1\"").arg(pluginName)); + } + m_Settings.setValue("PluginPersistance/" + pluginName + "/" + key, value); + if (sync) { + m_Settings.sync(); + } +} + +QString Settings::language() +{ + QString result = m_Settings.value("Settings/language", "").toString(); + if (result.isEmpty()) { + QStringList languagePreferences = QLocale::system().uiLanguages(); + if (languagePreferences.length() > 0) { + // the users most favoritest language + result = languagePreferences.at(0); + } else { + // fallback system locale + result = QLocale::system().name(); + } + } + return result; +} + +void Settings::updateServers(const QList &servers) +{ + m_Settings.beginGroup("Servers"); + QStringList oldServerKeys = m_Settings.childKeys(); + + foreach (const ServerInfo &server, servers) { + if (!oldServerKeys.contains(server.name)) { + // not yet known server + QVariantMap newVal; + newVal["premium"] = server.premium; + newVal["preferred"] = server.preferred ? 1 : 0; + newVal["lastSeen"] = server.lastSeen; + newVal["downloadCount"] = 0; + newVal["downloadSpeed"] = 0.0; + + m_Settings.setValue(server.name, newVal); + } else { + QVariantMap data = m_Settings.value(server.name).toMap(); + data["lastSeen"] = server.lastSeen; + data["premium"] = server.premium; + + m_Settings.setValue(server.name, data); + } + } + + m_Settings.endGroup(); + m_Settings.sync(); +} + +void Settings::addBlacklistPlugin(const QString &fileName) +{ + m_PluginBlacklist.insert(fileName); + writePluginBlacklist(); +} + +void Settings::writePluginBlacklist() +{ + m_Settings.beginWriteArray("pluginBlacklist"); + int idx = 0; + foreach (const QString &plugin, m_PluginBlacklist) { + m_Settings.setArrayIndex(idx++); + m_Settings.setValue("name", plugin); + } + + m_Settings.endArray(); +} + +void Settings::addLanguages(QComboBox *languageBox) +{ + languageBox->addItem("English", "en_US"); + + QDirIterator langIter(QCoreApplication::applicationDirPath() + "/translations", QDir::Files); + QString pattern = ToQString(AppConfig::translationPrefix()) + "_([a-z]{2,3}(_[A-Z]{2,2})?).qm"; + QRegExp exp(pattern); + while (langIter.hasNext()) { + langIter.next(); + QString file = langIter.fileName(); + if (exp.exactMatch(file)) { + QString languageCode = exp.cap(1); + QLocale locale(languageCode); + QString languageString = QLocale::languageToString(locale.language()); + if (locale.language() == QLocale::Chinese) { + if (languageCode == "zh_TW") { + languageString = "Chinese (traditional)"; + } else { + languageString = "Chinese (simplified)"; + } + } + languageBox->addItem(QString("%1").arg(languageString), exp.cap(1)); + } + } +} + +void Settings::addStyles(QComboBox *styleBox) +{ + styleBox->addItem("None", ""); +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + styleBox->addItem("Fusion", "Fusion"); +#else + styleBox->addItem("Plastique", "Plastique"); + styleBox->addItem("Cleanlooks", "Cleanlooks"); +#endif + + QDirIterator langIter(QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::stylesheetsPath()), QStringList("*.qss"), QDir::Files); + while (langIter.hasNext()) { + langIter.next(); + QString style = langIter.fileName(); + styleBox->addItem(style, style); + } +} + +void Settings::resetDialogs() +{ + m_Settings.beginGroup("DialogChoices"); + QStringList keys = m_Settings.childKeys(); + foreach (QString key, keys) { + m_Settings.remove(key); + } + + m_Settings.endGroup(); +} + + +void Settings::query(QWidget *parent) +{ + SettingsDialog dialog(parent); + + connect(&dialog, SIGNAL(resetDialogs()), this, SLOT(resetDialogs())); + + // General Page + QComboBox *languageBox = dialog.findChild("languageBox"); + QComboBox *styleBox = dialog.findChild("styleBox"); + QComboBox *logLevelBox = dialog.findChild("logLevelBox"); + QCheckBox *compactBox = dialog.findChild("compactBox"); + QCheckBox *showMetaBox = dialog.findChild("showMetaBox"); + + QLineEdit *downloadDirEdit = dialog.findChild("downloadDirEdit"); + QLineEdit *modDirEdit = dialog.findChild("modDirEdit"); + QLineEdit *cacheDirEdit = dialog.findChild("cacheDirEdit"); + + // nexus page + QCheckBox *loginCheckBox = dialog.findChild("loginCheckBox"); + QLineEdit *usernameEdit = dialog.findChild("usernameEdit"); + QLineEdit *passwordEdit = dialog.findChild("passwordEdit"); + QCheckBox *offlineBox = dialog.findChild("offlineBox"); + QCheckBox *proxyBox = dialog.findChild("proxyBox"); + + QListWidget *knownServersList = dialog.findChild("knownServersList"); + QListWidget *preferredServersList = dialog.findChild("preferredServersList"); + + // plugis page + QListWidget *pluginsList = dialog.findChild("pluginsList"); + QListWidget *pluginBlacklistList = dialog.findChild("pluginBlacklist"); + + // workarounds page + QCheckBox *forceEnableBox = dialog.findChild("forceEnableBox"); + QComboBox *mechanismBox = dialog.findChild("mechanismBox"); + QLineEdit *appIDEdit = dialog.findChild("appIDEdit"); + QLineEdit *nmmVersionEdit = dialog.findChild("nmmVersionEdit"); + QCheckBox *hideUncheckedBox = dialog.findChild("hideUncheckedBox"); + QCheckBox *displayForeignBox = dialog.findChild("displayForeignBox"); + + + // + // set up current settings + // + LoadMechanism::EMechanism mechanismID = getLoadMechanism(); + int index = 0; + + if (m_LoadMechanism.isDirectLoadingSupported()) { + mechanismBox->addItem(QObject::tr("Mod Organizer"), LoadMechanism::LOAD_MODORGANIZER); + if (mechanismID == LoadMechanism::LOAD_MODORGANIZER) { + index = mechanismBox->count() - 1; + } + } + + if (m_LoadMechanism.isScriptExtenderSupported()) { + mechanismBox->addItem(QObject::tr("Script Extender"), LoadMechanism::LOAD_SCRIPTEXTENDER); + if (mechanismID == LoadMechanism::LOAD_SCRIPTEXTENDER) { + index = mechanismBox->count() - 1; + } + } + + if (m_LoadMechanism.isProxyDLLSupported()) { + mechanismBox->addItem(QObject::tr("Proxy DLL"), LoadMechanism::LOAD_PROXYDLL); + if (mechanismID == LoadMechanism::LOAD_PROXYDLL) { + index = mechanismBox->count() - 1; + } + } + + mechanismBox->setCurrentIndex(index); + + { + addLanguages(languageBox); + QString languageCode = language(); + int currentID = languageBox->findData(languageCode); + // I made a mess. :( Most languages are stored with only the iso country code (2 characters like "de") but chinese + // with the exact language variant (zh_TW) so I have to search for both variants + if (currentID == -1) { + currentID = languageBox->findData(languageCode.mid(0, 2)); + } + if (currentID != -1) { + languageBox->setCurrentIndex(currentID); + } + } + + { + addStyles(styleBox); + int currentID = styleBox->findData(m_Settings.value("Settings/style", "").toString()); + if (currentID != -1) { + styleBox->setCurrentIndex(currentID); + } + } + + compactBox->setChecked(compactDownloads()); + showMetaBox->setChecked(metaDownloads()); + + hideUncheckedBox->setChecked(hideUncheckedPlugins()); + displayForeignBox->setChecked(displayForeign()); + forceEnableBox->setChecked(forceEnableCoreFiles()); + + appIDEdit->setText(getSteamAppID()); + + if (automaticLoginEnabled()) { + loginCheckBox->setChecked(true); + usernameEdit->setText(m_Settings.value("Settings/nexus_username", "").toString()); + passwordEdit->setText(deObfuscate(m_Settings.value("Settings/nexus_password", "").toString())); + } + + downloadDirEdit->setText(getDownloadDirectory()); + modDirEdit->setText(getModDirectory()); + cacheDirEdit->setText(getCacheDirectory()); + offlineBox->setChecked(offlineMode()); + proxyBox->setChecked(useProxy()); + nmmVersionEdit->setText(getNMMVersion()); + logLevelBox->setCurrentIndex(logLevel()); + + // display plugin settings + foreach (IPlugin *plugin, m_Plugins) { + QListWidgetItem *listItem = new QListWidgetItem(plugin->name(), pluginsList); + listItem->setData(Qt::UserRole, QVariant::fromValue((void*)plugin)); + listItem->setData(Qt::UserRole + 1, m_PluginSettings[plugin->name()]); + listItem->setData(Qt::UserRole + 2, m_PluginDescriptions[plugin->name()]); + pluginsList->addItem(listItem); + } + + // display plugin blacklist + foreach (const QString &pluginName, m_PluginBlacklist) { + pluginBlacklistList->addItem(pluginName); + } + + // display server preferences + m_Settings.beginGroup("Servers"); + foreach (const QString &key, m_Settings.childKeys()) { + QVariantMap val = m_Settings.value(key).toMap(); + QString type = val["premium"].toBool() ? "(premium)" : "(free)"; + + QString descriptor = key + " " + type; + if (val.contains("downloadSpeed") && val.contains("downloadCount") && (val["downloadCount"].toInt() > 0)) { + int bps = static_cast(val["downloadSpeed"].toDouble() / val["downloadCount"].toInt()); + descriptor += QString(" (%1 kbps)").arg(bps / 1024); + } + + QListWidgetItem *newItem = new QListWidgetItemEx(descriptor, Qt::UserRole + 1); + + newItem->setData(Qt::UserRole, key); + newItem->setData(Qt::UserRole + 1, val["preferred"].toInt()); + if (val["preferred"].toInt() > 0) { + preferredServersList->addItem(newItem); + } else { + knownServersList->addItem(newItem); + } + preferredServersList->sortItems(Qt::DescendingOrder); + } + m_Settings.endGroup(); + + if (dialog.exec() == QDialog::Accepted) { + // + // transfer modified settings to configuration file + // + + m_Settings.setValue("Settings/hide_unchecked_plugins", hideUncheckedBox->checkState() ? true : false); + m_Settings.setValue("Settings/force_enable_core_files", forceEnableBox->checkState() ? true : false); + m_Settings.setValue("Settings/compact_downloads", compactBox->isChecked()); + m_Settings.setValue("Settings/meta_downloads", showMetaBox->isChecked()); + m_Settings.setValue("Settings/load_mechanism", mechanismBox->itemData(mechanismBox->currentIndex()).toInt()); + + + { // advanced settings + if ((QDir::fromNativeSeparators(modDirEdit->text()) != QDir::fromNativeSeparators(getModDirectory())) && + (QMessageBox::question(NULL, tr("Confirm"), tr("Changing the mod directory affects all your profiles! " + "Mods not present (or named differently) in the new location will be disabled in all profiles. " + "There is no way to undo this unless you backed up your profiles manually. Proceed?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)) { + modDirEdit->setText(getModDirectory()); + } + + if (!QDir(downloadDirEdit->text()).exists()) { + QDir().mkpath(downloadDirEdit->text()); + } + if (!QDir(cacheDirEdit->text()).exists()) { + QDir().mkpath(cacheDirEdit->text()); + } + if (!QDir(modDirEdit->text()).exists()) { + QDir().mkpath(modDirEdit->text()); + } + + m_Settings.setValue("Settings/download_directory", QDir::toNativeSeparators(downloadDirEdit->text())); + m_Settings.setValue("Settings/cache_directory", QDir::toNativeSeparators(cacheDirEdit->text())); + m_Settings.setValue("Settings/mod_directory", QDir::toNativeSeparators(modDirEdit->text())); + } + + + QString oldLanguage = m_Settings.value("Settings/language", "en_US").toString(); + QString newLanguage = languageBox->itemData(languageBox->currentIndex()).toString(); + if (newLanguage != oldLanguage) { + m_Settings.setValue("Settings/language", newLanguage); + emit languageChanged(newLanguage); + } + + QString oldStyle = m_Settings.value("Settings/style", "").toString(); + QString newStyle = styleBox->itemData(styleBox->currentIndex()).toString(); + if (oldStyle != newStyle) { + m_Settings.setValue("Settings/style", newStyle); + emit styleChanged(newStyle); + } + + m_Settings.setValue("Settings/log_level", logLevelBox->currentIndex()); + + if (appIDEdit->text() != ToQString(GameInfo::instance().getSteamAPPId())) { + m_Settings.setValue("Settings/app_id", appIDEdit->text()); + } else { + m_Settings.remove("Settings/app_id"); + } + if (loginCheckBox->isChecked()) { + m_Settings.setValue("Settings/nexus_login", true); + m_Settings.setValue("Settings/nexus_username", usernameEdit->text()); + m_Settings.setValue("Settings/nexus_password", obfuscate(passwordEdit->text())); + } else { + m_Settings.setValue("Settings/nexus_login", false); + m_Settings.remove("Settings/nexus_username"); + m_Settings.remove("Settings/nexus_password"); + } + m_Settings.setValue("Settings/offline_mode", offlineBox->isChecked()); + m_Settings.setValue("Settings/use_proxy", proxyBox->isChecked()); + m_Settings.setValue("Settings/display_foreign", displayForeignBox->isChecked()); + + m_Settings.setValue("Settings/nmm_version", nmmVersionEdit->text()); + + // transfer plugin settings to in-memory structure + for (int i = 0; i < pluginsList->count(); ++i) { + QListWidgetItem *item = pluginsList->item(i); + m_PluginSettings[item->text()] = item->data(Qt::UserRole + 1).toMap(); + } + // store plugin settings on disc + for (auto iterPlugins = m_PluginSettings.begin(); iterPlugins != m_PluginSettings.end(); ++iterPlugins) { + for (auto iterSettings = iterPlugins->begin(); iterSettings != iterPlugins->end(); ++iterSettings) { + m_Settings.setValue("Plugins/" + iterPlugins.key() + "/" + iterSettings.key(), iterSettings.value()); + } + } + + // store plugin blacklist + m_PluginBlacklist.clear(); + foreach (QListWidgetItem *item, pluginBlacklistList->findItems("*", Qt::MatchWildcard)) { + m_PluginBlacklist.insert(item->text()); + } + writePluginBlacklist(); + + // store server preference + m_Settings.beginGroup("Servers"); + for (int i = 0; i < knownServersList->count(); ++i) { + QString key = knownServersList->item(i)->data(Qt::UserRole).toString(); + QVariantMap val = m_Settings.value(key).toMap(); + val["preferred"] = 0; + m_Settings.setValue(key, val); + } + int count = preferredServersList->count(); + for (int i = 0; i < count; ++i) { + QString key = preferredServersList->item(i)->data(Qt::UserRole).toString(); + QVariantMap val = m_Settings.value(key).toMap(); + val["preferred"] = count - i; + m_Settings.setValue(key, val); + } + m_Settings.endGroup(); + } +} diff --git a/src/settings.h b/src/settings.h index 40fd2c5a..5d398f49 100644 --- a/src/settings.h +++ b/src/settings.h @@ -1,310 +1,311 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#ifndef WORKAROUNDS_H -#define WORKAROUNDS_H - -#include "loadmechanism.h" -#include "serverinfo.h" -#include - -#include -#include -#include - - -/** - * manages the settings for Mod Organizer. The settings are not cached - * inside the class but read/written directly from/to disc - **/ -class Settings : public QObject -{ - - Q_OBJECT - -public: - - /** - * @brief constructor - **/ - Settings(); - - virtual ~Settings(); - - static Settings &instance(); - - /** - * unregister all plugins from settings - */ - void clearPlugins(); - - /** - * @brief register plugin to be configurable - * @param plugin the plugin to register - * @return true if the plugin may be registered, false if it is blacklisted - */ - void registerPlugin(MOBase::IPlugin *plugin); - - /** - * displays a SettingsDialog that allows the user to change settings. If the - * user accepts the changes, the settings are immediately written - **/ - void query(QWidget *parent); - - /** - * set up the settings for the specified plugins - **/ - void addPluginSettings(const std::vector &plugins); - - /** - * @return true if the user wants unchecked plugins (esp, esm) should be hidden from - * the virtual dat adirectory - **/ - bool hideUncheckedPlugins() const; - - /** - * @return true if files of the core game are forced-enabled so the user can't accidentally disable them - */ - bool forceEnableCoreFiles() const; - - /** - * @brief register download speed - * @param url complete download url - * @param bytesPerSecond download size in bytes per second - */ - void setDownloadSpeed(const QString &serverName, int bytesPerSecond); - - /** - * the steam appid is assigned by the steam platform to each product sold there. - * The appid may differ between different versions of a game so it may be impossible - * for Mod Organizer to automatically recognize it, though usually it does - * @return the steam appid for the game - **/ - QString getSteamAppID() const; - - /** - * retrieve the directory where downloads are stored (with native separators) - **/ - QString getDownloadDirectory() const; - - /** - * retrieve a sorted list of preferred servers - */ - std::map getPreferredServers(); - - /** - * retrieve the directory where mods are stored (with native separators) - **/ - QString getModDirectory() const; - - /** - * returns the version of nmm to impersonate when connecting to nexus - **/ - QString getNMMVersion() const; - - /** - * retrieve the directory where the web cache is stored (with native separators) - **/ - QString getCacheDirectory() const; - - /** - * @return true if the user has set up automatic login to nexus - **/ - bool automaticLoginEnabled() const; - - /** - * @brief retrieve the login information for nexus - * - * @param username (out) receives the user name for nexus - * @param password (out) received the password for nexus - * @return true if automatic login is active, false otherwise - **/ - bool getNexusLogin(QString &username, QString &password) const; - - /** - * @return true if the user disabled internet features - */ - bool offlineMode() const; - - /** - * @return true if the user chose compact downloads - */ - bool compactDownloads() const; - - /** - * @return true if the user chose meta downloads - */ - bool metaDownloads() const; - - /** - * @return the configured log level - */ - int logLevel() const; - - /** - * @brief set the nexus login information - * - * @param username username - * @param password password - */ - void setNexusLogin(QString username, QString password); - - /** - * @return the load mechanism to be used - **/ - LoadMechanism::EMechanism getLoadMechanism() const; - - /** - * @brief activate the load mechanism selected by the user - **/ - void setupLoadMechanism(); - - /** - * @return true if the user configured the use of a network proxy - */ - bool useProxy(); - - /** - * @return true if the user wants to see non-official plugins installed outside MO in his mod list - */ - bool displayForeign(); - - /** - * @brief sets the new motd hash - **/ - void setMotDHash(uint hash); - - /** - * @return hash of the last displayed message of the day - **/ - uint getMotDHash() const; - - /** - * @brief allows direct access to the wrapped QSettings object - * @return the wrapped QSettings object - */ - QSettings &directInterface() { return m_Settings; } - - /** - * @brief retrieve a setting for one of the installed plugins - * @param pluginName name of the plugin - * @param key name of the setting to retrieve - * @return the requested value as a QVariant - * @note an invalid QVariant is returned if the the plugin/setting is not declared - */ - QVariant pluginSetting(const QString &pluginName, const QString &key) const; - - /** - * @brief set a setting for one of the installed mods - * @param pluginName name of the plugin - * @param key name of the setting to change - * @param value the new value to set - * @throw an exception is thrown if pluginName is invalid - */ - void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value); - - /** - * @brief retrieve a persistent value for a plugin - * @param pluginName name of the plugin to store data for - * @param key id of the value to retrieve - * @param def default value to return if the value is not set - * @return the requested value - */ - QVariant pluginPersistent(const QString &pluginName, const QString &key, const QVariant &def) const; - - /** - * @brief set a persistent value for a plugin - * @param pluginName name of the plugin to store data for - * @param key id of the value to retrieve - * @param value value to set - * @throw an exception is thrown if pluginName is invalid - */ - void setPluginPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync); - - /** - * @return short code of the configured language (corresponding to the translation files) - */ - QString language(); - - /** - * @brief updates the list of known servers - * @param list of servers from a recent query - */ - void updateServers(const QList &servers); - - /** - * @brief add a plugin that is to be blacklisted - * @param fileName name of the plugin to blacklist - */ - void addBlacklistPlugin(const QString &fileName); - - /** - * @brief test if a plugin is blacklisted and shouldn't be loaded - * @param fileName name of the plugin - * @return true if the file is blacklisted - */ - bool pluginBlacklisted(const QString &fileName) const; - - /** - * @return all loaded MO plugins - */ - std::vector plugins() const { return m_Plugins; } - - /** - * @brief register MO as the handler for nxm links - * @param force set to true to enforce the registration dialog to show up, - * even if the user said earlier not to - */ - void registerAsNXMHandler(bool force); -private: - - QString obfuscate(const QString &password) const; - QString deObfuscate(const QString &password) const; - - void addLanguages(QComboBox *languageBox); - void addStyles(QComboBox *styleBox); - void readPluginBlacklist(); - void writePluginBlacklist(); - -private slots: - - void resetDialogs(); - -signals: - - void languageChanged(const QString &newLanguage); - void styleChanged(const QString &newStyle); - -private: - - static Settings *s_Instance; - - QSettings m_Settings; - - LoadMechanism m_LoadMechanism; - - std::vector m_Plugins; - - QMap > m_PluginSettings; - - QSet m_PluginBlacklist; - -}; - -#endif // WORKAROUNDS_H +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#ifndef WORKAROUNDS_H +#define WORKAROUNDS_H + +#include "loadmechanism.h" +#include "serverinfo.h" +#include + +#include +#include +#include + + +/** + * manages the settings for Mod Organizer. The settings are not cached + * inside the class but read/written directly from/to disc + **/ +class Settings : public QObject +{ + + Q_OBJECT + +public: + + /** + * @brief constructor + **/ + Settings(); + + virtual ~Settings(); + + static Settings &instance(); + + /** + * unregister all plugins from settings + */ + void clearPlugins(); + + /** + * @brief register plugin to be configurable + * @param plugin the plugin to register + * @return true if the plugin may be registered, false if it is blacklisted + */ + void registerPlugin(MOBase::IPlugin *plugin); + + /** + * displays a SettingsDialog that allows the user to change settings. If the + * user accepts the changes, the settings are immediately written + **/ + void query(QWidget *parent); + + /** + * set up the settings for the specified plugins + **/ + void addPluginSettings(const std::vector &plugins); + + /** + * @return true if the user wants unchecked plugins (esp, esm) should be hidden from + * the virtual dat adirectory + **/ + bool hideUncheckedPlugins() const; + + /** + * @return true if files of the core game are forced-enabled so the user can't accidentally disable them + */ + bool forceEnableCoreFiles() const; + + /** + * @brief register download speed + * @param url complete download url + * @param bytesPerSecond download size in bytes per second + */ + void setDownloadSpeed(const QString &serverName, int bytesPerSecond); + + /** + * the steam appid is assigned by the steam platform to each product sold there. + * The appid may differ between different versions of a game so it may be impossible + * for Mod Organizer to automatically recognize it, though usually it does + * @return the steam appid for the game + **/ + QString getSteamAppID() const; + + /** + * retrieve the directory where downloads are stored (with native separators) + **/ + QString getDownloadDirectory() const; + + /** + * retrieve a sorted list of preferred servers + */ + std::map getPreferredServers(); + + /** + * retrieve the directory where mods are stored (with native separators) + **/ + QString getModDirectory() const; + + /** + * returns the version of nmm to impersonate when connecting to nexus + **/ + QString getNMMVersion() const; + + /** + * retrieve the directory where the web cache is stored (with native separators) + **/ + QString getCacheDirectory() const; + + /** + * @return true if the user has set up automatic login to nexus + **/ + bool automaticLoginEnabled() const; + + /** + * @brief retrieve the login information for nexus + * + * @param username (out) receives the user name for nexus + * @param password (out) received the password for nexus + * @return true if automatic login is active, false otherwise + **/ + bool getNexusLogin(QString &username, QString &password) const; + + /** + * @return true if the user disabled internet features + */ + bool offlineMode() const; + + /** + * @return true if the user chose compact downloads + */ + bool compactDownloads() const; + + /** + * @return true if the user chose meta downloads + */ + bool metaDownloads() const; + + /** + * @return the configured log level + */ + int logLevel() const; + + /** + * @brief set the nexus login information + * + * @param username username + * @param password password + */ + void setNexusLogin(QString username, QString password); + + /** + * @return the load mechanism to be used + **/ + LoadMechanism::EMechanism getLoadMechanism() const; + + /** + * @brief activate the load mechanism selected by the user + **/ + void setupLoadMechanism(); + + /** + * @return true if the user configured the use of a network proxy + */ + bool useProxy(); + + /** + * @return true if the user wants to see non-official plugins installed outside MO in his mod list + */ + bool displayForeign(); + + /** + * @brief sets the new motd hash + **/ + void setMotDHash(uint hash); + + /** + * @return hash of the last displayed message of the day + **/ + uint getMotDHash() const; + + /** + * @brief allows direct access to the wrapped QSettings object + * @return the wrapped QSettings object + */ + QSettings &directInterface() { return m_Settings; } + + /** + * @brief retrieve a setting for one of the installed plugins + * @param pluginName name of the plugin + * @param key name of the setting to retrieve + * @return the requested value as a QVariant + * @note an invalid QVariant is returned if the the plugin/setting is not declared + */ + QVariant pluginSetting(const QString &pluginName, const QString &key) const; + + /** + * @brief set a setting for one of the installed mods + * @param pluginName name of the plugin + * @param key name of the setting to change + * @param value the new value to set + * @throw an exception is thrown if pluginName is invalid + */ + void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value); + + /** + * @brief retrieve a persistent value for a plugin + * @param pluginName name of the plugin to store data for + * @param key id of the value to retrieve + * @param def default value to return if the value is not set + * @return the requested value + */ + QVariant pluginPersistent(const QString &pluginName, const QString &key, const QVariant &def) const; + + /** + * @brief set a persistent value for a plugin + * @param pluginName name of the plugin to store data for + * @param key id of the value to retrieve + * @param value value to set + * @throw an exception is thrown if pluginName is invalid + */ + void setPluginPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync); + + /** + * @return short code of the configured language (corresponding to the translation files) + */ + QString language(); + + /** + * @brief updates the list of known servers + * @param list of servers from a recent query + */ + void updateServers(const QList &servers); + + /** + * @brief add a plugin that is to be blacklisted + * @param fileName name of the plugin to blacklist + */ + void addBlacklistPlugin(const QString &fileName); + + /** + * @brief test if a plugin is blacklisted and shouldn't be loaded + * @param fileName name of the plugin + * @return true if the file is blacklisted + */ + bool pluginBlacklisted(const QString &fileName) const; + + /** + * @return all loaded MO plugins + */ + std::vector plugins() const { return m_Plugins; } + + /** + * @brief register MO as the handler for nxm links + * @param force set to true to enforce the registration dialog to show up, + * even if the user said earlier not to + */ + void registerAsNXMHandler(bool force); +private: + + QString obfuscate(const QString &password) const; + QString deObfuscate(const QString &password) const; + + void addLanguages(QComboBox *languageBox); + void addStyles(QComboBox *styleBox); + void readPluginBlacklist(); + void writePluginBlacklist(); + +private slots: + + void resetDialogs(); + +signals: + + void languageChanged(const QString &newLanguage); + void styleChanged(const QString &newStyle); + +private: + + static Settings *s_Instance; + + QSettings m_Settings; + + LoadMechanism m_LoadMechanism; + + std::vector m_Plugins; + + QMap > m_PluginSettings; + QMap > m_PluginDescriptions; + + QSet m_PluginBlacklist; + +}; + +#endif // WORKAROUNDS_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index a7d44d72..d1947b5a 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -1,173 +1,182 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#include "settingsdialog.h" -#include "ui_settingsdialog.h" -#include "categoriesdialog.h" -#include "helper.h" -#include "noeditdelegate.h" -#include -#include -#include -#include -#include -#define WIN32_LEAN_AND_MEAN -#include -#include "settings.h" - - -using namespace MOBase; -using namespace MOShared; - - -SettingsDialog::SettingsDialog(QWidget *parent) - : TutorableDialog("SettingsDialog", parent), ui(new Ui::SettingsDialog) -{ - ui->setupUi(this); - - QShortcut *delShortcut = new QShortcut(QKeySequence(Qt::Key_Delete), ui->pluginBlacklist); - connect(delShortcut, SIGNAL(activated()), this, SLOT(deleteBlacklistItem())); -} - -SettingsDialog::~SettingsDialog() -{ - delete ui; -} - -void SettingsDialog::addPlugins(const std::vector &plugins) -{ - foreach (IPlugin *plugin, plugins) { - ui->pluginsList->addItem(plugin->name()); - } -} - -void SettingsDialog::accept() -{ - storeSettings(ui->pluginsList->currentItem()); - TutorableDialog::accept(); -} - - -void SettingsDialog::on_loginCheckBox_toggled(bool checked) -{ - QLineEdit *usernameEdit = findChild("usernameEdit"); - QLineEdit *passwordEdit = findChild("passwordEdit"); - if (checked) { - passwordEdit->setEnabled(true); - usernameEdit->setEnabled(true); - } else { - passwordEdit->setEnabled(false); - usernameEdit->setEnabled(false); - } -} - -void SettingsDialog::on_categoriesBtn_clicked() -{ - CategoriesDialog dialog(this); - if (dialog.exec() == QDialog::Accepted) { - dialog.commitChanges(); - } -} - -void SettingsDialog::on_bsaDateBtn_clicked() -{ - Helper::backdateBSAs(GameInfo::instance().getOrganizerDirectory(), GameInfo::instance().getGameDirectory().append(L"\\data")); -} - -void SettingsDialog::on_browseDownloadDirBtn_clicked() -{ - QString temp = QFileDialog::getExistingDirectory(this, tr("Select download directory"), ui->downloadDirEdit->text()); - if (!temp.isEmpty()) { - ui->downloadDirEdit->setText(temp); - } -} - -void SettingsDialog::on_browseModDirBtn_clicked() -{ - QString temp = QFileDialog::getExistingDirectory(this, tr("Select mod directory"), ui->downloadDirEdit->text()); - if (!temp.isEmpty()) { - ui->modDirEdit->setText(temp); - } -} - -void SettingsDialog::on_browseCacheDirBtn_clicked() -{ - QString temp = QFileDialog::getExistingDirectory(this, tr("Select cache directory"), ui->cacheDirEdit->text()); - if (!temp.isEmpty()) { - ui->cacheDirEdit->setText(temp); - } -} - -void SettingsDialog::on_resetDialogsButton_clicked() -{ - if (QMessageBox::question(this, tr("Confirm?"), - tr("This will make all dialogs show up again where you checked the \"Remember selection\"-box. Continue?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - emit resetDialogs(); - } -} - -void SettingsDialog::storeSettings(QListWidgetItem *pluginItem) -{ - if (pluginItem != NULL) { - QMap settings = pluginItem->data(Qt::UserRole + 1).toMap(); - - for (int i = 0; i < ui->pluginSettingsList->topLevelItemCount(); ++i) { - const QTreeWidgetItem *item = ui->pluginSettingsList->topLevelItem(i); - settings[item->text(0)] = item->data(1, Qt::DisplayRole); - } - - pluginItem->setData(Qt::UserRole + 1, settings); - } -} - -void SettingsDialog::on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) -{ - storeSettings(previous); - - ui->pluginSettingsList->clear(); - IPlugin *plugin = static_cast(current->data(Qt::UserRole).value()); - ui->authorLabel->setText(plugin->author()); - ui->versionLabel->setText(plugin->version().canonicalString()); - ui->descriptionLabel->setText(plugin->description()); - - QMap settings = current->data(Qt::UserRole + 1).toMap(); - ui->pluginSettingsList->setEnabled(settings.count() != 0); - for (auto iter = settings.begin(); iter != settings.end(); ++iter) { - QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList(iter.key())); - QVariant value = *iter; - - ui->pluginSettingsList->setItemDelegateForColumn(0, new NoEditDelegate()); - newItem->setData(1, Qt::DisplayRole, value); - newItem->setData(1, Qt::EditRole, value); - - newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); - ui->pluginSettingsList->addTopLevelItem(newItem); - } -} - -void SettingsDialog::deleteBlacklistItem() -{ - ui->pluginBlacklist->takeItem(ui->pluginBlacklist->currentIndex().row()); -} - -void SettingsDialog::on_associateButton_clicked() -{ - Settings::instance().registerAsNXMHandler(true); -} +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#include "settingsdialog.h" +#include "ui_settingsdialog.h" +#include "categoriesdialog.h" +#include "helper.h" +#include "noeditdelegate.h" +#include +#include +#include +#include +#include +#define WIN32_LEAN_AND_MEAN +#include +#include "settings.h" + + +using namespace MOBase; +using namespace MOShared; + + +SettingsDialog::SettingsDialog(QWidget *parent) + : TutorableDialog("SettingsDialog", parent), ui(new Ui::SettingsDialog) +{ + ui->setupUi(this); + + QShortcut *delShortcut = new QShortcut(QKeySequence(Qt::Key_Delete), ui->pluginBlacklist); + connect(delShortcut, SIGNAL(activated()), this, SLOT(deleteBlacklistItem())); +} + +SettingsDialog::~SettingsDialog() +{ + delete ui; +} + +void SettingsDialog::addPlugins(const std::vector &plugins) +{ + foreach (IPlugin *plugin, plugins) { + ui->pluginsList->addItem(plugin->name()); + } +} + +void SettingsDialog::accept() +{ + storeSettings(ui->pluginsList->currentItem()); + TutorableDialog::accept(); +} + + +void SettingsDialog::on_loginCheckBox_toggled(bool checked) +{ + QLineEdit *usernameEdit = findChild("usernameEdit"); + QLineEdit *passwordEdit = findChild("passwordEdit"); + if (checked) { + passwordEdit->setEnabled(true); + usernameEdit->setEnabled(true); + } else { + passwordEdit->setEnabled(false); + usernameEdit->setEnabled(false); + } +} + +void SettingsDialog::on_categoriesBtn_clicked() +{ + CategoriesDialog dialog(this); + if (dialog.exec() == QDialog::Accepted) { + dialog.commitChanges(); + } +} + +void SettingsDialog::on_bsaDateBtn_clicked() +{ + Helper::backdateBSAs(GameInfo::instance().getOrganizerDirectory(), GameInfo::instance().getGameDirectory().append(L"\\data")); +} + +void SettingsDialog::on_browseDownloadDirBtn_clicked() +{ + QString temp = QFileDialog::getExistingDirectory(this, tr("Select download directory"), ui->downloadDirEdit->text()); + if (!temp.isEmpty()) { + ui->downloadDirEdit->setText(temp); + } +} + +void SettingsDialog::on_browseModDirBtn_clicked() +{ + QString temp = QFileDialog::getExistingDirectory(this, tr("Select mod directory"), ui->downloadDirEdit->text()); + if (!temp.isEmpty()) { + ui->modDirEdit->setText(temp); + } +} + +void SettingsDialog::on_browseCacheDirBtn_clicked() +{ + QString temp = QFileDialog::getExistingDirectory(this, tr("Select cache directory"), ui->cacheDirEdit->text()); + if (!temp.isEmpty()) { + ui->cacheDirEdit->setText(temp); + } +} + +void SettingsDialog::on_resetDialogsButton_clicked() +{ + if (QMessageBox::question(this, tr("Confirm?"), + tr("This will make all dialogs show up again where you checked the \"Remember selection\"-box. Continue?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + emit resetDialogs(); + } +} + +void SettingsDialog::storeSettings(QListWidgetItem *pluginItem) +{ + if (pluginItem != NULL) { + QMap settings = pluginItem->data(Qt::UserRole + 1).toMap(); + + for (int i = 0; i < ui->pluginSettingsList->topLevelItemCount(); ++i) { + const QTreeWidgetItem *item = ui->pluginSettingsList->topLevelItem(i); + settings[item->text(0)] = item->data(1, Qt::DisplayRole); + } + + pluginItem->setData(Qt::UserRole + 1, settings); + } +} + +void SettingsDialog::on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +{ + storeSettings(previous); + + ui->pluginSettingsList->clear(); + IPlugin *plugin = static_cast(current->data(Qt::UserRole).value()); + ui->authorLabel->setText(plugin->author()); + ui->versionLabel->setText(plugin->version().canonicalString()); + ui->descriptionLabel->setText(plugin->description()); + + QMap settings = current->data(Qt::UserRole + 1).toMap(); + QMap descriptions = current->data(Qt::UserRole + 2).toMap(); + ui->pluginSettingsList->setEnabled(settings.count() != 0); + for (auto iter = settings.begin(); iter != settings.end(); ++iter) { + QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList(iter.key())); + QVariant value = *iter; + QString description; + { + auto descriptionIter = descriptions.find(iter.key()); + if (descriptionIter != descriptions.end()) { + description = descriptionIter->toString(); + } + } + + ui->pluginSettingsList->setItemDelegateForColumn(0, new NoEditDelegate()); + newItem->setData(1, Qt::DisplayRole, value); + newItem->setData(1, Qt::EditRole, value); + newItem->setToolTip(1, description); + + newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); + ui->pluginSettingsList->addTopLevelItem(newItem); + } +} + +void SettingsDialog::deleteBlacklistItem() +{ + ui->pluginBlacklist->takeItem(ui->pluginBlacklist->currentIndex().row()); +} + +void SettingsDialog::on_associateButton_clicked() +{ + Settings::instance().registerAsNXMHandler(true); +} -- cgit v1.3.1 From bd8267993d1ed0292200f3052e3c30d4fc9c84f7 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sun, 4 Jan 2015 10:13:12 +0100 Subject: - fixes after merge --- src/directoryrefresher.cpp | 4 +- src/editexecutablesdialog.cpp | 4 +- src/iuserinterface.h | 8 +- src/main.cpp | 2 +- src/mainwindow.cpp | 264 ++++++------------------------------------ src/mainwindow.h | 17 +-- src/modinfo.h | 9 -- src/modlist.cpp | 5 - src/nxmaccessmanager.cpp | 4 +- src/organizercore.cpp | 173 +++++++++++++++++++++++---- src/organizercore.h | 13 ++- src/organizerproxy.cpp | 25 +--- src/plugincontainer.cpp | 2 - src/plugincontainer.h | 36 +++--- src/pluginlist.cpp | 2 +- src/settings.cpp | 15 ++- src/settings.h | 3 +- src/shared/appconfig.inc | 3 + src/shared/stackdata.cpp | 2 +- src/syncoverwritedialog.cpp | 6 +- 20 files changed, 248 insertions(+), 349 deletions(-) (limited to 'src/settings.h') diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp index 24fda501..1a5c025d 100644 --- a/src/directoryrefresher.cpp +++ b/src/directoryrefresher.cpp @@ -65,12 +65,12 @@ void DirectoryRefresher::setMods(const std::vectorremoveFile(files[i]); } - static wchar_t *dirs[] = { L"fomod" }; + static const wchar_t *dirs[] = { L"fomod" }; for (int i = 0; i < sizeof(dirs) / sizeof(wchar_t*); ++i) { structure->removeDir(std::wstring(dirs[i])); } diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp index e760e3e8..d83d14c8 100644 --- a/src/editexecutablesdialog.cpp +++ b/src/editexecutablesdialog.cpp @@ -275,8 +275,8 @@ void EditExecutablesDialog::on_executablesListBox_clicked(const QModelIndex &cur ui->binaryEdit->setText(QDir::toNativeSeparators(selectedExecutable.m_BinaryInfo.absoluteFilePath())); ui->argumentsEdit->setText(selectedExecutable.m_Arguments); ui->workingDirEdit->setText(QDir::toNativeSeparators(selectedExecutable.m_WorkingDirectory)); - ui->closeCheckBox->setChecked(selectedExecutable.m_CloseMO == DEFAULT_CLOSE); - if (selectedExecutable.m_CloseMO == NEVER_CLOSE) { + ui->closeCheckBox->setChecked(selectedExecutable.m_CloseMO == ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE); + if (selectedExecutable.m_CloseMO == ExecutableInfo::CloseMOStyle::NEVER_CLOSE) { ui->closeCheckBox->setEnabled(false); ui->closeCheckBox->setToolTip(tr("MO must be kept running or this application will not work correctly.")); } else { diff --git a/src/iuserinterface.h b/src/iuserinterface.h index 76d4c75a..68cce2f7 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -11,9 +11,7 @@ class IUserInterface { public: - void storeSettings(QSettings &settings); - - virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = "") = 0; + virtual void storeSettings(QSettings &settings) = 0; virtual bool waitForProcessOrJob(HANDLE processHandle, LPDWORD exitCode = NULL) = 0; @@ -24,10 +22,6 @@ public: virtual void disconnectPlugins() = 0; - virtual bool close() = 0; - - virtual void setEnabled(bool enabled) = 0; - virtual void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) = 0; virtual void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives) = 0; diff --git a/src/main.cpp b/src/main.cpp index 5a57cf47..4aab45b5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -530,7 +530,7 @@ int main(int argc, char *argv[]) arguments.removeFirst(); // remove binary name // pass the remaining parameters to the binary try { - mainWindow.startApplication(exeName, arguments, QString(), selectedProfileName); + organizer.startApplication(exeName, arguments, QString(), selectedProfileName); } catch (const std::exception &e) { reportError(QObject::tr("failed to start application: %1").arg(e.what())); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5d9b12a9..a2df0c24 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -285,7 +285,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize connect(&m_OrganizerCore, &OrganizerCore::modInstalled, this, &MainWindow::modInstalled); - connect(&m_IntegratedBrowser, SIGNAL(requestDownload(QUrl,QNetworkReply*)), this, SLOT(requestDownload(QUrl,QNetworkReply*))); + connect(&m_IntegratedBrowser, SIGNAL(requestDownload(QUrl,QNetworkReply*)), &m_OrganizerCore, SLOT(requestDownload(QUrl,QNetworkReply*))); connect(this, SIGNAL(styleChanged(QString)), this, SLOT(updateStyle(QString))); @@ -318,6 +318,14 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize installTranslator(QFileInfo(fileName).baseName()); } + for (IPluginTool *toolPlugin : m_PluginContainer.plugins()) { + registerPluginTool(toolPlugin); + } + + for (IPluginModPage *modPagePlugin : m_PluginContainer.plugins()) { + registerModPage(modPagePlugin); + } + refreshExecutablesList(); updateToolBar(); } @@ -325,8 +333,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize MainWindow::~MainWindow() { - m_AboutToRun.disconnect_all_slots(); - m_ModInstalled.disconnect_all_slots(); m_PluginContainer.setUserInterface(nullptr, nullptr); m_OrganizerCore.setUserInterface(nullptr, nullptr); m_IntegratedBrowser.close(); @@ -919,7 +925,6 @@ void MainWindow::toolPluginInvoke() } } - void MainWindow::modPagePluginInvoke() { QAction *triggeredAction = qobject_cast(sender()); @@ -932,7 +937,6 @@ void MainWindow::modPagePluginInvoke() } } - void MainWindow::registerPluginTool(IPluginTool *tool) { QAction *action = new QAction(tool->icon(), tool->displayName(), ui->toolBar); @@ -944,7 +948,6 @@ void MainWindow::registerPluginTool(IPluginTool *tool) toolBtn->menu()->addAction(action); } - void MainWindow::registerModPage(IPluginModPage *modPage) { // turn the browser action into a drop-down menu if necessary @@ -1009,10 +1012,8 @@ void MainWindow::activateSelectedProfile() m_ModListSortProxy->setProfile(m_OrganizerCore.currentProfile()); - connect(m_OrganizerCore.currentProfile(), SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint))); - refreshSaveList(); - refreshModList(); + m_OrganizerCore.refreshModList(); } void MainWindow::on_profileBox_currentIndexChanged(int index) @@ -1415,7 +1416,7 @@ void MainWindow::updateBSAList(const QStringList &defaultArchives, const QString subItem->setExpanded(true); } - m_OrganizerCore.checkBSAList(); + checkBSAList(defaultArchives); } @@ -1597,7 +1598,7 @@ void MainWindow::installMod() if (fileName.length() == 0) { return; } else { - m_OrganizerCore.installMod(fileName); + m_OrganizerCore.installMod(fileName, QString()); } } catch (const std::exception &e) { reportError(e.what()); @@ -1694,7 +1695,7 @@ bool MainWindow::modifyExecutablesDialog() { bool result = false; try { - EditExecutablesDialog dialog(m_OrganizerCore.executablesList()); + EditExecutablesDialog dialog(*m_OrganizerCore.executablesList()); if (dialog.exec() == QDialog::Accepted) { m_OrganizerCore.setExecutablesDialog(dialog.getExecutablesList()); result = true; @@ -1866,7 +1867,7 @@ void MainWindow::modorder_changed() } m_OrganizerCore.refreshBSAList(); m_OrganizerCore.currentProfile()->writeModlist(); - m_OrganizerCore.saveArchiveList(); + saveArchiveList(); m_OrganizerCore.directoryStructure()->getFileRegister()->sortOrigins(); { // refresh selection @@ -1883,11 +1884,10 @@ void MainWindow::modorder_changed() } } -void MainWindow::modInstalled() +void MainWindow::modInstalled(const QString &modName) { QModelIndexList posList = - m_OrganizerCore.modList().match(m_OrganizerCore.modList().index(0, 0), - Qt::DisplayRole, static_cast(modName)); + m_OrganizerCore.modList()->match(m_OrganizerCore.modList()->index(0, 0), Qt::DisplayRole, modName); if (posList.count() == 1) { ui->modList->scrollTo(posList.at(0)); } @@ -2151,7 +2151,7 @@ void MainWindow::restoreBackup_clicked() if (!modDir.rename(modInfo->absolutePath(), destinationPath)) { reportError(tr("failed to rename \"%1\" to \"%2\"").arg(modInfo->absolutePath()).arg(destinationPath)); } - refreshModList(); + m_OrganizerCore.refreshModList(); } } } @@ -2262,7 +2262,7 @@ void MainWindow::resumeDownload(int downloadIndex) QString username, password; if (m_OrganizerCore.settings().getNexusLogin(username, password)) { //m_PostLoginTasks.push_back(boost::bind(&MainWindow::resumeDownload, _1, downloadIndex)); - m_OrganizerCore.doAfterLogin(std::bind(&MainWindow::resumeDownload, this, downloadIndex)); + m_OrganizerCore.doAfterLogin([this, downloadIndex] () { this->resumeDownload(downloadIndex); }); NexusInterface::instance()->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to resume a download"), this); @@ -2278,7 +2278,8 @@ void MainWindow::endorseMod(ModInfo::Ptr mod) } else { QString username, password; if (m_OrganizerCore.settings().getNexusLogin(username, password)) { - m_PostLoginTasks.push_back(boost::bind(&MainWindow::endorseMod, _1, mod)); + //m_PostLoginTasks.push_back(boost::bind(&MainWindow::endorseMod, _1, mod)); + m_OrganizerCore.doAfterLogin([&] () { this->endorseMod(mod); }); NexusInterface::instance()->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); @@ -2305,7 +2306,8 @@ void MainWindow::unendorse_clicked() ModInfo::getByIndex(m_ContextRow)->endorse(false); } else { if (m_OrganizerCore.settings().getNexusLogin(username, password)) { - m_PostLoginTasks.push_back(boost::mem_fn(&MainWindow::unendorse_clicked)); + //m_PostLoginTasks.push_back(boost::mem_fn(&MainWindow::unendorse_clicked)); + m_OrganizerCore.doAfterLogin([this] () { this->unendorse_clicked(); }); NexusInterface::instance()->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); @@ -2320,7 +2322,7 @@ void MainWindow::loginFailed(const QString &message) void MainWindow::windowTutorialFinished(const QString &windowName) { - m_OrganizerCore.settings().directInterface().setValue(QString("CompletedWindowTutorials/") + windowName, this); + m_OrganizerCore.settings().directInterface().setValue(QString("CompletedWindowTutorials/") + windowName, true); } void MainWindow::overwriteClosed(int) @@ -2489,17 +2491,7 @@ void MainWindow::information_clicked() } } -void MainWindow::syncOverwrite() -{ - ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); - SyncOverwriteDialog syncDialog(modInfo->absolutePath(), m_OrganizerCore.directoryStructure(), this); - if (syncDialog.exec() == QDialog::Accepted) { - syncDialog.apply(QDir::fromNativeSeparators(m_OrganizerCore.settings().getModDirectory())); - modInfo->testValid(); - refreshDirectoryStructure(); - } -} void MainWindow::createModFromOverwrite() { @@ -2527,11 +2519,15 @@ void MainWindow::createModFromOverwrite() return; } - ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(m_ContextRow); + unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool { + std::vector flags = mod->getFlags(); + return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end(); }); + + ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(overwriteIndex); shellMove(QStringList(QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"), QStringList(QDir::toNativeSeparators(newMod->absolutePath())), this); - refreshModList(); + m_OrganizerCore.refreshModList(); } void MainWindow::cancelModListEditor() @@ -2743,7 +2739,7 @@ void MainWindow::savePrimaryCategory() bool MainWindow::saveArchiveList() { - if (m_ArchivesInit) { + if (m_OrganizerCore.isArchivesInit()) { SafeWriteFile archiveFile(m_OrganizerCore.currentProfile()->getArchivesFileName()); for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) { QTreeWidgetItem *tlItem = ui->bsaList->topLevelItem(i); @@ -2777,7 +2773,7 @@ void MainWindow::checkModsForUpdates() } else { QString username, password; if (m_OrganizerCore.settings().getNexusLogin(username, password)) { - m_OrganizerCore.doAfterLogin(boost::mem_fn(&MainWindow::checkModsForUpdates)); + m_OrganizerCore.doAfterLogin([this] () { this->checkModsForUpdates(); }); NexusInterface::instance()->getAccessManager()->login(username, password); } else { // otherwise there will be no endorsement info m_ModsToUpdate = ModInfo::checkAllForUpdate(this); @@ -2974,7 +2970,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) std::vector flags = info->getFlags(); if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { if (QDir(info->absolutePath()).count() > 2) { - menu->addAction(tr("Sync to Mods..."), this, SLOT(syncOverwrite())); + menu->addAction(tr("Sync to Mods..."), &m_OrganizerCore, SLOT(syncOverwrite())); menu->addAction(tr("Create Mod..."), this, SLOT(createModFromOverwrite())); } } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) { @@ -3403,67 +3399,6 @@ void MainWindow::languageChange(const QString &newLanguage) ui->listOptionsBtn->setMenu(modListContextMenu()); } - -void MainWindow::installDownload(int index) -{ - try { - QString fileName = m_OrganizerCore.downloadManager()->getFilePath(index); - int modID = m_OrganizerCore.downloadManager()->getModID(index); - int fileID = m_OrganizerCore.downloadManager()->getFileInfo(index)->fileID; - GuessedValue modName; - - // see if there already are mods with the specified mod id - if (modID != 0) { - std::vector modInfo = ModInfo::getByModID(modID); - for (auto iter = modInfo.begin(); iter != modInfo.end(); ++iter) { - std::vector flags = (*iter)->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end()) { - modName.update((*iter)->name(), GUESS_PRESET); - (*iter)->saveMeta(); - } - } - } - - m_OrganizerCore.currentProfile()->writeModlistNow(); - - bool hasIniTweaks = false; - m_OrganizerCore.installationManager()->setModsDirectory(m_OrganizerCore.settings().getModDirectory()); - if (m_OrganizerCore.installationManager()->install(fileName, modName, hasIniTweaks)) { - MessageDialog::showMessage(tr("Installation successful"), this); - refreshModList(); - - QModelIndexList posList = m_OrganizerCore.modList()->match(m_OrganizerCore.modList()->index(0, 0), Qt::DisplayRole, static_cast(modName)); - if (posList.count() == 1) { - ui->modList->scrollTo(posList.at(0)); - } - int modIndex = ModInfo::getIndex(modName); - if (modIndex != UINT_MAX) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - modInfo->addInstalledFile(modID, fileID); - - if (hasIniTweaks && - (QMessageBox::question(this, tr("Configure Mod"), - tr("This mod contains ini tweaks. Do you want to configure them now?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - displayModInformation(modInfo, modIndex, ModInfoDialog::TAB_INIFILES); - } - - m_ModInstalled(modName); - } else { - reportError(tr("mod \"%1\" not found").arg(modName)); - } - m_OrganizerCore.downloadManager()->markInstalled(index); - - emit modInstalled(); - } else if (m_OrganizerCore.installationManager()->wasCancelled()) { - QMessageBox::information(this, tr("Installation cancelled"), tr("The mod was not installed completely."), QMessageBox::Ok); - } - } catch (const std::exception &e) { - reportError(e.what()); - } -} - - void MainWindow::writeDataToFile(QFile &file, const QString &directory, const DirectoryEntry &directoryEntry) { { // list files @@ -3711,7 +3646,7 @@ void MainWindow::openDataFile() QString arguments; switch (getBinaryExecuteInfo(targetInfo, binaryInfo, arguments)) { case 1: { - spawnBinaryDirect(binaryInfo, arguments, m_OrganizerCore.currentProfile()->getName(), targetInfo.absolutePath(), ""); + m_OrganizerCore.spawnBinaryDirect(binaryInfo, arguments, m_OrganizerCore.currentProfile()->getName(), targetInfo.absolutePath(), ""); } break; case 2: { ::ShellExecuteW(NULL, L"open", ToWString(targetInfo.absoluteFilePath()).c_str(), NULL, NULL, SW_SHOWNORMAL); @@ -3839,7 +3774,7 @@ void MainWindow::updateDownloadListDelegate() ui->downloadView->sortByColumn(1, Qt::AscendingOrder); ui->downloadView->header()->resizeSections(QHeaderView::Fixed); - connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), this, SLOT(installDownload(int))); + connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), &m_OrganizerCore, SLOT(installDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(queryInfo(int)), m_OrganizerCore.downloadManager(), SLOT(queryInfo(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(removeDownload(int, bool)), m_OrganizerCore.downloadManager(), SLOT(removeDownload(int, bool))); connect(ui->downloadView->itemDelegate(), SIGNAL(restoreDownload(int)), m_OrganizerCore.downloadManager(), SLOT(restoreDownload(int))); @@ -4354,135 +4289,12 @@ void MainWindow::processLOOTOut(const std::string &lootOut, std::string &reportU } } - -HANDLE MainWindow::startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile) -{ - QFileInfo binary; - QString arguments = args.join(" "); - QString currentDirectory = cwd; - QString profileName = profile; - if (profile.length() == 0) { - if (m_OrganizerCore.currentProfile() != NULL) { - profileName = m_OrganizerCore.currentProfile()->getName(); - } else { - throw MyException(tr("No profile set")); - } - } - QString steamAppID; - if (executable.contains('\\') || executable.contains('/')) { - // file path - - binary = QFileInfo(executable); - if (binary.isRelative()) { - // relative path, should be relative to game directory - binary = QFileInfo(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/" + executable); - } - - std::vector::iterator current, end; - m_OrganizerCore.executablesList()->getExecutables(current, end); - for (; current != end; ++current) { - if (current->m_BinaryInfo == binary) { - steamAppID = current->m_SteamAppID; - currentDirectory = current->m_WorkingDirectory; - } - } - - if (cwd.length() == 0) { - currentDirectory = binary.absolutePath(); - } - try { - const Executable &exe = m_ExecutablesList.findByBinary(binary); - steamAppID = exe.m_SteamAppID; - } catch (const std::runtime_error&) { - // nop - } - } else { - // only a file name, search executables list - try { - const Executable &exe = m_OrganizerCore.executablesList()->find(executable); - steamAppID = exe.m_SteamAppID; - if (arguments == "") { - arguments = exe.m_Arguments; - } - binary = exe.m_BinaryInfo; - if (cwd.length() == 0) { - currentDirectory = exe.m_WorkingDirectory; - } - } catch (const std::runtime_error&) { - qWarning("\"%s\" not set up as executable", executable.toUtf8().constData()); - binary = QFileInfo(executable); - } - } - - return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID); -} - - -bool MainWindow::waitForProcessOrJob(HANDLE handle, LPDWORD exitCode) -{ - LockedDialog *dialog = new LockedDialog(this); - dialog->show(); - setEnabled(false); - ON_BLOCK_EXIT([&] () { dialog->hide(); dialog->deleteLater(); this->setEnabled(true); }); - - DWORD retLen; - JOBOBJECT_BASIC_PROCESS_ID_LIST info; - - bool isJobHandle = true; - - ULONG lastProcessID = ULONG_MAX; - HANDLE processHandle = handle; - - DWORD res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); - while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0) && !dialog->unlockClicked()) { - if (isJobHandle) { - if (::QueryInformationJobObject(handle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) { - if (info.NumberOfProcessIdsInList == 0) { - // fake signaled state - res = WAIT_OBJECT_0; - break; - } else { - // this is indeed a job handle. Figure out one of the process handles as well. - if (lastProcessID != info.ProcessIdList[0]) { - lastProcessID = info.ProcessIdList[0]; - if (processHandle != handle) { - ::CloseHandle(processHandle); - } - processHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, lastProcessID); - } - } - } else { - // the info-object I passed only provides space for 1 process id. but since this code only cares about whether there - // is more than one that's good enough. ERROR_MORE_DATA simply signals there are at least two processes running. - // any other error probably means the handle is a regular process handle, probably caused by running MO in a job without - // the right to break out. - if (::GetLastError() != ERROR_MORE_DATA) { - isJobHandle = false; - } - } - } - - // keep processing events so the app doesn't appear dead - QCoreApplication::processEvents(); - - res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); - } - - if (exitCode != NULL) { - ::GetExitCodeProcess(processHandle, exitCode); - } - ::CloseHandle(processHandle); - - return res == WAIT_OBJECT_0; -} - - bool MainWindow::waitForProcessOrJob(HANDLE handle, LPDWORD exitCode) { LockedDialog *dialog = new LockedDialog(this); dialog->show(); setEnabled(false); - ON_BLOCK_EXIT([&] () { dialog->hide(); dialog->deleteLater(); this->setEnabled(true); }); + ON_BLOCK_EXIT([&] () { dialog->hide(); dialog->deleteLater(); setEnabled(true); }); DWORD retLen; JOBOBJECT_BASIC_PROCESS_ID_LIST info; @@ -4545,8 +4357,8 @@ void MainWindow::on_bossButton_clicked() bool success = false; try { - this->setEnabled(false); - ON_BLOCK_EXIT([&] () { this->setEnabled(true); }); + setEnabled(false); + ON_BLOCK_EXIT([&] () { setEnabled(true); }); QProgressDialog dialog(this); dialog.setLabelText(tr("Please wait while LOOT is running")); dialog.setMaximum(0); @@ -4792,7 +4604,7 @@ void MainWindow::on_restoreModsButton_clicked() QMessageBox::critical(this, tr("Restore failed"), tr("Failed to restore the backup. Errorcode: %1").arg(windowsErrorString(::GetLastError()))); } - refreshModList(false); + m_OrganizerCore.refreshModList(false); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 8adaeb1f..199047b1 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -109,8 +109,6 @@ public: std::string readFromPipe(HANDLE stdOutRead); void processLOOTOut(const std::string &lootOut, std::string &reportURL, std::string &errorMessages, QProgressDialog &dialog); - HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); - bool waitForProcessOrJob(HANDLE processHandle, LPDWORD exitCode = NULL); void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo); @@ -122,9 +120,6 @@ public: virtual void disconnectPlugins(); void unloadPlugins(); - virtual bool close(); - virtual void setEnabled(bool enabled); - void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab); public slots: @@ -163,8 +158,6 @@ protected: private: - void refreshModList(bool saveChanges = true); - void actionToToolButton(QAction *&sourceAction); void updateToolBar(); @@ -174,10 +167,7 @@ private: void startSteam(); - HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID); - void updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const MOShared::DirectoryEntry &directoryEntry, bool conflictsOnly); - void refreshDirectoryStructure(); bool refreshProfiles(bool selectProfile = true); void refreshExecutablesList(); void installMod(); @@ -365,15 +355,12 @@ private slots: void linkMenu(); void languageChange(const QString &newLanguage); - void modStatusChanged(unsigned int index); void saveSelectionChanged(QListWidgetItem *newItem); void windowTutorialFinished(const QString &windowName); BSA::EErrorCode extractBSA(BSA::Archive &archive, BSA::Folder::Ptr folder, const QString &destination, QProgressDialog &extractProgress); - void syncOverwrite(); - void createModFromOverwrite(); void procError(QProcess::ProcessError error); @@ -387,7 +374,6 @@ private slots: void linkClicked(const QString &url); - void installDownload(int index); void updateAvailable(); void motdReceived(const QString &motd); @@ -404,7 +390,7 @@ private slots: void modDetailsUpdated(bool success); void modlistChanged(int row); - void modInstalled(); + void modInstalled(const QString &modName); void nxmUpdatesAvailable(const std::vector &modIDs, QVariant userData, QVariant resultData, int requestID); void nxmEndorsementToggled(int, QVariant, QVariant resultData, int); @@ -481,7 +467,6 @@ private slots: void about(); void delayedRemove(); - void requestDownload(const QUrl &url, QNetworkReply *reply); void modlistSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); void modListSortIndicatorChanged(int column, Qt::SortOrder order); diff --git a/src/modinfo.h b/src/modinfo.h index d532202b..048196f7 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -418,11 +418,6 @@ public: */ virtual std::vector getContents() const { return std::vector(); } - /** - * @return a list of content types contained in a mod - */ - virtual std::vector getContents() const { return std::vector(); } - /** * @brief test if the specified flag is set for this mod * @param flag the flag to test @@ -1075,10 +1070,6 @@ public: static const char INT_IDENTIFIER[]; -public: - - static const char INT_IDENTIFIER[]; - public: virtual bool updateAvailable() const { return false; } diff --git a/src/modlist.cpp b/src/modlist.cpp index bfe8a70e..7b922c7c 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -657,11 +657,6 @@ void ModList::disconnectSlots() { m_ModStateChanged.disconnect_all_slots(); } -void ModList::disconnectSlots() { - m_ModMoved.disconnect_all_slots(); - m_ModStateChanged.disconnect_all_slots(); -} - IModList::ModStates ModList::state(unsigned int modIndex) const { IModList::ModStates result; diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 18b47707..9db9213c 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "utility.h" #include "selfupdater.h" #include "persistentcookiejar.h" +#include "settings.h" #include #include #include @@ -48,8 +49,7 @@ NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) , m_MOVersion(moVersion) , m_LoginAttempted(false) { - setCookieJar(new PersistentCookieJar( - QDir::fromNativeSeparators(MOBase::ToQString(MOShared::GameInfo::instance().getCacheDir())) + "/nexus_cookies.dat", this)); + setCookieJar(new PersistentCookieJar(Settings::instance().getCacheDirectory())); } NXMAccessManager::~NXMAccessManager() diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 1e787670..0c414a15 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -10,6 +10,7 @@ #include "report.h" #include "spawn.h" #include "safewritefile.h" +#include "syncoverwritedialog.h" #include #include #include @@ -76,7 +77,7 @@ static std::wstring getProcessName(DWORD processId) } } -bool MainWindow::testForSteam() +bool OrganizerCore::testForSteam() { size_t currentSize = 1024; std::unique_ptr processIDs; @@ -153,7 +154,7 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) , m_UserInterface(nullptr) , m_PluginContainer(nullptr) , m_CurrentProfile(nullptr) - , m_Settings() + , m_Settings(initSettings) , m_Updater(NexusInterface::instance()) , m_AboutToRun() , m_FinishedRun() @@ -244,7 +245,7 @@ void OrganizerCore::storeSettings() settings.setValue("binary", item.m_BinaryInfo.absoluteFilePath()); settings.setValue("arguments", item.m_Arguments); settings.setValue("workingDirectory", item.m_WorkingDirectory); - settings.setValue("closeOnStart", item.m_CloseMO == DEFAULT_CLOSE); + settings.setValue("closeOnStart", item.m_CloseMO == ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE); settings.setValue("steamAppID", item.m_SteamAppID); } } @@ -443,6 +444,7 @@ InstallationManager *OrganizerCore::installationManager() void OrganizerCore::setCurrentProfile(Profile *profile) { delete m_CurrentProfile; m_CurrentProfile = profile; + connect(m_CurrentProfile, SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint))); m_ModList.setProfile(profile); } @@ -601,6 +603,63 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName, const return nullptr; } +void OrganizerCore::installDownload(int index) +{ + try { + QString fileName = m_DownloadManager.getFilePath(index); + int modID = m_DownloadManager.getModID(index); + int fileID = m_DownloadManager.getFileInfo(index)->fileID; + GuessedValue modName; + + // see if there already are mods with the specified mod id + if (modID != 0) { + std::vector modInfo = ModInfo::getByModID(modID); + for (auto iter = modInfo.begin(); iter != modInfo.end(); ++iter) { + std::vector flags = (*iter)->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end()) { + modName.update((*iter)->name(), GUESS_PRESET); + (*iter)->saveMeta(); + } + } + } + + m_CurrentProfile->writeModlistNow(); + + bool hasIniTweaks = false; + m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); + if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) { + MessageDialog::showMessage(tr("Installation successful"), qApp->activeWindow()); + refreshModList(); + + int modIndex = ModInfo::getIndex(modName); + if (modIndex != UINT_MAX) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + modInfo->addInstalledFile(modID, fileID); + + if (hasIniTweaks + && m_UserInterface != nullptr + && (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"), + tr("This mod contains ini tweaks. Do you want to configure them now?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { + m_UserInterface->displayModInformation(modInfo, modIndex, ModInfoDialog::TAB_INIFILES); + } + + m_ModInstalled(modName); + } else { + reportError(tr("mod \"%1\" not found").arg(modName)); + } + m_DownloadManager.markInstalled(index); + + emit modInstalled(modName); + } else if (m_InstallationManager.wasCancelled()) { + QMessageBox::information(qApp->activeWindow(), tr("Installation cancelled"), + tr("The mod was not installed completely."), QMessageBox::Ok); + } + } catch (const std::exception &e) { + reportError(e.what()); + } +} + QString OrganizerCore::resolvePath(const QString &fileName) const { if (m_DirectoryStructure == nullptr) { @@ -709,10 +768,11 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &argument HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->getName(), currentDirectory, steamAppID); if (processHandle != INVALID_HANDLE_VALUE) { if (closeAfterStart && (m_UserInterface != nullptr)) { - m_UserInterface->close(); + qApp->closeAllWindows(); } else { - if (m_UserInterface != nullptr) { - m_UserInterface->setEnabled(false); + QWidget *mainWindow = qApp->activeWindow(); + if (mainWindow != nullptr) { + mainWindow->setEnabled(false); } // re-enable the locked dialog because what'd be the point otherwise? dialog->setEnabled(true); @@ -759,9 +819,10 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &argument } ::CloseHandle(processHandle); - if (m_UserInterface != nullptr) { - m_UserInterface->setEnabled(true); + if (mainWindow != nullptr) { + mainWindow->setEnabled(false); } + refreshDirectoryStructure(); // need to remove our stored load order because it may be outdated if a foreign tool changed the // file time. After removing that file, refreshESPList will use the file time as the order @@ -829,9 +890,53 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString & HANDLE OrganizerCore::startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile) { - if (m_UserInterface != nullptr) { - return m_UserInterface->startApplication(executable, args, cwd, profile); + QFileInfo binary; + QString arguments = args.join(" "); + QString currentDirectory = cwd; + QString profileName = profile; + if (profile.length() == 0) { + if (m_CurrentProfile != NULL) { + profileName = m_CurrentProfile->getName(); + } else { + throw MyException(tr("No profile set")); + } + } + QString steamAppID; + if (executable.contains('\\') || executable.contains('/')) { + // file path + binary = QFileInfo(executable); + if (binary.isRelative()) { + // relative path, should be relative to game directory + binary = QFileInfo(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/" + executable); + } + if (cwd.length() == 0) { + currentDirectory = binary.absolutePath(); + } + try { + const Executable &exe = m_ExecutablesList.findByBinary(binary); + steamAppID = exe.m_SteamAppID; + } catch (const std::runtime_error&) { + // nop + } + } else { + // only a file name, search executables list + try { + const Executable &exe = m_ExecutablesList.find(executable); + steamAppID = exe.m_SteamAppID; + if (arguments == "") { + arguments = exe.m_Arguments; + } + binary = exe.m_BinaryInfo; + if (cwd.length() == 0) { + currentDirectory = exe.m_WorkingDirectory; + } + } catch (const std::runtime_error&) { + qWarning("\"%s\" not set up as executable", executable.toUtf8().constData()); + binary = QFileInfo(executable); + } } + + return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID); } bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) const @@ -890,12 +995,29 @@ void OrganizerCore::refreshESPList() } } +QStringList OrganizerCore::defaultArchiveList() +{ + QStringList result; + wchar_t buffer[256]; + std::wstring iniFileName = ToWString(QDir::toNativeSeparators(m_CurrentProfile->getIniFileName())); + + if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().append(L"2").c_str(), + L"", buffer, 256, iniFileName.c_str()) != 0) { + result.append(ToQString(buffer).split(',')); + } + + for (int i = 0; i < m_DefaultArchives.count(); ++i) { + result[i] = result[i].trimmed(); + } + + return result; +} void OrganizerCore::refreshBSAList() { m_ArchivesInit = false; - m_DefaultArchives.clear(); + m_DefaultArchives = defaultArchiveList(); wchar_t buffer[256]; std::wstring iniFileName = ToWString(QDir::toNativeSeparators(m_CurrentProfile->getIniFileName())); @@ -909,15 +1031,6 @@ void OrganizerCore::refreshBSAList() } } - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().append(L"2").c_str(), - L"", buffer, 256, iniFileName.c_str()) != 0) { - m_DefaultArchives.append(ToQString(buffer).split(',')); - } - - for (int i = 0; i < m_DefaultArchives.count(); ++i) { - m_DefaultArchives[i] = m_DefaultArchives[i].trimmed(); - } - m_ActiveArchives.clear(); auto iter = enabledArchives(); @@ -927,7 +1040,7 @@ void OrganizerCore::refreshBSAList() } if (m_UserInterface != nullptr) { - m_UserInterface->updateBSAList(); + m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives); } m_ArchivesInit = true; @@ -1185,11 +1298,27 @@ void OrganizerCore::loginFailedUpdate(const QString &message) MessageDialog::showMessage(tr("login failed: %1. You need to log-in with Nexus to update MO.").arg(message), qApp->activeWindow()); } +void OrganizerCore::syncOverwrite() +{ + unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool { + std::vector flags = mod->getFlags(); + return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end(); }); + + ModInfo::Ptr modInfo = ModInfo::getByIndex(overwriteIndex); + SyncOverwriteDialog syncDialog(modInfo->absolutePath(), m_DirectoryStructure, qApp->activeWindow()); + if (syncDialog.exec() == QDialog::Accepted) { + syncDialog.apply(QDir::fromNativeSeparators(m_Settings.getModDirectory())); + modInfo->testValid(); + refreshDirectoryStructure(); + } + +} + std::vector OrganizerCore::activeProblems() const { std::vector problems; - if (enabledCount() > 255) { + if (m_PluginList.enabledCount() > 255) { problems.push_back(PROBLEM_TOOMANYPLUGINS); } return problems; diff --git a/src/organizercore.h b/src/organizercore.h index ad63dc51..1e03b20c 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -92,6 +92,8 @@ public: ModListSortProxy *createModListProxyModel(); PluginListSortProxy *createPluginListProxyModel(); + HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID); + bool isArchivesInit() const { return m_ArchivesInit; } bool saveCurrentLists(); @@ -107,7 +109,7 @@ public: void requestDownload(const QUrl &url, QNetworkReply *reply); - void doAfterLogin(std::function &function) { m_PostLoginTasks.append(function); } + void doAfterLogin(const std::function &function) { m_PostLoginTasks.append(function); } void spawnBinary(const QFileInfo &binary, const QString &arguments = "", const QDir ¤tDirectory = QDir(), bool closeAfterStart = true, const QString &steamAppID = ""); @@ -118,6 +120,8 @@ public: void loginFailed(const QString &message); void loginFailedUpdate(const QString &message); + void syncOverwrite(); + public: virtual MOBase::IGameInfo &gameInfo() const; virtual MOBase::IModRepositoryBridge *createNexusBridge() const; @@ -162,6 +166,7 @@ public slots: void profileRefresh(); void externalMessage(const QString &message); + void installDownload(int index); void refreshLists(); @@ -171,7 +176,7 @@ signals: * @brief emitted after a mod has been installed * @node this is currently only used for tutorials */ - void modInstalled(); + void modInstalled(const QString &modName); private: @@ -181,9 +186,11 @@ private: bool nexusLogin(); - HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID); void updateModActiveState(int index, bool active); + bool testForSteam(); + QStringList defaultArchiveList(); + private slots: void directory_refreshed(); diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 367ae001..2ce5b3ee 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -100,11 +100,6 @@ HANDLE OrganizerProxy::startApplication(const QString &executable, const QString return m_Proxied->startApplication(executable, args, cwd, profile); } -bool OrganizerProxy::waitForApplication(HANDLE handle, LPDWORD exitCode) const -{ - return m_Proxied->waitForProcessOrJob(handle, exitCode); -} - bool OrganizerProxy::waitForApplication(HANDLE handle, LPDWORD exitCode) const { return m_Proxied->waitForApplication(handle, exitCode); @@ -125,16 +120,14 @@ bool OrganizerProxy::onModInstalled(const std::function return m_Proxied->onModInstalled(func); } - void OrganizerProxy::refreshModList(bool saveChanges) { m_Proxied->refreshModList(saveChanges); } - IModInterface *OrganizerProxy::installMod(const QString &fileName) { - return m_Proxied->installMod(fileName); + return m_Proxied->installMod(fileName, QString()); } QString OrganizerProxy::resolvePath(const QString &fileName) const @@ -157,22 +150,6 @@ QStringList OrganizerProxy::getFileOrigins(const QString &fileName) const return m_Proxied->getFileOrigins(fileName); } -QStringList OrganizerProxy::getFileOrigins(const QString &fileName) const -{ - QStringList result; - const FileEntry::Ptr file = m_Proxied->m_DirectoryStructure->searchFile(ToWString(QFileInfo(fileName).fileName()), NULL); - - if (file.get() != NULL) { - result.append(ToQString(m_Proxied->m_DirectoryStructure->getOriginByID(file->getOrigin()).getName())); - foreach (int i, file->getAlternatives()) { - result.append(ToQString(m_Proxied->m_DirectoryStructure->getOriginByID(i).getName())); - } - } else { - qDebug("%s not found", qPrintable(fileName)); - } - return result; -} - QList OrganizerProxy::findFileInfos(const QString &path, const std::function &filter) const { return m_Proxied->findFileInfos(path, filter); diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index bd96828e..9e1da11b 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -90,7 +90,6 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName) IPluginModPage *modPage = qobject_cast(plugin); if (verifyPlugin(modPage)) { bf::at_key(m_Plugins).push_back(modPage); - registerModPage(modPage); return true; } } @@ -106,7 +105,6 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName) IPluginTool *tool = qobject_cast(plugin); if (verifyPlugin(tool)) { bf::at_key(m_Plugins).push_back(tool); - registerPluginTool(tool); return true; } } diff --git a/src/plugincontainer.h b/src/plugincontainer.h index 213b4154..3f4a98d7 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -15,6 +15,7 @@ #include #ifndef Q_MOC_RUN #include +#include #endif // Q_MOC_RUN #include @@ -25,6 +26,21 @@ class PluginContainer : public QObject, public MOBase::IPluginDiagnose Q_OBJECT Q_INTERFACES(MOBase::IPluginDiagnose) +private: + + typedef boost::fusion::map< + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair> + > PluginMap; + + static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1; + public: PluginContainer(OrganizerCore *organizer); @@ -37,8 +53,9 @@ public: MOBase::IPluginGame *managedGame(const QString &name) const; template - std::vector plugins() const { - return boost::fusion::at_key(m_Plugins); + const std::vector &plugins() const { + typename boost::fusion::result_of::at_key::type temp = boost::fusion::at_key(m_Plugins); + return temp; } const PreviewGenerator &previewGenerator() const; @@ -66,21 +83,6 @@ private: bool registerPlugin(QObject *pluginObj, const QString &fileName); bool unregisterPlugin(QObject *pluginObj, const QString &fileName); -private: - - typedef boost::fusion::map< - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair> - > PluginMap; - - static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1; - private: OrganizerCore *m_Organizer; diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index b2d02cd2..8642ef13 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -1168,7 +1168,7 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) } -PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, FILETIME time, +PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, FILETIME, const QString &originName, const QString &fullPath, bool hasIni) : m_Name(name), m_FullPath(fullPath), m_Enabled(enabled), m_ForceEnabled(enabled), diff --git a/src/settings.cpp b/src/settings.cpp index 274e5979..8571a83b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -60,8 +60,8 @@ static const unsigned char Key2[20] = { 0x99, 0xb8, 0x76, 0x42, 0x3e, 0xc1, 0x60 Settings *Settings::s_Instance = NULL; -Settings::Settings() - : m_Settings(ToQString(GameInfo::instance().getIniFilename()), QSettings::IniFormat) +Settings::Settings(const QSettings &settingsSource) + : m_Settings(settingsSource.fileName(), settingsSource.format()) { if (s_Instance != NULL) { throw std::runtime_error("second instance of \"Settings\" created"); @@ -181,7 +181,7 @@ QString Settings::getSteamAppID() const QString Settings::getDownloadDirectory() const { - return QDir::toNativeSeparators(m_Settings.value("Settings/download_directory", ToQString(GameInfo::instance().getDownloadDir())).toString()); + return getConfigurablePath("download_directory", ToQString(AppConfig::downloadPath())); } @@ -219,14 +219,19 @@ std::map Settings::getPreferredServers() return result; } +QString Settings::getConfigurablePath(const QString &key, const QString &def) const +{ + return m_Settings.value(QString("settings/") + key, qApp->property("dataPath").toString() + "/" + def).toString(); +} + QString Settings::getCacheDirectory() const { - return QDir::toNativeSeparators(m_Settings.value("Settings/cache_directory", ToQString(GameInfo::instance().getCacheDir())).toString()); + return getConfigurablePath("cache_directory", ToQString(AppConfig::cachePath())); } QString Settings::getModDirectory() const { - return QDir::toNativeSeparators(m_Settings.value("Settings/mod_directory", ToQString(GameInfo::instance().getModsDir())).toString()); + return getConfigurablePath("mod_directory", ToQString(AppConfig::modsPath())); } QString Settings::getNMMVersion() const diff --git a/src/settings.h b/src/settings.h index 5d398f49..cea4729a 100644 --- a/src/settings.h +++ b/src/settings.h @@ -43,7 +43,7 @@ public: /** * @brief constructor **/ - Settings(); + Settings(const QSettings &settingsSource); virtual ~Settings(); @@ -281,6 +281,7 @@ private: void addStyles(QComboBox *styleBox); void readPluginBlacklist(); void writePluginBlacklist(); + QString getConfigurablePath(const QString &key, const QString &def) const; private slots: diff --git a/src/shared/appconfig.inc b/src/shared/appconfig.inc index 7ce2724f..2ea4d627 100644 --- a/src/shared/appconfig.inc +++ b/src/shared/appconfig.inc @@ -1,7 +1,10 @@ APPPARAM(std::wstring, translationPrefix, L"organizer") APPPARAM(std::wstring, pluginPath, L"plugins") +APPPARAM(std::wstring, modsPath, L"mods") +APPPARAM(std::wstring, downloadPath, L"downloads") APPPARAM(std::wstring, overwritePath, L"overwrite") APPPARAM(std::wstring, stylesheetsPath, L"stylesheets") +APPPARAM(std::wstring, cachePath, L"webcache") APPPARAM(std::wstring, profileTweakIni, L"profile_tweaks.ini") APPPARAM(std::wstring, logFile, L"ModOrganizer.log") APPPARAM(std::wstring, proxyDLLTarget, L"steam_api.dll") diff --git a/src/shared/stackdata.cpp b/src/shared/stackdata.cpp index 7f6f6e52..39e69569 100644 --- a/src/shared/stackdata.cpp +++ b/src/shared/stackdata.cpp @@ -1,9 +1,9 @@ #include "stackdata.h" +#include "util.h" #include #include #include -#include #include diff --git a/src/syncoverwritedialog.cpp b/src/syncoverwritedialog.cpp index 380719d8..5f99bb83 100644 --- a/src/syncoverwritedialog.cpp +++ b/src/syncoverwritedialog.cpp @@ -129,7 +129,7 @@ void SyncOverwriteDialog::applyTo(QTreeWidgetItem *item, const QString &path, co QTreeWidgetItem *child = item->child(i); QString filePath; if (path.length() != 0) { - filePath = path.mid(0).append("/").append(child->text(0)); + filePath = path + "/" + child->text(0); } else { filePath = child->text(0); } @@ -141,8 +141,8 @@ void SyncOverwriteDialog::applyTo(QTreeWidgetItem *item, const QString &path, co int originID = comboBox->itemData(comboBox->currentIndex(), Qt::UserRole).toInt(); if (originID != -1) { FilesOrigin &origin = m_DirectoryStructure->getOriginByID(originID); - QString source = m_SourcePath.mid(0).append("/").append(filePath); - QString destination = modDirectory.mid(0).append("/").append(ToQString(origin.getName())).append("/").append(filePath); + QString source = m_SourcePath + "/" + filePath; + QString destination = modDirectory + "/" + ToQString(origin.getName()) + "/" + filePath; if (!QFile::remove(destination)) { reportError(tr("failed to remove %1").arg(destination)); } else if (!QFile::rename(source, destination)) { -- cgit v1.3.1 From 9edc39633b82b5e02d33c4b0a395fe110af28eb4 Mon Sep 17 00:00:00 2001 From: Tannin Date: Tue, 6 Jan 2015 19:31:53 +0100 Subject: - bugfixes - moved more functionality to game-plugins - further decoupled management functionality from the UI - created another "tutorial" which is only a single page with relevant parts of the ui highlighted with info as tooltips --- src/dlls.manifest.debug.qt5 | 16 ++- src/dlls.manifest.qt5 | 7 +- src/executableslist.cpp | 2 - src/iuserinterface.h | 3 + src/logbuffer.cpp | 8 +- src/main.cpp | 63 ++++++++--- src/mainwindow.cpp | 132 ++++++----------------- src/mainwindow.h | 5 - src/modinfo.cpp | 3 +- src/modlist.cpp | 4 +- src/motddialog.cpp | 2 - src/organizer.pro | 11 +- src/organizercore.cpp | 198 +++++++++++++++------------------- src/organizercore.h | 18 +++- src/organizerproxy.cpp | 2 +- src/pluginlist.h | 1 - src/profile.cpp | 47 ++++---- src/profile.h | 16 ++- src/profilesdialog.cpp | 26 +++-- src/profilesdialog.h | 7 +- src/settings.cpp | 12 ++- src/settings.h | 12 +++ src/shared/appconfig.inc | 5 +- src/shared/fallout3info.cpp | 105 ------------------ src/shared/fallout3info.h | 10 -- src/shared/falloutnvinfo.cpp | 81 -------------- src/shared/falloutnvinfo.h | 11 -- src/shared/gameinfo.cpp | 43 +------- src/shared/gameinfo.h | 15 +-- src/shared/oblivioninfo.cpp | 105 ------------------ src/shared/oblivioninfo.h | 11 -- src/shared/skyriminfo.cpp | 109 +------------------ src/shared/skyriminfo.h | 16 --- src/transfersavesdialog.cpp | 23 ++-- src/transfersavesdialog.h | 5 +- src/tutorials/Highlight.qml | 1 - src/tutorials/Tooltip.qml | 31 ++++++ src/tutorials/TooltipArea.qml | 55 ++++++++++ src/tutorials/TutorialDescription.qml | 5 +- src/tutorials/TutorialOverlay.qml | 93 +++++++++------- src/tutorials/tutorial_primer_main.js | 111 +++++++++++++++++++ src/tutorials/tutorials.js | 28 +++-- 42 files changed, 590 insertions(+), 868 deletions(-) create mode 100644 src/tutorials/Tooltip.qml create mode 100644 src/tutorials/TooltipArea.qml create mode 100644 src/tutorials/tutorial_primer_main.js (limited to 'src/settings.h') diff --git a/src/dlls.manifest.debug.qt5 b/src/dlls.manifest.debug.qt5 index 1bbdf691..e49b33b8 100644 --- a/src/dlls.manifest.debug.qt5 +++ b/src/dlls.manifest.debug.qt5 @@ -1,19 +1,27 @@ - - - + + + - + + + + + + + + + diff --git a/src/dlls.manifest.qt5 b/src/dlls.manifest.qt5 index 59918f2b..b8f2d467 100644 --- a/src/dlls.manifest.qt5 +++ b/src/dlls.manifest.qt5 @@ -1,9 +1,9 @@ - - - + + + @@ -19,6 +19,7 @@ + diff --git a/src/executableslist.cpp b/src/executableslist.cpp index 25b6bb0f..65a2ea80 100644 --- a/src/executableslist.cpp +++ b/src/executableslist.cpp @@ -92,7 +92,6 @@ void ExecutablesList::getExecutables(std::vector::iterator &begin, s end = m_Executables.end(); } - void ExecutablesList::getExecutables(std::vector::const_iterator &begin, std::vector::const_iterator &end) const { @@ -100,7 +99,6 @@ void ExecutablesList::getExecutables(std::vector::const_iterator &be end = m_Executables.end(); } - const Executable &ExecutablesList::find(const QString &title) const { for (std::vector::const_iterator iter = m_Executables.begin(); iter != m_Executables.end(); ++iter) { diff --git a/src/iuserinterface.h b/src/iuserinterface.h index 4799b934..ff683bce 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -20,6 +20,9 @@ public: virtual void disconnectPlugins() = 0; + virtual bool closeWindow() = 0; + virtual void setWindowEnabled(bool enabled) = 0; + virtual void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) = 0; virtual void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives) = 0; diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp index 635ce13e..fdfce6c5 100644 --- a/src/logbuffer.cpp +++ b/src/logbuffer.cpp @@ -132,8 +132,12 @@ void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, const QSt if (type == QtDebugMsg) { fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), qPrintable(message)); } else { - fprintf(stdout, "%s [%c] (%s:%u) %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), - context.file, context.line, qPrintable(message)); + if (context.line != 0) { + fprintf(stdout, "%s [%c] (%s:%u) %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), + context.file, context.line, qPrintable(message)); + } else { + fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), qPrintable(message)); + } } fflush(stdout); } diff --git a/src/main.cpp b/src/main.cpp index a3ddac20..92ebf8bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -81,20 +81,52 @@ along with Mod Organizer. If not, see . using namespace MOBase; using namespace MOShared; +bool createAndMakeWritable(const std::wstring &subPath) +{ + QString fullPath = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(subPath); + + if (!QDir(fullPath).exists()) { + QDir().mkdir(fullPath); + } + + QFileInfo fileInfo(fullPath); + if (!fileInfo.exists() || !fileInfo.isWritable()) { + if (QMessageBox::question(nullptr, QObject::tr("Permissions required"), + QObject::tr("The current user account doesn't have the required access rights to run " + "Mod Organizer. The neccessary changes can be made automatically (the MO directory " + "will be made writable for the current user account). You will be asked to run " + "\"helper.exe\" with administrative rights."), + QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) { + if (!Helper::init(GameInfo::instance().getOrganizerDirectory())) { + return false; + } + } else { + return false; + } + // no matter which directory didn't exist/wasn't writable, the helper + // should have created them all so we don't have to worry this message box would appear repeatedly + } + return true; +} + bool bootstrap() { - GameInfo &gameInfo = GameInfo::instance(); - // remove the temporary backup directory in case we're restarting after an update - QString moDirectory = QDir::fromNativeSeparators(ToQString(gameInfo.getOrganizerDirectory())); - QString backupDirectory = moDirectory + "/update_backup"; + QString backupDirectory = qApp->applicationDirPath() + "/update_backup"; if (QDir(backupDirectory).exists()) { shellDelete(QStringList(backupDirectory)); } // cycle logfile - removeOldFiles(ToQString(GameInfo::instance().getLogDir()), "ModOrganizer*.log", 5, QDir::Name); + removeOldFiles(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath()), + "ModOrganizer*.log", 5, QDir::Name); + + createAndMakeWritable(AppConfig::profilesPath()); + createAndMakeWritable(AppConfig::modsPath()); + createAndMakeWritable(AppConfig::downloadPath()); + createAndMakeWritable(AppConfig::overwritePath()); + createAndMakeWritable(AppConfig::logPath()); // verify the hook-dll exists QString dllName = qApp->applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName()); @@ -248,6 +280,7 @@ QString determineProfile(QStringList arguments, const QSettings &settings) } if (selectedProfileName.isEmpty()) { qDebug("no configured profile"); + selectedProfileName = "Default"; } else { qDebug("configured profile: %s", qPrintable(selectedProfileName)); } @@ -304,6 +337,11 @@ int main(int argc, char *argv[]) , ToWString(QDir::currentPath()).c_str(), SW_SHOWNORMAL); return 1; } + + if (!bootstrap()) { // requires gameinfo to be initialised! + return -1; + } + LogBuffer::init(100, QtDebugMsg, qApp->property("dataPath").toString() + "/logs/mo_interface.log"); qDebug("Working directory: %s", qPrintable(QDir::toNativeSeparators(QDir::currentPath()))); @@ -353,8 +391,7 @@ int main(int argc, char *argv[]) } } // we continue for the primary instance OR if MO has been called with parameters - - QSettings settings(dataPath + "/ModOrganizer.ini", QSettings::IniFormat); + QSettings settings(dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName()), QSettings::IniFormat); OrganizerCore organizer(settings); @@ -362,7 +399,6 @@ int main(int argc, char *argv[]) pluginContainer.loadPlugins(); QString gamePath = QString::fromUtf8(settings.value("gamePath", "").toByteArray()); - bool done = false; while (!done) { if (!GameInfo::init(ToWString(application.applicationDirPath()), ToWString(dataPath), ToWString(QDir::toNativeSeparators(gamePath)))) { @@ -418,6 +454,10 @@ int main(int argc, char *argv[]) settings.setValue("gamePath", gamePath.toUtf8().constData()); } + organizer.setManagedGame(ToQString(GameInfo::instance().getGameName())); + + organizer.createDefaultProfile(); + if (pluginContainer.managedGame(ToQString(GameInfo::instance().getGameName())) == nullptr) { reportError(QObject::tr("Plugin to handle %1 not installed").arg(ToQString(GameInfo::instance().getGameName()))); return 1; @@ -443,9 +483,6 @@ int main(int argc, char *argv[]) organizer.updateExecutablesList(settings); - if (!bootstrap()) { // requires gameinfo to be initialised! - return -1; - } QString selectedProfileName = determineProfile(arguments, settings); organizer.setCurrentProfile(selectedProfileName); @@ -468,7 +505,7 @@ int main(int argc, char *argv[]) } qDebug("initializing tutorials"); - TutorialManager::init(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getTutorialDir())).append("/")); + TutorialManager::init(qApp->applicationDirPath() + "/" + QString::fromStdWString(AppConfig::tutorialsPath()) + "/"); if (!application.setStyleFile(settings.value("Settings/style", "").toString())) { // disable invalid stylesheet @@ -485,8 +522,6 @@ int main(int argc, char *argv[]) mainWindow.readSettings(); - mainWindow.createFirstProfile(); - qDebug("displaying main window"); mainWindow.show(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a15da6a4..594849f2 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -137,7 +137,11 @@ using namespace MOShared; -MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, OrganizerCore &organizerCore, PluginContainer &pluginContainer, QWidget *parent) +MainWindow::MainWindow(const QString &exeName + , QSettings &initSettings + , OrganizerCore &organizerCore + , PluginContainer &pluginContainer + , QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , m_Tutorial(this, "MainWindow") @@ -157,7 +161,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize { ui->setupUi(this); this->setWindowTitle(ToQString(GameInfo::instance().getGameName()) + " Mod Organizer v" + m_OrganizerCore.getVersion().displayString()); - languageChange(m_OrganizerCore.settings().language()); ui->logList->setModel(LogBuffer::instance()); @@ -280,6 +283,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString))); + connect(ui->tabWidget, SIGNAL(currentChanged(int)), &TutorialManager::instance(), SIGNAL(tabChanged(int))); connect(ui->modList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(modListSortIndicatorChanged(int,Qt::SortOrder))); connect(ui->toolBar, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(toolBar_customContextMenuRequested(QPoint))); @@ -313,7 +317,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize ::SetDllDirectoryW(ToWString(QDir::toNativeSeparators(qApp->applicationDirPath() + "/dlls")).c_str()); m_OrganizerCore.setUserInterface(this, this); - for (const QString &fileName : m_PluginContainer.pluginFileNames()) { installTranslator(QFileInfo(fileName).baseName()); } @@ -326,6 +329,9 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize registerModPage(modPagePlugin); } + // refresh profiles so the current profile can be activated + refreshProfiles(false); + ui->profileBox->setCurrentText(m_OrganizerCore.currentProfile()->getName()); refreshExecutablesList(); @@ -477,7 +483,6 @@ void MainWindow::actionToToolButton(QAction *&sourceAction) sourceAction = newAction; } - void MainWindow::updateToolBar() { foreach (QAction *action, ui->toolBar->actions()) { @@ -560,7 +565,7 @@ void MainWindow::updateProblemsButton() bool MainWindow::errorReported(QString &logFile) { - QDir dir(ToQString(GameInfo::instance().getLogDir())); + QDir dir(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath())); QFileInfoList files = dir.entryInfoList(QStringList("ModOrganizer_??_??_??_??_??.log"), QDir::Files, QDir::Name | QDir::Reversed); @@ -655,7 +660,7 @@ void MainWindow::createHelpWidget() } std::sort(tutorials.begin(), tutorials.end(), - [](const ActionList::value_type &LHS, const ActionList::value_type &RHS) { + [] (const ActionList::value_type &LHS, const ActionList::value_type &RHS) { return LHS.first < RHS.first; } ); for (auto iter = tutorials.begin(); iter != tutorials.end(); ++iter) { @@ -816,16 +821,6 @@ void MainWindow::closeEvent(QCloseEvent* event) } -void MainWindow::createFirstProfile() -{ - if (!refreshProfiles(false)) { - qDebug("creating default profile"); - Profile newProf("Default", false); - refreshProfiles(false); - } -} - - void MainWindow::setBrowserGeometry(const QByteArray &geometry) { m_IntegratedBrowser.restoreGeometry(geometry); @@ -1033,9 +1028,9 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) } if (ui->profileBox->currentIndex() == 0) { - ProfilesDialog(m_GamePath).exec(); + ProfilesDialog(m_GamePath, m_OrganizerCore.managedGame(), this).exec(); while (!refreshProfiles()) { - ProfilesDialog(m_GamePath).exec(); + ProfilesDialog(m_GamePath, m_OrganizerCore.managedGame(), this).exec(); } ui->profileBox->setCurrentIndex(previousIndex); } else { @@ -1181,21 +1176,15 @@ bool MainWindow::refreshProfiles(bool selectProfile) profileBox->clear(); profileBox->addItem(QObject::tr("")); - QDir profilesDir(ToQString(GameInfo::instance().getProfilesDir())); + QDir profilesDir(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath())); profilesDir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); QDirIterator profileIter(profilesDir); - int newIndex = profileIter.hasNext() ? 1 : 0; - int currentIndex = 0; while (profileIter.hasNext()) { profileIter.next(); - ++currentIndex; try { profileBox->addItem(profileIter.fileName()); - if (currentProfileName == profileIter.fileName()) { - newIndex = currentIndex; - } } catch (const std::runtime_error& error) { reportError(QObject::tr("failed to parse profile %1: %2").arg(profileIter.fileName()).arg(error.what())); } @@ -1206,20 +1195,13 @@ bool MainWindow::refreshProfiles(bool selectProfile) if (selectProfile) { if (profileBox->count() > 1) { - if (currentProfileName.length() != 0) { - if ((newIndex != 0) && (profileBox->count() > newIndex)) { - profileBox->setCurrentIndex(newIndex); - } else { - profileBox->setCurrentIndex(1); - } + profileBox->setCurrentText(currentProfileName); + if (profileBox->currentIndex() == 0) { + profileBox->setCurrentIndex(1); } - return true; - } else { - return false; } - } else { - return profileBox->count() > 1; } + return profileBox->count() > 1; } @@ -1301,7 +1283,7 @@ void MainWindow::refreshSaveList() ::GetPrivateProfileStringW(L"General", L"SLocalSavePath", L"Saves", path, MAX_PATH, (ToWString(m_OrganizerCore.currentProfile()->getPath()) + L"\\" + GameInfo::instance().getIniFileNames().at(0)).c_str()); - savesDir.setPath(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getDocumentsDir() + L"\\" + path))); + savesDir.setPath(m_OrganizerCore.managedGame()->documentsDirectory().absoluteFilePath(QString::fromWCharArray(path))); } if (m_SavesWatcher.directories().length() > 0) { @@ -1310,11 +1292,10 @@ void MainWindow::refreshSaveList() m_SavesWatcher.addPath(savesDir.absolutePath()); QStringList filters; - filters << ToQString(GameInfo::instance().getSaveGameExtension()); + filters << QString("*.") + m_OrganizerCore.managedGame()->savegameExtension(); savesDir.setNameFilters(filters); QFileInfoList files = savesDir.entryInfoList(QDir::Files, QDir::Time); - foreach (const QFileInfo &file, files) { QListWidgetItem *item = new QListWidgetItem(file.fileName()); item->setData(Qt::UserRole, file.absoluteFilePath()); @@ -1414,14 +1395,16 @@ void MainWindow::updateBSAList(const QStringList &defaultArchives, const QString subItem->setExpanded(true); } - checkBSAList(defaultArchives); + checkBSAList(); } -void MainWindow::checkBSAList(const QStringList &defaultArchives) +void MainWindow::checkBSAList() { ui->bsaList->blockSignals(true); + QStringList defaultArchives = m_OrganizerCore.defaultArchiveList(); + bool warning = false; for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) { @@ -1512,7 +1495,7 @@ void MainWindow::activateProxy(bool activate) void MainWindow::readSettings() { - QSettings settings(ToQString(GameInfo::instance().getIniFilename()), QSettings::IniFormat); + QSettings settings(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::iniFileName()), QSettings::IniFormat); if (settings.contains("window_geometry")) { restoreGeometry(settings.value("window_geometry").toByteArray()); @@ -1791,7 +1774,7 @@ void MainWindow::on_actionAdd_Profile_triggered() { bool repeat = true; while (repeat) { - ProfilesDialog profilesDialog(m_GamePath); + ProfilesDialog profilesDialog(m_GamePath, m_OrganizerCore.managedGame(), this); profilesDialog.exec(); if (refreshProfiles() && !profilesDialog.failed()) { repeat = false; @@ -1975,7 +1958,7 @@ void MainWindow::modRenamed(const QString &oldName, const QString &newName) //TODO this functionality should be in the Profile class QString modlistName = QString("%1/%2/modlist.txt") - .arg(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()))) + .arg(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath())) .arg(profileName); QFile modList(modlistName); @@ -3176,7 +3159,7 @@ void MainWindow::fixMods_clicked() // search in overwrite { - QDir overwriteDir(ToQString(GameInfo::instance().getOverwriteDir())); + QDir overwriteDir(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::overwritePath())); QStringList esps = overwriteDir.entryList(espFilter); foreach (const QString &esp, esps) { std::map >::iterator iter = missingPlugins.find(esp); @@ -3398,6 +3381,8 @@ void MainWindow::languageChange(const QString &newLanguage) updateProblemsButton(); ui->listOptionsBtn->setMenu(modListContextMenu()); +} + void MainWindow::writeDataToFile(QFile &file, const QString &directory, const DirectoryEntry &directoryEntry) { { // list files @@ -3433,7 +3418,6 @@ void MainWindow::writeDataToFile(QFile &file, const QString &directory, const Di } } - void MainWindow::writeDataToFile() { QString fileName = QFileDialog::getSaveFileName(this); @@ -4286,62 +4270,6 @@ void MainWindow::processLOOTOut(const std::string &lootOut, std::string &errorMe } } } -bool MainWindow::waitForProcessOrJob(HANDLE handle, LPDWORD exitCode) -{ - LockedDialog *dialog = new LockedDialog(this); - dialog->show(); - setEnabled(false); - ON_BLOCK_EXIT([&] () { dialog->hide(); dialog->deleteLater(); setEnabled(true); }); - - DWORD retLen; - JOBOBJECT_BASIC_PROCESS_ID_LIST info; - - bool isJobHandle = true; - - ULONG lastProcessID = ULONG_MAX; - HANDLE processHandle = handle; - - DWORD res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); - while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0) && !dialog->unlockClicked()) { - if (isJobHandle) { - if (::QueryInformationJobObject(handle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) { - if (info.NumberOfProcessIdsInList == 0) { - // fake signaled state - res = WAIT_OBJECT_0; - break; - } else { - // this is indeed a job handle. Figure out one of the process handles as well. - if (lastProcessID != info.ProcessIdList[0]) { - lastProcessID = info.ProcessIdList[0]; - if (processHandle != handle) { - ::CloseHandle(processHandle); - } - processHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, lastProcessID); - } - } - } else { - // the info-object I passed only provides space for 1 process id. but since this code only cares about whether there - // is more than one that's good enough. ERROR_MORE_DATA simply signals there are at least two processes running. - // any other error probably means the handle is a regular process handle, probably caused by running MO in a job without - // the right to break out. - if (::GetLastError() != ERROR_MORE_DATA) { - isJobHandle = false; - } - } - } - - // keep processing events so the app doesn't appear dead - QCoreApplication::processEvents(); - - res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); - } - - if (exitCode != NULL) { - ::GetExitCodeProcess(processHandle, exitCode); - } - ::CloseHandle(processHandle); - - return res == WAIT_OBJECT_0; } void MainWindow::on_bossButton_clicked() diff --git a/src/mainwindow.h b/src/mainwindow.h index feaedef8..52c202a7 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -39,7 +39,6 @@ along with Mod Organizer. If not, see . #include #include "directoryrefresher.h" #include -#include #include "settings.h" #include "downloadmanager.h" #include "installationmanager.h" @@ -98,8 +97,6 @@ public: void setModListSorting(int index); void setESPListSorting(int index); - void createFirstProfile(); - bool saveArchiveList(); void registerPluginTool(MOBase::IPluginTool *tool); @@ -303,8 +300,6 @@ private: OrganizerCore &m_OrganizerCore; PluginContainer &m_PluginContainer; - MOBase::IPluginGame *m_ActiveGame; - QString m_CurrentLanguage; std::vector m_Translators; diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 4befd232..e956377c 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -29,6 +29,7 @@ along with Mod Organizer. If not, see . #include #include +#include #include @@ -1012,7 +1013,7 @@ bool ModInfoOverwrite::isEmpty() const QString ModInfoOverwrite::absolutePath() const { - return QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOverwriteDir())); + return QDir::fromNativeSeparators(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::overwritePath())); } std::vector ModInfoOverwrite::getFlags() const diff --git a/src/modlist.cpp b/src/modlist.cpp index 6d55764a..55344865 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -25,6 +25,7 @@ along with Mod Organizer. If not, see . #include "qtgroupingproxy.h" #include "viewmarkingscrollbar.h" #include +#include #include #include #include @@ -41,6 +42,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include @@ -750,7 +752,7 @@ bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &pa ModInfo::Ptr modInfo = ModInfo::getByIndex(row); QDir modDirectory(modInfo->absolutePath()); - QDir gameDirectory(QDir::fromNativeSeparators(ToQString(MOShared::GameInfo::instance().getOverwriteDir()))); + QDir gameDirectory(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::overwritePath())); unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool { std::vector flags = mod->getFlags(); diff --git a/src/motddialog.cpp b/src/motddialog.cpp index 59ace4d1..96d88542 100644 --- a/src/motddialog.cpp +++ b/src/motddialog.cpp @@ -28,8 +28,6 @@ MotDDialog::MotDDialog(const QString &message, QWidget *parent) { ui->setupUi(this); ui->motdView->setHtml(BBCode::convertToHTML(message)); -// ui->motdView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); -// connect(ui->motdView->page(), SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); connect(ui->motdView, SIGNAL(anchorClicked(QUrl)), this, SLOT(linkClicked(QUrl))); } diff --git a/src/organizer.pro b/src/organizer.pro index 4524a09b..b67fb3e5 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -249,7 +249,7 @@ LIBS += -L"E:/Visual Leak Detector/lib/Win32" #DEFINES += LEAK_CHECK_WITH_VLD # custom leak detection -LIBS += -lDbgHelp +#LIBS += -lDbgHelp # model tests #SOURCES += modeltest.cpp @@ -257,7 +257,7 @@ LIBS += -lDbgHelp #DEFINES += TEST_MODELS -INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk ../boss_modified/boss-api "$${BOOSTPATH}" +INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk "$${LOOTPATH}" "$${BOOSTPATH}" LIBS += -L"$${BOOSTPATH}/stage/lib" @@ -317,7 +317,7 @@ LIBS += -L"$${ZLIBPATH}/build" -lzlibstatic DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _SCL_SECURE_NO_WARNINGS NOMINMAX -DEFINES += BOOST_DISABLE_ASSERTS NDEBUG +DEFINES += BOOST_DISABLE_ASSERTS NDEBUG QT_MESSAGELOGCONTEXT #DEFINES += QMLJSDEBUGGER HGID = $$system(hg id -i) @@ -361,3 +361,8 @@ CONFIG(debug, debug|release) { QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\..\\dlls.*manifest) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) } } + +DISTFILES += \ + tutorials/tutorial_primer_main.js \ + tutorials/Tooltip.qml \ + tutorials/TooltipArea.qml diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 5b322a0a..936e5529 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -77,52 +77,6 @@ static std::wstring getProcessName(DWORD processId) } } -bool OrganizerCore::testForSteam() -{ - size_t currentSize = 1024; - std::unique_ptr processIDs; - DWORD bytesReturned; - bool success = false; - while (!success) { - processIDs.reset(new DWORD[currentSize]); - if (!::EnumProcesses(processIDs.get(), currentSize * sizeof(DWORD), &bytesReturned)) { - qWarning("failed to determine if steam is running"); - return true; - } - if (bytesReturned == (currentSize * sizeof(DWORD))) { - // maximum size used, list probably truncated - currentSize *= 2; - } else { - success = true; - } - } - TCHAR processName[MAX_PATH]; - for (unsigned int i = 0; i < bytesReturned / sizeof(DWORD); ++i) { - memset(processName, '\0', sizeof(TCHAR) * MAX_PATH); - if (processIDs[i] != 0) { - HANDLE process = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIDs[i]); - - if (process != nullptr) { - HMODULE module; - DWORD ignore; - - // first module in a process is always the binary - if (EnumProcessModules(process, &module, sizeof(HMODULE) * 1, &ignore)) { - ::GetModuleBaseName(process, module, processName, MAX_PATH); - if ((_tcsicmp(processName, TEXT("steam.exe")) == 0) || - (_tcsicmp(processName, TEXT("steamservice.exe")) == 0)) { - return true; - } - } - } else { - qDebug("can't open process %lu: %lu", processIDs[i], ::GetLastError()); - } - } - } - - return false; -} - static void startSteam(QWidget *widget) { QSettings steamSettings("HKEY_CURRENT_USER\\Software\\Valve\\Steam", QSettings::NativeFormat); @@ -153,6 +107,7 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) : m_GameInfo(new GameInfoImpl()) , m_UserInterface(nullptr) , m_PluginContainer(nullptr) + , m_GameName() , m_CurrentProfile(nullptr) , m_Settings(initSettings) , m_Updater(NexusInterface::instance()) @@ -188,6 +143,8 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginSuccessful(bool)), this, SLOT(loginSuccessful(bool))); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); + connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame*)), &m_Settings, SLOT(managedGameChanged(MOBase::IPluginGame*))); + // make directory refresher run in a separate thread m_RefresherThread.start(); m_DirectoryRefresher.moveToThread(&m_RefresherThread); @@ -217,7 +174,7 @@ OrganizerCore::~OrganizerCore() void OrganizerCore::storeSettings() { - QString iniFile = ToQString(GameInfo::instance().getIniFilename()); + QString iniFile = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::iniFileName()); shellCopy(iniFile, iniFile + ".new", true, qApp->activeWindow()); QSettings::Status result = QSettings::NoError; @@ -275,8 +232,57 @@ void OrganizerCore::storeSettings() } } +bool OrganizerCore::testForSteam() +{ + size_t currentSize = 1024; + std::unique_ptr processIDs; + DWORD bytesReturned; + bool success = false; + while (!success) { + processIDs.reset(new DWORD[currentSize]); + if (!::EnumProcesses(processIDs.get(), currentSize * sizeof(DWORD), &bytesReturned)) { + qWarning("failed to determine if steam is running"); + return true; + } + if (bytesReturned == (currentSize * sizeof(DWORD))) { + // maximum size used, list probably truncated + currentSize *= 2; + } else { + success = true; + } + } + TCHAR processName[MAX_PATH]; + for (unsigned int i = 0; i < bytesReturned / sizeof(DWORD); ++i) { + memset(processName, '\0', sizeof(TCHAR) * MAX_PATH); + if (processIDs[i] != 0) { + HANDLE process = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIDs[i]); + + if (process != nullptr) { + HMODULE module; + DWORD ignore; + + // first module in a process is always the binary + if (EnumProcessModules(process, &module, sizeof(HMODULE) * 1, &ignore)) { + ::GetModuleBaseName(process, module, processName, MAX_PATH); + if ((_tcsicmp(processName, TEXT("steam.exe")) == 0) || + (_tcsicmp(processName, TEXT("steamservice.exe")) == 0)) { + return true; + } + } + } + } + } + + return false; +} + void OrganizerCore::updateExecutablesList(QSettings &settings) { + if (m_PluginContainer == nullptr) { + qCritical("can't update executables list now"); + return; + } + m_ExecutablesList.init(m_PluginContainer->managedGame(ToQString(GameInfo::instance().getGameName()))); qDebug("setting up configured executables"); @@ -337,6 +343,11 @@ void OrganizerCore::connectPlugins(PluginContainer *container) { m_DownloadManager.setSupportedExtensions(m_InstallationManager.getSupportedExtensions()); m_PluginContainer = container; + + if (!m_GameName.isEmpty()) { + m_GamePlugin = m_PluginContainer->managedGame(m_GameName); + emit managedGameChanged(m_GamePlugin); + } } void OrganizerCore::disconnectPlugins() @@ -348,9 +359,19 @@ void OrganizerCore::disconnectPlugins() m_PluginList.disconnectSlots(); m_Settings.clearPlugins(); + m_GamePlugin = nullptr; m_PluginContainer = nullptr; } +void OrganizerCore::setManagedGame(const QString &gameName) +{ + m_GameName = gameName; + if (m_PluginContainer != nullptr) { + m_GamePlugin = m_PluginContainer->managedGame(m_GameName); + emit managedGameChanged(m_GamePlugin); + } +} + Settings &OrganizerCore::settings() { return m_Settings; @@ -449,11 +470,19 @@ InstallationManager *OrganizerCore::installationManager() return &m_InstallationManager; } +void OrganizerCore::createDefaultProfile() +{ + QString profilesPath = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath()); + qDebug("%d", QDir(profilesPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot).size()); + if (QDir(profilesPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot).size() == 0) { + Profile newProf("Default", managedGame(), false); + } +} + void OrganizerCore::setCurrentProfile(const QString &profileName) { QString profileDir = qApp->property("dataPath").toString() + "/" + ToQString(AppConfig::profilesPath()) + "/" + profileName; - - Profile *newProfile = new Profile(QDir(profileDir)); + Profile *newProfile = new Profile(QDir(profileDir), managedGame()); delete m_CurrentProfile; m_CurrentProfile = newProfile; @@ -576,8 +605,7 @@ void OrganizerCore::setPersistent(const QString &pluginName, const QString &key, QString OrganizerCore::pluginDataPath() const { - QString pluginPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())) + "/" + ToQString(AppConfig::pluginPath()); - return pluginPath + "/data"; + return qApp->applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()) + "/data"; } MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName, const QString &initModName) @@ -676,63 +704,6 @@ void OrganizerCore::installDownload(int index) } } -void OrganizerCore::installDownload(int downloadIndex) -{ - try { - QString fileName = m_DownloadManager.getFilePath(downloadIndex); - int modID = m_DownloadManager.getModID(downloadIndex); - GuessedValue modName; - - // see if there already are mods with the specified mod id - if (modID != 0) { - std::vector modInfo = ModInfo::getByModID(modID); - for (auto iter = modInfo.begin(); iter != modInfo.end(); ++iter) { - std::vector flags = (*iter)->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end()) { - modName.update((*iter)->name(), GUESS_PRESET); - (*iter)->saveMeta(); - } - } - } - - m_CurrentProfile->writeModlistNow(); - - bool hasIniTweaks = false; - m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); - if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) { - MessageDialog::showMessage(tr("Installation successful"), qApp->activeWindow()); - refreshModList(); - - int modIndex = ModInfo::getIndex(modName); - if (modIndex != UINT_MAX) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - - if (hasIniTweaks - && (m_UserInterface != nullptr) - && (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"), - tr("This mod contains ini tweaks. Do you want to configure them now?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - m_UserInterface->displayModInformation(modInfo, modIndex, ModInfoDialog::TAB_INIFILES); - } - - m_ModInstalled(modName); - } else { - reportError(tr("mod \"%1\" not found").arg(modName)); - } - m_DownloadManager.markInstalled(downloadIndex); - - emit modInstalled(modName); - } else if (m_InstallationManager.wasCancelled()) { - QMessageBox::information(qApp->activeWindow(), - tr("Installation cancelled"), - tr("The mod was not installed completely."), - QMessageBox::Ok); - } - } catch (const std::exception &e) { - reportError(e.what()); - } -} - QString OrganizerCore::resolvePath(const QString &fileName) const { if (m_DirectoryStructure == nullptr) { @@ -1011,7 +982,7 @@ HANDLE OrganizerCore::startApplication(const QString &executable, const QStringL return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID); } -bool OrganizerCore::waitForProcessOrJob(HANDLE handle, LPDWORD exitCode) +bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) { if (m_UserInterface != nullptr) { m_UserInterface->lock(); @@ -1131,7 +1102,7 @@ QStringList OrganizerCore::defaultArchiveList() result.append(ToQString(buffer).split(',')); } - for (int i = 0; i < m_DefaultArchives.count(); ++i) { + for (int i = 0; i < result.count(); ++i) { result[i] = result[i].trimmed(); } @@ -1284,6 +1255,11 @@ PluginListSortProxy *OrganizerCore::createPluginListProxyModel() return result; } +IPluginGame *OrganizerCore::managedGame() const +{ + return m_GamePlugin; +} + std::set OrganizerCore::enabledArchives() { std::set result; @@ -1308,8 +1284,6 @@ void OrganizerCore::refreshDirectoryStructure() m_DirectoryRefresher.setMods(activeModList, enabledArchives()); QTimer::singleShot(0, &m_DirectoryRefresher, SLOT(refresh())); - } else { - qDebug("directory update"); } } diff --git a/src/organizercore.h b/src/organizercore.h index cf8d10f5..4d9b0a7c 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,9 @@ class PluginContainer; +namespace MOBase { + class IPluginGame; +} class OrganizerCore : public QObject, public MOBase::IPluginDiagnose { @@ -68,6 +72,8 @@ public: void connectPlugins(PluginContainer *container); void disconnectPlugins(); + void setManagedGame(const QString &gameName); + void updateExecutablesList(QSettings &settings); void startMOUpdate(); @@ -92,7 +98,7 @@ public: ModListSortProxy *createModListProxyModel(); PluginListSortProxy *createPluginListProxyModel(); - HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID); + MOBase::IPluginGame *managedGame() const; bool isArchivesInit() const { return m_ArchivesInit; } @@ -116,6 +122,8 @@ public: void syncOverwrite(); + void createDefaultProfile(); + public: MOBase::IGameInfo &gameInfo() const; MOBase::IModRepositoryBridge *createNexusBridge() const; @@ -132,7 +140,7 @@ public: QVariant persistent(const QString &pluginName, const QString &key, const QVariant &def) const; void setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync); QString pluginDataPath() const; - virtual MOBase::IModInterface *installMod(const QString &fileName); + virtual MOBase::IModInterface *installMod(const QString &fileName, const QString &initModName); QString resolvePath(const QString &fileName) const; QStringList listDirectories(const QString &directoryName) const; QStringList findFiles(const QString &path, const std::function &filter) const; @@ -147,6 +155,7 @@ public: bool onAboutToRun(const std::function &func); bool onFinishedRun(const std::function &func); void refreshModList(bool saveChanges = true); + QStringList defaultArchiveList(); public: // IPluginDiagnose interface @@ -181,6 +190,8 @@ signals: */ void modInstalled(const QString &modName); + void managedGameChanged(MOBase::IPluginGame *gamePlugin); + private: void storeSettings(); @@ -190,7 +201,6 @@ private: void updateModActiveState(int index, bool active); bool testForSteam(); - QStringList defaultArchiveList(); private slots: @@ -211,6 +221,8 @@ private: IUserInterface *m_UserInterface; PluginContainer *m_PluginContainer; + QString m_GameName; + MOBase::IPluginGame *m_GamePlugin; Profile *m_CurrentProfile; diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index ff030d40..2ce5b3ee 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -102,7 +102,7 @@ HANDLE OrganizerProxy::startApplication(const QString &executable, const QString bool OrganizerProxy::waitForApplication(HANDLE handle, LPDWORD exitCode) const { - return m_Proxied->waitForProcessOrJob(handle, exitCode); + return m_Proxied->waitForApplication(handle, exitCode); } bool OrganizerProxy::onAboutToRun(const std::function &func) diff --git a/src/pluginlist.h b/src/pluginlist.h index da644ad3..e0913840 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -33,7 +33,6 @@ along with Mod Organizer. If not, see . #include #include #include "pdll.h" -#include template diff --git a/src/profile.cpp b/src/profile.cpp index 21c9d1d8..775b309e 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -28,6 +28,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -55,12 +56,11 @@ void Profile::touchFile(QString fileName) } } -Profile::Profile(const QString &name, bool useDefaultSettings) +Profile::Profile(const QString &name, IPluginGame *gamePlugin, bool useDefaultSettings) : m_SaveTimer(nullptr) { initTimer(); - QString profilesDir = qApp->property("datapath").toString() + "/" + ToQString(AppConfig::profilesPath()); -qDebug("pd %s", qPrintable(profilesDir)); + QString profilesDir = qApp->property("dataPath").toString() + "/" + ToQString(AppConfig::profilesPath()); QDir profileBase(profilesDir); QString fixedName = name; @@ -79,8 +79,15 @@ qDebug("pd %s", qPrintable(profilesDir)); touchFile("modlist.txt"); touchFile("archives.txt"); -qDebug("fp %s", qPrintable(fullPath)); - GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings); + IPluginGame::ProfileSettings settings = IPluginGame::CONFIGURATION + | IPluginGame::MODS + | IPluginGame::SAVEGAMES; + + if (useDefaultSettings) { + settings |= IPluginGame::PREFER_DEFAULTS; + } + + gamePlugin->initializeProfile(fullPath, settings); } catch (...) { // clean up in case of an error shellDelete(QStringList(profileBase.absoluteFilePath(fixedName))); @@ -90,15 +97,21 @@ qDebug("fp %s", qPrintable(fullPath)); } -Profile::Profile(const QDir &directory) +Profile::Profile(const QDir &directory, IPluginGame *gamePlugin) : m_Directory(directory), m_SaveTimer(nullptr) { + assert(gamePlugin != nullptr); initTimer(); + if (!QFile::exists(m_Directory.filePath("modlist.txt"))) { qWarning("missing modlist.txt in %s", qPrintable(directory.path())); + touchFile(m_Directory.filePath("modlist.txt")); } - GameInfo::instance().repairProfile(ToWString(m_Directory.absolutePath())); + IPluginGame::ProfileSettings settings = IPluginGame::CONFIGURATION + | IPluginGame::MODS + | IPluginGame::SAVEGAMES; + gamePlugin->initializeProfile(directory, settings); if (!QFile::exists(getIniFileName())) { reportError(QObject::tr("\"%1\" is missing or inaccessible").arg(getIniFileName())); @@ -492,29 +505,18 @@ void Profile::setModPriority(unsigned int index, int &newPriority) writeModlist(); } - -Profile Profile::createFrom(const QString &name, const Profile &reference) +Profile *Profile::createPtrFrom(const QString &name, const Profile &reference, MOBase::IPluginGame *gamePlugin) { - QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name); + QString profileDirectory = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath()) + "/" + name; reference.copyFilesTo(profileDirectory); - return Profile(QDir(profileDirectory)); + return new Profile(QDir(profileDirectory), gamePlugin); } - -Profile *Profile::createPtrFrom(const QString &name, const Profile &reference) -{ - QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name); - reference.copyFilesTo(profileDirectory); - return new Profile(QDir(profileDirectory)); -} - - void Profile::copyFilesTo(QString &target) const { copyDir(m_Directory.absolutePath(), target, false); } - std::vector Profile::splitDZString(const wchar_t *buffer) const { std::vector result; @@ -528,7 +530,6 @@ std::vector Profile::splitDZString(const wchar_t *buffer) const return result; } - void Profile::mergeTweak(const QString &tweakName, const QString &tweakedIni) const { static const int bufferSize = 32768; @@ -787,7 +788,7 @@ QString Profile::getPath() const void Profile::rename(const QString &newName) { - QDir profileDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()))); + QDir profileDir(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath())); profileDir.rename(getName(), newName); m_Directory = profileDir.absoluteFilePath(newName); } diff --git a/src/profile.h b/src/profile.h index aa27d6e8..34efd4b2 100644 --- a/src/profile.h +++ b/src/profile.h @@ -31,6 +31,10 @@ along with Mod Organizer. If not, see . #include +namespace MOBase { + class IPluginGame; +} + /** * @brief represents a profile **/ @@ -59,7 +63,7 @@ public: * @param name name of the new profile * @param filter save game filter. Defaults to <no filter>. **/ - Profile(const QString &name, bool useDefaultSettings); + Profile(const QString &name, MOBase::IPluginGame *gamePlugin, bool useDefaultSettings); /** * @brief constructor * @@ -68,7 +72,7 @@ public: * invoking this should always produce a working profile * @param directory directory to read the profile from **/ - Profile(const QDir &directory); + Profile(const QDir &directory, MOBase::IPluginGame *gamePlugin); Profile(const Profile &reference); @@ -83,13 +87,7 @@ public: * @param name of the new profile * @param reference profile to copy from **/ - static Profile createFrom(const QString &name, const Profile &reference); - - /** - * @param name of the new profile - * @param reference profile to copy from - **/ - static Profile *createPtrFrom(const QString &name, const Profile &reference); + static Profile *createPtrFrom(const QString &name, const Profile &reference, MOBase::IPluginGame *gamePlugin); /** * @brief write out the modlist.txt diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp index 26891b3c..02566db3 100644 --- a/src/profilesdialog.cpp +++ b/src/profilesdialog.cpp @@ -26,6 +26,7 @@ along with Mod Organizer. If not, see . #include "profileinputdialog.h" #include "mainwindow.h" #include +#include #include #include #include @@ -40,12 +41,16 @@ using namespace MOShared; Q_DECLARE_METATYPE(Profile::Ptr) -ProfilesDialog::ProfilesDialog(const QString &gamePath, QWidget *parent) - : TutorableDialog("Profiles", parent), ui(new Ui::ProfilesDialog), m_GamePath(gamePath), m_FailState(false) +ProfilesDialog::ProfilesDialog(const QString &gamePath, MOBase::IPluginGame *gamePlugin, QWidget *parent) + : TutorableDialog("Profiles", parent) + , ui(new Ui::ProfilesDialog) + , m_GamePlugin(gamePlugin) + , m_GamePath(gamePath) + , m_FailState(false) { ui->setupUi(this); - QDir profilesDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()))); + QDir profilesDir(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath())); profilesDir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); m_ProfilesList = findChild("profilesList"); @@ -93,20 +98,19 @@ void ProfilesDialog::addItem(const QString &name) QDir profileDir(name); QListWidgetItem *newItem = new QListWidgetItem(profileDir.dirName(), m_ProfilesList); try { - newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(new Profile(profileDir)))); + newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(new Profile(profileDir, m_GamePlugin)))); m_FailState = false; } catch (const std::exception& e) { reportError(tr("failed to create profile: %1").arg(e.what())); } } - void ProfilesDialog::createProfile(const QString &name, bool useDefaultSettings) { try { QListWidget *profilesList = findChild("profilesList"); QListWidgetItem *newItem = new QListWidgetItem(name, profilesList); - newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(new Profile(name, useDefaultSettings)))); + newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(new Profile(name, m_GamePlugin, useDefaultSettings)))); profilesList->addItem(newItem); m_FailState = false; } catch (const std::exception&) { @@ -115,13 +119,12 @@ void ProfilesDialog::createProfile(const QString &name, bool useDefaultSettings) } } - void ProfilesDialog::createProfile(const QString &name, const Profile &reference) { try { QListWidget *profilesList = findChild("profilesList"); QListWidgetItem *newItem = new QListWidgetItem(name, profilesList); - newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(Profile::createPtrFrom(name, reference)))); + newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(Profile::createPtrFrom(name, reference, m_GamePlugin)))); profilesList->addItem(newItem); m_FailState = false; } catch (const std::exception&) { @@ -130,7 +133,6 @@ void ProfilesDialog::createProfile(const QString &name, const Profile &reference } } - void ProfilesDialog::on_addProfileButton_clicked() { ProfileInputDialog dialog(this); @@ -178,7 +180,9 @@ void ProfilesDialog::on_removeProfileButton_clicked() Profile::Ptr currentProfile = profilesList->currentItem()->data(Qt::UserRole).value(); QString profilePath; if (currentProfile.get() == nullptr) { - profilePath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())) + "/" + profilesList->currentItem()->text(); + profilePath = qApp->property("dataPath").toString() + + "/" + QString::fromStdWString(AppConfig::profilesPath()) + + "/" + profilesList->currentItem()->text(); if (QMessageBox::question(this, tr("Profile broken"), tr("This profile you're about to delete seems to be broken or the path is invalid. " "I'm about to delete the following folder: \"%1\". Proceed?").arg(profilePath), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { @@ -315,6 +319,6 @@ void ProfilesDialog::on_localSavesBox_stateChanged(int state) void ProfilesDialog::on_transferButton_clicked() { const Profile::Ptr currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value(); - TransferSavesDialog transferDialog(*currentProfile, this); + TransferSavesDialog transferDialog(*currentProfile, m_GamePlugin, this); transferDialog.exec(); } diff --git a/src/profilesdialog.h b/src/profilesdialog.h index 905d49b1..18b3932f 100644 --- a/src/profilesdialog.h +++ b/src/profilesdialog.h @@ -29,6 +29,10 @@ namespace Ui { class ProfilesDialog; } +namespace MOBase { + class IPluginGame; +} + /** * @brief Dialog that can be used to create/delete/modify profiles @@ -46,7 +50,7 @@ public: * @param parent parent widget * @todo the game path could be retrieved from GameInfo just as easily **/ - explicit ProfilesDialog(const QString &gamePath, QWidget *parent = 0); + explicit ProfilesDialog(const QString &gamePath, MOBase::IPluginGame *gamePlugin, QWidget *parent = 0); ~ProfilesDialog(); /** @@ -87,6 +91,7 @@ private slots: private: Ui::ProfilesDialog *ui; + MOBase::IPluginGame *m_GamePlugin; QString m_GamePath; QListWidget *m_ProfilesList; bool m_FailState; diff --git a/src/settings.cpp b/src/settings.cpp index f44edb24..a052620d 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -22,10 +22,11 @@ along with Mod Organizer. If not, see . #include "settingsdialog.h" #include "utility.h" #include "helper.h" +#include "json.h" #include #include #include -#include "json.h" +#include #include #include @@ -117,6 +118,11 @@ void Settings::registerAsNXMHandler(bool force) } } +void Settings::managedGameChanged(IPluginGame *gamePlugin) +{ + m_GamePlugin = gamePlugin; +} + void Settings::registerPlugin(IPlugin *plugin) { m_Plugins.push_back(plugin); @@ -176,7 +182,7 @@ bool Settings::automaticLoginEnabled() const QString Settings::getSteamAppID() const { - return m_Settings.value("Settings/app_id", ToQString(GameInfo::instance().getSteamAPPId(m_Settings.value("game_edition", 0).toInt()))).toString(); + return m_Settings.value("Settings/app_id", m_GamePlugin->steamAPPId()).toString(); } QString Settings::getDownloadDirectory() const @@ -691,7 +697,7 @@ void Settings::query(QWidget *parent) m_Settings.setValue("Settings/log_level", logLevelBox->currentIndex()); - if (appIDEdit->text() != ToQString(GameInfo::instance().getSteamAPPId())) { + if (appIDEdit->text() != m_GamePlugin->steamAPPId()) { m_Settings.setValue("Settings/app_id", appIDEdit->text()); } else { m_Settings.remove("Settings/app_id"); diff --git a/src/settings.h b/src/settings.h index cea4729a..51e109c4 100644 --- a/src/settings.h +++ b/src/settings.h @@ -29,6 +29,11 @@ along with Mod Organizer. If not, see . #include +namespace MOBase { + class IPluginGame; +} + + /** * manages the settings for Mod Organizer. The settings are not cached * inside the class but read/written directly from/to disc @@ -272,6 +277,11 @@ public: * even if the user said earlier not to */ void registerAsNXMHandler(bool force); + +public slots: + + void managedGameChanged(MOBase::IPluginGame *gamePlugin); + private: QString obfuscate(const QString &password) const; @@ -296,6 +306,8 @@ private: static Settings *s_Instance; + MOBase::IPluginGame *m_GamePlugin; + QSettings m_Settings; LoadMechanism m_LoadMechanism; diff --git a/src/shared/appconfig.inc b/src/shared/appconfig.inc index 4450763f..e2dbfe20 100644 --- a/src/shared/appconfig.inc +++ b/src/shared/appconfig.inc @@ -6,8 +6,11 @@ APPPARAM(std::wstring, downloadPath, L"downloads") APPPARAM(std::wstring, overwritePath, L"overwrite") APPPARAM(std::wstring, stylesheetsPath, L"stylesheets") APPPARAM(std::wstring, cachePath, L"webcache") +APPPARAM(std::wstring, tutorialsPath, L"tutorials") +APPPARAM(std::wstring, logPath, L"logs") APPPARAM(std::wstring, profileTweakIni, L"profile_tweaks.ini") -APPPARAM(std::wstring, logFile, L"ModOrganizer.log") +APPPARAM(std::wstring, logFileName, L"ModOrganizer.log") +APPPARAM(std::wstring, iniFileName, L"ModOrganizer.ini") APPPARAM(std::wstring, proxyDLLTarget, L"steam_api.dll") APPPARAM(std::wstring, proxyDLLOrig, L"steam_api_orig.dll") // needs to be identical to the value used in proxydll-project APPPARAM(std::wstring, proxyDLLSource, L"proxy.dll") diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index 6c420044..c8688cc4 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -84,21 +84,6 @@ bool Fallout3Info::isInvalidationBSA(const std::wstring &bsaName) return false; } -std::wstring Fallout3Info::getDocumentsDir() -{ - std::wostringstream temp; - temp << getMyGamesDirectory() << L"\\Fallout3"; - - return temp.str(); -} - -std::wstring Fallout3Info::getSaveGameDir() -{ - std::wostringstream temp; - temp << getDocumentsDir() << L"\\Saves"; - return temp.str(); -} - std::vector Fallout3Info::getPrimaryPlugins() { return boost::assign::list_of(L"fallout3.esm"); @@ -134,11 +119,6 @@ std::vector Fallout3Info::getIniFileNames() return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini"); } -std::wstring Fallout3Info::getSaveGameExtension() -{ - return L"*.fos"; -} - std::wstring Fallout3Info::getReferenceDataFile() { return L"Fallout - Meshes.bsa"; @@ -155,21 +135,11 @@ std::vector Fallout3Info::getSteamVariants() const return boost::assign::list_of(L"Regular")(L"Game Of The Year"); } -std::wstring Fallout3Info::getSteamAPPId(int variant) const -{ - switch (variant) { - case 1: return L"22370"; - default: return L"22300"; - } -} - - std::wstring Fallout3Info::getSEName() { return L"fose"; } - std::wstring Fallout3Info::getNexusPage(bool nmmScheme) { if (nmmScheme) { @@ -192,68 +162,6 @@ int Fallout3Info::getNexusModIDStatic() } -void Fallout3Info::createProfile(const std::wstring &directory, bool useDefaults) -{ - std::wostringstream target; - - // copy plugins.txt - target << directory << "\\plugins.txt"; - - if (!FileExists(target.str())) { - std::wostringstream source; - source << getLocalAppFolder() << "\\Fallout3\\plugins.txt"; - if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) { - HANDLE file = ::CreateFileW(target.str().c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); - ::CloseHandle(file); - } - } - - // copy ini-file - target.str(L""); target.clear(); - target << directory << L"\\fallout.ini"; - - if (!FileExists(target.str())) { - std::wostringstream source; - if (useDefaults) { - source << getGameDirectory() << L"\\fallout_default.ini"; - } else { - source << getMyGamesDirectory() << L"\\Fallout3"; - if (FileExists(source.str(), L"fallout.ini")) { - source << L"\\fallout.ini"; - } else { - source.str(L""); - source << getGameDirectory() << L"\\fallout_default.ini"; - } - } - - if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) { - if (::GetLastError() != ERROR_FILE_EXISTS) { - std::ostringstream stream; - stream << "failed to copy ini file: " << ToString(source.str(), false); - throw windows_error(stream.str()); - } - } - } - { // copy falloutprefs.ini-file - std::wstring target = directory + L"\\falloutprefs.ini"; - if (!FileExists(target)) { - std::wstring source = getMyGamesDirectory() + L"\\Fallout3\\falloutprefs.ini"; - if (!::CopyFileW(source.c_str(), target.c_str(), true)) { - if (::GetLastError() != ERROR_FILE_EXISTS) { - throw windows_error(std::string("failed to copy ini file: ") + ToString(source, false)); - } - } - } - } -} - - -void Fallout3Info::repairProfile(const std::wstring &directory) -{ - createProfile(directory, false); -} - - bool Fallout3Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*) { static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", nullptr }; @@ -266,17 +174,4 @@ bool Fallout3Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*) return false; } - -/*std::vector Fallout3Info::getExecutables() -{ - std::vector result; - result.push_back(ExecutableInfo(L"FOSE", L"fose_loader.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"Fallout 3", L"fallout3.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"Fallout Mod Manager", L"fomm/fomm.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"Construction Kit", L"geck.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"Fallout Launcher", L"FalloutLauncher.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"BOSS", L"BOSS/BOSS.exe", L"", L"", NEVER_CLOSE)); - - return result; -}*/ } // namespace MOShared diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h index 7e39cce4..6a211d4f 100644 --- a/src/shared/fallout3info.h +++ b/src/shared/fallout3info.h @@ -50,11 +50,6 @@ public: virtual bool isInvalidationBSA(const std::wstring &bsaName); - // full path to this games "My Games"-directory - virtual std::wstring getDocumentsDir(); - - virtual std::wstring getSaveGameDir(); - virtual std::vector getPrimaryPlugins(); virtual std::vector getVanillaBSAs(); @@ -64,12 +59,10 @@ public: // file name of this games ini (no path) virtual std::vector getIniFileNames(); - virtual std::wstring getSaveGameExtension(); virtual std::wstring getReferenceDataFile(); virtual std::wstring getOMODExt(); virtual std::vector getSteamVariants() const; - virtual std::wstring getSteamAPPId(int variant = 0) const; virtual std::wstring getSEName(); @@ -80,9 +73,6 @@ public: virtual int getNexusModID() { return getNexusModIDStatic(); } virtual int getNexusGameID() { return 120; } - virtual void createProfile(const std::wstring &directory, bool useDefaults); - virtual void repairProfile(const std::wstring &directory); - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index f589124a..8e701887 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -85,21 +85,6 @@ bool FalloutNVInfo::isInvalidationBSA(const std::wstring &bsaName) return false; } -std::wstring FalloutNVInfo::getDocumentsDir() -{ - std::wostringstream temp; - temp << getMyGamesDirectory() << L"\\FalloutNV"; - - return temp.str(); -} - -std::wstring FalloutNVInfo::getSaveGameDir() -{ - std::wostringstream temp; - temp << getDocumentsDir() << L"\\Saves"; - return temp.str(); -} - std::vector FalloutNVInfo::getPrimaryPlugins() { return boost::assign::list_of(L"falloutnv.esm"); @@ -139,11 +124,6 @@ std::vector FalloutNVInfo::getIniFileNames() return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini"); } -std::wstring FalloutNVInfo::getSaveGameExtension() -{ - return L"*.fos"; -} - std::wstring FalloutNVInfo::getReferenceDataFile() { return L"Fallout - Meshes.bsa"; @@ -156,61 +136,6 @@ std::wstring FalloutNVInfo::getOMODExt() } -std::wstring FalloutNVInfo::getSteamAPPId(int) const -{ - return L"22380"; -} - - -void FalloutNVInfo::createProfile(const std::wstring &directory, bool useDefaults) -{ - std::wstring target = directory + L"\\plugins.txt"; - - // copy plugins.txt - if (!FileExists(target)) { - std::wstring source = getLocalAppFolder() + L"\\FalloutNV\\plugins.txt"; - if (!::CopyFileW(source.c_str(), target.c_str(), true)) { - HANDLE file = ::CreateFileW(target.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); - ::CloseHandle(file); - } - } - - // copy ini-file - target = directory + L"\\fallout.ini"; - - if (!FileExists(target)) { - std::wstring source; - if (useDefaults) { - source = getGameDirectory() + L"\\fallout_default.ini"; - } else { - source = getMyGamesDirectory() + L"\\FalloutNV"; - if (FileExists(source, L"fallout.ini")) { - source += L"\\fallout.ini"; - } else { - source = getGameDirectory() + L"\\fallout_default.ini"; - } - } - - if (!::CopyFileW(source.c_str(), target.c_str(), true)) { - if (::GetLastError() != ERROR_FILE_EXISTS) { - throw windows_error("failed to copy ini file: " + ToString(source, false)); - } - } - } - { // copy falloutprefs.ini-file - std::wstring target = directory + L"\\falloutprefs.ini"; - if (!FileExists(target)) { - std::wstring source = getMyGamesDirectory() + L"\\FalloutNV\\falloutprefs.ini"; - if (!::CopyFileW(source.c_str(), target.c_str(), true)) { - if (::GetLastError() != ERROR_FILE_EXISTS) { - throw windows_error("failed to copy ini file: " + ToString(source, false)); - } - } - } - } -} - - std::wstring FalloutNVInfo::getSEName() { return L"nvse"; @@ -239,12 +164,6 @@ int FalloutNVInfo::getNexusModIDStatic() } -void FalloutNVInfo::repairProfile(const std::wstring &directory) -{ - createProfile(directory, false); -} - - bool FalloutNVInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) { static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", nullptr }; diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h index 231311de..513b7218 100644 --- a/src/shared/falloutnvinfo.h +++ b/src/shared/falloutnvinfo.h @@ -52,11 +52,6 @@ public: virtual bool isInvalidationBSA(const std::wstring &bsaName); - // full path to this games "My Games"-directory - virtual std::wstring getDocumentsDir(); - - virtual std::wstring getSaveGameDir(); - virtual std::vector getPrimaryPlugins(); virtual std::vector getVanillaBSAs(); @@ -66,12 +61,9 @@ public: // file name of this games ini (no path) virtual std::vector getIniFileNames(); - virtual std::wstring getSaveGameExtension(); virtual std::wstring getReferenceDataFile(); virtual std::wstring getOMODExt(); - virtual std::wstring getSteamAPPId(int variant = 0) const; - virtual std::wstring getSEName(); virtual std::wstring getNexusPage(bool nmmScheme = true); @@ -81,9 +73,6 @@ public: virtual int getNexusModID() { return getNexusModIDStatic(); } virtual int getNexusGameID() { return 130; } - virtual void createProfile(const std::wstring &directory, bool useDefaults); - virtual void repairProfile(const std::wstring &directory); - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index 3727ea49..6d8957ad 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -135,49 +135,9 @@ std::wstring GameInfo::getGameDirectory() const return m_GameDirectory; } -std::wstring GameInfo::getModsDir() const -{ - return m_OrganizerDirectory + L"\\mods"; -} - -std::wstring GameInfo::getProfilesDir() const -{ - return m_OrganizerDirectory + L"\\profiles"; -} - -std::wstring GameInfo::getIniFilename() const -{ - return m_OrganizerDirectory + L"\\ModOrganizer.ini"; -} - - -std::wstring GameInfo::getOverwriteDir() const -{ - return m_OrganizerDirectory + L"\\overwrite"; -} - - -std::wstring GameInfo::getLogDir() const -{ - return m_OrganizerDirectory + L"\\logs"; -} - - -std::wstring GameInfo::getLootDir() const -{ - return m_OrganizerDirectory + L"\\loot"; -} - - -std::wstring GameInfo::getTutorialDir() const -{ - return m_OrganizerDirectory + L"\\tutorials"; -} - - bool GameInfo::requiresSteam() const { - return FileExists(getGameDirectory().append(L"\\steam_api.dll")); + return FileExists(getGameDirectory() + L"\\steam_api.dll"); } std::vector GameInfo::getSteamVariants() const @@ -185,7 +145,6 @@ std::vector GameInfo::getSteamVariants() const return boost::assign::list_of(L"Regular"); } - std::wstring GameInfo::getLocalAppFolder() const { wchar_t localAppFolder[MAX_PATH]; diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index a9da0b97..dbb935f6 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -93,18 +93,13 @@ public: virtual std::wstring getGameName() const = 0; virtual std::wstring getGameShortName() const = 0; - // full path to this games "My Games"-directory - virtual std::wstring getDocumentsDir() = 0; - - virtual std::wstring getSaveGameDir() = 0; - /// determine the load order mechanism used by this game. this may throw an /// exception if the mechanism can't be determined virtual LoadOrderMechanism getLoadOrderMechanism() const { return TYPE_FILETIME; } virtual std::wstring getGameDirectory() const; // get absolute path to the directory where omo stores its mods - virtual std::wstring getModsDir() const; +/* virtual std::wstring getModsDir() const; // get absolute path to the directory where omo stores its profiles virtual std::wstring getProfilesDir() const; @@ -112,7 +107,7 @@ public: virtual std::wstring getOverwriteDir() const; virtual std::wstring getLogDir() const; virtual std::wstring getLootDir() const; - virtual std::wstring getTutorialDir() const; + virtual std::wstring getTutorialDir() const;*/ virtual bool requiresBSAInvalidation() const { return true; } virtual bool requiresSteam() const; @@ -137,13 +132,11 @@ public: // file name of this games ini file(s) virtual std::vector getIniFileNames() = 0; - virtual std::wstring getSaveGameExtension() = 0; virtual std::wstring getReferenceDataFile() = 0; virtual std::wstring getOMODExt() = 0; virtual std::vector getSteamVariants() const; - virtual std::wstring getSteamAPPId(int variant = 0) const = 0; virtual std::wstring getSEName() = 0; @@ -152,10 +145,6 @@ public: virtual int getNexusModID() = 0; virtual int getNexusGameID() = 0; - // clone relevant files to the specified directory - virtual void createProfile(const std::wstring &directory, bool useDefaults) = 0; - virtual void repairProfile(const std::wstring &directory) = 0; - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) = 0; public: diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index 586bcaef..c945dcf4 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -86,22 +86,6 @@ bool OblivionInfo::isInvalidationBSA(const std::wstring &bsaName) return false; } -std::wstring OblivionInfo::getDocumentsDir() -{ - std::wostringstream temp; - temp << getMyGamesDirectory() << L"\\Oblivion"; - - return temp.str(); -} - -std::wstring OblivionInfo::getSaveGameDir() -{ - std::wostringstream temp; - temp << getDocumentsDir() << L"\\Saves"; - return temp.str(); -} - - std::vector OblivionInfo::getPrimaryPlugins() { return boost::assign::list_of(L"oblivion.esm"); @@ -147,63 +131,6 @@ std::vector OblivionInfo::getIniFileNames() } -void OblivionInfo::createProfile(const std::wstring &directory, bool useDefaults) -{ - std::wostringstream target; - - // copy plugins.txt - target << directory << "\\plugins.txt"; - - if (!FileExists(target.str())) { - std::wostringstream source; - source << getLocalAppFolder() << "\\Oblivion\\plugins.txt"; - if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) { - HANDLE file = ::CreateFileW(target.str().c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); - ::CloseHandle(file); - } - } - - // copy ini-file - target.str(L""); target.clear(); - target << directory << L"\\oblivion.ini"; - - if (!FileExists(target.str())) { - std::wostringstream source; - if (useDefaults) { - source << getGameDirectory() << L"\\oblivion_default.ini"; - } else { - source << getMyGamesDirectory() << L"Oblivion"; - if (FileExists(source.str(), L"oblivion.ini")) { - source << L"\\oblivion.ini"; - } else { - source.str(L""); - source << getGameDirectory() << L"\\oblivion_default.ini"; - } - } - if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) { - if (::GetLastError() != ERROR_FILE_EXISTS) { - std::ostringstream stream; - stream << "failed to copy ini file: " << ToString(source.str(), false); - throw windows_error(stream.str()); - } - } - } - - { // copy oblivionprefs.ini-file - std::wstring target = directory + L"\\oblivionprefs.ini"; - if (!FileExists(target)) { - std::wstring source = getMyGamesDirectory() + L"\\Oblivion\\oblivionprefs.ini"; - if (!::CopyFileW(source.c_str(), target.c_str(), true)) { - if ((::CreateFileW(target.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr) == INVALID_HANDLE_VALUE) && - (::GetLastError() != ERROR_FILE_EXISTS)) { - throw windows_error(std::string("failed to create ini file: ") + ToString(target, false)); - } - } - } - } -} - - std::wstring OblivionInfo::getSEName() { return L"obse"; @@ -231,13 +158,6 @@ int OblivionInfo::getNexusModIDStatic() return 38277; } - -void OblivionInfo::repairProfile(const std::wstring &directory) -{ - createProfile(directory, false); -} - - bool OblivionInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) { static LPCWSTR profileFiles[] = { L"oblivion.ini", L"oblivionprefs.ini", L"plugins.txt", nullptr }; @@ -250,12 +170,6 @@ bool OblivionInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) return false; } - -std::wstring OblivionInfo::getSaveGameExtension() -{ - return L"*.ess"; -} - std::wstring OblivionInfo::getReferenceDataFile() { return L"Oblivion - Meshes.bsa"; @@ -268,23 +182,4 @@ std::wstring OblivionInfo::getOMODExt() } -std::wstring OblivionInfo::getSteamAPPId(int) const -{ - return L"22330"; -} - -/* -std::vector OblivionInfo::getExecutables() -{ - std::vector result; - result.push_back(ExecutableInfo(L"OBSE", L"obse_loader.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"Oblivion", L"oblivion.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"Oblivion Mod Manager", L"OblivionModManager.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"Construction Set", L"TESConstructionSet.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"Oblivion Launcher", L"OblivionLauncher.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"BOSS", L"BOSS/BOSS.exe", L"", L"", NEVER_CLOSE)); - result.push_back(ExecutableInfo(L"BOSS (old)", L"Data/BOSS.exe", L"", L"", NEVER_CLOSE)); - - return result; -}*/ } // namespace MOShared diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h index cb506c01..a93f510e 100644 --- a/src/shared/oblivioninfo.h +++ b/src/shared/oblivioninfo.h @@ -48,11 +48,6 @@ public: virtual bool isInvalidationBSA(const std::wstring &bsaName); - // full path to this games "My Games"-directory - virtual std::wstring getDocumentsDir(); - - virtual std::wstring getSaveGameDir(); - virtual std::vector getPrimaryPlugins(); virtual std::vector getVanillaBSAs(); @@ -62,12 +57,9 @@ public: // file name of this games ini (no path) virtual std::vector getIniFileNames(); - virtual std::wstring getSaveGameExtension(); virtual std::wstring getReferenceDataFile(); virtual std::wstring getOMODExt(); - virtual std::wstring getSteamAPPId(int variant = 0) const; - virtual std::wstring getSEName(); virtual std::wstring getNexusPage(bool nmmScheme = true); @@ -77,9 +69,6 @@ public: virtual int getNexusModID() { return getNexusModIDStatic(); } virtual int getNexusGameID() { return 101; } - virtual void createProfile(const std::wstring &directory, bool useDefaults); - virtual void repairProfile(const std::wstring &directory); - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 1d00c4f3..feaf88a3 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -97,7 +97,7 @@ bool SkyrimInfo::isInvalidationBSA(const std::wstring &bsaName) GameInfo::LoadOrderMechanism SkyrimInfo::getLoadOrderMechanism() const { - std::wstring fileName = getGameDirectory().append(L"\\TESV.exe"); + std::wstring fileName = getGameDirectory() + L"\\TESV.exe"; try { VS_FIXEDFILEINFO versionInfo = GetFileVersion(fileName); @@ -113,21 +113,6 @@ GameInfo::LoadOrderMechanism SkyrimInfo::getLoadOrderMechanism() const } } -std::wstring SkyrimInfo::getDocumentsDir() -{ - std::wostringstream temp; - temp << getMyGamesDirectory() << L"\\Skyrim"; - - return temp.str(); -} - -std::wstring SkyrimInfo::getSaveGameDir() -{ - std::wostringstream temp; - temp << getDocumentsDir() << L"\\Saves"; - return temp.str(); -} - std::vector SkyrimInfo::getPrimaryPlugins() { return boost::assign::list_of(L"skyrim.esm")(L"update.esm"); @@ -170,11 +155,6 @@ std::vector SkyrimInfo::getIniFileNames() return boost::assign::list_of(L"skyrim.ini")(L"skyrimprefs.ini"); } -std::wstring SkyrimInfo::getSaveGameExtension() -{ - return L"*.ess"; -} - std::wstring SkyrimInfo::getReferenceDataFile() { return L"Skyrim - Meshes.bsa"; @@ -186,13 +166,6 @@ std::wstring SkyrimInfo::getOMODExt() return L"fomod"; } - -std::wstring SkyrimInfo::getSteamAPPId(int) const -{ - return L"72850"; -} - - std::wstring SkyrimInfo::getSEName() { return L"skse"; @@ -220,73 +193,6 @@ int SkyrimInfo::getNexusModIDStatic() return 1334; } - -void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults) -{ - { // copy plugins.txt - std::wstring target = directory + L"\\plugins.txt"; - if (!FileExists(target)) { - std::wostringstream source; - source << getLocalAppFolder() << "\\Skyrim\\plugins.txt"; - if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) { - HANDLE file = ::CreateFileW(target.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); - ::CloseHandle(file); - } - } - target = directory + L"\\loadorder.txt"; - if (!FileExists(target)) { - std::wstring source = getLocalAppFolder() + L"\\Skyrim\\loadorder.txt"; - if (!::CopyFileW(source.c_str(), target.c_str(), true)) { - HANDLE file = ::CreateFileW(target.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); - ::CloseHandle(file); - } - } - } - - { // copy skyrim.ini-file - std::wstring target = directory + L"\\skyrim.ini"; - if (!FileExists(target)) { - std::wstring source; - if (useDefaults) { - source = getGameDirectory() + L"\\skyrim_default.ini"; - } else { - source = getMyGamesDirectory() + L"\\Skyrim"; - if (FileExists(source, L"skyrim.ini")) { - source += L"\\skyrim.ini"; - } else { - source = getGameDirectory() + L"\\skyrim_default.ini"; - } - } - if (!::CopyFileW(source.c_str(), target.c_str(), true)) { - if (::GetLastError() != ERROR_FILE_EXISTS) { - throw windows_error(std::string("failed to copy ini file: ") + ToString(source, false) + " to " + ToString(target, false)); - } - } - } - } - - { // copy skyrimprefs.ini-file - std::wstring target = directory + L"\\skyrimprefs.ini"; - if (!FileExists(target)) { - std::wstring source = getMyGamesDirectory() + L"\\Skyrim\\skyrimprefs.ini"; - if (!::CopyFileW(source.c_str(), target.c_str(), true)) { - log("failed to copy ini file %ls", source.c_str()); - // create empty - if (::CreateFileW(target.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) == INVALID_HANDLE_VALUE) { - throw windows_error(std::string("failed to copy ini file: ") + ToString(source, false)); - } - } - } - } -} - - -void SkyrimInfo::repairProfile(const std::wstring &directory) -{ - createProfile(directory, false); -} - - bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) { static LPCWSTR profileFiles[] = { L"skyrim.ini", L"skyrimprefs.ini", L"loadorder.txt", nullptr }; @@ -306,17 +212,4 @@ bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPa } -/*std::vector SkyrimInfo::getExecutables() -{ - std::vector result; - result.push_back(ExecutableInfo(L"SKSE", L"skse_loader.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"SBW", L"SBW.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"Skyrim", L"TESV.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"Skyrim Launcher", L"SkyrimLauncher.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"BOSS", L"BOSS/BOSS.exe", L"", L"", DEFAULT_STAY)); - result.push_back(ExecutableInfo(L"Creation Kit", L"CreationKit.exe", L"", L"", DEFAULT_STAY, L"202480")); - - return result; -}*/ - } // namespace MOShared diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h index ad6ab95d..bf08fb25 100644 --- a/src/shared/skyriminfo.h +++ b/src/shared/skyriminfo.h @@ -49,17 +49,11 @@ public: virtual LoadOrderMechanism getLoadOrderMechanism() const; virtual bool requiresBSAInvalidation() const { return true; } -// virtual bool requiresSteam() const { return true; } virtual std::wstring getInvalidationBSA(); virtual bool isInvalidationBSA(const std::wstring &bsaName); - // full path to this games "My Games"-directory - virtual std::wstring getDocumentsDir(); - - virtual std::wstring getSaveGameDir(); - virtual std::vector getPrimaryPlugins(); virtual std::vector getVanillaBSAs(); @@ -70,12 +64,9 @@ public: // file name of this games ini (no path) virtual std::vector getIniFileNames(); - virtual std::wstring getSaveGameExtension(); virtual std::wstring getReferenceDataFile(); virtual std::wstring getOMODExt(); - virtual std::wstring getSteamAPPId(int variant = 0) const; - virtual std::wstring getSEName(); virtual std::wstring getNexusPage(bool nmmScheme = true); @@ -87,15 +78,8 @@ public: static int getNexusGameIDStatic() { return 110; } virtual int getNexusGameID() { return getNexusGameIDStatic(); } - virtual void createProfile(const std::wstring &directory, bool useDefaults); - virtual void repairProfile(const std::wstring &directory); - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); - // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to - // the game directory - //virtual std::vector getExecutables(); - virtual std::wstring archiveListKey() { return L"SResourceArchiveList"; } private: diff --git a/src/transfersavesdialog.cpp b/src/transfersavesdialog.cpp index b07b1673..5d20c253 100644 --- a/src/transfersavesdialog.cpp +++ b/src/transfersavesdialog.cpp @@ -31,8 +31,11 @@ using namespace MOBase; using namespace MOShared; -TransferSavesDialog::TransferSavesDialog(const Profile &profile, QWidget *parent) - : TutorableDialog("TransferSaves", parent), ui(new Ui::TransferSavesDialog), m_Profile(profile) +TransferSavesDialog::TransferSavesDialog(const Profile &profile, IPluginGame *gamePlugin, QWidget *parent) + : TutorableDialog("TransferSaves", parent) + , ui(new Ui::TransferSavesDialog) + , m_Profile(profile) + , m_GamePlugin(gamePlugin) { ui->setupUi(this); refreshGlobalSaves(); @@ -51,10 +54,10 @@ void TransferSavesDialog::refreshGlobalSaves() { m_GlobalSaves.clear(); - QDir savesDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getSaveGameDir()))); + QDir savesDir(m_GamePlugin->savesDirectory()); QStringList filters; - filters << ToQString(GameInfo::instance().getSaveGameExtension()); + filters << m_GamePlugin->savegameExtension(); savesDir.setNameFilters(filters); QStringList files = savesDir.entryList(QDir::Files, QDir::Time); @@ -71,10 +74,10 @@ void TransferSavesDialog::refreshLocalSaves() { m_LocalSaves.clear(); - QDir savesDir(m_Profile.getPath().append("/saves")); + QDir savesDir(m_Profile.getPath() + "/saves"); QStringList filters; - filters << ToQString(GameInfo::instance().getSaveGameExtension()); + filters << m_GamePlugin->savegameExtension(); savesDir.setNameFilters(filters); QStringList files = savesDir.entryList(QDir::Files, QDir::Time); @@ -232,7 +235,7 @@ void TransferSavesDialog::on_moveToGlobalBtn_clicked() "that this will mess up the running number of save games.").arg(selectedCharacter), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - QString destination = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getSaveGameDir())); + QDir destination = m_GamePlugin->savesDirectory(); OverwriteMode overwriteMode = OVERWRITE_ASK; for (std::vector::const_iterator iter = m_LocalSaves.begin(); iter != m_LocalSaves.end(); ++iter) { @@ -240,7 +243,7 @@ void TransferSavesDialog::on_moveToGlobalBtn_clicked() QStringList files = (*iter)->saveFiles(); foreach (const QString &file, files) { QFileInfo fileInfo(file); - QString destinationFile = destination.mid(0).append("/").append(fileInfo.fileName()); + QString destinationFile = destination.filePath(fileInfo.fileName()); if (QFile::exists(destinationFile)) { if (testOverwrite(overwriteMode, destinationFile)) { QFile::remove(destinationFile); @@ -271,7 +274,7 @@ void TransferSavesDialog::on_copyToGlobalBtn_clicked() "that this will mess up the running number of save games.").arg(selectedCharacter), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - QString destination = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getSaveGameDir())); + QDir destination = m_GamePlugin->savesDirectory(); OverwriteMode overwriteMode = OVERWRITE_ASK; for (std::vector::const_iterator iter = m_LocalSaves.begin(); iter != m_LocalSaves.end(); ++iter) { @@ -279,7 +282,7 @@ void TransferSavesDialog::on_copyToGlobalBtn_clicked() QStringList files = (*iter)->saveFiles(); foreach (const QString &file, files) { QFileInfo fileInfo(file); - QString destinationFile = destination.mid(0).append("/").append(fileInfo.fileName()); + QString destinationFile = destination.filePath(fileInfo.fileName()); if (QFile::exists(destinationFile)) { if (testOverwrite(overwriteMode, destinationFile)) { QFile::remove(destinationFile); diff --git a/src/transfersavesdialog.h b/src/transfersavesdialog.h index 37a85000..4d466bf2 100644 --- a/src/transfersavesdialog.h +++ b/src/transfersavesdialog.h @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "tutorabledialog.h" #include "savegamegamebyro.h" #include "profile.h" +#include namespace Ui { class TransferSavesDialog; @@ -33,7 +34,7 @@ class TransferSavesDialog : public MOBase::TutorableDialog Q_OBJECT public: - explicit TransferSavesDialog(const Profile &profile, QWidget *parent = 0); + explicit TransferSavesDialog(const Profile &profile, MOBase::IPluginGame *gamePlugin, QWidget *parent = 0); ~TransferSavesDialog(); private slots: @@ -74,6 +75,8 @@ private: Profile m_Profile; + MOBase::IPluginGame *m_GamePlugin; + std::vector m_GlobalSaves; std::vector m_LocalSaves; diff --git a/src/tutorials/Highlight.qml b/src/tutorials/Highlight.qml index bb4db78f..55474c91 100644 --- a/src/tutorials/Highlight.qml +++ b/src/tutorials/Highlight.qml @@ -1,4 +1,3 @@ -// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 Rectangle { diff --git a/src/tutorials/Tooltip.qml b/src/tutorials/Tooltip.qml new file mode 100644 index 00000000..6424d9ab --- /dev/null +++ b/src/tutorials/Tooltip.qml @@ -0,0 +1,31 @@ +import QtQuick 1.1 + + +Rectangle { + color: "#EE888888" + width: childrenRect.width + 15 + height: childrenRect.height + 15 + z: 20000 + clip: false + border.color: "black" + border.width: 1 + property alias text: tooltipText.text + Text { + id: tooltipText + font.pointSize: 11 + wrapMode: Text.WordWrap + anchors.left: parent.left + anchors.top: parent.top + anchors.leftMargin: 7 + anchors.rightMargin: 7 + z: parent.z + color: "black" + onTextChanged: { + if (width > 200) + width = 200 + } + } + visible: false +} + + diff --git a/src/tutorials/TooltipArea.qml b/src/tutorials/TooltipArea.qml new file mode 100644 index 00000000..48a50f76 --- /dev/null +++ b/src/tutorials/TooltipArea.qml @@ -0,0 +1,55 @@ +import QtQuick 1.1 + +Rectangle { + radius: 2 + color: clickable ? "#AA6666AA" : "#AA882222" + height: 100 + border.color: "black" + border.width: 2 + z: 1 + smooth: true + property bool wasLocked: false + property bool clickable: false + property string tooltipText: "" + + MouseArea { + id: clickArea + anchors.fill: parent + hoverEnabled: true + + onMousePositionChanged: { + if (parent.parent.width - (parent.x + mouseX) < tooltip.width + 50) { + tooltip.x = parent.x + mouseX - 15 - tooltip.width + } else { + tooltip.x = parent.x + mouseX + 15 + } + + if (parent.parent.height - (parent.y + mouseY) < tooltip.height + 50) { + tooltip.y = parent.y + mouseY - 15 - tooltip.height + } else { + tooltip.y = parent.y + mouseY + 15 + } + } + + onEntered: { + tooltip.visible = true + tooltip.text = tooltipText + } + + onPressed: { + wasLocked = tutToplevel.backgroundEnabled() + if (wasLocked && clickable) { + tutorialControl.simulateClick(mouseX + parent.x, mouseY + parent.y) + } + mouse.accepted = false + } + + onCanceled: { + tooltip.visible = false + } + + onExited: { + tooltip.visible = false + } + } +} diff --git a/src/tutorials/TutorialDescription.qml b/src/tutorials/TutorialDescription.qml index b295e477..498573d2 100644 --- a/src/tutorials/TutorialDescription.qml +++ b/src/tutorials/TutorialDescription.qml @@ -15,8 +15,9 @@ Rectangle { border.color: "black" border.width: 3 smooth: true - opacity: 0.9 - color: "#707070" + //opacity: 0.9 + z: 10000 + color: "#FF707070" Image { id: continueIcon diff --git a/src/tutorials/TutorialOverlay.qml b/src/tutorials/TutorialOverlay.qml index f2aad027..15fe9603 100644 --- a/src/tutorials/TutorialOverlay.qml +++ b/src/tutorials/TutorialOverlay.qml @@ -2,52 +2,67 @@ import QtQuick 1.1 import "tutorials.js" as Logic Rectangle { - color: "transparent" - - property int step : 0 - property alias description: tutDescription - property alias highlight: tutHighlight - property alias text: tutDescription.text - property alias boxOpacity: tutDescription.opacity - property int offsetBottom: 50 - property int maxWidth: 400 - - function init() { - Logic.init() - Logic.nextStep() - } + color: "transparent" - function enableBackground(enabled) { - disabledBackground.visible = enabled - } -/* - signal nextStep + id: tutToplevel - onNextStep: {*/ - function nextStep() { - if (step == 0) { - Logic.init() - } + property int step : 0 + property alias description: tutDescription + property alias highlight: tutHighlight + property alias text: tutDescription.text + property alias boxOpacity: tutDescription.opacity + property int offsetBottom: 50 + property int maxWidth: 400 - Logic.nextStep() - } + signal tabChanged(int index) + + function init() { + Logic.init() + Logic.nextStep() + } + + function enableBackground(enabled) { + disabledBackground.visible = enabled + } - TutorialDescription { - id: tutDescription - innerWidth: maxWidth - anchors.bottomMargin: offsetBottom - onClicked: { - Logic.clickNext() - } + function backgroundEnabled() { + return disabledBackground.visible + } + + function nextStep() { + if (step == 0) { + Logic.init() } - Rectangle { - id: disabledBackground - anchors.fill: parent - opacity: 0.2 - color: "#808080" + Logic.nextStep() + } + + TutorialDescription { + id: tutDescription + innerWidth: maxWidth + anchors.bottomMargin: offsetBottom + onClicked: { + Logic.clickNext() } + } + + Rectangle { + id: disabledBackground + anchors.fill: parent + opacity: 0.2 + color: "#808080" + } + + Connections { + target: manager + onTabChanged: tabChanged(index) + } - Highlight { id: tutHighlight } + Tooltip { + id: tooltip + } + Highlight { + id: tutHighlight + } } diff --git a/src/tutorials/tutorial_primer_main.js b/src/tutorials/tutorial_primer_main.js new file mode 100644 index 00000000..12df2c86 --- /dev/null +++ b/src/tutorials/tutorial_primer_main.js @@ -0,0 +1,111 @@ +//TL Overview#5 + + +var tooltips = [] + +function tooltipWidget(widgetName, explanation, maxheight, clickable) { + var rect = tutorialControl.getRect(widgetName) + var component = Qt.createComponent("TooltipArea.qml") + if (component.status === Component.Error) { + console.log("b" + component.errorString()) + } + if (typeof clickable === 'undefined') { + clickable = false + } + if ((typeof maxheight === 'undefined') || maxheight === 0) { + maxheight = rect.height + } + + var obj = component.createObject(tutToplevel, + { "x" : rect.x, + "y" : rect.y, + "width" : rect.width, + "height" : maxheight + }) + obj.tooltipText = explanation + obj.clickable = clickable + obj.visible = true + + tooltips.push(obj) +} + +function tooltipAction(actionName, explanation, maxheight, clickable) { + var rect = tutorialControl.getActionRect(actionName) + var component = Qt.createComponent("TooltipArea.qml") + if (typeof clickable === 'undefined') { + clickable = false + } + if ((typeof maxheight === 'undefined') || maxheight === 0) { + maxheight = rect.height + } + var obj = component.createObject(tutToplevel, + { "x" : rect.x, + "y" : rect.y, + "width" : rect.width, + "height" : maxheight + }) + obj.tooltipText = explanation + obj.clickable = clickable + obj.visible = true + + tooltips.push(obj) +} + +function setupTooptips() { + for (var tip in tooltips) { + tooltips[tip].destroy() + } + tooltips = [] + + tooltipWidget("modList", qsTr("This window shows all the mods that are installed. The column headers can be used for sorting. Only checked mods are active in the current profile.")) + tooltipWidget("profileBox", qsTr("Each profile is a separate set of enabled mods and ini settings.")) + tooltipWidget("groupCombo", qsTr("The dropdown allows various ways of grouping the mods shown in the mod list.")) + tooltipWidget("displayCategoriesBtn", qsTr("Show/hide the category pane.")) + tooltipWidget("modFilterEdit", qsTr("Quickly filter the mod list as you type.")) + tooltipWidget("qt_tabwidget_tabbar", qsTr("Switch between information views."), 0, true) + tooltipWidget("categoriesList", qsTr("This shows mod categories and some meta categories (in angle-brackets). Select some to filter the mod list. For example select \"\" to show only active mods.")) + tooltipWidget("executablesListBox", qsTr("Customizable list for choosing the program to run.")) + tooltipWidget("startButton", qsTr("When this button is clicked, Mod Organizer creates a virtual directory structure then runs the program selected to the left.")) + tooltipWidget("linkButton", qsTr("Will create a shortcut for quick access. The shortcut can be placed in the toolbar at the top, in the Start Menu or on the Windows Desktop.")) + tooltipWidget("logList", qsTr("Log messages produced by MO. Please note that messages with a light bulb usually don't require your attention.")) + + tooltipAction("actionSettings", qsTr("Configure Mod Organizer.")) + tooltipAction("actionProblems", qsTr("Reports potential Problems about the current setup.")) + tooltipAction("actionUpdate", qsTr("Activates if there is an update for MO. Please note that if, for any reason, MO can't communicate with NMM, this will not work either.")) + + switch (manager.findControl("tabWidget").currentIndex) { + case 0: + tooltipWidget("espList", qsTr("Plugins (esp/esm files) of the mods in the current profile. They need to be checked to be loaded.")) + tooltipWidget("bossButton", qsTr("Automatically sort plugins using the bundled LOOT application.")) + tooltipWidget("espFilterEdit", qsTr("Quickly filter plugin list as you type.")) + break + case 1: + tooltipWidget("bsaList", qsTr("All the asset archives (.bsa files) for all active mods.")) + break + case 2: + tooltipWidget("dataTree", qsTr("The directory tree and all files that the program will see.")) + break + case 3: + tooltipWidget("savegameList", qsTr("Save game browser. Shows all the saves for the current profile and whether or not the current mod-load status is correct.")) + break + case 4: + tooltipWidget("downloadView", qsTr("Shows the mods that have been downloaded and if they’ve been installed.")) + break + } +} + +function getTutorialSteps() { + return [ + function() { + tutorial.text = qsTr("Click to quit") + + setupTooptips() + + onTabChanged(setupTooptips) + + waitForClick() + } + ] +} + + diff --git a/src/tutorials/tutorials.js b/src/tutorials/tutorials.js index f2b18856..eb23eab7 100644 --- a/src/tutorials/tutorials.js +++ b/src/tutorials/tutorials.js @@ -52,15 +52,25 @@ function clickNext() function nextStep() { - waitingForClick = false; - description.continueVisible = false - if (step < tutorialSteps.length) { - tutorialControl.lockUI(false) - step++ - tutorialSteps[step - 1]() - } else { - tutorialControl.finish() - } + waitingForClick = false; + description.continueVisible = false + if (step < tutorialSteps.length) { + tutorialControl.lockUI(false) + step++ + tutorialSteps[step - 1]() + } else { + tutorialControl.finish() + } +} + +function sameStep() +{ + tutorialSteps[step - 1]() +} + +function onTabChanged(func) +{ + tutToplevel.tabChanged.connect(func) } function init() -- cgit v1.3.1