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/profile.cpp | 1564 +++++++++++++++++++++++++++---------------------------- 1 file changed, 782 insertions(+), 782 deletions(-) (limited to 'src/profile.cpp') diff --git a/src/profile.cpp b/src/profile.cpp index 2bbdea66..fbfb3266 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -1,782 +1,782 @@ -/* -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 "profile.h" -#include "report.h" -#include "gameinfo.h" -#include "windows_error.h" -#include "dummybsa.h" -#include "modinfo.h" -#include "safewritefile.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include - -using namespace MOBase; -using namespace MOShared; - -Profile::Profile() - : m_SaveTimer(NULL) -{ - initTimer(); -} - -Profile::Profile(const QString &name, bool useDefaultSettings) - : m_SaveTimer(NULL) -{ - initTimer(); - QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())); - QDir profileBase(profilesDir); - - QString fixedName = name; - if (!fixDirectoryName(fixedName)) { - throw MyException(tr("invalid profile name %1").arg(name)); - } - - if (!profileBase.exists() || !profileBase.mkdir(fixedName)) { - throw MyException(tr("failed to create %1").arg(fixedName).toUtf8().constData()); - } - QString fullPath = profilesDir + "/" + fixedName; - m_Directory = QDir(fullPath); - QFile modList(m_Directory.filePath("modlist.txt")); - if (!modList.open(QIODevice::ReadWrite)) { - profileBase.rmdir(fixedName); - throw std::runtime_error(QObject::tr("failed to create %1").arg(m_Directory.filePath("modlist.txt")).toUtf8().constData()); - } - modList.close(); - - try { - GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings); - } catch (...) { - // clean up in case of an error - shellDelete(QStringList(profileBase.absoluteFilePath(fixedName))); - throw; - } - refreshModStatus(); -} - - -Profile::Profile(const QDir& directory) - : m_Directory(directory), m_SaveTimer(NULL) -{ - initTimer(); - if (!QFile::exists(m_Directory.filePath("modlist.txt"))) { - qWarning("missing modlist.txt in %s", qPrintable(directory.path())); - } - - GameInfo::instance().repairProfile(ToWString(m_Directory.absolutePath())); - - if (!QFile::exists(getIniFileName())) { - reportError(QObject::tr("\"%1\" is missing or inaccessible").arg(getIniFileName())); - } - refreshModStatus(); -} - - -Profile::Profile(const Profile& reference) - : m_Directory(reference.m_Directory), m_SaveTimer(NULL) -{ - initTimer(); - refreshModStatus(); -} - - -Profile::~Profile() -{ - writeModlistNow(); -} - - -void Profile::initTimer() -{ - m_SaveTimer = new QTimer(this); - m_SaveTimer->setSingleShot(true); - connect(m_SaveTimer, SIGNAL(timeout()), this, SLOT(writeModlistNow())); -} - - -bool Profile::exists() const -{ - return m_Directory.exists(); -} - - -void Profile::writeModlist() const -{ - if (!m_SaveTimer->isActive()) { - m_SaveTimer->start(2000); - } -} - - -void Profile::cancelWriteModlist() const -{ - m_SaveTimer->stop(); -} - - -void Profile::writeModlistNow(bool onlyOnTimer) const -{ - if (onlyOnTimer && !m_SaveTimer->isActive()) return; - - m_SaveTimer->stop(); - if (!m_Directory.exists()) return; - - try { - QString fileName = getModlistFileName(); - SafeWriteFile file(fileName); - - file->write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8()); - if (m_ModStatus.empty()) { - return; - } - - for (int i = m_ModStatus.size() - 1; i >= 0; --i) { - // the priority order was inverted on load so it has to be inverted again - unsigned int index = m_ModIndexByPriority[i]; - if (index != UINT_MAX) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - std::vector flags = modInfo->getFlags(); - if ((modInfo->getFixedPriority() == INT_MIN)) { - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - file->write("*"); - } else if (m_ModStatus[index].m_Enabled) { - file->write("+"); - } else { - file->write("-"); - } - file->write(modInfo->name().toUtf8()); - file->write("\r\n"); - } - } - } - - if (file.commitIfDifferent(m_LastModlistHash)) { - qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData()); - } - } catch (const std::exception &e) { - reportError(tr("failed to write mod list: %1").arg(e.what())); - return; - } -} - - -void Profile::createTweakedIniFile() -{ - QString tweakedIni = m_Directory.absoluteFilePath("initweaks.ini"); - - if (QFile::exists(tweakedIni) && !shellDeleteQuiet(tweakedIni)) { - reportError(tr("failed to update tweaked ini file, wrong settings may be used: %1").arg(windowsErrorString(::GetLastError()))); - return; - } - - for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { - if (m_ModStatus[i].m_Enabled) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); - mergeTweaks(modInfo, tweakedIni); - } - } - - mergeTweak(getProfileTweaks(), tweakedIni); - - bool error = false; - if (!::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", ToWString(tweakedIni).c_str())) { - error = true; - } - - if (localSavesEnabled()) { - if (!::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"1", ToWString(tweakedIni).c_str())) { - error = true; - } - - if (!::WritePrivateProfileStringW(L"General", L"SLocalSavePath", - AppConfig::localSavePlaceholder(), - ToWString(tweakedIni).c_str())) { - error = true; - } - } - - if (error) { - reportError(tr("failed to create tweaked ini: %1").arg(getCurrentErrorStringA().c_str())); - } - qDebug("%s saved", qPrintable(QDir::toNativeSeparators(tweakedIni))); -} - - -void Profile::refreshModStatus() -{ - QFile file(getModlistFileName()); - if (!file.exists()) { - throw MyException(tr("\"%1\" is missing or inaccessible").arg(getModlistFileName())); - } - - bool modStatusModified = false; - m_ModStatus.clear(); - m_ModStatus.resize(ModInfo::getNumMods()); - - std::set namesRead; - - // load mods from file and update enabled state and priority for them - file.open(QIODevice::ReadOnly); - int index = 0; - while (!file.atEnd()) { - QByteArray line = file.readLine(); - bool enabled = true; - QString modName; - if (line.length() == 0) { - // empty line - continue; - } else if (line.at(0) == '#') { - // comment line - continue; - } else if (line.at(0) == '-') { - enabled = false; - modName = QString::fromUtf8(line.mid(1).trimmed().constData()); - } else if ((line.at(0) == '+') - || (line.at(0) == '*')) { - modName = QString::fromUtf8(line.mid(1).trimmed().constData()); - } else { - modName = QString::fromUtf8(line.trimmed().constData()); - } - if (modName.size() > 0) { - if (namesRead.find(modName) != namesRead.end()) { - continue; - } else { - namesRead.insert(modName); - } - unsigned int modindex = ModInfo::getIndex(modName); - if (modindex != UINT_MAX) { - ModInfo::Ptr info = ModInfo::getByIndex(modindex); - if ((modindex < m_ModStatus.size()) - && (info->getFixedPriority() == INT_MIN)) { - m_ModStatus[modindex].m_Enabled = enabled || info->alwaysEnabled(); - if (m_ModStatus[modindex].m_Priority == -1) { - if (static_cast(index) >= m_ModStatus.size()) { - throw MyException(tr("invalid index %1").arg(index)); - } - m_ModStatus[modindex].m_Priority = index++; - } - } else { - qDebug("mod \"%s\" (profile \"%s\") not found", - modName.toUtf8().constData(), m_Directory.path().toUtf8().constData()); - // need to rewrite the modlist to fix this - modStatusModified = true; - } - } - } else { - // line was empty after trimming - } - } - - int numKnownMods = index; - - int topInsert = 0; - - // invert priority order to match that of the pluginlist. Also - // give priorities to mods not referenced in the profile - for (size_t i = 0; i < m_ModStatus.size(); ++i) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); - if (modInfo->getFixedPriority() == INT_MAX) { - continue; - } - - if (m_ModStatus[i].m_Priority != -1) { - m_ModStatus[i].m_Priority = numKnownMods - m_ModStatus[i].m_Priority - 1; - } else { - if (static_cast(index) >= m_ModStatus.size()) { - throw MyException(tr("invalid index %1").arg(index)); - } - if (modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { - m_ModStatus[i].m_Priority = --topInsert; - } else { - m_ModStatus[i].m_Priority = index++; - } - // also, mark the mod-list as changed - modStatusModified = true; - } - } - // to support insertion of new mods at the top we may now have mods with negative priority. shift them all up - // to align priority with 0 - if (topInsert < 0) { - int offset = topInsert * -1; - for (size_t i = 0; i < m_ModStatus.size(); ++i) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); - if (modInfo->getFixedPriority() == INT_MAX) { - continue; - } - - m_ModStatus[i].m_Priority += offset; - } - } - - file.close(); - updateIndices(); - if (modStatusModified) { - writeModlist(); - } -} - - -void Profile::dumpModStatus() const -{ - for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { - ModInfo::Ptr info = ModInfo::getByIndex(i); - qWarning("%d: %s - %d (%s)", i, info->name().toUtf8().constData(), m_ModStatus[i].m_Priority, - m_ModStatus[i].m_Enabled ? "enabled" : "disabled"); - } -} - - -void Profile::updateIndices() -{ - m_NumRegularMods = 0; - m_ModIndexByPriority.clear(); - m_ModIndexByPriority.resize(m_ModStatus.size(), UINT_MAX); - for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { - int priority = m_ModStatus[i].m_Priority; - if (priority < 0) { - // don't assign this to mapping at all, it's probably the overwrite mod - continue; - } else if (priority >= static_cast(m_ModIndexByPriority.size())) { - qCritical("invalid priority %d for mod", priority); - continue; - } else { - ++m_NumRegularMods; - m_ModIndexByPriority.at(priority) = i; - } - } -} - - -std::vector > Profile::getActiveMods() -{ - std::vector > result; - for (std::vector::const_iterator iter = m_ModIndexByPriority.begin(); - iter != m_ModIndexByPriority.end(); ++iter) { - if ((*iter != UINT_MAX) && m_ModStatus[*iter].m_Enabled) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(*iter); - result.push_back(std::make_tuple(modInfo->name(), modInfo->absolutePath(), m_ModStatus[*iter].m_Priority)); - } - } - - 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(); }); - - if (overwriteIndex != UINT_MAX) { - ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(overwriteIndex); - result.push_back(std::make_tuple(overwriteInfo->name(), overwriteInfo->absolutePath(), UINT_MAX)); - } else { - reportError(tr("Overwrite directory couldn't be parsed")); - } - return result; -} - - -unsigned int Profile::modIndexByPriority(unsigned int priority) const -{ - if (priority >= m_ModStatus.size()) { - throw MyException(tr("invalid priority %1").arg(priority)); - } - - return m_ModIndexByPriority[priority]; -} - - -void Profile::setModEnabled(unsigned int index, bool enabled) -{ - if (index >= m_ModStatus.size()) { - throw MyException(tr("invalid index %1").arg(index)); - } - - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - // we could quit in the following case, this shouldn't be a change anyway, - // but at least this allows the situation to be fixed in case of an error - if (modInfo->alwaysEnabled()) { - enabled = true; - } - - if (enabled != m_ModStatus[index].m_Enabled) { - m_ModStatus[index].m_Enabled = enabled; - emit modStatusChanged(index); - } -} - - -bool Profile::modEnabled(unsigned int index) const -{ - if (index >= m_ModStatus.size()) { - throw MyException(tr("invalid index %1").arg(index)); - } - - return m_ModStatus[index].m_Enabled; -} - - -int Profile::getModPriority(unsigned int index) const -{ - if (index >= m_ModStatus.size()) { - throw MyException(tr("invalid index %1").arg(index)); - } - - return m_ModStatus[index].m_Priority; -} - - -void Profile::setModPriority(unsigned int index, int &newPriority) -{ - if (m_ModStatus.at(index).m_Overwrite) { - // can't change priority of the overwrite - return; - } - - int newPriorityTemp = (std::max)(0, (std::min)(m_ModStatus.size() - 1, newPriority)); - - // don't try to place below overwrite - while ((m_ModIndexByPriority.at(newPriorityTemp) >= m_ModStatus.size()) || - m_ModStatus.at(m_ModIndexByPriority.at(newPriorityTemp)).m_Overwrite) { - --newPriorityTemp; - } - - int oldPriority = m_ModStatus.at(index).m_Priority; - if (newPriorityTemp > oldPriority) { - // priority is higher than the old, so the gap we left is in lower priorities - for (int i = oldPriority + 1; i <= newPriorityTemp; ++i) { - --m_ModStatus.at(m_ModIndexByPriority.at(i)).m_Priority; - } - } else { - for (int i = newPriorityTemp; i < oldPriority; ++i) { - ++m_ModStatus.at(m_ModIndexByPriority.at(i)).m_Priority; - } - ++newPriority; - } - - m_ModStatus.at(index).m_Priority = newPriorityTemp; - - updateIndices(); - writeModlist(); -} - - -Profile Profile::createFrom(const QString &name, const Profile &reference) -{ - QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name); - reference.copyFilesTo(profileDirectory); - return Profile(QDir(profileDirectory)); -} - - -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; - const wchar_t *pos = buffer; - size_t length = wcslen(pos); - while (length != 0U) { - result.push_back(pos); - pos += length + 1; - length = wcslen(pos); - } - return result; -} - - -void Profile::mergeTweak(const QString &tweakName, const QString &tweakedIni) const -{ - static const int bufferSize = 32768; - - std::wstring tweakNameW = ToWString(tweakName); - std::wstring tweakedIniW = ToWString(tweakedIni); - QScopedArrayPointer buffer(new wchar_t[bufferSize]); - - // retrieve a list of sections - DWORD size = ::GetPrivateProfileSectionNamesW( - buffer.data(), bufferSize, tweakNameW.c_str()); - - if (size == bufferSize - 2) { - // unfortunately there is no good way to find the required size - // of the buffer - throw MyException(QString("Buffer too small. Please report this as a bug. " - "For now you might want to split up %1").arg(tweakName)); - } - - std::vector sections = splitDZString(buffer.data()); - - // now iterate over all sections and retrieve a list of keys in each - for (std::vector::iterator iter = sections.begin(); - iter != sections.end(); ++iter) { - // retrieve the names of all keys - size = ::GetPrivateProfileStringW(iter->c_str(), NULL, NULL, buffer.data(), - bufferSize, tweakNameW.c_str()); - if (size == bufferSize - 2) { - throw MyException(QString("Buffer too small. Please report this as a bug. " - "For now you might want to split up %1").arg(tweakName)); - } - - std::vector keys = splitDZString(buffer.data()); - - for (std::vector::iterator keyIter = keys.begin(); - keyIter != keys.end(); ++keyIter) { - //TODO this treats everything as strings but how could I differentiate the type? - ::GetPrivateProfileStringW(iter->c_str(), keyIter->c_str(), - NULL, buffer.data(), bufferSize, ToWString(tweakName).c_str()); - ::WritePrivateProfileStringW(iter->c_str(), keyIter->c_str(), - buffer.data(), tweakedIniW.c_str()); - } - } -} - -void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const -{ - std::vector iniTweaks = modInfo->getIniTweaks(); - for (std::vector::iterator iter = iniTweaks.begin(); - iter != iniTweaks.end(); ++iter) { - mergeTweak(*iter, tweakedIni); - } -} - - -bool Profile::invalidationActive(bool *supported) const -{ - if (GameInfo::instance().requiresBSAInvalidation()) { - *supported = true; - wchar_t buffer[1024]; - std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); - // epic ms fail: GetPrivateProfileString uses errno (for whatever reason) to signal a fail since the return value - // has a different meaning (number of bytes copied). HOWEVER, it will not set errno to 0 if NO error occured - errno = 0; - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - L"", buffer, 1024, iniFileName.c_str()) == 0) { - if (errno != 0x02) { - if (supported != NULL) { - *supported = false; - } - return false; - } else { - QString errorMessage = tr("failed to parse ini file (%1)").arg(ToQString(iniFileName)); - throw windows_error(errorMessage.toUtf8().constData()); - } - } - QStringList archives = ToQString(buffer).split(','); - - for (int i = 0; i < archives.count(); ++i) { - QString bsaName = archives.at(i).trimmed(); - if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) { - return true; - } - } - } else { - *supported = false; - } - return false; -} - - -void Profile::deactivateInvalidation() const -{ - if (GameInfo::instance().requiresBSAInvalidation()) { - wchar_t buffer[1024]; - std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); - errno = 0; - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - L"", buffer, 1024, iniFileName.c_str()) == 0) { - if (errno == 0x02) { - QString errorMessage = tr("failed to parse ini file (%1): %2").arg(QDir::toNativeSeparators(getIniFileName())).arg(::GetLastError()); - throw windows_error(errorMessage.toUtf8().constData()); - } else { - return; - } - } - QStringList archives = ToQString(buffer).split(", "); - - for (int i = 0; i < archives.count();) { - QString bsaName = archives.at(i).trimmed(); - if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) { - archives.removeAt(i); - } else { - ++i; - } - } - - // just to be safe... - ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); - - if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"0", iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"ArchiveInvalidation.txt", iniFileName.c_str())) { - QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); - throw windows_error(errorMessage.toUtf8().constData()); - } - } -} - - -void Profile::activateInvalidation(const QString& dataDirectory) const -{ - if (GameInfo::instance().requiresBSAInvalidation()) { - wchar_t buffer[1024]; - std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); - errno = 0; - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - L"", buffer, 1024, iniFileName.c_str()) == 0) { - if (errno == 0x02) { - throw windows_error("failed to parse ini file"); - } else { - // ignore. shouldn't have gotten here anyway - return; - } - } - QStringList archives = ToQString(buffer).split(", "); - - QString invalidationBSA = ToQString(GameInfo::instance().getInvalidationBSA()); - - if (!archives.contains(invalidationBSA)) { - archives.insert(0, invalidationBSA); - } - - // just to be safe... - ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); - - if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"", iniFileName.c_str())) { - QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); - throw windows_error(errorMessage.toUtf8().constData()); - } - - QString bsaFile = dataDirectory + "/" + invalidationBSA; - if (!QFile::exists(bsaFile)) { - DummyBSA bsa; - bsa.write(bsaFile); - } - } -} - - -bool Profile::localSavesEnabled() const -{ - return m_Directory.exists("saves"); -} - - -bool Profile::enableLocalSaves(bool enable) -{ - if (enable) { - if (m_Directory.exists("_saves")) { - m_Directory.rename("_saves", "saves"); - } else { - m_Directory.mkdir("saves"); - } - } else { - QMessageBox::StandardButton res = QMessageBox::question(QApplication::activeModalWidget(), tr("Delete savegames?"), - tr("Do you want to delete local savegames? (If you select \"No\", the save games " - "will show up again if you re-enable local savegames)"), - QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, - QMessageBox::Cancel); - if (res == QMessageBox::Yes) { - shellDelete(QStringList(m_Directory.absoluteFilePath("saves"))); - } else if (res == QMessageBox::No) { - m_Directory.rename("saves", "_saves"); - } else { - return false; - } - } - - // default: assume success - return true; -} - - -QString Profile::getModlistFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("modlist.txt")); -} - -QString Profile::getPluginsFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("plugins.txt")); -} - -QString Profile::getLoadOrderFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("loadorder.txt")); -} - -QString Profile::getLockedOrderFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("lockedorder.txt")); -} - -QString Profile::getArchivesFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("archives.txt")); -} - -QString Profile::getDeleterFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("hide_plugins.txt")); -} - -QString Profile::getIniFileName() const -{ - std::wstring primaryIniFile = *(GameInfo::instance().getIniFileNames().begin()); - return m_Directory.absoluteFilePath(ToQString(primaryIniFile)); -} - -QString Profile::getProfileTweaks() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath(ToQString(AppConfig::profileTweakIni()))); -} - -QString Profile::getPath() const -{ - return QDir::cleanPath(m_Directory.absolutePath()); -} - -void Profile::rename(const QString &newName) -{ - QDir profileDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()))); - profileDir.rename(getName(), newName); - m_Directory = profileDir.absoluteFilePath(newName); -} +/* +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 "profile.h" +#include "report.h" +#include "gameinfo.h" +#include "windows_error.h" +#include "dummybsa.h" +#include "modinfo.h" +#include "safewritefile.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +using namespace MOBase; +using namespace MOShared; + +Profile::Profile() + : m_SaveTimer(NULL) +{ + initTimer(); +} + +Profile::Profile(const QString &name, bool useDefaultSettings) + : m_SaveTimer(NULL) +{ + initTimer(); + QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())); + QDir profileBase(profilesDir); + + QString fixedName = name; + if (!fixDirectoryName(fixedName)) { + throw MyException(tr("invalid profile name %1").arg(name)); + } + + if (!profileBase.exists() || !profileBase.mkdir(fixedName)) { + throw MyException(tr("failed to create %1").arg(fixedName).toUtf8().constData()); + } + QString fullPath = profilesDir + "/" + fixedName; + m_Directory = QDir(fullPath); + QFile modList(m_Directory.filePath("modlist.txt")); + if (!modList.open(QIODevice::ReadWrite)) { + profileBase.rmdir(fixedName); + throw std::runtime_error(QObject::tr("failed to create %1").arg(m_Directory.filePath("modlist.txt")).toUtf8().constData()); + } + modList.close(); + + try { + GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings); + } catch (...) { + // clean up in case of an error + shellDelete(QStringList(profileBase.absoluteFilePath(fixedName))); + throw; + } + refreshModStatus(); +} + + +Profile::Profile(const QDir& directory) + : m_Directory(directory), m_SaveTimer(NULL) +{ + initTimer(); + if (!QFile::exists(m_Directory.filePath("modlist.txt"))) { + qWarning("missing modlist.txt in %s", qPrintable(directory.path())); + } + + GameInfo::instance().repairProfile(ToWString(m_Directory.absolutePath())); + + if (!QFile::exists(getIniFileName())) { + reportError(QObject::tr("\"%1\" is missing or inaccessible").arg(getIniFileName())); + } + refreshModStatus(); +} + + +Profile::Profile(const Profile& reference) + : m_Directory(reference.m_Directory), m_SaveTimer(NULL) +{ + initTimer(); + refreshModStatus(); +} + + +Profile::~Profile() +{ + writeModlistNow(); +} + + +void Profile::initTimer() +{ + m_SaveTimer = new QTimer(this); + m_SaveTimer->setSingleShot(true); + connect(m_SaveTimer, SIGNAL(timeout()), this, SLOT(writeModlistNow())); +} + + +bool Profile::exists() const +{ + return m_Directory.exists(); +} + + +void Profile::writeModlist() const +{ + if (!m_SaveTimer->isActive()) { + m_SaveTimer->start(2000); + } +} + + +void Profile::cancelWriteModlist() const +{ + m_SaveTimer->stop(); +} + + +void Profile::writeModlistNow(bool onlyOnTimer) const +{ + if (onlyOnTimer && !m_SaveTimer->isActive()) return; + + m_SaveTimer->stop(); + if (!m_Directory.exists()) return; + + try { + QString fileName = getModlistFileName(); + SafeWriteFile file(fileName); + + file->write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8()); + if (m_ModStatus.empty()) { + return; + } + + for (int i = m_ModStatus.size() - 1; i >= 0; --i) { + // the priority order was inverted on load so it has to be inverted again + unsigned int index = m_ModIndexByPriority[i]; + if (index != UINT_MAX) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + std::vector flags = modInfo->getFlags(); + if ((modInfo->getFixedPriority() == INT_MIN)) { + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { + file->write("*"); + } else if (m_ModStatus[index].m_Enabled) { + file->write("+"); + } else { + file->write("-"); + } + file->write(modInfo->name().toUtf8()); + file->write("\r\n"); + } + } + } + + if (file.commitIfDifferent(m_LastModlistHash)) { + qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData()); + } + } catch (const std::exception &e) { + reportError(tr("failed to write mod list: %1").arg(e.what())); + return; + } +} + + +void Profile::createTweakedIniFile() +{ + QString tweakedIni = m_Directory.absoluteFilePath("initweaks.ini"); + + if (QFile::exists(tweakedIni) && !shellDeleteQuiet(tweakedIni)) { + reportError(tr("failed to update tweaked ini file, wrong settings may be used: %1").arg(windowsErrorString(::GetLastError()))); + return; + } + + for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { + if (m_ModStatus[i].m_Enabled) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + mergeTweaks(modInfo, tweakedIni); + } + } + + mergeTweak(getProfileTweaks(), tweakedIni); + + bool error = false; + if (!::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", ToWString(tweakedIni).c_str())) { + error = true; + } + + if (localSavesEnabled()) { + if (!::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"1", ToWString(tweakedIni).c_str())) { + error = true; + } + + if (!::WritePrivateProfileStringW(L"General", L"SLocalSavePath", + AppConfig::localSavePlaceholder(), + ToWString(tweakedIni).c_str())) { + error = true; + } + } + + if (error) { + reportError(tr("failed to create tweaked ini: %1").arg(getCurrentErrorStringA().c_str())); + } + qDebug("%s saved", qPrintable(QDir::toNativeSeparators(tweakedIni))); +} + + +void Profile::refreshModStatus() +{ + QFile file(getModlistFileName()); + if (!file.exists()) { + throw MyException(tr("\"%1\" is missing or inaccessible").arg(getModlistFileName())); + } + + bool modStatusModified = false; + m_ModStatus.clear(); + m_ModStatus.resize(ModInfo::getNumMods()); + + std::set namesRead; + + // load mods from file and update enabled state and priority for them + file.open(QIODevice::ReadOnly); + int index = 0; + while (!file.atEnd()) { + QByteArray line = file.readLine(); + bool enabled = true; + QString modName; + if (line.length() == 0) { + // empty line + continue; + } else if (line.at(0) == '#') { + // comment line + continue; + } else if (line.at(0) == '-') { + enabled = false; + modName = QString::fromUtf8(line.mid(1).trimmed().constData()); + } else if ((line.at(0) == '+') + || (line.at(0) == '*')) { + modName = QString::fromUtf8(line.mid(1).trimmed().constData()); + } else { + modName = QString::fromUtf8(line.trimmed().constData()); + } + if (modName.size() > 0) { + if (namesRead.find(modName) != namesRead.end()) { + continue; + } else { + namesRead.insert(modName); + } + unsigned int modindex = ModInfo::getIndex(modName); + if (modindex != UINT_MAX) { + ModInfo::Ptr info = ModInfo::getByIndex(modindex); + if ((modindex < m_ModStatus.size()) + && (info->getFixedPriority() == INT_MIN)) { + m_ModStatus[modindex].m_Enabled = enabled || info->alwaysEnabled(); + if (m_ModStatus[modindex].m_Priority == -1) { + if (static_cast(index) >= m_ModStatus.size()) { + throw MyException(tr("invalid index %1").arg(index)); + } + m_ModStatus[modindex].m_Priority = index++; + } + } else { + qDebug("mod \"%s\" (profile \"%s\") not found", + modName.toUtf8().constData(), m_Directory.path().toUtf8().constData()); + // need to rewrite the modlist to fix this + modStatusModified = true; + } + } + } else { + // line was empty after trimming + } + } + + int numKnownMods = index; + + int topInsert = 0; + + // invert priority order to match that of the pluginlist. Also + // give priorities to mods not referenced in the profile + for (size_t i = 0; i < m_ModStatus.size(); ++i) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + if (modInfo->getFixedPriority() == INT_MAX) { + continue; + } + + if (m_ModStatus[i].m_Priority != -1) { + m_ModStatus[i].m_Priority = numKnownMods - m_ModStatus[i].m_Priority - 1; + } else { + if (static_cast(index) >= m_ModStatus.size()) { + throw MyException(tr("invalid index %1").arg(index)); + } + if (modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { + m_ModStatus[i].m_Priority = --topInsert; + } else { + m_ModStatus[i].m_Priority = index++; + } + // also, mark the mod-list as changed + modStatusModified = true; + } + } + // to support insertion of new mods at the top we may now have mods with negative priority. shift them all up + // to align priority with 0 + if (topInsert < 0) { + int offset = topInsert * -1; + for (size_t i = 0; i < m_ModStatus.size(); ++i) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + if (modInfo->getFixedPriority() == INT_MAX) { + continue; + } + + m_ModStatus[i].m_Priority += offset; + } + } + + file.close(); + updateIndices(); + if (modStatusModified) { + writeModlist(); + } +} + + +void Profile::dumpModStatus() const +{ + for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { + ModInfo::Ptr info = ModInfo::getByIndex(i); + qWarning("%d: %s - %d (%s)", i, info->name().toUtf8().constData(), m_ModStatus[i].m_Priority, + m_ModStatus[i].m_Enabled ? "enabled" : "disabled"); + } +} + + +void Profile::updateIndices() +{ + m_NumRegularMods = 0; + m_ModIndexByPriority.clear(); + m_ModIndexByPriority.resize(m_ModStatus.size(), UINT_MAX); + for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { + int priority = m_ModStatus[i].m_Priority; + if (priority < 0) { + // don't assign this to mapping at all, it's probably the overwrite mod + continue; + } else if (priority >= static_cast(m_ModIndexByPriority.size())) { + qCritical("invalid priority %d for mod", priority); + continue; + } else { + ++m_NumRegularMods; + m_ModIndexByPriority.at(priority) = i; + } + } +} + + +std::vector > Profile::getActiveMods() +{ + std::vector > result; + for (std::vector::const_iterator iter = m_ModIndexByPriority.begin(); + iter != m_ModIndexByPriority.end(); ++iter) { + if ((*iter != UINT_MAX) && m_ModStatus[*iter].m_Enabled) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(*iter); + result.push_back(std::make_tuple(modInfo->name(), modInfo->absolutePath(), m_ModStatus[*iter].m_Priority)); + } + } + + 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(); }); + + if (overwriteIndex != UINT_MAX) { + ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(overwriteIndex); + result.push_back(std::make_tuple(overwriteInfo->name(), overwriteInfo->absolutePath(), UINT_MAX)); + } else { + reportError(tr("Overwrite directory couldn't be parsed")); + } + return result; +} + + +unsigned int Profile::modIndexByPriority(unsigned int priority) const +{ + if (priority >= m_ModStatus.size()) { + throw MyException(tr("invalid priority %1").arg(priority)); + } + + return m_ModIndexByPriority[priority]; +} + + +void Profile::setModEnabled(unsigned int index, bool enabled) +{ + if (index >= m_ModStatus.size()) { + throw MyException(tr("invalid index %1").arg(index)); + } + + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + // we could quit in the following case, this shouldn't be a change anyway, + // but at least this allows the situation to be fixed in case of an error + if (modInfo->alwaysEnabled()) { + enabled = true; + } + + if (enabled != m_ModStatus[index].m_Enabled) { + m_ModStatus[index].m_Enabled = enabled; + emit modStatusChanged(index); + } +} + + +bool Profile::modEnabled(unsigned int index) const +{ + if (index >= m_ModStatus.size()) { + throw MyException(tr("invalid index %1").arg(index)); + } + + return m_ModStatus[index].m_Enabled; +} + + +int Profile::getModPriority(unsigned int index) const +{ + if (index >= m_ModStatus.size()) { + throw MyException(tr("invalid index %1").arg(index)); + } + + return m_ModStatus[index].m_Priority; +} + + +void Profile::setModPriority(unsigned int index, int &newPriority) +{ + if (m_ModStatus.at(index).m_Overwrite) { + // can't change priority of the overwrite + return; + } + + int newPriorityTemp = (std::max)(0, (std::min)(m_ModStatus.size() - 1, newPriority)); + + // don't try to place below overwrite + while ((m_ModIndexByPriority.at(newPriorityTemp) >= m_ModStatus.size()) || + m_ModStatus.at(m_ModIndexByPriority.at(newPriorityTemp)).m_Overwrite) { + --newPriorityTemp; + } + + int oldPriority = m_ModStatus.at(index).m_Priority; + if (newPriorityTemp > oldPriority) { + // priority is higher than the old, so the gap we left is in lower priorities + for (int i = oldPriority + 1; i <= newPriorityTemp; ++i) { + --m_ModStatus.at(m_ModIndexByPriority.at(i)).m_Priority; + } + } else { + for (int i = newPriorityTemp; i < oldPriority; ++i) { + ++m_ModStatus.at(m_ModIndexByPriority.at(i)).m_Priority; + } + ++newPriority; + } + + m_ModStatus.at(index).m_Priority = newPriorityTemp; + + updateIndices(); + writeModlist(); +} + + +Profile Profile::createFrom(const QString &name, const Profile &reference) +{ + QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name); + reference.copyFilesTo(profileDirectory); + return Profile(QDir(profileDirectory)); +} + + +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; + const wchar_t *pos = buffer; + size_t length = wcslen(pos); + while (length != 0U) { + result.push_back(pos); + pos += length + 1; + length = wcslen(pos); + } + return result; +} + + +void Profile::mergeTweak(const QString &tweakName, const QString &tweakedIni) const +{ + static const int bufferSize = 32768; + + std::wstring tweakNameW = ToWString(tweakName); + std::wstring tweakedIniW = ToWString(tweakedIni); + QScopedArrayPointer buffer(new wchar_t[bufferSize]); + + // retrieve a list of sections + DWORD size = ::GetPrivateProfileSectionNamesW( + buffer.data(), bufferSize, tweakNameW.c_str()); + + if (size == bufferSize - 2) { + // unfortunately there is no good way to find the required size + // of the buffer + throw MyException(QString("Buffer too small. Please report this as a bug. " + "For now you might want to split up %1").arg(tweakName)); + } + + std::vector sections = splitDZString(buffer.data()); + + // now iterate over all sections and retrieve a list of keys in each + for (std::vector::iterator iter = sections.begin(); + iter != sections.end(); ++iter) { + // retrieve the names of all keys + size = ::GetPrivateProfileStringW(iter->c_str(), NULL, NULL, buffer.data(), + bufferSize, tweakNameW.c_str()); + if (size == bufferSize - 2) { + throw MyException(QString("Buffer too small. Please report this as a bug. " + "For now you might want to split up %1").arg(tweakName)); + } + + std::vector keys = splitDZString(buffer.data()); + + for (std::vector::iterator keyIter = keys.begin(); + keyIter != keys.end(); ++keyIter) { + //TODO this treats everything as strings but how could I differentiate the type? + ::GetPrivateProfileStringW(iter->c_str(), keyIter->c_str(), + NULL, buffer.data(), bufferSize, ToWString(tweakName).c_str()); + ::WritePrivateProfileStringW(iter->c_str(), keyIter->c_str(), + buffer.data(), tweakedIniW.c_str()); + } + } +} + +void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const +{ + std::vector iniTweaks = modInfo->getIniTweaks(); + for (std::vector::iterator iter = iniTweaks.begin(); + iter != iniTweaks.end(); ++iter) { + mergeTweak(*iter, tweakedIni); + } +} + + +bool Profile::invalidationActive(bool *supported) const +{ + if (GameInfo::instance().requiresBSAInvalidation()) { + *supported = true; + wchar_t buffer[1024]; + std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); + // epic ms fail: GetPrivateProfileString uses errno (for whatever reason) to signal a fail since the return value + // has a different meaning (number of bytes copied). HOWEVER, it will not set errno to 0 if NO error occured + errno = 0; + if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), + L"", buffer, 1024, iniFileName.c_str()) == 0) { + if (errno != 0x02) { + if (supported != NULL) { + *supported = false; + } + return false; + } else { + QString errorMessage = tr("failed to parse ini file (%1)").arg(ToQString(iniFileName)); + throw windows_error(errorMessage.toUtf8().constData()); + } + } + QStringList archives = ToQString(buffer).split(','); + + for (int i = 0; i < archives.count(); ++i) { + QString bsaName = archives.at(i).trimmed(); + if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) { + return true; + } + } + } else { + *supported = false; + } + return false; +} + + +void Profile::deactivateInvalidation() const +{ + if (GameInfo::instance().requiresBSAInvalidation()) { + wchar_t buffer[1024]; + std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); + errno = 0; + if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), + L"", buffer, 1024, iniFileName.c_str()) == 0) { + if (errno == 0x02) { + QString errorMessage = tr("failed to parse ini file (%1): %2").arg(QDir::toNativeSeparators(getIniFileName())).arg(::GetLastError()); + throw windows_error(errorMessage.toUtf8().constData()); + } else { + return; + } + } + QStringList archives = ToQString(buffer).split(", "); + + for (int i = 0; i < archives.count();) { + QString bsaName = archives.at(i).trimmed(); + if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) { + archives.removeAt(i); + } else { + ++i; + } + } + + // just to be safe... + ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); + + if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), + ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || + !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"0", iniFileName.c_str()) || + !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"ArchiveInvalidation.txt", iniFileName.c_str())) { + QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); + throw windows_error(errorMessage.toUtf8().constData()); + } + } +} + + +void Profile::activateInvalidation(const QString& dataDirectory) const +{ + if (GameInfo::instance().requiresBSAInvalidation()) { + wchar_t buffer[1024]; + std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); + errno = 0; + if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), + L"", buffer, 1024, iniFileName.c_str()) == 0) { + if (errno == 0x02) { + throw windows_error("failed to parse ini file"); + } else { + // ignore. shouldn't have gotten here anyway + return; + } + } + QStringList archives = ToQString(buffer).split(", "); + + QString invalidationBSA = ToQString(GameInfo::instance().getInvalidationBSA()); + + if (!archives.contains(invalidationBSA)) { + archives.insert(0, invalidationBSA); + } + + // just to be safe... + ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); + + if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), + ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || + !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", iniFileName.c_str()) || + !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"", iniFileName.c_str())) { + QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); + throw windows_error(errorMessage.toUtf8().constData()); + } + + QString bsaFile = dataDirectory + "/" + invalidationBSA; + if (!QFile::exists(bsaFile)) { + DummyBSA bsa; + bsa.write(bsaFile); + } + } +} + + +bool Profile::localSavesEnabled() const +{ + return m_Directory.exists("saves"); +} + + +bool Profile::enableLocalSaves(bool enable) +{ + if (enable) { + if (m_Directory.exists("_saves")) { + m_Directory.rename("_saves", "saves"); + } else { + m_Directory.mkdir("saves"); + } + } else { + QMessageBox::StandardButton res = QMessageBox::question(QApplication::activeModalWidget(), tr("Delete savegames?"), + tr("Do you want to delete local savegames? (If you select \"No\", the save games " + "will show up again if you re-enable local savegames)"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Cancel); + if (res == QMessageBox::Yes) { + shellDelete(QStringList(m_Directory.absoluteFilePath("saves"))); + } else if (res == QMessageBox::No) { + m_Directory.rename("saves", "_saves"); + } else { + return false; + } + } + + // default: assume success + return true; +} + + +QString Profile::getModlistFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("modlist.txt")); +} + +QString Profile::getPluginsFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("plugins.txt")); +} + +QString Profile::getLoadOrderFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("loadorder.txt")); +} + +QString Profile::getLockedOrderFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("lockedorder.txt")); +} + +QString Profile::getArchivesFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("archives.txt")); +} + +QString Profile::getDeleterFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("hide_plugins.txt")); +} + +QString Profile::getIniFileName() const +{ + std::wstring primaryIniFile = *(GameInfo::instance().getIniFileNames().begin()); + return m_Directory.absoluteFilePath(ToQString(primaryIniFile)); +} + +QString Profile::getProfileTweaks() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath(ToQString(AppConfig::profileTweakIni()))); +} + +QString Profile::getPath() const +{ + return QDir::cleanPath(m_Directory.absolutePath()); +} + +void Profile::rename(const QString &newName) +{ + QDir profileDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()))); + profileDir.rename(getName(), newName); + m_Directory = profileDir.absoluteFilePath(newName); +} -- cgit v1.3.1 From 0050cb07409d775efe14e728c1cef4a2c33aa1db Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 21 Jul 2014 19:14:24 +0200 Subject: - download-list will no longer show a file as having incomplete data if there is no file version - added a new mod column with icons displaying the content of the mod - MO now differentiates between mods using an internal name that disambiguates between foreign and regular mods --- src/aboutdialog.cpp | 7 ++- src/downloadmanager.cpp | 3 +- src/icondelegate.cpp | 17 ------- src/icondelegate.h | 1 - src/mainwindow.cpp | 12 ++++- src/modflagicondelegate.cpp | 17 +++++++ src/modflagicondelegate.h | 1 + src/modinfo.cpp | 26 +++++++++- src/modinfo.h | 32 ++++++++++++ src/modlist.cpp | 74 ++++++++++++++++++++++++++-- src/modlist.h | 10 ++++ src/modlistsortproxy.cpp | 7 +++ src/pluginflagicondelegate.cpp | 27 +++++++--- src/pluginflagicondelegate.h | 27 ++++++++-- src/pluginlist.cpp | 3 -- src/profile.cpp | 21 ++++---- src/resources.qrc | 12 +++++ src/resources/contents/breastplate.png | Bin 0 -> 10578 bytes src/resources/contents/checkbox-tree.png | Bin 0 -> 1144 bytes src/resources/contents/conversation.png | Bin 0 -> 7980 bytes src/resources/contents/double-quaver.png | Bin 0 -> 5877 bytes src/resources/contents/empty-chessboard.png | Bin 0 -> 959 bytes src/resources/contents/hand-of-god.png | Bin 0 -> 7105 bytes src/resources/contents/jigsaw-piece.png | Bin 0 -> 5971 bytes src/resources/contents/lyre.png | Bin 0 -> 9368 bytes src/resources/contents/tinker.png | Bin 0 -> 6475 bytes src/resources/contents/usable.png | Bin 0 -> 9919 bytes 27 files changed, 241 insertions(+), 56 deletions(-) create mode 100644 src/resources/contents/breastplate.png create mode 100644 src/resources/contents/checkbox-tree.png create mode 100644 src/resources/contents/conversation.png create mode 100644 src/resources/contents/double-quaver.png create mode 100644 src/resources/contents/empty-chessboard.png create mode 100644 src/resources/contents/hand-of-god.png create mode 100644 src/resources/contents/jigsaw-piece.png create mode 100644 src/resources/contents/lyre.png create mode 100644 src/resources/contents/tinker.png create mode 100644 src/resources/contents/usable.png (limited to 'src/profile.cpp') diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp index a8f9b4db..b5bfeb04 100644 --- a/src/aboutdialog.cpp +++ b/src/aboutdialog.cpp @@ -39,13 +39,12 @@ AboutDialog::AboutDialog(const QString &version, QWidget *parent) addLicense("Qt 4.8.5", LICENSE_LGPL3); addLicense("Qt Json", LICENSE_GPL3); addLicense("Boost Library", LICENSE_BOOST); - addLicense("Tango Icon Theme", LICENSE_NONE); - addLicense("RRZE Icon Set", LICENSE_CCBY3); addLicense("7-zip", LICENSE_LGPL3); addLicense("ZLib", LICENSE_ZLIB); addLicense("NIF File Format Library", LICENSE_BSD3); - addLicense("BOSS (modified)", LICENSE_GPL3); - addLicense("Alphanum Algorithm", LICENSE_ZLIB); + addLicense("Tango Icon Theme", LICENSE_NONE); + addLicense("RRZE Icon Set", LICENSE_CCBY3); + addLicense("Icons by Lorc, Delapouite and sbed available on http://game-icons.net", LICENSE_CCBY3); ui->nameLabel->setText(QString("%1 %2").arg(ui->nameLabel->text()).arg(version)); #ifdef HGID diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 391ac7b9..856ca79c 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -831,7 +831,7 @@ bool DownloadManager::isInfoIncomplete(int index) const // other repositories currently don't support re-querying info anyway return false; } - return (info->m_FileInfo->fileID == 0) || (info->m_FileInfo->modID == 0) || !info->m_FileInfo->version.isValid(); + return (info->m_FileInfo->fileID == 0) || (info->m_FileInfo->modID == 0); } @@ -1181,7 +1181,6 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant userD QVariantMap result = resultData.toMap(); info->name = result["name"].toString(); - qDebug("file info received for %s", qPrintable(info->name)); info->version.parse(result["version"].toString()); if (!info->version.isValid()) { info->version = info->newestVersion; diff --git a/src/icondelegate.cpp b/src/icondelegate.cpp index f7cf6977..8c2fe5cc 100644 --- a/src/icondelegate.cpp +++ b/src/icondelegate.cpp @@ -51,20 +51,3 @@ void IconDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, painter->restore(); } - -QSize IconDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &modelIndex) const -{ - int count = getNumIcons(modelIndex); - unsigned int index = modelIndex.data(Qt::UserRole + 1).toInt(); - QSize result; - if (index < ModInfo::getNumMods()) { - result = QSize(count * 40, 20); - } else { - result = QSize(1, 20); - } - if (option.rect.width() > 0) { - result.setWidth(std::min(option.rect.width(), result.width())); - } - return result; -} - diff --git a/src/icondelegate.h b/src/icondelegate.h index 072343bb..b24de102 100644 --- a/src/icondelegate.h +++ b/src/icondelegate.h @@ -33,7 +33,6 @@ public: explicit IconDelegate(QObject *parent = 0); virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; - virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; signals: diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c3182321..a3887a23 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -209,14 +209,22 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget ui->modList->setModel(m_ModListSortProxy); + GenericIconDelegate *contentDelegate = new GenericIconDelegate(ui->modList, Qt::UserRole + 3, ModList::COL_CONTENT, 150); + connect(ui->modList->header(), SIGNAL(sectionResized(int,int,int)), contentDelegate, SLOT(columnResized(int,int,int))); ui->modList->sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder); ui->modList->setItemDelegateForColumn(ModList::COL_FLAGS, new ModFlagIconDelegate(ui->modList)); + ui->modList->setItemDelegateForColumn(ModList::COL_CONTENT, contentDelegate); //ui->modList->setAcceptDrops(true); ui->modList->header()->installEventFilter(&m_ModList); if (initSettings.contains("mod_list_state")) { ui->modList->header()->restoreState(initSettings.value("mod_list_state").toByteArray()); + + // hack: force the resize-signal to be triggered because restoreState doesn't seem to do that + ui->modList->header()->resizeSection(ModList::COL_CONTENT, ui->modList->header()->sectionSize(ModList::COL_CONTENT) + 1); + ui->modList->header()->resizeSection(ModList::COL_CONTENT, ui->modList->header()->sectionSize(ModList::COL_CONTENT) - 1); } else { // hide these columns by default + ui->modList->header()->setSectionHidden(ModList::COL_CONTENT, true); ui->modList->header()->setSectionHidden(ModList::COL_MODID, true); ui->modList->header()->setSectionHidden(ModList::COL_INSTALLTIME, true); } @@ -230,7 +238,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget ui->espList->setModel(m_PluginListSortProxy); ui->espList->sortByColumn(PluginList::COL_PRIORITY, Qt::AscendingOrder); - ui->espList->setItemDelegateForColumn(PluginList::COL_FLAGS, new PluginFlagIconDelegate(ui->espList)); + ui->espList->setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(ui->espList)); if (initSettings.contains("plugin_list_state")) { ui->espList->header()->restoreState(initSettings.value("plugin_list_state").toByteArray()); } @@ -2758,7 +2766,7 @@ void MainWindow::modorder_changed() if (m_CurrentProfile->modEnabled(i)) { ModInfo::Ptr modInfo = ModInfo::getByIndex(i); // priorities in the directory structure are one higher because data is 0 - m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())).setPriority(priority + 1); + m_DirectoryStructure->getOriginByName(ToWString(modInfo->internalName())).setPriority(priority + 1); } } refreshBSAList(); diff --git a/src/modflagicondelegate.cpp b/src/modflagicondelegate.cpp index 914503a1..96b2bd58 100644 --- a/src/modflagicondelegate.cpp +++ b/src/modflagicondelegate.cpp @@ -68,3 +68,20 @@ size_t ModFlagIconDelegate::getNumIcons(const QModelIndex &index) const } } + +QSize ModFlagIconDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &modelIndex) const +{ + int count = getNumIcons(modelIndex); + unsigned int index = modelIndex.data(Qt::UserRole + 1).toInt(); + QSize result; + if (index < ModInfo::getNumMods()) { + result = QSize(count * 40, 20); + } else { + result = QSize(1, 20); + } + if (option.rect.width() > 0) { + result.setWidth(std::min(option.rect.width(), result.width())); + } + return result; +} + diff --git a/src/modflagicondelegate.h b/src/modflagicondelegate.h index b64ca08f..cc652c06 100644 --- a/src/modflagicondelegate.h +++ b/src/modflagicondelegate.h @@ -7,6 +7,7 @@ class ModFlagIconDelegate : public IconDelegate { public: explicit ModFlagIconDelegate(QObject *parent = 0); + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; private: virtual QList getIcons(const QModelIndex &index) const; virtual size_t getNumIcons(const QModelIndex &index) const; diff --git a/src/modinfo.cpp b/src/modinfo.cpp index b0351cf4..cb20567e 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -232,7 +232,7 @@ void ModInfo::updateIndices() QRegExp backupRegEx(".*backup[0-9]*$"); for (unsigned int i = 0; i < s_Collection.size(); ++i) { - QString modName = s_Collection[i]->name(); + QString modName = s_Collection[i]->internalName(); int modID = s_Collection[i]->getNexusID(); s_ModsByName[modName] = i; s_ModsByModID[modID].push_back(i); @@ -804,6 +804,28 @@ std::vector ModInfoRegular::getFlags() const } +std::vector ModInfoRegular::getContents() const +{ + std::vector result; + QDir dir(absolutePath()); + if (dir.entryList(QStringList() << "*.esp" << "*.esm").size() > 0) { + result.push_back(CONTENT_PLUGIN); + } + QString sePluginPath = ToQString(GameInfo::instance().getSEName()) + "/plugins"; + if (dir.exists("textures")) result.push_back(CONTENT_TEXTURE); + if (dir.exists("meshes")) result.push_back(CONTENT_MESH); + if (dir.exists("interface") + || dir.exists("menus")) result.push_back(CONTENT_INTERFACE); + if (dir.exists("music")) result.push_back(CONTENT_MUSIC); + if (dir.exists("sound")) result.push_back(CONTENT_SOUND); + if (dir.exists("scripts")) result.push_back(CONTENT_SCRIPT); + if (dir.exists(sePluginPath)) result.push_back(CONTENT_SKSE); + if (dir.exists("strings")) result.push_back(CONTENT_STRING); + if (dir.exists("SkyProc Patchers")) result.push_back(CONTENT_SKYPROC); + return result; +} + + int ModInfoRegular::getHighlight() const { return isValid() ? HIGHLIGHT_NONE: HIGHLIGHT_INVALID; @@ -959,6 +981,8 @@ QStringList ModInfoOverwrite::archives() const return result; } +const char ModInfoForeign::INT_IDENTIFIER[] = "__int__foreign"; + QString ModInfoForeign::name() const { return m_Name; diff --git a/src/modinfo.h b/src/modinfo.h index 0c144936..e2121828 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -68,6 +68,19 @@ public: FLAG_CONFLICT_REDUNDANT }; + enum EContent { + CONTENT_PLUGIN, + CONTENT_TEXTURE, + CONTENT_MESH, + CONTENT_INTERFACE, + CONTENT_MUSIC, + CONTENT_SOUND, + CONTENT_SCRIPT, + CONTENT_SKSE, + CONTENT_SKYPROC, + CONTENT_STRING + }; + enum EHighlight { HIGHLIGHT_NONE = 0, HIGHLIGHT_INVALID = 1, @@ -303,6 +316,13 @@ public: **/ virtual QString name() const = 0; + /** + * @brief getter for an internal name. This is usually the same as the regular name, but with special mod types it might be + * this is used to distinguish between mods that have the same visible name + * @return internal mod name + */ + virtual QString internalName() const { return name(); } + /** * @brief getter for the mod path * @@ -368,6 +388,11 @@ public: */ virtual std::vector getFlags() const = 0; + /** + * @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 @@ -798,6 +823,8 @@ public: */ virtual std::vector getFlags() const; + virtual std::vector getContents() const; + /** * @return an indicator if and how this mod should be highlighted by the UI */ @@ -974,6 +1001,10 @@ class ModInfoForeign : public ModInfoWithConflictInfo friend class ModInfo; +public: + + static const char INT_IDENTIFIER[]; + public: virtual bool updateAvailable() const { return false; } @@ -994,6 +1025,7 @@ public: virtual void endorse(bool) {} virtual bool isEmpty() const { return false; } virtual QString name() const; + virtual QString internalName() const { return name() + INT_IDENTIFIER; } virtual QString notes() const { return ""; } virtual QDateTime creationTime() const; virtual QString absolutePath() const; diff --git a/src/modlist.cpp b/src/modlist.cpp index ff126faa..ca173b18 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -52,15 +52,23 @@ ModList::ModList(QObject *parent) : QAbstractItemModel(parent), m_Profile(NULL), m_Modified(false), m_FontMetrics(QFont()), m_DropOnItems(false) { + m_ContentIcons[ModInfo::CONTENT_PLUGIN] = std::make_tuple(QIcon(":/MO/gui/content/plugin"), ":/MO/gui/content/plugin", tr("Game plugins (esp/esm)")); + m_ContentIcons[ModInfo::CONTENT_INTERFACE] = std::make_tuple(QIcon(":/MO/gui/content/interface"), ":/MO/gui/content/interface", tr("Interface")); + m_ContentIcons[ModInfo::CONTENT_MESH] = std::make_tuple(QIcon(":/MO/gui/content/mesh"), ":/MO/gui/content/mesh", tr("Meshes")); + m_ContentIcons[ModInfo::CONTENT_MUSIC] = std::make_tuple(QIcon(":/MO/gui/content/music"), ":/MO/gui/content/music", tr("Music")); + m_ContentIcons[ModInfo::CONTENT_SCRIPT] = std::make_tuple(QIcon(":/MO/gui/content/script"), ":/MO/gui/content/script", tr("Scripts (Papyrus)")); + m_ContentIcons[ModInfo::CONTENT_SKSE] = std::make_tuple(QIcon(":/MO/gui/content/skse"), ":/MO/gui/content/skse", tr("Script Extender Plugin")); + m_ContentIcons[ModInfo::CONTENT_SKYPROC] = std::make_tuple(QIcon(":/MO/gui/content/skyproc"), ":/MO/gui/content/skyproc", tr("SkyProc Patcher")); + m_ContentIcons[ModInfo::CONTENT_SOUND] = std::make_tuple(QIcon(":/MO/gui/content/sound"), ":/MO/gui/content/sound", tr("Sound")); + m_ContentIcons[ModInfo::CONTENT_STRING] = std::make_tuple(QIcon(":/MO/gui/content/string"), ":/MO/gui/content/string", tr("Strings")); + m_ContentIcons[ModInfo::CONTENT_TEXTURE] = std::make_tuple(QIcon(":/MO/gui/content/texture"), ":/MO/gui/content/texture", tr("Textures")); } - void ModList::setProfile(Profile *profile) { m_Profile = profile; } - int ModList::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { @@ -70,7 +78,6 @@ int ModList::rowCount(const QModelIndex &parent) const } } - bool ModList::hasChildren(const QModelIndex &parent) const { if (!parent.isValid()) { @@ -129,6 +136,35 @@ QString ModList::getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const } +QVariantList ModList::contentsToIcons(const std::vector &contents) const +{ + QVariantList result; + std::set contentsSet(contents.begin(), contents.end()); + for (auto iter = m_ContentIcons.begin(); iter != m_ContentIcons.end(); ++iter) { + if (contentsSet.find(iter->first) != contentsSet.end()) { + result.append(std::get<0>(iter->second)); + } else { + result.append(QIcon()); + } + } + return result; +} + +QString ModList::contentsToToolTip(const std::vector &contents) const +{ + QString result(""); + + std::set contentsSet(contents.begin(), contents.end()); + for (auto iter = m_ContentIcons.begin(); iter != m_ContentIcons.end(); ++iter) { + if (contentsSet.find(iter->first) != contentsSet.end()) { + result.append(QString("").arg(std::get<1>(iter->second)).arg(std::get<2>(iter->second))); + } + } + result.append("
%2
"); + return result; +} + + QVariant ModList::data(const QModelIndex &modelIndex, int role) const { if (m_Profile == NULL) return QVariant(); @@ -139,7 +175,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) { - if (column == COL_FLAGS) { + if ((column == COL_FLAGS) + || (column == COL_CONTENT)) { return QVariant(); } else if (column == COL_NAME) { return modInfo->name(); @@ -252,6 +289,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const case COL_PRIORITY: return QtGroupingProxy::AGGR_MIN; default: return QtGroupingProxy::AGGR_NONE; } + } else if (role == Qt::UserRole + 3) { + return contentsToIcons(modInfo->getContents()); } else if (role == Qt::FontRole) { QFont result; if (column == COL_NAME) { @@ -313,6 +352,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } return result; + } else if (column == COL_CONTENT) { + return contentsToToolTip(modInfo->getContents()); } else if (column == COL_NAME) { try { return modInfo->getDescription(); @@ -560,7 +601,6 @@ void ModList::changeModPriority(std::vector sourceIndices, int newPriority) break; } } - for (std::vector::const_iterator iter = sourceIndices.begin(); iter != sourceIndices.end(); ++iter) { int oldPriority = m_Profile->getModPriority(*iter); @@ -640,6 +680,17 @@ IModList::ModStates ModList::state(unsigned int modIndex) const return result; } +QString ModList::displayName(const QString &internalName) const +{ + unsigned int modIndex = ModInfo::getIndex(internalName); + if (modIndex == UINT_MAX) { + // might be better to throw an exception? + return internalName; + } else { + return ModInfo::getByIndex(modIndex)->name(); + } +} + IModList::ModStates ModList::state(const QString &name) const { unsigned int modIndex = ModInfo::getIndex(name); @@ -880,6 +931,7 @@ QString ModList::getColumnName(int column) { switch (column) { case COL_FLAGS: return tr("Flags"); + case COL_CONTENT: return tr("Content"); case COL_NAME: return tr("Mod Name"); case COL_VERSION: return tr("Version"); case COL_PRIORITY: return tr("Priority"); @@ -901,6 +953,18 @@ QString ModList::getColumnToolTip(int column) case COL_CATEGORY: return tr("Category of the mod."); case COL_MODID: return tr("Id of the mod as used on Nexus."); case COL_FLAGS: return tr("Emblemes to highlight things that might require attention."); + case COL_CONTENT: return tr("Depicts the content of the mod:
" + "Game plugins (esp/esm)
" + "interface
" + "Meshes
" + "Textures
" + "Sounds
" + "Music
" + "Strings
" + "Scripts (Papyrus)
" + "Script Extender plugins
" + "SkyProc Patcher
" + ); case COL_INSTALLTIME: return tr("Time this mod was installed"); default: return tr("unknown"); } diff --git a/src/modlist.h b/src/modlist.h index 2e8d6a84..6116a913 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -52,6 +52,7 @@ public: enum EColumn { COL_NAME, COL_FLAGS, + COL_CONTENT, COL_CATEGORY, COL_MODID, COL_VERSION, @@ -104,6 +105,9 @@ public: public: + /// \copydoc MOBase::IModList::displayName + virtual QString displayName(const QString &internalName) const; + /// \copydoc MOBase::IModList::state virtual ModStates state(const QString &name) const; @@ -234,6 +238,10 @@ private: static QString getColumnToolTip(int column); + QVariantList contentsToIcons(const std::vector &content) const; + + QString contentsToToolTip(const std::vector &contents) const; + ModList::EColumn getEnabledColumn(int index) const; QVariant categoryData(int categoryID, int column, int role) const; @@ -287,6 +295,8 @@ private: SignalModStateChanged m_ModStateChanged; SignalModMoved m_ModMoved; + std::map > m_ContentIcons; + }; #endif // MODLIST_H diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 77702689..d790e277 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -159,6 +159,13 @@ bool ModListSortProxy::lessThan(const QModelIndex &left, if (leftMod->getFlags().size() != rightMod->getFlags().size()) lt = leftMod->getFlags().size() < rightMod->getFlags().size(); } break; + case ModList::COL_CONTENT: { + int lLen = leftMod->getContents().size(); + int rLen = rightMod->getContents().size(); + if (lLen != rLen) { + lt = lLen < rLen; + } + } break; case ModList::COL_NAME: { int comp = QString::compare(leftMod->name(), rightMod->name(), Qt::CaseInsensitive); if (comp != 0) diff --git a/src/pluginflagicondelegate.cpp b/src/pluginflagicondelegate.cpp index 761555d7..a4f66c04 100644 --- a/src/pluginflagicondelegate.cpp +++ b/src/pluginflagicondelegate.cpp @@ -3,24 +3,37 @@ #include -PluginFlagIconDelegate::PluginFlagIconDelegate(QObject *parent) +GenericIconDelegate::GenericIconDelegate(QObject *parent, int role, int logicalIndex, int compactSize) : IconDelegate(parent) + , m_Role(role) + , m_LogicalIndex(logicalIndex) + , m_CompactSize(compactSize) + , m_Compact(false) { } -QList PluginFlagIconDelegate::getIcons(const QModelIndex &index) const +void GenericIconDelegate::columnResized(int logicalIndex, int, int newSize) +{ + if (logicalIndex == m_LogicalIndex) { + m_Compact = newSize < m_CompactSize; + } +} + +QList GenericIconDelegate::getIcons(const QModelIndex &index) const { QList result; if (index.isValid()) { - foreach (const QVariant &var, index.data(Qt::UserRole + 1).toList()) { - result.append(var.value()); + foreach (const QVariant &var, index.data(m_Role).toList()) { + QIcon icon = var.value(); + if (!m_Compact || !icon.isNull()) { + result.append(icon); + } } } return result; } -size_t PluginFlagIconDelegate::getNumIcons(const QModelIndex &index) const +size_t GenericIconDelegate::getNumIcons(const QModelIndex &index) const { - return index.data(Qt::UserRole + 1).toList().count(); + return index.data(m_Role).toList().count(); } - diff --git a/src/pluginflagicondelegate.h b/src/pluginflagicondelegate.h index 554e968b..3c05db72 100644 --- a/src/pluginflagicondelegate.h +++ b/src/pluginflagicondelegate.h @@ -3,15 +3,34 @@ #include "icondelegate.h" -class PluginFlagIconDelegate : public IconDelegate +/** + * @brief an icon delegate that takes the list of icons from a user-defines data role + */ +class GenericIconDelegate : public IconDelegate { +Q_OBJECT public: - PluginFlagIconDelegate(QObject *parent = NULL); - - // IconDelegate interface + /** + * @brief constructor + * @param parent parent object + * @param role role of the itemmodel from which the icon list can be queried (as a QVariantList) + * @param logicalIndex logical index within the model. This is part of a "hack". Normally "empty" icons will be allocated the same + * space as a regular icon. This way the model can use empty icons as spacers and thus align same icons horizontally. + * Now, if you set the logical Index to a valid column and connect the columnResized slot to the sectionResized signal + * of the view, the delegate will turn off this behaviour if the column is smaller than "compactSize" + * @param compactSize see explanation of logicalIndex + */ + GenericIconDelegate(QObject *parent = NULL, int role = Qt::UserRole + 1, int logicalIndex = -1, int compactSize = 150); +public slots: + void columnResized(int logicalIndex, int oldSize, int newSize); private: virtual QList getIcons(const QModelIndex &index) const; virtual size_t getNumIcons(const QModelIndex &index) const; +private: + int m_Role; + int m_LogicalIndex; + int m_CompactSize; + bool m_Compact; }; #endif // PLUGINFLAGICONDELEGATE_H diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 4794414a..5159871d 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -844,9 +844,6 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const if (!bossInfoIter->second.m_Messages.isEmpty()) { result.append(QIcon(":/MO/gui/information")); } - /*if (bossInfoIter->second.m_LOOTUnrecognized) { - result.append(QIcon(":/MO/gui/help")); - }*/ } if (m_ESPs[index].m_HasIni) { result.append(QIcon(":/MO/gui/attachment")); diff --git a/src/profile.cpp b/src/profile.cpp index fbfb3266..ca47d327 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -265,22 +265,23 @@ void Profile::refreshModStatus() modName = QString::fromUtf8(line.trimmed().constData()); } if (modName.size() > 0) { - if (namesRead.find(modName) != namesRead.end()) { + QString lookupName = modName + (line.at(0) == '*' ? ModInfoForeign::INT_IDENTIFIER : ""); + if (namesRead.find(lookupName) != namesRead.end()) { continue; } else { - namesRead.insert(modName); + namesRead.insert(lookupName); } - unsigned int modindex = ModInfo::getIndex(modName); - if (modindex != UINT_MAX) { - ModInfo::Ptr info = ModInfo::getByIndex(modindex); - if ((modindex < m_ModStatus.size()) + unsigned int modIndex = ModInfo::getIndex(lookupName); + if (modIndex != UINT_MAX) { + ModInfo::Ptr info = ModInfo::getByIndex(modIndex); + if ((modIndex < m_ModStatus.size()) && (info->getFixedPriority() == INT_MIN)) { - m_ModStatus[modindex].m_Enabled = enabled || info->alwaysEnabled(); - if (m_ModStatus[modindex].m_Priority == -1) { + m_ModStatus[modIndex].m_Enabled = enabled || info->alwaysEnabled(); + if (m_ModStatus[modIndex].m_Priority == -1) { if (static_cast(index) >= m_ModStatus.size()) { throw MyException(tr("invalid index %1").arg(index)); } - m_ModStatus[modindex].m_Priority = index++; + m_ModStatus[modIndex].m_Priority = index++; } } else { qDebug("mod \"%s\" (profile \"%s\") not found", @@ -381,7 +382,7 @@ std::vector > Profile::getActiveMods() iter != m_ModIndexByPriority.end(); ++iter) { if ((*iter != UINT_MAX) && m_ModStatus[*iter].m_Enabled) { ModInfo::Ptr modInfo = ModInfo::getByIndex(*iter); - result.push_back(std::make_tuple(modInfo->name(), modInfo->absolutePath(), m_ModStatus[*iter].m_Priority)); + result.push_back(std::make_tuple(modInfo->internalName(), modInfo->absolutePath(), m_ModStatus[*iter].m_Priority)); } } diff --git a/src/resources.qrc b/src/resources.qrc index 3287ad5d..20b0e3fe 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -66,4 +66,16 @@ resources/badge_9.png resources/badge_more.png + + resources/contents/jigsaw-piece.png + resources/contents/hand-of-god.png + resources/contents/empty-chessboard.png + resources/contents/double-quaver.png + resources/contents/lyre.png + resources/contents/usable.png + resources/contents/checkbox-tree.png + resources/contents/tinker.png + resources/contents/breastplate.png + resources/contents/conversation.png + diff --git a/src/resources/contents/breastplate.png b/src/resources/contents/breastplate.png new file mode 100644 index 00000000..e42d0888 Binary files /dev/null and b/src/resources/contents/breastplate.png differ diff --git a/src/resources/contents/checkbox-tree.png b/src/resources/contents/checkbox-tree.png new file mode 100644 index 00000000..ba6da286 Binary files /dev/null and b/src/resources/contents/checkbox-tree.png differ diff --git a/src/resources/contents/conversation.png b/src/resources/contents/conversation.png new file mode 100644 index 00000000..73e5980e Binary files /dev/null and b/src/resources/contents/conversation.png differ diff --git a/src/resources/contents/double-quaver.png b/src/resources/contents/double-quaver.png new file mode 100644 index 00000000..a7e06c72 Binary files /dev/null and b/src/resources/contents/double-quaver.png differ diff --git a/src/resources/contents/empty-chessboard.png b/src/resources/contents/empty-chessboard.png new file mode 100644 index 00000000..1ccbbd12 Binary files /dev/null and b/src/resources/contents/empty-chessboard.png differ diff --git a/src/resources/contents/hand-of-god.png b/src/resources/contents/hand-of-god.png new file mode 100644 index 00000000..9d55580e Binary files /dev/null and b/src/resources/contents/hand-of-god.png differ diff --git a/src/resources/contents/jigsaw-piece.png b/src/resources/contents/jigsaw-piece.png new file mode 100644 index 00000000..0f276ea0 Binary files /dev/null and b/src/resources/contents/jigsaw-piece.png differ diff --git a/src/resources/contents/lyre.png b/src/resources/contents/lyre.png new file mode 100644 index 00000000..1ee4765f Binary files /dev/null and b/src/resources/contents/lyre.png differ diff --git a/src/resources/contents/tinker.png b/src/resources/contents/tinker.png new file mode 100644 index 00000000..8ac7bfdd Binary files /dev/null and b/src/resources/contents/tinker.png differ diff --git a/src/resources/contents/usable.png b/src/resources/contents/usable.png new file mode 100644 index 00000000..dbbf6619 Binary files /dev/null and b/src/resources/contents/usable.png differ -- cgit v1.3.1 From 5aa5c90d39f46cfcf8d169b4087f30fcccd89640 Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 10 Sep 2014 20:35:57 +0200 Subject: profiles are now created with an (empty) archives.txt --- src/profile.cpp | 1572 ++++++++++++++++++++++++++++--------------------------- src/profile.h | 661 +++++++++++------------ 2 files changed, 1120 insertions(+), 1113 deletions(-) (limited to 'src/profile.cpp') diff --git a/src/profile.cpp b/src/profile.cpp index ca47d327..77e4f813 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -1,783 +1,789 @@ -/* -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 "profile.h" -#include "report.h" -#include "gameinfo.h" -#include "windows_error.h" -#include "dummybsa.h" -#include "modinfo.h" -#include "safewritefile.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include - -using namespace MOBase; -using namespace MOShared; - -Profile::Profile() - : m_SaveTimer(NULL) -{ - initTimer(); -} - -Profile::Profile(const QString &name, bool useDefaultSettings) - : m_SaveTimer(NULL) -{ - initTimer(); - QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())); - QDir profileBase(profilesDir); - - QString fixedName = name; - if (!fixDirectoryName(fixedName)) { - throw MyException(tr("invalid profile name %1").arg(name)); - } - - if (!profileBase.exists() || !profileBase.mkdir(fixedName)) { - throw MyException(tr("failed to create %1").arg(fixedName).toUtf8().constData()); - } - QString fullPath = profilesDir + "/" + fixedName; - m_Directory = QDir(fullPath); - QFile modList(m_Directory.filePath("modlist.txt")); - if (!modList.open(QIODevice::ReadWrite)) { - profileBase.rmdir(fixedName); - throw std::runtime_error(QObject::tr("failed to create %1").arg(m_Directory.filePath("modlist.txt")).toUtf8().constData()); - } - modList.close(); - - try { - GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings); - } catch (...) { - // clean up in case of an error - shellDelete(QStringList(profileBase.absoluteFilePath(fixedName))); - throw; - } - refreshModStatus(); -} - - -Profile::Profile(const QDir& directory) - : m_Directory(directory), m_SaveTimer(NULL) -{ - initTimer(); - if (!QFile::exists(m_Directory.filePath("modlist.txt"))) { - qWarning("missing modlist.txt in %s", qPrintable(directory.path())); - } - - GameInfo::instance().repairProfile(ToWString(m_Directory.absolutePath())); - - if (!QFile::exists(getIniFileName())) { - reportError(QObject::tr("\"%1\" is missing or inaccessible").arg(getIniFileName())); - } - refreshModStatus(); -} - - -Profile::Profile(const Profile& reference) - : m_Directory(reference.m_Directory), m_SaveTimer(NULL) -{ - initTimer(); - refreshModStatus(); -} - - -Profile::~Profile() -{ - writeModlistNow(); -} - - -void Profile::initTimer() -{ - m_SaveTimer = new QTimer(this); - m_SaveTimer->setSingleShot(true); - connect(m_SaveTimer, SIGNAL(timeout()), this, SLOT(writeModlistNow())); -} - - -bool Profile::exists() const -{ - return m_Directory.exists(); -} - - -void Profile::writeModlist() const -{ - if (!m_SaveTimer->isActive()) { - m_SaveTimer->start(2000); - } -} - - -void Profile::cancelWriteModlist() const -{ - m_SaveTimer->stop(); -} - - -void Profile::writeModlistNow(bool onlyOnTimer) const -{ - if (onlyOnTimer && !m_SaveTimer->isActive()) return; - - m_SaveTimer->stop(); - if (!m_Directory.exists()) return; - - try { - QString fileName = getModlistFileName(); - SafeWriteFile file(fileName); - - file->write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8()); - if (m_ModStatus.empty()) { - return; - } - - for (int i = m_ModStatus.size() - 1; i >= 0; --i) { - // the priority order was inverted on load so it has to be inverted again - unsigned int index = m_ModIndexByPriority[i]; - if (index != UINT_MAX) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - std::vector flags = modInfo->getFlags(); - if ((modInfo->getFixedPriority() == INT_MIN)) { - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - file->write("*"); - } else if (m_ModStatus[index].m_Enabled) { - file->write("+"); - } else { - file->write("-"); - } - file->write(modInfo->name().toUtf8()); - file->write("\r\n"); - } - } - } - - if (file.commitIfDifferent(m_LastModlistHash)) { - qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData()); - } - } catch (const std::exception &e) { - reportError(tr("failed to write mod list: %1").arg(e.what())); - return; - } -} - - -void Profile::createTweakedIniFile() -{ - QString tweakedIni = m_Directory.absoluteFilePath("initweaks.ini"); - - if (QFile::exists(tweakedIni) && !shellDeleteQuiet(tweakedIni)) { - reportError(tr("failed to update tweaked ini file, wrong settings may be used: %1").arg(windowsErrorString(::GetLastError()))); - return; - } - - for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { - if (m_ModStatus[i].m_Enabled) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); - mergeTweaks(modInfo, tweakedIni); - } - } - - mergeTweak(getProfileTweaks(), tweakedIni); - - bool error = false; - if (!::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", ToWString(tweakedIni).c_str())) { - error = true; - } - - if (localSavesEnabled()) { - if (!::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"1", ToWString(tweakedIni).c_str())) { - error = true; - } - - if (!::WritePrivateProfileStringW(L"General", L"SLocalSavePath", - AppConfig::localSavePlaceholder(), - ToWString(tweakedIni).c_str())) { - error = true; - } - } - - if (error) { - reportError(tr("failed to create tweaked ini: %1").arg(getCurrentErrorStringA().c_str())); - } - qDebug("%s saved", qPrintable(QDir::toNativeSeparators(tweakedIni))); -} - - -void Profile::refreshModStatus() -{ - QFile file(getModlistFileName()); - if (!file.exists()) { - throw MyException(tr("\"%1\" is missing or inaccessible").arg(getModlistFileName())); - } - - bool modStatusModified = false; - m_ModStatus.clear(); - m_ModStatus.resize(ModInfo::getNumMods()); - - std::set namesRead; - - // load mods from file and update enabled state and priority for them - file.open(QIODevice::ReadOnly); - int index = 0; - while (!file.atEnd()) { - QByteArray line = file.readLine(); - bool enabled = true; - QString modName; - if (line.length() == 0) { - // empty line - continue; - } else if (line.at(0) == '#') { - // comment line - continue; - } else if (line.at(0) == '-') { - enabled = false; - modName = QString::fromUtf8(line.mid(1).trimmed().constData()); - } else if ((line.at(0) == '+') - || (line.at(0) == '*')) { - modName = QString::fromUtf8(line.mid(1).trimmed().constData()); - } else { - modName = QString::fromUtf8(line.trimmed().constData()); - } - if (modName.size() > 0) { - QString lookupName = modName + (line.at(0) == '*' ? ModInfoForeign::INT_IDENTIFIER : ""); - if (namesRead.find(lookupName) != namesRead.end()) { - continue; - } else { - namesRead.insert(lookupName); - } - unsigned int modIndex = ModInfo::getIndex(lookupName); - if (modIndex != UINT_MAX) { - ModInfo::Ptr info = ModInfo::getByIndex(modIndex); - if ((modIndex < m_ModStatus.size()) - && (info->getFixedPriority() == INT_MIN)) { - m_ModStatus[modIndex].m_Enabled = enabled || info->alwaysEnabled(); - if (m_ModStatus[modIndex].m_Priority == -1) { - if (static_cast(index) >= m_ModStatus.size()) { - throw MyException(tr("invalid index %1").arg(index)); - } - m_ModStatus[modIndex].m_Priority = index++; - } - } else { - qDebug("mod \"%s\" (profile \"%s\") not found", - modName.toUtf8().constData(), m_Directory.path().toUtf8().constData()); - // need to rewrite the modlist to fix this - modStatusModified = true; - } - } - } else { - // line was empty after trimming - } - } - - int numKnownMods = index; - - int topInsert = 0; - - // invert priority order to match that of the pluginlist. Also - // give priorities to mods not referenced in the profile - for (size_t i = 0; i < m_ModStatus.size(); ++i) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); - if (modInfo->getFixedPriority() == INT_MAX) { - continue; - } - - if (m_ModStatus[i].m_Priority != -1) { - m_ModStatus[i].m_Priority = numKnownMods - m_ModStatus[i].m_Priority - 1; - } else { - if (static_cast(index) >= m_ModStatus.size()) { - throw MyException(tr("invalid index %1").arg(index)); - } - if (modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { - m_ModStatus[i].m_Priority = --topInsert; - } else { - m_ModStatus[i].m_Priority = index++; - } - // also, mark the mod-list as changed - modStatusModified = true; - } - } - // to support insertion of new mods at the top we may now have mods with negative priority. shift them all up - // to align priority with 0 - if (topInsert < 0) { - int offset = topInsert * -1; - for (size_t i = 0; i < m_ModStatus.size(); ++i) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); - if (modInfo->getFixedPriority() == INT_MAX) { - continue; - } - - m_ModStatus[i].m_Priority += offset; - } - } - - file.close(); - updateIndices(); - if (modStatusModified) { - writeModlist(); - } -} - - -void Profile::dumpModStatus() const -{ - for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { - ModInfo::Ptr info = ModInfo::getByIndex(i); - qWarning("%d: %s - %d (%s)", i, info->name().toUtf8().constData(), m_ModStatus[i].m_Priority, - m_ModStatus[i].m_Enabled ? "enabled" : "disabled"); - } -} - - -void Profile::updateIndices() -{ - m_NumRegularMods = 0; - m_ModIndexByPriority.clear(); - m_ModIndexByPriority.resize(m_ModStatus.size(), UINT_MAX); - for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { - int priority = m_ModStatus[i].m_Priority; - if (priority < 0) { - // don't assign this to mapping at all, it's probably the overwrite mod - continue; - } else if (priority >= static_cast(m_ModIndexByPriority.size())) { - qCritical("invalid priority %d for mod", priority); - continue; - } else { - ++m_NumRegularMods; - m_ModIndexByPriority.at(priority) = i; - } - } -} - - -std::vector > Profile::getActiveMods() -{ - std::vector > result; - for (std::vector::const_iterator iter = m_ModIndexByPriority.begin(); - iter != m_ModIndexByPriority.end(); ++iter) { - if ((*iter != UINT_MAX) && m_ModStatus[*iter].m_Enabled) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(*iter); - result.push_back(std::make_tuple(modInfo->internalName(), modInfo->absolutePath(), m_ModStatus[*iter].m_Priority)); - } - } - - 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(); }); - - if (overwriteIndex != UINT_MAX) { - ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(overwriteIndex); - result.push_back(std::make_tuple(overwriteInfo->name(), overwriteInfo->absolutePath(), UINT_MAX)); - } else { - reportError(tr("Overwrite directory couldn't be parsed")); - } - return result; -} - - -unsigned int Profile::modIndexByPriority(unsigned int priority) const -{ - if (priority >= m_ModStatus.size()) { - throw MyException(tr("invalid priority %1").arg(priority)); - } - - return m_ModIndexByPriority[priority]; -} - - -void Profile::setModEnabled(unsigned int index, bool enabled) -{ - if (index >= m_ModStatus.size()) { - throw MyException(tr("invalid index %1").arg(index)); - } - - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - // we could quit in the following case, this shouldn't be a change anyway, - // but at least this allows the situation to be fixed in case of an error - if (modInfo->alwaysEnabled()) { - enabled = true; - } - - if (enabled != m_ModStatus[index].m_Enabled) { - m_ModStatus[index].m_Enabled = enabled; - emit modStatusChanged(index); - } -} - - -bool Profile::modEnabled(unsigned int index) const -{ - if (index >= m_ModStatus.size()) { - throw MyException(tr("invalid index %1").arg(index)); - } - - return m_ModStatus[index].m_Enabled; -} - - -int Profile::getModPriority(unsigned int index) const -{ - if (index >= m_ModStatus.size()) { - throw MyException(tr("invalid index %1").arg(index)); - } - - return m_ModStatus[index].m_Priority; -} - - -void Profile::setModPriority(unsigned int index, int &newPriority) -{ - if (m_ModStatus.at(index).m_Overwrite) { - // can't change priority of the overwrite - return; - } - - int newPriorityTemp = (std::max)(0, (std::min)(m_ModStatus.size() - 1, newPriority)); - - // don't try to place below overwrite - while ((m_ModIndexByPriority.at(newPriorityTemp) >= m_ModStatus.size()) || - m_ModStatus.at(m_ModIndexByPriority.at(newPriorityTemp)).m_Overwrite) { - --newPriorityTemp; - } - - int oldPriority = m_ModStatus.at(index).m_Priority; - if (newPriorityTemp > oldPriority) { - // priority is higher than the old, so the gap we left is in lower priorities - for (int i = oldPriority + 1; i <= newPriorityTemp; ++i) { - --m_ModStatus.at(m_ModIndexByPriority.at(i)).m_Priority; - } - } else { - for (int i = newPriorityTemp; i < oldPriority; ++i) { - ++m_ModStatus.at(m_ModIndexByPriority.at(i)).m_Priority; - } - ++newPriority; - } - - m_ModStatus.at(index).m_Priority = newPriorityTemp; - - updateIndices(); - writeModlist(); -} - - -Profile Profile::createFrom(const QString &name, const Profile &reference) -{ - QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name); - reference.copyFilesTo(profileDirectory); - return Profile(QDir(profileDirectory)); -} - - -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; - const wchar_t *pos = buffer; - size_t length = wcslen(pos); - while (length != 0U) { - result.push_back(pos); - pos += length + 1; - length = wcslen(pos); - } - return result; -} - - -void Profile::mergeTweak(const QString &tweakName, const QString &tweakedIni) const -{ - static const int bufferSize = 32768; - - std::wstring tweakNameW = ToWString(tweakName); - std::wstring tweakedIniW = ToWString(tweakedIni); - QScopedArrayPointer buffer(new wchar_t[bufferSize]); - - // retrieve a list of sections - DWORD size = ::GetPrivateProfileSectionNamesW( - buffer.data(), bufferSize, tweakNameW.c_str()); - - if (size == bufferSize - 2) { - // unfortunately there is no good way to find the required size - // of the buffer - throw MyException(QString("Buffer too small. Please report this as a bug. " - "For now you might want to split up %1").arg(tweakName)); - } - - std::vector sections = splitDZString(buffer.data()); - - // now iterate over all sections and retrieve a list of keys in each - for (std::vector::iterator iter = sections.begin(); - iter != sections.end(); ++iter) { - // retrieve the names of all keys - size = ::GetPrivateProfileStringW(iter->c_str(), NULL, NULL, buffer.data(), - bufferSize, tweakNameW.c_str()); - if (size == bufferSize - 2) { - throw MyException(QString("Buffer too small. Please report this as a bug. " - "For now you might want to split up %1").arg(tweakName)); - } - - std::vector keys = splitDZString(buffer.data()); - - for (std::vector::iterator keyIter = keys.begin(); - keyIter != keys.end(); ++keyIter) { - //TODO this treats everything as strings but how could I differentiate the type? - ::GetPrivateProfileStringW(iter->c_str(), keyIter->c_str(), - NULL, buffer.data(), bufferSize, ToWString(tweakName).c_str()); - ::WritePrivateProfileStringW(iter->c_str(), keyIter->c_str(), - buffer.data(), tweakedIniW.c_str()); - } - } -} - -void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const -{ - std::vector iniTweaks = modInfo->getIniTweaks(); - for (std::vector::iterator iter = iniTweaks.begin(); - iter != iniTweaks.end(); ++iter) { - mergeTweak(*iter, tweakedIni); - } -} - - -bool Profile::invalidationActive(bool *supported) const -{ - if (GameInfo::instance().requiresBSAInvalidation()) { - *supported = true; - wchar_t buffer[1024]; - std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); - // epic ms fail: GetPrivateProfileString uses errno (for whatever reason) to signal a fail since the return value - // has a different meaning (number of bytes copied). HOWEVER, it will not set errno to 0 if NO error occured - errno = 0; - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - L"", buffer, 1024, iniFileName.c_str()) == 0) { - if (errno != 0x02) { - if (supported != NULL) { - *supported = false; - } - return false; - } else { - QString errorMessage = tr("failed to parse ini file (%1)").arg(ToQString(iniFileName)); - throw windows_error(errorMessage.toUtf8().constData()); - } - } - QStringList archives = ToQString(buffer).split(','); - - for (int i = 0; i < archives.count(); ++i) { - QString bsaName = archives.at(i).trimmed(); - if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) { - return true; - } - } - } else { - *supported = false; - } - return false; -} - - -void Profile::deactivateInvalidation() const -{ - if (GameInfo::instance().requiresBSAInvalidation()) { - wchar_t buffer[1024]; - std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); - errno = 0; - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - L"", buffer, 1024, iniFileName.c_str()) == 0) { - if (errno == 0x02) { - QString errorMessage = tr("failed to parse ini file (%1): %2").arg(QDir::toNativeSeparators(getIniFileName())).arg(::GetLastError()); - throw windows_error(errorMessage.toUtf8().constData()); - } else { - return; - } - } - QStringList archives = ToQString(buffer).split(", "); - - for (int i = 0; i < archives.count();) { - QString bsaName = archives.at(i).trimmed(); - if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) { - archives.removeAt(i); - } else { - ++i; - } - } - - // just to be safe... - ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); - - if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"0", iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"ArchiveInvalidation.txt", iniFileName.c_str())) { - QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); - throw windows_error(errorMessage.toUtf8().constData()); - } - } -} - - -void Profile::activateInvalidation(const QString& dataDirectory) const -{ - if (GameInfo::instance().requiresBSAInvalidation()) { - wchar_t buffer[1024]; - std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); - errno = 0; - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - L"", buffer, 1024, iniFileName.c_str()) == 0) { - if (errno == 0x02) { - throw windows_error("failed to parse ini file"); - } else { - // ignore. shouldn't have gotten here anyway - return; - } - } - QStringList archives = ToQString(buffer).split(", "); - - QString invalidationBSA = ToQString(GameInfo::instance().getInvalidationBSA()); - - if (!archives.contains(invalidationBSA)) { - archives.insert(0, invalidationBSA); - } - - // just to be safe... - ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); - - if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"", iniFileName.c_str())) { - QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); - throw windows_error(errorMessage.toUtf8().constData()); - } - - QString bsaFile = dataDirectory + "/" + invalidationBSA; - if (!QFile::exists(bsaFile)) { - DummyBSA bsa; - bsa.write(bsaFile); - } - } -} - - -bool Profile::localSavesEnabled() const -{ - return m_Directory.exists("saves"); -} - - -bool Profile::enableLocalSaves(bool enable) -{ - if (enable) { - if (m_Directory.exists("_saves")) { - m_Directory.rename("_saves", "saves"); - } else { - m_Directory.mkdir("saves"); - } - } else { - QMessageBox::StandardButton res = QMessageBox::question(QApplication::activeModalWidget(), tr("Delete savegames?"), - tr("Do you want to delete local savegames? (If you select \"No\", the save games " - "will show up again if you re-enable local savegames)"), - QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, - QMessageBox::Cancel); - if (res == QMessageBox::Yes) { - shellDelete(QStringList(m_Directory.absoluteFilePath("saves"))); - } else if (res == QMessageBox::No) { - m_Directory.rename("saves", "_saves"); - } else { - return false; - } - } - - // default: assume success - return true; -} - - -QString Profile::getModlistFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("modlist.txt")); -} - -QString Profile::getPluginsFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("plugins.txt")); -} - -QString Profile::getLoadOrderFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("loadorder.txt")); -} - -QString Profile::getLockedOrderFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("lockedorder.txt")); -} - -QString Profile::getArchivesFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("archives.txt")); -} - -QString Profile::getDeleterFileName() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath("hide_plugins.txt")); -} - -QString Profile::getIniFileName() const -{ - std::wstring primaryIniFile = *(GameInfo::instance().getIniFileNames().begin()); - return m_Directory.absoluteFilePath(ToQString(primaryIniFile)); -} - -QString Profile::getProfileTweaks() const -{ - return QDir::cleanPath(m_Directory.absoluteFilePath(ToQString(AppConfig::profileTweakIni()))); -} - -QString Profile::getPath() const -{ - return QDir::cleanPath(m_Directory.absolutePath()); -} - -void Profile::rename(const QString &newName) -{ - QDir profileDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()))); - profileDir.rename(getName(), newName); - m_Directory = profileDir.absoluteFilePath(newName); -} +/* +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 "profile.h" +#include "report.h" +#include "gameinfo.h" +#include "windows_error.h" +#include "dummybsa.h" +#include "modinfo.h" +#include "safewritefile.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +using namespace MOBase; +using namespace MOShared; + +Profile::Profile() + : m_SaveTimer(NULL) +{ + initTimer(); +} + +void Profile::touchFile(QString fileName) +{ + QFile modList(m_Directory.filePath(fileName)); + if (!modList.open(QIODevice::ReadWrite)) { + throw std::runtime_error(QObject::tr("failed to create %1").arg(m_Directory.filePath(fileName)).toUtf8().constData()); + } +} + +Profile::Profile(const QString &name, bool useDefaultSettings) + : m_SaveTimer(NULL) +{ + initTimer(); + QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())); + QDir profileBase(profilesDir); + + QString fixedName = name; + if (!fixDirectoryName(fixedName)) { + throw MyException(tr("invalid profile name %1").arg(name)); + } + + if (!profileBase.exists() || !profileBase.mkdir(fixedName)) { + throw MyException(tr("failed to create %1").arg(fixedName).toUtf8().constData()); + } + QString fullPath = profilesDir + "/" + fixedName; + m_Directory = QDir(fullPath); + + try { + // create files. Needs to happen after m_Directory was set! + touchFile("modlist.txt"); + touchFile("archives.txt"); + + GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings); + } catch (...) { + // clean up in case of an error + shellDelete(QStringList(profileBase.absoluteFilePath(fixedName))); + throw; + } + refreshModStatus(); +} + + +Profile::Profile(const QDir& directory) + : m_Directory(directory), m_SaveTimer(NULL) +{ + initTimer(); + if (!QFile::exists(m_Directory.filePath("modlist.txt"))) { + qWarning("missing modlist.txt in %s", qPrintable(directory.path())); + } + + GameInfo::instance().repairProfile(ToWString(m_Directory.absolutePath())); + + if (!QFile::exists(getIniFileName())) { + reportError(QObject::tr("\"%1\" is missing or inaccessible").arg(getIniFileName())); + } + refreshModStatus(); +} + + +Profile::Profile(const Profile& reference) + : m_Directory(reference.m_Directory), m_SaveTimer(NULL) +{ + initTimer(); + refreshModStatus(); +} + + +Profile::~Profile() +{ + writeModlistNow(); +} + + +void Profile::initTimer() +{ + m_SaveTimer = new QTimer(this); + m_SaveTimer->setSingleShot(true); + connect(m_SaveTimer, SIGNAL(timeout()), this, SLOT(writeModlistNow())); +} + + +bool Profile::exists() const +{ + return m_Directory.exists(); +} + + +void Profile::writeModlist() const +{ + if (!m_SaveTimer->isActive()) { + m_SaveTimer->start(2000); + } +} + + +void Profile::cancelWriteModlist() const +{ + m_SaveTimer->stop(); +} + + +void Profile::writeModlistNow(bool onlyOnTimer) const +{ + if (onlyOnTimer && !m_SaveTimer->isActive()) return; + + m_SaveTimer->stop(); + if (!m_Directory.exists()) return; + + try { + QString fileName = getModlistFileName(); + SafeWriteFile file(fileName); + + file->write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8()); + if (m_ModStatus.empty()) { + return; + } + + for (int i = m_ModStatus.size() - 1; i >= 0; --i) { + // the priority order was inverted on load so it has to be inverted again + unsigned int index = m_ModIndexByPriority[i]; + if (index != UINT_MAX) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + std::vector flags = modInfo->getFlags(); + if ((modInfo->getFixedPriority() == INT_MIN)) { + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { + file->write("*"); + } else if (m_ModStatus[index].m_Enabled) { + file->write("+"); + } else { + file->write("-"); + } + file->write(modInfo->name().toUtf8()); + file->write("\r\n"); + } + } + } + + if (file.commitIfDifferent(m_LastModlistHash)) { + qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData()); + } + } catch (const std::exception &e) { + reportError(tr("failed to write mod list: %1").arg(e.what())); + return; + } +} + + +void Profile::createTweakedIniFile() +{ + QString tweakedIni = m_Directory.absoluteFilePath("initweaks.ini"); + + if (QFile::exists(tweakedIni) && !shellDeleteQuiet(tweakedIni)) { + reportError(tr("failed to update tweaked ini file, wrong settings may be used: %1").arg(windowsErrorString(::GetLastError()))); + return; + } + + for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { + if (m_ModStatus[i].m_Enabled) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + mergeTweaks(modInfo, tweakedIni); + } + } + + mergeTweak(getProfileTweaks(), tweakedIni); + + bool error = false; + if (!::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", ToWString(tweakedIni).c_str())) { + error = true; + } + + if (localSavesEnabled()) { + if (!::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"1", ToWString(tweakedIni).c_str())) { + error = true; + } + + if (!::WritePrivateProfileStringW(L"General", L"SLocalSavePath", + AppConfig::localSavePlaceholder(), + ToWString(tweakedIni).c_str())) { + error = true; + } + } + + if (error) { + reportError(tr("failed to create tweaked ini: %1").arg(getCurrentErrorStringA().c_str())); + } + qDebug("%s saved", qPrintable(QDir::toNativeSeparators(tweakedIni))); +} + + +void Profile::refreshModStatus() +{ + QFile file(getModlistFileName()); + if (!file.exists()) { + throw MyException(tr("\"%1\" is missing or inaccessible").arg(getModlistFileName())); + } + + bool modStatusModified = false; + m_ModStatus.clear(); + m_ModStatus.resize(ModInfo::getNumMods()); + + std::set namesRead; + + // load mods from file and update enabled state and priority for them + file.open(QIODevice::ReadOnly); + int index = 0; + while (!file.atEnd()) { + QByteArray line = file.readLine(); + bool enabled = true; + QString modName; + if (line.length() == 0) { + // empty line + continue; + } else if (line.at(0) == '#') { + // comment line + continue; + } else if (line.at(0) == '-') { + enabled = false; + modName = QString::fromUtf8(line.mid(1).trimmed().constData()); + } else if ((line.at(0) == '+') + || (line.at(0) == '*')) { + modName = QString::fromUtf8(line.mid(1).trimmed().constData()); + } else { + modName = QString::fromUtf8(line.trimmed().constData()); + } + if (modName.size() > 0) { + QString lookupName = modName + (line.at(0) == '*' ? ModInfoForeign::INT_IDENTIFIER : ""); + if (namesRead.find(lookupName) != namesRead.end()) { + continue; + } else { + namesRead.insert(lookupName); + } + unsigned int modIndex = ModInfo::getIndex(lookupName); + if (modIndex != UINT_MAX) { + ModInfo::Ptr info = ModInfo::getByIndex(modIndex); + if ((modIndex < m_ModStatus.size()) + && (info->getFixedPriority() == INT_MIN)) { + m_ModStatus[modIndex].m_Enabled = enabled || info->alwaysEnabled(); + if (m_ModStatus[modIndex].m_Priority == -1) { + if (static_cast(index) >= m_ModStatus.size()) { + throw MyException(tr("invalid index %1").arg(index)); + } + m_ModStatus[modIndex].m_Priority = index++; + } + } else { + qDebug("mod \"%s\" (profile \"%s\") not found", + modName.toUtf8().constData(), m_Directory.path().toUtf8().constData()); + // need to rewrite the modlist to fix this + modStatusModified = true; + } + } + } else { + // line was empty after trimming + } + } + + int numKnownMods = index; + + int topInsert = 0; + + // invert priority order to match that of the pluginlist. Also + // give priorities to mods not referenced in the profile + for (size_t i = 0; i < m_ModStatus.size(); ++i) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + if (modInfo->getFixedPriority() == INT_MAX) { + continue; + } + + if (m_ModStatus[i].m_Priority != -1) { + m_ModStatus[i].m_Priority = numKnownMods - m_ModStatus[i].m_Priority - 1; + } else { + if (static_cast(index) >= m_ModStatus.size()) { + throw MyException(tr("invalid index %1").arg(index)); + } + if (modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { + m_ModStatus[i].m_Priority = --topInsert; + } else { + m_ModStatus[i].m_Priority = index++; + } + // also, mark the mod-list as changed + modStatusModified = true; + } + } + // to support insertion of new mods at the top we may now have mods with negative priority. shift them all up + // to align priority with 0 + if (topInsert < 0) { + int offset = topInsert * -1; + for (size_t i = 0; i < m_ModStatus.size(); ++i) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + if (modInfo->getFixedPriority() == INT_MAX) { + continue; + } + + m_ModStatus[i].m_Priority += offset; + } + } + + file.close(); + updateIndices(); + if (modStatusModified) { + writeModlist(); + } +} + + +void Profile::dumpModStatus() const +{ + for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { + ModInfo::Ptr info = ModInfo::getByIndex(i); + qWarning("%d: %s - %d (%s)", i, info->name().toUtf8().constData(), m_ModStatus[i].m_Priority, + m_ModStatus[i].m_Enabled ? "enabled" : "disabled"); + } +} + + +void Profile::updateIndices() +{ + m_NumRegularMods = 0; + m_ModIndexByPriority.clear(); + m_ModIndexByPriority.resize(m_ModStatus.size(), UINT_MAX); + for (unsigned int i = 0; i < m_ModStatus.size(); ++i) { + int priority = m_ModStatus[i].m_Priority; + if (priority < 0) { + // don't assign this to mapping at all, it's probably the overwrite mod + continue; + } else if (priority >= static_cast(m_ModIndexByPriority.size())) { + qCritical("invalid priority %d for mod", priority); + continue; + } else { + ++m_NumRegularMods; + m_ModIndexByPriority.at(priority) = i; + } + } +} + + +std::vector > Profile::getActiveMods() +{ + std::vector > result; + for (std::vector::const_iterator iter = m_ModIndexByPriority.begin(); + iter != m_ModIndexByPriority.end(); ++iter) { + if ((*iter != UINT_MAX) && m_ModStatus[*iter].m_Enabled) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(*iter); + result.push_back(std::make_tuple(modInfo->internalName(), modInfo->absolutePath(), m_ModStatus[*iter].m_Priority)); + } + } + + 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(); }); + + if (overwriteIndex != UINT_MAX) { + ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(overwriteIndex); + result.push_back(std::make_tuple(overwriteInfo->name(), overwriteInfo->absolutePath(), UINT_MAX)); + } else { + reportError(tr("Overwrite directory couldn't be parsed")); + } + return result; +} + + +unsigned int Profile::modIndexByPriority(unsigned int priority) const +{ + if (priority >= m_ModStatus.size()) { + throw MyException(tr("invalid priority %1").arg(priority)); + } + + return m_ModIndexByPriority[priority]; +} + + +void Profile::setModEnabled(unsigned int index, bool enabled) +{ + if (index >= m_ModStatus.size()) { + throw MyException(tr("invalid index %1").arg(index)); + } + + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + // we could quit in the following case, this shouldn't be a change anyway, + // but at least this allows the situation to be fixed in case of an error + if (modInfo->alwaysEnabled()) { + enabled = true; + } + + if (enabled != m_ModStatus[index].m_Enabled) { + m_ModStatus[index].m_Enabled = enabled; + emit modStatusChanged(index); + } +} + + +bool Profile::modEnabled(unsigned int index) const +{ + if (index >= m_ModStatus.size()) { + throw MyException(tr("invalid index %1").arg(index)); + } + + return m_ModStatus[index].m_Enabled; +} + + +int Profile::getModPriority(unsigned int index) const +{ + if (index >= m_ModStatus.size()) { + throw MyException(tr("invalid index %1").arg(index)); + } + + return m_ModStatus[index].m_Priority; +} + + +void Profile::setModPriority(unsigned int index, int &newPriority) +{ + if (m_ModStatus.at(index).m_Overwrite) { + // can't change priority of the overwrite + return; + } + + int newPriorityTemp = (std::max)(0, (std::min)(m_ModStatus.size() - 1, newPriority)); + + // don't try to place below overwrite + while ((m_ModIndexByPriority.at(newPriorityTemp) >= m_ModStatus.size()) || + m_ModStatus.at(m_ModIndexByPriority.at(newPriorityTemp)).m_Overwrite) { + --newPriorityTemp; + } + + int oldPriority = m_ModStatus.at(index).m_Priority; + if (newPriorityTemp > oldPriority) { + // priority is higher than the old, so the gap we left is in lower priorities + for (int i = oldPriority + 1; i <= newPriorityTemp; ++i) { + --m_ModStatus.at(m_ModIndexByPriority.at(i)).m_Priority; + } + } else { + for (int i = newPriorityTemp; i < oldPriority; ++i) { + ++m_ModStatus.at(m_ModIndexByPriority.at(i)).m_Priority; + } + ++newPriority; + } + + m_ModStatus.at(index).m_Priority = newPriorityTemp; + + updateIndices(); + writeModlist(); +} + + +Profile Profile::createFrom(const QString &name, const Profile &reference) +{ + QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name); + reference.copyFilesTo(profileDirectory); + return Profile(QDir(profileDirectory)); +} + + +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; + const wchar_t *pos = buffer; + size_t length = wcslen(pos); + while (length != 0U) { + result.push_back(pos); + pos += length + 1; + length = wcslen(pos); + } + return result; +} + + +void Profile::mergeTweak(const QString &tweakName, const QString &tweakedIni) const +{ + static const int bufferSize = 32768; + + std::wstring tweakNameW = ToWString(tweakName); + std::wstring tweakedIniW = ToWString(tweakedIni); + QScopedArrayPointer buffer(new wchar_t[bufferSize]); + + // retrieve a list of sections + DWORD size = ::GetPrivateProfileSectionNamesW( + buffer.data(), bufferSize, tweakNameW.c_str()); + + if (size == bufferSize - 2) { + // unfortunately there is no good way to find the required size + // of the buffer + throw MyException(QString("Buffer too small. Please report this as a bug. " + "For now you might want to split up %1").arg(tweakName)); + } + + std::vector sections = splitDZString(buffer.data()); + + // now iterate over all sections and retrieve a list of keys in each + for (std::vector::iterator iter = sections.begin(); + iter != sections.end(); ++iter) { + // retrieve the names of all keys + size = ::GetPrivateProfileStringW(iter->c_str(), NULL, NULL, buffer.data(), + bufferSize, tweakNameW.c_str()); + if (size == bufferSize - 2) { + throw MyException(QString("Buffer too small. Please report this as a bug. " + "For now you might want to split up %1").arg(tweakName)); + } + + std::vector keys = splitDZString(buffer.data()); + + for (std::vector::iterator keyIter = keys.begin(); + keyIter != keys.end(); ++keyIter) { + //TODO this treats everything as strings but how could I differentiate the type? + ::GetPrivateProfileStringW(iter->c_str(), keyIter->c_str(), + NULL, buffer.data(), bufferSize, ToWString(tweakName).c_str()); + ::WritePrivateProfileStringW(iter->c_str(), keyIter->c_str(), + buffer.data(), tweakedIniW.c_str()); + } + } +} + +void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const +{ + std::vector iniTweaks = modInfo->getIniTweaks(); + for (std::vector::iterator iter = iniTweaks.begin(); + iter != iniTweaks.end(); ++iter) { + mergeTweak(*iter, tweakedIni); + } +} + + +bool Profile::invalidationActive(bool *supported) const +{ + if (GameInfo::instance().requiresBSAInvalidation()) { + *supported = true; + wchar_t buffer[1024]; + std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); + // epic ms fail: GetPrivateProfileString uses errno (for whatever reason) to signal a fail since the return value + // has a different meaning (number of bytes copied). HOWEVER, it will not set errno to 0 if NO error occured + errno = 0; + if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), + L"", buffer, 1024, iniFileName.c_str()) == 0) { + if (errno != 0x02) { + if (supported != NULL) { + *supported = false; + } + return false; + } else { + QString errorMessage = tr("failed to parse ini file (%1)").arg(ToQString(iniFileName)); + throw windows_error(errorMessage.toUtf8().constData()); + } + } + QStringList archives = ToQString(buffer).split(','); + + for (int i = 0; i < archives.count(); ++i) { + QString bsaName = archives.at(i).trimmed(); + if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) { + return true; + } + } + } else { + *supported = false; + } + return false; +} + + +void Profile::deactivateInvalidation() const +{ + if (GameInfo::instance().requiresBSAInvalidation()) { + wchar_t buffer[1024]; + std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); + errno = 0; + if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), + L"", buffer, 1024, iniFileName.c_str()) == 0) { + if (errno == 0x02) { + QString errorMessage = tr("failed to parse ini file (%1): %2").arg(QDir::toNativeSeparators(getIniFileName())).arg(::GetLastError()); + throw windows_error(errorMessage.toUtf8().constData()); + } else { + return; + } + } + QStringList archives = ToQString(buffer).split(", "); + + for (int i = 0; i < archives.count();) { + QString bsaName = archives.at(i).trimmed(); + if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) { + archives.removeAt(i); + } else { + ++i; + } + } + + // just to be safe... + ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); + + if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), + ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || + !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"0", iniFileName.c_str()) || + !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"ArchiveInvalidation.txt", iniFileName.c_str())) { + QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); + throw windows_error(errorMessage.toUtf8().constData()); + } + } +} + + +void Profile::activateInvalidation(const QString& dataDirectory) const +{ + if (GameInfo::instance().requiresBSAInvalidation()) { + wchar_t buffer[1024]; + std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); + errno = 0; + if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), + L"", buffer, 1024, iniFileName.c_str()) == 0) { + if (errno == 0x02) { + throw windows_error("failed to parse ini file"); + } else { + // ignore. shouldn't have gotten here anyway + return; + } + } + QStringList archives = ToQString(buffer).split(", "); + + QString invalidationBSA = ToQString(GameInfo::instance().getInvalidationBSA()); + + if (!archives.contains(invalidationBSA)) { + archives.insert(0, invalidationBSA); + } + + // just to be safe... + ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); + + if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), + ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || + !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", iniFileName.c_str()) || + !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"", iniFileName.c_str())) { + QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); + throw windows_error(errorMessage.toUtf8().constData()); + } + + QString bsaFile = dataDirectory + "/" + invalidationBSA; + if (!QFile::exists(bsaFile)) { + DummyBSA bsa; + bsa.write(bsaFile); + } + } +} + + +bool Profile::localSavesEnabled() const +{ + return m_Directory.exists("saves"); +} + + +bool Profile::enableLocalSaves(bool enable) +{ + if (enable) { + if (m_Directory.exists("_saves")) { + m_Directory.rename("_saves", "saves"); + } else { + m_Directory.mkdir("saves"); + } + } else { + QMessageBox::StandardButton res = QMessageBox::question(QApplication::activeModalWidget(), tr("Delete savegames?"), + tr("Do you want to delete local savegames? (If you select \"No\", the save games " + "will show up again if you re-enable local savegames)"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Cancel); + if (res == QMessageBox::Yes) { + shellDelete(QStringList(m_Directory.absoluteFilePath("saves"))); + } else if (res == QMessageBox::No) { + m_Directory.rename("saves", "_saves"); + } else { + return false; + } + } + + // default: assume success + return true; +} + + +QString Profile::getModlistFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("modlist.txt")); +} + +QString Profile::getPluginsFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("plugins.txt")); +} + +QString Profile::getLoadOrderFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("loadorder.txt")); +} + +QString Profile::getLockedOrderFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("lockedorder.txt")); +} + +QString Profile::getArchivesFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("archives.txt")); +} + +QString Profile::getDeleterFileName() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("hide_plugins.txt")); +} + +QString Profile::getIniFileName() const +{ + std::wstring primaryIniFile = *(GameInfo::instance().getIniFileNames().begin()); + return m_Directory.absoluteFilePath(ToQString(primaryIniFile)); +} + +QString Profile::getProfileTweaks() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath(ToQString(AppConfig::profileTweakIni()))); +} + +QString Profile::getPath() const +{ + return QDir::cleanPath(m_Directory.absolutePath()); +} + +void Profile::rename(const QString &newName) +{ + QDir profileDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()))); + profileDir.rename(getName(), newName); + m_Directory = profileDir.absoluteFilePath(newName); +} diff --git a/src/profile.h b/src/profile.h index fa34ab9e..aa27d6e8 100644 --- a/src/profile.h +++ b/src/profile.h @@ -1,330 +1,331 @@ -/* -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 PROFILE_H -#define PROFILE_H - - -#include "modinfo.h" - -#include -#include -#include -#include -#include -#include - - -/** - * @brief represents a profile - **/ -class Profile : public QObject -{ - - Q_OBJECT - -public: - - typedef boost::shared_ptr Ptr; - -public: - - /** - * @brief default constructor - * @todo This constructor initialised nothing, the resulting object is not usable - **/ - Profile(); - - /** - * @brief constructor - * - * This constructor is used to create a new profile so it is to be assumed a profile - * by this name does not yet exist - * @param name name of the new profile - * @param filter save game filter. Defaults to <no filter>. - **/ - Profile(const QString &name, bool useDefaultSettings); - /** - * @brief constructor - * - * This constructor is used to open an existing profile though it will also try to repair - * the profile if important files are missing (including the directory itself) so technically, - * invoking this should always produce a working profile - * @param directory directory to read the profile from - **/ - Profile(const QDir &directory); - - Profile(const Profile &reference); - - ~Profile(); - - /** - * @return true if this profile (still) exists on disc - */ - bool exists() const; - - /** - * @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); - - /** - * @brief write out the modlist.txt - **/ - void writeModlist() const; - - /** - * cancel any potential request to write modlist.txt. This is used to ensure no invalid modlist is - * saved while it's being modified - */ - void cancelWriteModlist() const; - - /** - * @brief test if this profile uses archive invalidation - * - * @param supported if this is not null, the parameter will be set to false if invalidation is not supported in this profile - * @return true if archive invalidation is active - * @note currently, invalidation is not supported if the relevant entry in the ini file does not exist - **/ - bool invalidationActive(bool *supported) const; - - /** - * @brief deactivate archive invalidation if it was active - **/ - void deactivateInvalidation() const; - - /** - * @brief activate archive invalidation - * - * @param dataDirectory data directory of the game - * @todo passing the data directory as a parameter is useless, the function should - * be able to query it from GameInfo - **/ - void activateInvalidation(const QString &dataDirectory) const; - - /** - * @return true if this profile uses local save games - */ - bool localSavesEnabled() const; - - /** - * @brief enables or disables the use of local save games for this profile - * disabling this does not delete exising local saves but they will not be visible - * in the game - * @param enable if true, local saves are enabled, otherewise they are disabled - */ - bool enableLocalSaves(bool enable); - - /** - * @return name of the profile (this is identical to its directory name) - **/ - QString getName() const { return m_Directory.dirName(); } - - /** - * @return the path of the plugins file in this profile - * @todo is this required? can the functionality using this function be moved to the Profile-class? - **/ - QString getPluginsFileName() const; - - /** - * @return the path of the loadorder file in this profile - **/ - QString getLoadOrderFileName() const; - - /** - * @return the path of the file containing locked mod indices - */ - QString getLockedOrderFileName() const; - - /** - * @return the path of the modlist file in this profile - */ - QString getModlistFileName() const; - - /** - * @return path of the archives file in this profile - */ - QString getArchivesFileName() const; - - /** - * @return the path of the delete file in this profile - * @note the deleter file lists plugins that should be hidden from the game and other tools - **/ - QString getDeleterFileName() const; - - /** - * @return the path of the ini file in this profile - * @todo since the game can contain multiple ini files (i.e. skyrim.ini skyrimprefs.ini) - * the concept of this function is somewhat broken - **/ - QString getIniFileName() const; - - /** - * @return the path of the tweak ini in this profile - */ - QString getProfileTweaks() const; - - /** - * @return path to this profile - **/ - QString getPath() const; - - void rename(const QString &newName); - - /** - * @brief create the ini file to be used by the game - * - * the tweaked ini file constructed by this file is a merger - * of the game-ini of this profile with ini tweaks applied */ - void createTweakedIniFile(); - - /** - * @brief re-read the modlist.txt and update the mod status from it - **/ - void refreshModStatus(); - - /** - * @brief retrieve a list of mods that are enabled in this profile - * - * @return list of active mods sorted by priority (ascending). "first" is the mod name, "second" is its path - **/ - std::vector > getActiveMods(); - - /** - * retrieve the number of mods for which this object has status information. - * This is usually the same as ModInfo::getNumMods() except between - * calls to ModInfo::updateFromDisc() and the Profile::refreshModStatus() - * - * @return number of mods for which the profile has status information - **/ - unsigned int numMods() const { return m_ModStatus.size(); } - - /** - * @return the number of mods that can be enabled and where the priority can be modified - */ - unsigned int numRegularMods() const { return m_NumRegularMods; } - - /** - * @brief retrieve the mod index based on the priority - * - * @param priority priority to look up - * @return the index of the mod - * @throw std::exception an exception is thrown if there is no mod with the specified priority - **/ - unsigned int modIndexByPriority(unsigned int priority) const; - - /** - * @brief enable or disable a mod - * - * @param index index of the mod to enable/disable - * @param enabled true if the mod is to be enabled, false if it is to be disabled - **/ - void setModEnabled(unsigned int index, bool enabled); - - /** - * change the priority of a mod. Of course this also changes the priority of other mods. - * The priority of the mods in the range ]old, new priority] are shifted so that no gaps - * are possible. - * - * @param index index of the mod to change - * @param newPriority the new priority value - * - * @todo what happens if the new priority is outside the range? - **/ - void setModPriority(unsigned int index, int &newPriority); - - /** - * @brief determine if a mod is enabled - * - * @param index index of the mod to look up - * @return true if the mod is enabled, false otherwise - **/ - bool modEnabled(unsigned int index) const; - - /** - * @brief query the priority of a mod - * - * @param index index of the mod to look up - * @return priority of the specified mod - **/ - int getModPriority(unsigned int index) const; - - void dumpModStatus() const; - -signals: - - /** - * @brief emitted whenever the status (enabled/disabled) of a mod changed - * - * @param index index of the mod that changed - **/ - void modStatusChanged(unsigned int index); - -public slots: - - void writeModlistNow(bool onlyOnTimer = false) const; - -private: - - class ModStatus { - friend class Profile; - public: - ModStatus() : m_Overwrite(false), m_Enabled(false), m_Priority(-1) {} - private: - bool m_Overwrite; - bool m_Enabled; - int m_Priority; - }; - -private: - Profile& operator=(const Profile &reference); // not implemented - - void initTimer(); - - void updateIndices(); - - void copyFilesTo(QString &target) const; - - std::vector splitDZString(const wchar_t *buffer) const; - void mergeTweak(const QString &tweakName, const QString &tweakedIni) const; - void mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const; - -private: - - QDir m_Directory; - - mutable QByteArray m_LastModlistHash; - std::vector m_ModStatus; - std::vector m_ModIndexByPriority; - unsigned int m_NumRegularMods; - - QTimer *m_SaveTimer; -}; - -Q_DECLARE_METATYPE(Profile) - - -#endif // PROFILE_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 PROFILE_H +#define PROFILE_H + + +#include "modinfo.h" + +#include +#include +#include +#include +#include +#include + + +/** + * @brief represents a profile + **/ +class Profile : public QObject +{ + + Q_OBJECT + +public: + + typedef boost::shared_ptr Ptr; + +public: + + /** + * @brief default constructor + * @todo This constructor initialised nothing, the resulting object is not usable + **/ + Profile(); + + /** + * @brief constructor + * + * This constructor is used to create a new profile so it is to be assumed a profile + * by this name does not yet exist + * @param name name of the new profile + * @param filter save game filter. Defaults to <no filter>. + **/ + Profile(const QString &name, bool useDefaultSettings); + /** + * @brief constructor + * + * This constructor is used to open an existing profile though it will also try to repair + * the profile if important files are missing (including the directory itself) so technically, + * invoking this should always produce a working profile + * @param directory directory to read the profile from + **/ + Profile(const QDir &directory); + + Profile(const Profile &reference); + + ~Profile(); + + /** + * @return true if this profile (still) exists on disc + */ + bool exists() const; + + /** + * @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); + + /** + * @brief write out the modlist.txt + **/ + void writeModlist() const; + + /** + * cancel any potential request to write modlist.txt. This is used to ensure no invalid modlist is + * saved while it's being modified + */ + void cancelWriteModlist() const; + + /** + * @brief test if this profile uses archive invalidation + * + * @param supported if this is not null, the parameter will be set to false if invalidation is not supported in this profile + * @return true if archive invalidation is active + * @note currently, invalidation is not supported if the relevant entry in the ini file does not exist + **/ + bool invalidationActive(bool *supported) const; + + /** + * @brief deactivate archive invalidation if it was active + **/ + void deactivateInvalidation() const; + + /** + * @brief activate archive invalidation + * + * @param dataDirectory data directory of the game + * @todo passing the data directory as a parameter is useless, the function should + * be able to query it from GameInfo + **/ + void activateInvalidation(const QString &dataDirectory) const; + + /** + * @return true if this profile uses local save games + */ + bool localSavesEnabled() const; + + /** + * @brief enables or disables the use of local save games for this profile + * disabling this does not delete exising local saves but they will not be visible + * in the game + * @param enable if true, local saves are enabled, otherewise they are disabled + */ + bool enableLocalSaves(bool enable); + + /** + * @return name of the profile (this is identical to its directory name) + **/ + QString getName() const { return m_Directory.dirName(); } + + /** + * @return the path of the plugins file in this profile + * @todo is this required? can the functionality using this function be moved to the Profile-class? + **/ + QString getPluginsFileName() const; + + /** + * @return the path of the loadorder file in this profile + **/ + QString getLoadOrderFileName() const; + + /** + * @return the path of the file containing locked mod indices + */ + QString getLockedOrderFileName() const; + + /** + * @return the path of the modlist file in this profile + */ + QString getModlistFileName() const; + + /** + * @return path of the archives file in this profile + */ + QString getArchivesFileName() const; + + /** + * @return the path of the delete file in this profile + * @note the deleter file lists plugins that should be hidden from the game and other tools + **/ + QString getDeleterFileName() const; + + /** + * @return the path of the ini file in this profile + * @todo since the game can contain multiple ini files (i.e. skyrim.ini skyrimprefs.ini) + * the concept of this function is somewhat broken + **/ + QString getIniFileName() const; + + /** + * @return the path of the tweak ini in this profile + */ + QString getProfileTweaks() const; + + /** + * @return path to this profile + **/ + QString getPath() const; + + void rename(const QString &newName); + + /** + * @brief create the ini file to be used by the game + * + * the tweaked ini file constructed by this file is a merger + * of the game-ini of this profile with ini tweaks applied */ + void createTweakedIniFile(); + + /** + * @brief re-read the modlist.txt and update the mod status from it + **/ + void refreshModStatus(); + + /** + * @brief retrieve a list of mods that are enabled in this profile + * + * @return list of active mods sorted by priority (ascending). "first" is the mod name, "second" is its path + **/ + std::vector > getActiveMods(); + + /** + * retrieve the number of mods for which this object has status information. + * This is usually the same as ModInfo::getNumMods() except between + * calls to ModInfo::updateFromDisc() and the Profile::refreshModStatus() + * + * @return number of mods for which the profile has status information + **/ + unsigned int numMods() const { return m_ModStatus.size(); } + + /** + * @return the number of mods that can be enabled and where the priority can be modified + */ + unsigned int numRegularMods() const { return m_NumRegularMods; } + + /** + * @brief retrieve the mod index based on the priority + * + * @param priority priority to look up + * @return the index of the mod + * @throw std::exception an exception is thrown if there is no mod with the specified priority + **/ + unsigned int modIndexByPriority(unsigned int priority) const; + + /** + * @brief enable or disable a mod + * + * @param index index of the mod to enable/disable + * @param enabled true if the mod is to be enabled, false if it is to be disabled + **/ + void setModEnabled(unsigned int index, bool enabled); + + /** + * change the priority of a mod. Of course this also changes the priority of other mods. + * The priority of the mods in the range ]old, new priority] are shifted so that no gaps + * are possible. + * + * @param index index of the mod to change + * @param newPriority the new priority value + * + * @todo what happens if the new priority is outside the range? + **/ + void setModPriority(unsigned int index, int &newPriority); + + /** + * @brief determine if a mod is enabled + * + * @param index index of the mod to look up + * @return true if the mod is enabled, false otherwise + **/ + bool modEnabled(unsigned int index) const; + + /** + * @brief query the priority of a mod + * + * @param index index of the mod to look up + * @return priority of the specified mod + **/ + int getModPriority(unsigned int index) const; + + void dumpModStatus() const; + +signals: + + /** + * @brief emitted whenever the status (enabled/disabled) of a mod changed + * + * @param index index of the mod that changed + **/ + void modStatusChanged(unsigned int index); + +public slots: + + void writeModlistNow(bool onlyOnTimer = false) const; + +private: + + class ModStatus { + friend class Profile; + public: + ModStatus() : m_Overwrite(false), m_Enabled(false), m_Priority(-1) {} + private: + bool m_Overwrite; + bool m_Enabled; + int m_Priority; + }; + +private: + Profile& operator=(const Profile &reference); // not implemented + + void initTimer(); + + void updateIndices(); + + void copyFilesTo(QString &target) const; + + std::vector splitDZString(const wchar_t *buffer) const; + void mergeTweak(const QString &tweakName, const QString &tweakedIni) const; + void mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const; + void touchFile(QString fileName); + +private: + + QDir m_Directory; + + mutable QByteArray m_LastModlistHash; + std::vector m_ModStatus; + std::vector m_ModIndexByPriority; + unsigned int m_NumRegularMods; + + QTimer *m_SaveTimer; +}; + +Q_DECLARE_METATYPE(Profile) + + +#endif // PROFILE_H -- cgit v1.3.1 From 93bd29c13d3355b2544c2fd40dff1f4f985f9b57 Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 24 Sep 2014 19:51:51 +0200 Subject: - several style fixes suggested by static analysis - will now support up to 4 levels of version numbers (major.minor.subminor.subsubminor --- src/browserdialog.cpp | 2 +- src/categories.cpp | 8 +++--- src/installationmanager.h | 2 +- src/main.cpp | 11 +++----- src/mainwindow.cpp | 55 +++++++++++++++++++--------------------- src/modinfo.cpp | 2 +- src/modlist.cpp | 8 ++++-- src/modlist.h | 2 +- src/modlistsortproxy.cpp | 2 +- src/nexusinterface.cpp | 5 +++- src/pluginlist.cpp | 50 ++++++++++++++++++------------------- src/profile.cpp | 4 ++- src/selfupdater.h | 3 ++- src/settings.cpp | 2 +- src/shared/directoryentry.cpp | 6 ++--- src/shared/directoryentry.h | 2 +- src/shared/fallout3info.cpp | 21 ++++++++-------- src/shared/fallout3info.h | 6 ++--- src/shared/falloutnvinfo.cpp | 58 +++++++++++++++++++------------------------ src/shared/falloutnvinfo.h | 6 ++--- src/shared/gameinfo.cpp | 40 ++++++++--------------------- src/shared/leaktrace.cpp | 2 +- src/shared/oblivioninfo.cpp | 21 ++++++++-------- src/shared/oblivioninfo.h | 6 ++--- src/shared/skyriminfo.cpp | 53 ++++++++++++++++++--------------------- src/shared/skyriminfo.h | 8 +++--- 26 files changed, 176 insertions(+), 209 deletions(-) (limited to 'src/profile.cpp') diff --git a/src/browserdialog.cpp b/src/browserdialog.cpp index 521459d0..f93ffcae 100644 --- a/src/browserdialog.cpp +++ b/src/browserdialog.cpp @@ -22,10 +22,10 @@ along with Mod Organizer. If not, see . #include "messagedialog.h" #include "report.h" -#include "json.h" #include "persistentcookiejar.h" #include +#include "json.h" #include #include diff --git a/src/categories.cpp b/src/categories.cpp index 28b1f4a2..57e18a28 100644 --- a/src/categories.cpp +++ b/src/categories.cpp @@ -232,7 +232,7 @@ void CategoryFactory::loadDefaultCategories() int CategoryFactory::getParentID(unsigned int index) const { - if ((index < 0) || (index >= m_Categories.size())) { + if (index >= m_Categories.size()) { throw MyException(QObject::tr("invalid index %1").arg(index)); } @@ -267,7 +267,7 @@ bool CategoryFactory::isDecendantOf(int id, int parentID) const bool CategoryFactory::hasChildren(unsigned int index) const { - if ((index < 0) || (index >= m_Categories.size())) { + if (index >= m_Categories.size()) { throw MyException(QObject::tr("invalid index %1").arg(index)); } @@ -277,7 +277,7 @@ bool CategoryFactory::hasChildren(unsigned int index) const QString CategoryFactory::getCategoryName(unsigned int index) const { - if ((index < 0) || (index >= m_Categories.size())) { + if (index >= m_Categories.size()) { throw MyException(QObject::tr("invalid index %1").arg(index)); } @@ -287,7 +287,7 @@ QString CategoryFactory::getCategoryName(unsigned int index) const int CategoryFactory::getCategoryID(unsigned int index) const { - if ((index < 0) || (index >= m_Categories.size())) { + if (index >= m_Categories.size()) { throw MyException(QObject::tr("invalid index %1").arg(index)); } diff --git a/src/installationmanager.h b/src/installationmanager.h index d430c065..336c1ce3 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -55,7 +55,7 @@ public: **/ explicit InstallationManager(QWidget *parent); - ~InstallationManager(); + virtual ~InstallationManager(); /** * @brief update the directory where mods are to be installed diff --git a/src/main.cpp b/src/main.cpp index 3198208a..642bfa1a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -455,14 +455,9 @@ int main(int argc, char *argv[]) settings.setValue("gamePath", gamePath.toUtf8().constData()); } - int edition = 0; - if (settings.contains("game_edition")) { - edition = settings.value("game_edition").toInt(); - } else { + if (!settings.contains("game_edition")) { std::vector editions = GameInfo::instance().getSteamVariants(); - if (editions.size() < 2) { - edition = 0; - } else { + if (editions.size() > 1) { SelectionDialog selection(QObject::tr("Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!)"), NULL); int index = 0; for (auto iter = editions.begin(); iter != editions.end(); ++iter) { @@ -475,7 +470,7 @@ int main(int argc, char *argv[]) } } } - +#pragma message("edition isn't used?") qDebug("managing game at %s", qPrintable(QDir::toNativeSeparators(gamePath))); ExecutablesList executablesList; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ae5c1e48..19be758e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1137,7 +1137,6 @@ void MainWindow::registerPluginTool(IPluginTool *tool) void MainWindow::registerModPage(IPluginModPage *modPage) { - QToolButton *browserBtn = NULL; // turn the browser action into a drop-down menu if necessary if (ui->actionNexus->menu() == NULL) { QAction *nexusAction = ui->actionNexus; @@ -1147,10 +1146,8 @@ void MainWindow::registerModPage(IPluginModPage *modPage) ui->toolBar->removeAction(nexusAction); actionToToolButton(ui->actionNexus); - browserBtn = qobject_cast(ui->toolBar->widgetForAction(ui->actionNexus)); + QToolButton *browserBtn = qobject_cast(ui->toolBar->widgetForAction(ui->actionNexus)); browserBtn->menu()->addAction(nexusAction); - } else { - browserBtn = qobject_cast(ui->toolBar->widgetForAction(ui->actionNexus)); } QAction *action = new QAction(modPage->icon(), modPage->displayName(), ui->toolBar); @@ -1434,30 +1431,32 @@ void MainWindow::spawnBinary(const QFileInfo &binary, const QString &arguments, DWORD retLen; JOBOBJECT_BASIC_PROCESS_ID_LIST info; - bool isJobHandle = true; + { + bool isJobHandle = true; - DWORD res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE); - while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0) && !dialog->unlockClicked()) { - if (isJobHandle) { - if (::QueryInformationJobObject(processHandle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) { - if (info.NumberOfProcessIdsInList == 0) { - break; - } - } 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; + DWORD res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE); + while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0) && !dialog->unlockClicked()) { + if (isJobHandle) { + if (::QueryInformationJobObject(processHandle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) { + if (info.NumberOfProcessIdsInList == 0) { + break; + } + } 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(); + // keep processing events so the app doesn't appear dead + QCoreApplication::processEvents(); - res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE); + res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE); + } } ::CloseHandle(processHandle); @@ -2191,11 +2190,7 @@ void MainWindow::storeSettings() QSettings::Status result = QSettings::NoError; { QSettings settings(iniFile + ".new", QSettings::IniFormat); - if (m_CurrentProfile != NULL) { - settings.setValue("selected_profile", m_CurrentProfile->getName().toUtf8().constData()); - } else { - settings.remove("selected_profile"); - } + settings.setValue("selected_profile", m_CurrentProfile->getName().toUtf8().constData()); settings.setValue("mod_list_state", ui->modList->header()->saveState()); settings.setValue("plugin_list_state", ui->espList->header()->saveState()); @@ -5560,11 +5555,11 @@ void MainWindow::on_bossButton_clicked() DWORD retLen; JOBOBJECT_BASIC_PROCESS_ID_LIST info; - bool isJobHandle = true; - ULONG lastProcessID; HANDLE processHandle = loot; if (loot != INVALID_HANDLE_VALUE) { + bool isJobHandle = true; + ULONG lastProcessID; DWORD res = ::MsgWaitForMultipleObjects(1, &loot, false, 1000, QS_KEY | QS_MOUSE); while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0)) { if (isJobHandle) { diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 796dab71..189e67b2 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -156,7 +156,7 @@ bool ModInfo::removeMod(unsigned int index) auto iter = s_ModsByModID.find(modInfo->getNexusID()); if (iter != s_ModsByModID.end()) { std::vector indices = iter->second; - std::remove(indices.begin(), indices.end(), index); + indices.erase(std::remove(indices.begin(), indices.end(), index), indices.end()); s_ModsByModID[modInfo->getNexusID()] = indices; } diff --git a/src/modlist.cpp b/src/modlist.cpp index eedf1ec6..fb8df15e 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -49,8 +49,12 @@ using namespace MOBase; ModList::ModList(QObject *parent) - : QAbstractItemModel(parent), m_Profile(NULL), m_Modified(false), - m_FontMetrics(QFont()), m_DropOnItems(false) + : QAbstractItemModel(parent) + , m_Profile(NULL) + , m_NexusInterface(NULL) + , m_Modified(false) + , m_FontMetrics(QFont()) + , m_DropOnItems(false) { m_ContentIcons[ModInfo::CONTENT_PLUGIN] = std::make_tuple(QIcon(":/MO/gui/content/plugin"), ":/MO/gui/content/plugin", tr("Game plugins (esp/esm)")); m_ContentIcons[ModInfo::CONTENT_INTERFACE] = std::make_tuple(QIcon(":/MO/gui/content/interface"), ":/MO/gui/content/interface", tr("Interface")); diff --git a/src/modlist.h b/src/modlist.h index 632689c6..cf52b2ec 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -267,7 +267,7 @@ private: struct TModInfo { TModInfo(unsigned int index, ModInfo::Ptr modInfo) - : modInfo(modInfo), nameOrder(index) {} + : modInfo(modInfo), nameOrder(index), priorityOrder(0), modIDOrder(0), categoryOrder(0) {} ModInfo::Ptr modInfo; unsigned int nameOrder; unsigned int priorityOrder; diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index da5d99d5..8907e712 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -298,7 +298,7 @@ bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const } break; case CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED: { ModInfo::EEndorsedState state = info->endorsedState(); - if ((state == ModInfo::ENDORSED_FALSE) && (state != ModInfo::ENDORSED_NEVER)) return true; + if ((state == ModInfo::ENDORSED_FALSE) || (state == ModInfo::ENDORSED_NEVER)) return true; } break; case CategoryFactory::CATEGORY_SPECIAL_MANAGED: { if (!info->hasFlag(ModInfo::FLAG_FOREIGN)) return true; diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 30221f4b..b4006097 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -20,7 +20,7 @@ along with Mod Organizer. If not, see . #include "nexusinterface.h" #include "nxmaccessmanager.h" #include "utility.h" -#include +#include "json.h" #include "selectiondialog.h" #include #include @@ -580,6 +580,7 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_URL(url) , m_SubModule(subModule) , m_NexusGameID(nexusGameId) + , m_Endorse(false) {} NexusInterface::NXMRequestInfo::NXMRequestInfo(std::vector modIDList @@ -600,6 +601,7 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(std::vector modIDList , m_URL(url) , m_SubModule(subModule) , m_NexusGameID(nexusGameId) + , m_Endorse(false) {} NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID @@ -620,4 +622,5 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_URL(url) , m_SubModule(subModule) , m_NexusGameID(nexusGameId) + , m_Endorse(false) {} diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index ff370fa4..973e3cfc 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -685,7 +685,7 @@ QString PluginList::origin(const QString &name) const { auto iter = m_ESPsByName.find(name.toLower()); if (iter == m_ESPsByName.end()) { - return false; + return QString(); } else { return m_ESPs[iter->second].m_OriginName; } @@ -836,7 +836,7 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const std::set_difference(m_ESPs[index].m_Masters.begin(), m_ESPs[index].m_Masters.end(), m_ESPs[index].m_MasterUnset.begin(), m_ESPs[index].m_MasterUnset.end(), std::inserter(enabledMasters, enabledMasters.end())); - if (enabledMasters.size() > 0) { + if (!enabledMasters.empty()) { text += "
" + tr("Enabled Masters") + ": " + SetJoin(enabledMasters, ", "); } if (m_ESPs[index].m_HasIni) { @@ -1102,34 +1102,34 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) ((keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down))) { QItemSelectionModel *selectionModel = itemView->selectionModel(); const QSortFilterProxyModel *proxyModel = qobject_cast(selectionModel->model()); - int diff = -1; - if (((keyEvent->key() == Qt::Key_Up) && (proxyModel->sortOrder() == Qt::DescendingOrder)) || - ((keyEvent->key() == Qt::Key_Down) && (proxyModel->sortOrder() == Qt::AscendingOrder))) { - diff = 1; - } - QModelIndexList rows = selectionModel->selectedRows(); - // remove elements that aren't supposed to be movable - QMutableListIterator iter(rows); - while (iter.hasNext()) { - if ((iter.next().flags() & Qt::ItemIsDragEnabled) == 0) { - iter.remove(); + if (proxyModel != NULL) { + int diff = -1; + if (((keyEvent->key() == Qt::Key_Up) && (proxyModel->sortOrder() == Qt::DescendingOrder)) || + ((keyEvent->key() == Qt::Key_Down) && (proxyModel->sortOrder() == Qt::AscendingOrder))) { + diff = 1; } - } - if (keyEvent->key() == Qt::Key_Down) { - for (int i = 0; i < rows.size() / 2; ++i) { - rows.swap(i, rows.size() - i - 1); + QModelIndexList rows = selectionModel->selectedRows(); + // remove elements that aren't supposed to be movable + QMutableListIterator iter(rows); + while (iter.hasNext()) { + if ((iter.next().flags() & Qt::ItemIsDragEnabled) == 0) { + iter.remove(); + } } - } - foreach (QModelIndex idx, rows) { - if (proxyModel != NULL) { - idx = proxyModel->mapToSource(idx); + if (keyEvent->key() == Qt::Key_Down) { + for (int i = 0; i < rows.size() / 2; ++i) { + rows.swap(i, rows.size() - i - 1); + } } - int newPriority = m_ESPs[idx.row()].m_Priority + diff; - if ((newPriority >= 0) && (newPriority < rowCount())) { - setPluginPriority(idx.row(), newPriority); + foreach (QModelIndex idx, rows) { + idx = proxyModel->mapToSource(idx); + int newPriority = m_ESPs[idx.row()].m_Priority + diff; + if ((newPriority >= 0) && (newPriority < rowCount())) { + setPluginPriority(idx.row(), newPriority); + } } + refreshLoadOrder(); } - refreshLoadOrder(); return true; } else if (keyEvent->key() == Qt::Key_Space) { QItemSelectionModel *selectionModel = itemView->selectionModel(); diff --git a/src/profile.cpp b/src/profile.cpp index 77e4f813..958084d7 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -585,7 +585,9 @@ void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const bool Profile::invalidationActive(bool *supported) const { if (GameInfo::instance().requiresBSAInvalidation()) { - *supported = true; + if (supported != NULL) { + *supported = true; + } wchar_t buffer[1024]; std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); // epic ms fail: GetPrivateProfileString uses errno (for whatever reason) to signal a fail since the return value diff --git a/src/selfupdater.h b/src/selfupdater.h index 9648f15a..75b0ff45 100644 --- a/src/selfupdater.h +++ b/src/selfupdater.h @@ -65,7 +65,8 @@ public: * @todo passing the nexus interface is unneccessary **/ SelfUpdater(NexusInterface *nexusInterface, QWidget *parent); - ~SelfUpdater(); + + virtual ~SelfUpdater(); /** * @brief start the update process diff --git a/src/settings.cpp b/src/settings.cpp index 04a5f279..0ca811a3 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -25,7 +25,7 @@ along with Mod Organizer. If not, see . #include #include #include -#include +#include "json.h" #include #include diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 5d785822..24868a93 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -325,13 +325,13 @@ static bool ByOriginPriority(DirectoryEntry *entry, int LHS, int RHS) FileEntry::FileEntry() - : m_Index(UINT_MAX), m_Name(), m_Parent(NULL), m_LastAccessed(time(NULL)) + : m_Index(UINT_MAX), m_Name(), m_Origin(-1), 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_LastAccessed(time(NULL)) + : m_Index(index), m_Name(name), m_Origin(-1), m_Parent(parent), m_Archive(L""), m_LastAccessed(time(NULL)) { LEAK_TRACE; } @@ -636,7 +636,7 @@ void DirectoryEntry::insertFile(const std::wstring &filePath, FilesOrigin &origi void DirectoryEntry::removeFile(FileEntry::Index index) { - if (m_Files.size() != 0) { + if (!m_Files.empty()) { auto iter = std::find_if(m_Files.begin(), m_Files.end(), [&index](const std::pair &iter) -> bool { return iter.second == index; } ); diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 096f373e..d588ab02 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -219,7 +219,7 @@ public: void clear(); bool isPopulated() const { return m_Populated; } - bool isEmpty() const { return (m_Files.size() == 0) && (m_SubDirectories.size() == 0); } + bool isEmpty() const { return m_Files.empty() && m_SubDirectories.empty(); } const DirectoryEntry *getParent() const { return m_Parent; } diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index 9487d2de..22db91ac 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -54,15 +54,17 @@ std::wstring Fallout3Info::getRegPathStatic() 0, KEY_QUERY_VALUE, &key); if (errorcode != ERROR_SUCCESS) { - return L""; + return std::wstring(); } WCHAR temp[MAX_PATH]; DWORD bufferSize = MAX_PATH; - errorcode = ::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize); - - return std::wstring(temp); + if (::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { + return std::wstring(temp); + } else { + return std::wstring(); + } } std::wstring Fallout3Info::getInvalidationBSA() @@ -233,15 +235,12 @@ void Fallout3Info::createProfile(const std::wstring &directory, bool useDefaults } } { // copy falloutprefs.ini-file - std::wstring target = directory.substr().append(L"\\falloutprefs.ini"); + std::wstring target = directory + L"\\falloutprefs.ini"; if (!FileExists(target)) { - std::wostringstream source; - source << getMyGamesDirectory() << L"\\Fallout3\\falloutprefs.ini"; - if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) { + std::wstring source = getMyGamesDirectory() + L"\\Fallout3\\falloutprefs.ini"; + if (!::CopyFileW(source.c_str(), target.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()); + throw windows_error(std::string("failed to copy ini file: ") + ToString(source, false)); } } } diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h index 8e4c260d..d1356de1 100644 --- a/src/shared/fallout3info.h +++ b/src/shared/fallout3info.h @@ -38,7 +38,7 @@ public: virtual unsigned long getBSAVersion(); static std::wstring getRegPathStatic(); - virtual std::wstring getRegPath() { return Fallout3Info::getRegPathStatic(); } + virtual std::wstring getRegPath() { return getRegPathStatic(); } virtual std::wstring getBinaryName() { return L"Fallout3.exe"; } virtual GameInfo::Type getType() { return TYPE_FALLOUT3; } @@ -75,9 +75,9 @@ public: virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); - virtual std::wstring getNexusInfoUrl() { return Fallout3Info::getNexusInfoUrlStatic(); } + virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); - virtual int getNexusModID() { return Fallout3Info::getNexusModIDStatic(); } + virtual int getNexusModID() { return getNexusModIDStatic(); } virtual int getNexusGameID() { return 120; } virtual void createProfile(const std::wstring &directory, bool useDefaults); diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index 9bba7fe4..0dde4db1 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -55,15 +55,17 @@ std::wstring FalloutNVInfo::getRegPathStatic() 0, KEY_QUERY_VALUE, &key); if (errorcode != ERROR_SUCCESS) { - return L""; + return std::wstring(); } WCHAR temp[MAX_PATH]; DWORD bufferSize = MAX_PATH; - errorcode = ::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize); - - return std::wstring(temp); + if (::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { + return std::wstring(temp); + } else { + return std::wstring(); + } } std::wstring FalloutNVInfo::getInvalidationBSA() @@ -162,56 +164,46 @@ std::wstring FalloutNVInfo::getSteamAPPId(int) const void FalloutNVInfo::createProfile(const std::wstring &directory, bool useDefaults) { - std::wostringstream target; + std::wstring target = directory + L"\\plugins.txt"; // copy plugins.txt - target << directory << "\\plugins.txt"; - - if (!FileExists(target.str())) { - std::wostringstream source; - source << getLocalAppFolder() << "\\FalloutNV\\plugins.txt"; - if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) { - HANDLE file = ::CreateFileW(target.str().c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + 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, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); ::CloseHandle(file); } } // copy ini-file - target.str(L""); target.clear(); - target << directory << L"\\fallout.ini"; + target = directory + L"\\fallout.ini"; - if (!FileExists(target.str())) { - std::wostringstream source; + if (!FileExists(target)) { + std::wstring source; if (useDefaults) { - source << getGameDirectory() << L"\\fallout_default.ini"; + source = getGameDirectory() + L"\\fallout_default.ini"; } else { - source << getMyGamesDirectory() << L"\\FalloutNV"; - if (FileExists(source.str(), L"fallout.ini")) { - source << L"\\fallout.ini"; + source = getMyGamesDirectory() + L"\\FalloutNV"; + if (FileExists(source, L"fallout.ini")) { + source += L"\\fallout.ini"; } else { - source.str(L""); - source << getGameDirectory() << L"\\fallout_default.ini"; + source = getGameDirectory() + L"\\fallout_default.ini"; } } - if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) { + if (!::CopyFileW(source.c_str(), target.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()); + throw windows_error("failed to copy ini file: " + ToString(source, false)); } } } { // copy falloutprefs.ini-file - std::wstring target = directory.substr().append(L"\\falloutprefs.ini"); + std::wstring target = directory + L"\\falloutprefs.ini"; if (!FileExists(target)) { - std::wostringstream source; - source << getMyGamesDirectory() << L"\\FalloutNV\\falloutprefs.ini"; - if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) { + std::wstring source = getMyGamesDirectory() + L"\\FalloutNV\\falloutprefs.ini"; + if (!::CopyFileW(source.c_str(), target.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()); + throw windows_error("failed to copy ini file: " + ToString(source, false)); } } } diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h index cfd373c7..50a0d00d 100644 --- a/src/shared/falloutnvinfo.h +++ b/src/shared/falloutnvinfo.h @@ -38,7 +38,7 @@ public: virtual unsigned long getBSAVersion(); static std::wstring getRegPathStatic(); - virtual std::wstring getRegPath() { return FalloutNVInfo::getRegPathStatic(); } + virtual std::wstring getRegPath() { return getRegPathStatic(); } virtual std::wstring getBinaryName() { return L"FalloutNV.exe"; } virtual GameInfo::Type getType() { return TYPE_FALLOUTNV; } @@ -76,9 +76,9 @@ public: virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); - virtual std::wstring getNexusInfoUrl() { return FalloutNVInfo::getNexusInfoUrlStatic(); } + virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); - virtual int getNexusModID() { return FalloutNVInfo::getNexusModIDStatic(); } + virtual int getNexusModID() { return getNexusModIDStatic(); } virtual int getNexusGameID() { return 130; } virtual void createProfile(const std::wstring &directory, bool useDefaults); diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index 21e9a586..5439efff 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -57,7 +57,7 @@ void GameInfo::identifyMyGamesDirectory(const std::wstring &file) { // this function attempts 3 (three!) ways to determine the correct "My Games" folder. wchar_t myDocuments[MAX_PATH]; - memset(myDocuments, '\0', MAX_PATH); + memset(myDocuments, '\0', MAX_PATH * sizeof(wchar_t)); m_MyGamesDirectory.clear(); @@ -137,71 +137,53 @@ std::wstring GameInfo::getGameDirectory() const std::wstring GameInfo::getModsDir() const { - std::wostringstream temp; - temp << m_OrganizerDataDirectory << L"\\mods"; - return temp.str(); + return m_OrganizerDirectory + L"\\mods"; } std::wstring GameInfo::getProfilesDir() const { - std::wostringstream temp; - temp << m_OrganizerDataDirectory << L"\\profiles"; - return temp.str(); + return m_OrganizerDirectory + L"\\profiles"; } std::wstring GameInfo::getIniFilename() const { - std::wostringstream temp; - temp << m_OrganizerDataDirectory << L"\\ModOrganizer.ini"; - return temp.str(); + return m_OrganizerDirectory + L"\\ModOrganizer.ini"; } std::wstring GameInfo::getDownloadDir() const { - std::wostringstream temp; - temp << m_OrganizerDataDirectory << L"\\downloads"; - return temp.str(); + return m_OrganizerDirectory + L"\\downloads"; } std::wstring GameInfo::getCacheDir() const { - std::wostringstream temp; - temp << m_OrganizerDataDirectory << L"\\webcache"; - return temp.str(); + return m_OrganizerDirectory + L"\\webcache"; } std::wstring GameInfo::getOverwriteDir() const { - std::wostringstream temp; - temp << m_OrganizerDataDirectory << "\\overwrite"; - return temp.str(); + return m_OrganizerDirectory + L"\\overwrite"; } std::wstring GameInfo::getLogDir() const { - std::wostringstream temp; - temp << m_OrganizerDataDirectory << "\\logs"; - return temp.str(); + return m_OrganizerDirectory + L"\\logs"; } std::wstring GameInfo::getLootDir() const { - std::wostringstream temp; - temp << m_OrganizerDirectory << "\\loot"; - return temp.str(); + return m_OrganizerDirectory + L"\\loot"; } std::wstring GameInfo::getTutorialDir() const { - std::wostringstream temp; - temp << m_OrganizerDirectory << "\\tutorials"; - return temp.str(); + return m_OrganizerDirectory + L"\\tutorials"; } @@ -219,7 +201,7 @@ std::vector GameInfo::getSteamVariants() const std::wstring GameInfo::getLocalAppFolder() const { wchar_t localAppFolder[MAX_PATH]; - memset(localAppFolder, '\0', MAX_PATH); + memset(localAppFolder, '\0', MAX_PATH * sizeof(wchar_t)); if (::SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, localAppFolder) == S_OK) { return localAppFolder; diff --git a/src/shared/leaktrace.cpp b/src/shared/leaktrace.cpp index 68e57609..729eb42e 100644 --- a/src/shared/leaktrace.cpp +++ b/src/shared/leaktrace.cpp @@ -36,7 +36,7 @@ class StackData { public: StackData() - : m_FunctionName("Dummy"), m_CodeLine(0) + : m_Count(0), m_Hash(0UL), m_FunctionName("Dummy"), m_CodeLine(0) {} StackData(const char *functionName, int line) { m_Count = ::CaptureStackBackTrace(FRAMES_TO_SKIP, FRAMES_TO_CAPTURE, m_Stack, &m_Hash); diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index f317812f..790fcdb0 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -55,15 +55,17 @@ std::wstring OblivionInfo::getRegPathStatic() 0, KEY_QUERY_VALUE, &key); if (errorcode != ERROR_SUCCESS) { - return L""; + return std::wstring(); } WCHAR temp[MAX_PATH]; DWORD bufferSize = MAX_PATH; - errorcode = ::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize); - - return std::wstring(temp); + if (::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { + return std::wstring(temp); + } else { + return std::wstring(); + } } std::wstring OblivionInfo::getInvalidationBSA() @@ -188,16 +190,13 @@ void OblivionInfo::createProfile(const std::wstring &directory, bool useDefaults } { // copy oblivionprefs.ini-file - std::wstring target = directory.substr().append(L"\\oblivionprefs.ini"); + std::wstring target = directory + L"\\oblivionprefs.ini"; if (!FileExists(target)) { - std::wostringstream source; - source << getMyGamesDirectory() << L"\\Oblivion\\oblivionprefs.ini"; - if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) { + 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, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL) == INVALID_HANDLE_VALUE) && (::GetLastError() != ERROR_FILE_EXISTS)) { - std::ostringstream stream; - stream << "failed to create ini file: " << ToString(target.c_str(), false); - throw windows_error(stream.str()); + throw windows_error(std::string("failed to create ini file: ") + ToString(target, false)); } } } diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h index 02fd90b4..e64ae37b 100644 --- a/src/shared/oblivioninfo.h +++ b/src/shared/oblivioninfo.h @@ -36,7 +36,7 @@ public: virtual unsigned long getBSAVersion(); static std::wstring getRegPathStatic(); - virtual std::wstring getRegPath() { return OblivionInfo::getRegPathStatic(); } + virtual std::wstring getRegPath() { return getRegPathStatic(); } virtual std::wstring getBinaryName() { return L"Oblivion.exe"; } virtual GameInfo::Type getType() { return TYPE_OBLIVION; } @@ -72,9 +72,9 @@ public: virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); - virtual std::wstring getNexusInfoUrl() { return OblivionInfo::getNexusInfoUrlStatic(); } + virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); - virtual int getNexusModID() { return OblivionInfo::getNexusModIDStatic(); } + virtual int getNexusModID() { return getNexusModIDStatic(); } virtual int getNexusGameID() { return 101; } virtual void createProfile(const std::wstring &directory, bool useDefaults); diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index c985fe9f..319e58d5 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -63,15 +63,17 @@ std::wstring SkyrimInfo::getRegPathStatic() 0, KEY_QUERY_VALUE, &key); if (errorcode != ERROR_SUCCESS) { - return L""; + return std::wstring(); } WCHAR temp[MAX_PATH]; DWORD bufferSize = MAX_PATH; - errorcode = ::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize); - - return std::wstring(temp); + if (::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { + return std::wstring(temp); + } else { + return std::wstring(); + } } @@ -222,7 +224,7 @@ int SkyrimInfo::getNexusModIDStatic() void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults) { { // copy plugins.txt - std::wstring target = directory.substr().append(L"\\plugins.txt"); + std::wstring target = directory + L"\\plugins.txt"; if (!FileExists(target)) { std::wostringstream source; source << getLocalAppFolder() << "\\Skyrim\\plugins.txt"; @@ -231,11 +233,10 @@ void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults) ::CloseHandle(file); } } - target = directory.substr().append(L"\\loadorder.txt"); + target = directory + L"\\loadorder.txt"; if (!FileExists(target)) { - std::wostringstream source; - source << getLocalAppFolder() << "\\Skyrim\\loadorder.txt"; - if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) { + 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, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); ::CloseHandle(file); } @@ -243,42 +244,36 @@ void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults) } { // copy skyrim.ini-file - std::wstring target = directory.substr().append(L"\\skyrim.ini"); + std::wstring target = directory + L"\\skyrim.ini"; if (!FileExists(target)) { - std::wostringstream source; + std::wstring source; if (useDefaults) { - source << getGameDirectory() << L"\\skyrim_default.ini"; + source = getGameDirectory() + L"\\skyrim_default.ini"; } else { - source << getMyGamesDirectory() << L"\\Skyrim"; - if (FileExists(source.str(), L"skyrim.ini")) { - source << L"\\skyrim.ini"; + source = getMyGamesDirectory() + L"\\Skyrim"; + if (FileExists(source, L"skyrim.ini")) { + source += L"\\skyrim.ini"; } else { - source.str(L""); - source << getGameDirectory() << L"\\skyrim_default.ini"; + source = getGameDirectory() + L"\\skyrim_default.ini"; } } - if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) { + if (!::CopyFileW(source.c_str(), target.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()); + throw windows_error(std::string("failed to copy ini file: ") + ToString(source, false)); } } } } { // copy skyrimprefs.ini-file - std::wstring target = directory.substr().append(L"\\skyrimprefs.ini"); + std::wstring target = directory + L"\\skyrimprefs.ini"; if (!FileExists(target)) { - std::wostringstream source; - source << getMyGamesDirectory() << L"\\Skyrim\\skyrimprefs.ini"; - if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) { - log("failed to copy ini file %ls", source.str().c_str()); + 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, 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()); + throw windows_error(std::string("failed to copy ini file: ") + ToString(source, false)); } } } diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h index 132f2aee..a7aff8dc 100644 --- a/src/shared/skyriminfo.h +++ b/src/shared/skyriminfo.h @@ -38,7 +38,7 @@ public: virtual unsigned long getBSAVersion(); static std::wstring getRegPathStatic(); - virtual std::wstring getRegPath() { return SkyrimInfo::getRegPathStatic(); } + virtual std::wstring getRegPath() { return getRegPathStatic(); } virtual std::wstring getBinaryName() { return L"TESV.exe"; } virtual GameInfo::Type getType() { return TYPE_SKYRIM; } @@ -81,11 +81,11 @@ public: virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); - virtual std::wstring getNexusInfoUrl() { return SkyrimInfo::getNexusInfoUrlStatic(); } + virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); - virtual int getNexusModID() { return SkyrimInfo::getNexusModIDStatic(); } + virtual int getNexusModID() { return getNexusModIDStatic(); } static int getNexusGameIDStatic() { return 110; } - virtual int getNexusGameID() { return SkyrimInfo::getNexusGameIDStatic(); } + virtual int getNexusGameID() { return getNexusGameIDStatic(); } virtual void createProfile(const std::wstring &directory, bool useDefaults); virtual void repairProfile(const std::wstring &directory); -- cgit v1.3.1 From db0e278817cf5a36e15f1945c52e73726598e8d9 Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 29 Sep 2014 20:35:35 +0200 Subject: - moved the hook-recursion-protection to tls - some code cleanup and consolidation - hook.dll will now report all of its own exceptions - some more logging during startup - changed the way urls are encoded for download requests - now displaying (one of the) process name(s) while waiting for a program to end - bugfix: spawned processes were forced to leave the job --- src/downloadmanager.cpp | 50 ++++++++++++++++--------------------------- src/mainwindow.cpp | 15 +++++++++++++ src/profile.cpp | 7 +++--- src/shared/directoryentry.cpp | 10 ++++----- src/spawn.cpp | 2 +- 5 files changed, 42 insertions(+), 42 deletions(-) (limited to 'src/profile.cpp') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index bc31adf4..b3b18a38 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -331,7 +331,8 @@ bool DownloadManager::addDownload(const QStringList &URLs, fileName = "unknown"; } - QNetworkRequest request(URLs.first()); + QUrl preferredUrl = QUrl::fromEncoded(URLs.first().toLocal8Bit()); + QNetworkRequest request(preferredUrl); return addDownload(m_NexusInterface->getAccessManager()->get(request), URLs, fileName, modID, fileID, fileInfo); } @@ -1198,47 +1199,34 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant userD m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(test), QString())); } - -// sort function to sort by best download server -bool DownloadManager::ServerByPreference(const std::map &preferredServers, const QVariant &LHS, const QVariant &RHS) +int evaluateFileInfoMap(const QVariantMap &map, const std::map &preferredServers) { - int LHSVal = 0; - int RHSVal = 0; + int result = 0; - QVariantMap LHSMap = LHS.toMap(); - QVariantMap RHSMap = RHS.toMap(); - - int LHSUsers = LHSMap["ConnectedUsers"].toInt(); - int RHSUsers = RHSMap["ConnectedUsers"].toInt(); + int users = map["ConnectedUsers"].toInt(); // 0 users is probably a sign that the server is offline. Since there is currently no // mechanism to try a different server, we avoid those without users - if (LHSUsers == 0) { - LHSVal -= 500; - } else { - LHSVal -= LHSUsers; - } - if (RHSUsers == 0) { - RHSVal -= 500; + if (users == 0) { + result -= 500; } else { - RHSVal -= RHSUsers; + result -= users; } - // user preference. This is a bit silly because the more servers on the preferred list the higher the boost - auto LHSPreference = preferredServers.find(LHSMap["Name"].toString()); - auto RHSPreference = preferredServers.find(RHSMap["Name"].toString()); + auto preference = preferredServers.find(map["Name"].toString()); - if (LHSPreference != preferredServers.end()) { - LHSVal += 100 + LHSPreference->second * 20; - } - if (RHSPreference != preferredServers.end()) { - RHSVal += 100 + RHSPreference->second * 20; + if (preference != preferredServers.end()) { + result += 100 + preference->second * 20; } - // premium isn't valued high because premium servers already get a massive boost for having few users online - if (LHSMap["IsPremium"].toBool()) LHSVal += 5; - if (RHSMap["IsPremium"].toBool()) RHSVal += 5; + if (map["IsPremium"].toBool()) result += 5; + + return result; +} - return RHSVal < LHSVal; +// sort function to sort by best download server +bool DownloadManager::ServerByPreference(const std::map &preferredServers, const QVariant &LHS, const QVariant &RHS) +{ + return evaluateFileInfoMap(LHS.toMap(), preferredServers) > evaluateFileInfoMap(RHS.toMap(), preferredServers); } int DownloadManager::startDownloadURLs(const QStringList &urls) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 19be758e..17743312 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1410,6 +1410,15 @@ HANDLE MainWindow::spawnBinaryDirect(const QFileInfo &binary, const QString &arg } } +std::wstring getProcessName(DWORD processId) +{ + HANDLE process = ::OpenProcess(PROCESS_QUERY_INFORMATION, false, processId); + + DWORD value = MAX_PATH; + wchar_t buffer[MAX_PATH]; + ::QueryFullProcessImageNameW(process, 0, buffer, &value); + return buffer; +} void MainWindow::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory, bool closeAfterStart, const QString &steamAppID) { @@ -1432,6 +1441,7 @@ void MainWindow::spawnBinary(const QFileInfo &binary, const QString &arguments, JOBOBJECT_BASIC_PROCESS_ID_LIST info; { + DWORD currentProcess = 0UL; bool isJobHandle = true; DWORD res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE); @@ -1439,6 +1449,11 @@ void MainWindow::spawnBinary(const QFileInfo &binary, const QString &arguments, if (isJobHandle) { if (::QueryInformationJobObject(processHandle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) { if (info.NumberOfProcessIdsInList == 0) { + } else { + if (info.ProcessIdList[0] != currentProcess) { + currentProcess = info.ProcessIdList[0]; + dialog->setProcessName(ToQString(getProcessName(currentProcess))); + } break; } } else { diff --git a/src/profile.cpp b/src/profile.cpp index 958084d7..6e9d8f0f 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -217,7 +217,7 @@ void Profile::createTweakedIniFile() } if (localSavesEnabled()) { - if (!::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"1", ToWString(tweakedIni).c_str())) { + if (!::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"0", ToWString(tweakedIni).c_str())) { error = true; } @@ -238,7 +238,7 @@ void Profile::createTweakedIniFile() void Profile::refreshModStatus() { QFile file(getModlistFileName()); - if (!file.exists()) { + if (!file.open(QIODevice::ReadOnly)) { throw MyException(tr("\"%1\" is missing or inaccessible").arg(getModlistFileName())); } @@ -249,10 +249,9 @@ void Profile::refreshModStatus() std::set namesRead; // load mods from file and update enabled state and priority for them - file.open(QIODevice::ReadOnly); int index = 0; while (!file.atEnd()) { - QByteArray line = file.readLine(); + QByteArray line = file.readLine().trimmed(); bool enabled = true; QString modName; if (line.length() == 0) { diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 24868a93..0adf0812 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -72,8 +72,7 @@ public: } bool exists(const std::wstring &name) { - std::map::iterator iter = m_OriginsNameMap.find(name); - return iter != m_OriginsNameMap.end(); + return m_OriginsNameMap.find(name) != m_OriginsNameMap.end(); } FilesOrigin &getByID(Index ID) { @@ -369,16 +368,14 @@ std::wstring FileEntry::getFullPath() const bool ignore = false; result = m_Parent->getOriginByID(getOrigin(ignore)).getPath(); //base directory for origin recurseParents(result, m_Parent); // all intermediate directories - result.append(L"\\").append(m_Name); // the actual filename - return result; + return result + L"\\" + m_Name; } std::wstring FileEntry::getRelativePath() const { std::wstring result; recurseParents(result, m_Parent); // all intermediate directories - result.append(L"\\").append(m_Name); // the actual filename - return result; + return result + L"\\" + m_Name; } @@ -446,6 +443,7 @@ void DirectoryEntry::addFromOrigin(const std::wstring &originName, const std::ws boost::scoped_array buffer(new wchar_t[MAXPATH_UNICODE + 1]); memset(buffer.get(), L'\0', MAXPATH_UNICODE + 1); int offset = _snwprintf(buffer.get(), MAXPATH_UNICODE, L"%ls", directory.c_str()); + buffer.get()[offset] = L'\0'; addFiles(origin, buffer.get(), offset); } m_Populated = true; diff --git a/src/spawn.cpp b/src/spawn.cpp index ddcf573e..cd7e202e 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -120,7 +120,7 @@ HANDLE startBinary(const QFileInfo &binary, JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo; ::QueryInformationJobObject(NULL, JobObjectExtendedLimitInformation, &jobInfo, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), NULL); - jobInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_BREAKAWAY_OK; + jobInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK; HANDLE jobObject = ::CreateJobObject(NULL, NULL); -- cgit v1.3.1 From 78f628e0af2f2df562c40ac1424b432b6a969055 Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 28 Nov 2014 11:06:28 +0100 Subject: cleanup und bugfixes after refactoring --- src/browserdialog.cpp | 4 +- src/browserdialog.h | 1 - src/downloadmanager.cpp | 2 +- src/executableslist.cpp | 16 ++- src/installationmanager.cpp | 3 +- src/iuserinterface.h | 14 +- src/main.cpp | 159 ++++++--------------- src/mainwindow.cpp | 337 +++++++++++--------------------------------- src/mainwindow.h | 42 +++--- src/modinfo.cpp | 1 + src/nexusinterface.cpp | 20 ++- src/nexusinterface.h | 4 + src/nxmaccessmanager.cpp | 4 +- src/organizer.pro | 5 +- src/organizercore.cpp | 239 ++++++++++++++++++++++++++----- src/organizercore.h | 91 ++++++------ src/organizerproxy.cpp | 2 +- src/plugincontainer.cpp | 20 ++- src/plugincontainer.h | 35 ++--- src/pluginlist.cpp | 4 +- src/pluginlist.h | 2 +- src/profile.cpp | 8 +- src/selfupdater.cpp | 26 ++-- src/selfupdater.h | 1 + src/settings.cpp | 27 ++-- src/settings.h | 2 +- src/shared/appconfig.inc | 1 + src/shared/gameinfo.cpp | 20 +-- src/shared/gameinfo.h | 2 - src/shared/skyriminfo.cpp | 2 +- 30 files changed, 526 insertions(+), 568 deletions(-) (limited to 'src/profile.cpp') diff --git a/src/browserdialog.cpp b/src/browserdialog.cpp index 933b4bc0..82cd8d49 100644 --- a/src/browserdialog.cpp +++ b/src/browserdialog.cpp @@ -24,11 +24,11 @@ along with Mod Organizer. If not, see . #include "report.h" #include "persistentcookiejar.h" -#include #include "json.h" #include #include +#include "settings.h" #include #include #include @@ -49,7 +49,7 @@ BrowserDialog::BrowserDialog(QWidget *parent) ui->setupUi(this); m_AccessManager->setCookieJar(new PersistentCookieJar( - QDir::fromNativeSeparators(MOBase::ToQString(MOShared::GameInfo::instance().getCacheDir())) + "/cookies.dat", this)); + QDir::fromNativeSeparators(Settings::instance().getCacheDirectory() + "/cookies.dat"))); Qt::WindowFlags flags = windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint; Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint; diff --git a/src/browserdialog.h b/src/browserdialog.h index 10b44fac..680f5c03 100644 --- a/src/browserdialog.h +++ b/src/browserdialog.h @@ -23,7 +23,6 @@ along with Mod Organizer. If not, see . #include "browserview.h" #include "tutorialcontrol.h" #include -#include #include #include #include diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index b803d170..d785a939 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -980,7 +980,7 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) int index = 0; try { DownloadInfo *info = findDownload(this->sender(), &index); - if (info != NULL) { + if (info != nullptr) { if (info->m_State == STATE_CANCELING) { setState(info, STATE_CANCELED); } else if (info->m_State == STATE_PAUSING) { diff --git a/src/executableslist.cpp b/src/executableslist.cpp index 11158c5b..c46cea10 100644 --- a/src/executableslist.cpp +++ b/src/executableslist.cpp @@ -72,18 +72,20 @@ ExecutablesList::~ExecutablesList() void ExecutablesList::init(IPluginGame *game) { + Q_ASSERT(game != nullptr); m_Executables.clear(); for (const ExecutableInfo &info : game->executables()) { - addExecutableInternal(info.title(), - info.binary().absoluteFilePath(), - info.arguments().join(" "), - info.workingDirectory().absolutePath(), - info.closeMO(), - info.steamAppID()); + if (info.isValid()) { + addExecutableInternal(info.title(), + info.binary().absoluteFilePath(), + info.arguments().join(" "), + info.workingDirectory().absolutePath(), + info.closeMO(), + info.steamAppID()); + } } } - void ExecutablesList::getExecutables(std::vector::iterator &begin, std::vector::iterator &end) { begin = m_Executables.begin(); diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 0fb1b78d..25fe06a4 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -68,7 +68,8 @@ template T resolveFunction(QLibrary &lib, const char *name) InstallationManager::InstallationManager() - : m_InstallationProgress(nullptr), m_SupportedExtensions(boost::assign::list_of("zip")("rar")("7z")("fomod")("001")) + : m_InstallationProgress(nullptr) + , m_SupportedExtensions({ "zip", "rar", "7z", "fomod", "001" }) { QLibrary archiveLib("dlls\\archive.dll"); if (!archiveLib.load()) { diff --git a/src/iuserinterface.h b/src/iuserinterface.h index 76d4c75a..5cb61a25 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -11,11 +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 bool waitForProcessOrJob(HANDLE processHandle, LPDWORD exitCode = NULL) = 0; + virtual void storeSettings(QSettings &settings) = 0; virtual void registerPluginTool(MOBase::IPluginTool *tool) = 0; virtual void registerModPage(MOBase::IPluginModPage *modPage) = 0; @@ -24,9 +20,9 @@ public: virtual void disconnectPlugins() = 0; - virtual bool close() = 0; + virtual bool closeWindow() = 0; - virtual void setEnabled(bool enabled) = 0; + virtual void setWindowEnabled(bool enabled) = 0; virtual void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) = 0; @@ -34,6 +30,10 @@ public: virtual bool saveArchiveList() = 0; + virtual void lock() = 0; + virtual void unlock() = 0; + virtual bool unlockClicked() = 0; + }; #endif // IUSERINTERFACE_H diff --git a/src/main.cpp b/src/main.cpp index 0e112873..3f0360c2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -82,14 +82,13 @@ using namespace MOBase; using namespace MOShared; -// set up required folders (for a first install or after an update or to fix a broken installation) 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.mid(0).append("/update_backup"); + QString backupDirectory = moDirectory + "/update_backup"; if (QDir(backupDirectory).exists()) { shellDelete(QStringList(backupDirectory)); } @@ -97,47 +96,6 @@ bool bootstrap() // cycle logfile removeOldFiles(ToQString(GameInfo::instance().getLogDir()), "ModOrganizer*.log", 5, QDir::Name); - // create organizer directories - QString dirNames[] = { - QDir::fromNativeSeparators(ToQString(gameInfo.getProfilesDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getModsDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getDownloadDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getOverwriteDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getLogDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getTutorialDir())) - }; - static const int NUM_DIRECTORIES = sizeof(dirNames) / sizeof(QString); - - // optimistic run: try to simply create the directories: - for (int i = 0; i < NUM_DIRECTORIES; ++i) { - if (!QDir(dirNames[i]).exists()) { - QDir().mkdir(dirNames[i]); - } - } - - // verify all directories exist and are writable, - // otherwise invoke the helper to create them and make them writable - for (int i = 0; i < NUM_DIRECTORIES; ++i) { - QFileInfo fileInfo(dirNames[i]); - if (!fileInfo.exists() || !fileInfo.isWritable()) { - if (QMessageBox::question(NULL, 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 can break the loop - break; - } - } - // verify the hook-dll exists QString dllName = qApp->applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName()); @@ -154,29 +112,6 @@ bool bootstrap() return true; } - -void cleanupDir() -{ - // files from previous versions of MO that are no longer - // required (in that location) - QString fileNames[] = { - "NCC/GamebryoBase.dll", - "plugins/helloWorld.dll", - "plugins/testnexus.py" - }; - - static const int NUM_FILES = sizeof(fileNames) / sizeof(QString); - - qDebug("cleaning up unused files"); - - for (int i = 0; i < NUM_FILES; ++i) { - if (QFile::remove(QApplication::applicationDirPath().append("/").append(fileNames[i]))) { - qDebug("%s removed in cleanup", - QApplication::applicationDirPath().append("/").append(fileNames[i]).toUtf8().constData()); - } - } -} - bool isNxmLink(const QString &link) { return link.left(6).toLower() == "nxm://"; @@ -299,6 +234,27 @@ bool HaveWriteAccess(const std::wstring &path) } +QString determineProfile(QStringList arguments, const QSettings &settings) +{ + QString selectedProfileName = QString::fromUtf8(settings.value("selected_profile", "").toByteArray()); + { // see if there is a profile on the command line + int profileIndex = arguments.indexOf("-p", 1); + if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) { + qDebug("profile overwritten on command line"); + selectedProfileName = arguments.at(profileIndex + 1); + } + arguments.removeAt(profileIndex); + arguments.removeAt(profileIndex); + } + if (selectedProfileName.isEmpty()) { + qDebug("no configured profile"); + } else { + qDebug("configured profile: %s", qPrintable(selectedProfileName)); + } + + return selectedProfileName; +} + int main(int argc, char *argv[]) { MOApplication application(argc, argv); @@ -414,7 +370,7 @@ int main(int argc, char *argv[]) reportError(QObject::tr("No game identified in \"%1\". The directory is required to contain " "the game binary and its launcher.").arg(gamePath)); } - SelectionDialog selection(QObject::tr("Please select the game to manage"), NULL); + SelectionDialog selection(QObject::tr("Please select the game to manage"), nullptr); { // add options QString skyrimPath = ToQString(SkyrimInfo::getRegPathStatic()); @@ -462,6 +418,11 @@ int main(int argc, char *argv[]) settings.setValue("gamePath", gamePath.toUtf8().constData()); } + if (pluginContainer.managedGame(ToQString(GameInfo::instance().getGameName())) == nullptr) { + reportError(QObject::tr("Plugin to handle %1 not installed").arg(ToQString(GameInfo::instance().getGameName()))); + return 1; + } + if (!settings.contains("game_edition")) { std::vector editions = GameInfo::instance().getSteamVariants(); if (editions.size() > 1) { @@ -486,7 +447,25 @@ int main(int argc, char *argv[]) return -1; } - cleanupDir(); + QString selectedProfileName = determineProfile(arguments, settings); + organizer.setCurrentProfile(selectedProfileName); + + // if we have a command line parameter, it is either a nxm link or + // a binary to start + if ((arguments.size() > 1) && (!isNxmLink(arguments.at(1)))) { + QString exeName = arguments.at(1); + qDebug("starting %s from command line", qPrintable(exeName)); + arguments.removeFirst(); // remove application name (ModOrganizer.exe) + arguments.removeFirst(); // remove binary name + // pass the remaining parameters to the binary + try { + organizer.startApplication(exeName, arguments, QString(), QString()); + return 0; + } catch (const std::exception &e) { + reportError(QObject::tr("failed to start application: %1").arg(e.what())); + return 1; + } + } qDebug("initializing tutorials"); TutorialManager::init(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getTutorialDir())).append("/")); @@ -506,52 +485,8 @@ int main(int argc, char *argv[]) mainWindow.readSettings(); - QString selectedProfileName = QString::fromUtf8(settings.value("selected_profile", "").toByteArray()); - - { // see if there is a profile on the command line - int profileIndex = arguments.indexOf("-p", 1); - if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) { - qDebug("profile overwritten on command line"); - selectedProfileName = arguments.at(profileIndex + 1); - } - arguments.removeAt(profileIndex); - arguments.removeAt(profileIndex); - } - if (selectedProfileName.isEmpty()) { - qDebug("no configured profile"); - } else { - qDebug("configured profile: %s", qPrintable(selectedProfileName)); - } - - // if we have a command line parameter, it is either a nxm link or - // a binary to start - if ((arguments.size() > 1) && (!isNxmLink(arguments.at(1)))) { - QString exeName = arguments.at(1); - qDebug("starting %s from command line", qPrintable(exeName)); - arguments.removeFirst(); // remove application name (ModOrganizer.exe) - arguments.removeFirst(); // remove binary name - // pass the remaining parameters to the binary - try { - mainWindow.startApplication(exeName, arguments, QString(), selectedProfileName); - } catch (const std::exception &e) { - reportError(QObject::tr("failed to start application: %1").arg(e.what())); - } - - return 0; - } - mainWindow.createFirstProfile(); - if (selectedProfileName.length() != 0) { - if (!mainWindow.setCurrentProfile(selectedProfileName)) { - mainWindow.setCurrentProfile(1); - qWarning("failed to set profile: %s", - selectedProfileName.toUtf8().constData()); - } - } else { - mainWindow.setCurrentProfile(1); - } - qDebug("displaying main window"); mainWindow.show(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4762358e..d4a7f4fc 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -16,7 +16,6 @@ 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 Q_MOC_RUN #include "mainwindow.h" #include "ui_mainwindow.h" @@ -68,7 +67,6 @@ along with Mod Organizer. If not, see . #include #include #include -#endif // Q_MOC_RUN #include #include #include @@ -176,6 +174,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize m_RefreshProgress->setTextVisible(true); m_RefreshProgress->setRange(0, 100); m_RefreshProgress->setValue(0); + m_RefreshProgress->setVisible(false); statusBar()->addWidget(m_RefreshProgress, 1000); statusBar()->clearMessage(); @@ -255,7 +254,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), m_PluginListSortProxy, SLOT(updateFilter(QString))); connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString))); - connect(m_OrganizerCore.pluginList(), SIGNAL(saveTimer()), this, SLOT(savePluginList())); connect(ui->bsaList, SIGNAL(itemsMoved()), this, SLOT(bsaList_itemMoved())); @@ -265,6 +263,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize connect(m_OrganizerCore.directoryRefresher(), SIGNAL(progress(int)), this, SLOT(refresher_progress(int))); connect(m_OrganizerCore.directoryRefresher(), SIGNAL(error(QString)), this, SLOT(showError(QString))); + connect(m_OrganizerCore.downloadManager(), SIGNAL(downloadAdded()), ui->downloadView, SLOT(scrollToBottom())); + connect(&m_SavesWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(refreshSavesIfOpen())); connect(&m_OrganizerCore.settings(), SIGNAL(languageChanged(QString)), this, SLOT(languageChange(QString))); @@ -274,9 +274,9 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize connect(m_OrganizerCore.updater(), SIGNAL(updateAvailable()), this, SLOT(updateAvailable())); connect(m_OrganizerCore.updater(), SIGNAL(motdAvailable(QString)), this, SLOT(motdReceived(QString))); - connect(NexusInterface::instance(), SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString))); + connect(NexusInterface::instance(), SIGNAL(requestNXMDownload(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); connect(NexusInterface::instance(), SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(int,int,QVariant,QVariant,int))); - connect(NexusInterface::instance(), SIGNAL(needLogin()), this, SLOT(nexusLogin())); + connect(NexusInterface::instance(), SIGNAL(needLogin()), &m_OrganizerCore, SLOT(nexusLogin())); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString))); @@ -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,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize installTranslator(QFileInfo(fileName).baseName()); } + ui->profileBox->setCurrentText(m_OrganizerCore.currentProfile()->getName()); + refreshExecutablesList(); updateToolBar(); } @@ -999,18 +1001,12 @@ void MainWindow::setExecutableIndex(int index) void MainWindow::activateSelectedProfile() { - QString profileName = ui->profileBox->currentText(); - qDebug("activate profile \"%s\"", qPrintable(profileName)); - QString profileDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())) - .append("/").append(profileName); - m_OrganizerCore.setCurrentProfile(new Profile(QDir(profileDir))); + m_OrganizerCore.setCurrentProfile(ui->profileBox->currentText()); 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) @@ -1330,7 +1326,7 @@ static bool BySortValue(const std::pair &LHS, const st void MainWindow::updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives) { - + m_DefaultArchives = defaultArchives; ui->bsaList->clear(); #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) ui->bsaList->header()->setSectionResizeMode(QHeaderView::ResizeToContents); @@ -1413,11 +1409,11 @@ void MainWindow::updateBSAList(const QStringList &defaultArchives, const QString subItem->setExpanded(true); } - m_OrganizerCore.checkBSAList(); + checkBSAList(); } -void MainWindow::checkBSAList(const QStringList &defaultArchives) +void MainWindow::checkBSAList() { ui->bsaList->blockSignals(true); @@ -1433,7 +1429,7 @@ void MainWindow::checkBSAList(const QStringList &defaultArchives) item->setToolTip(0, QString()); if (item->checkState(0) == Qt::Unchecked) { - if (defaultArchives.contains(filename)) { + if (m_DefaultArchives.contains(filename)) { item->setIcon(0, QIcon(":/MO/gui/warning")); item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!")); modWarning = true; @@ -1557,8 +1553,34 @@ void MainWindow::storeSettings(QSettings &settings) settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked()); settings.setValue("manage_bsas", ui->manageArchivesBox->isChecked()); + + settings.setValue("selected_executable", ui->executablesListBox->currentIndex()); } +void MainWindow::lock() +{ + m_LockDialog = new LockedDialog(qApp->activeWindow()); + m_LockDialog->show(); + setEnabled(false); +} + +void MainWindow::unlock() +{ + if (m_LockDialog != nullptr) { + m_LockDialog->hide(); + m_LockDialog->deleteLater(); + } + setEnabled(true); +} + +bool MainWindow::unlockClicked() +{ + if (m_LockDialog != nullptr) { + return m_LockDialog->unlockClicked(); + } else { + return false; + } +} void MainWindow::on_btnRefreshData_clicked() { @@ -1581,18 +1603,20 @@ void MainWindow::on_tabWidget_currentChanged(int index) } -void MainWindow::installMod() +void MainWindow::installMod(QString fileName) { try { - QStringList extensions = m_OrganizerCore.installationManager()->getSupportedExtensions(); - for (auto iter = extensions.begin(); iter != extensions.end(); ++iter) { - *iter = "*." + *iter; - } + if (fileName.isEmpty()) { + QStringList extensions = m_OrganizerCore.installationManager()->getSupportedExtensions(); + for (auto iter = extensions.begin(); iter != extensions.end(); ++iter) { + *iter = "*." + *iter; + } - QString fileName = FileDialogMemory::getOpenFileName("installMod", this, tr("Choose Mod"), QString(), - tr("Mod Archive").append(QString(" (%1)").arg(extensions.join(" ")))); + fileName = FileDialogMemory::getOpenFileName("installMod", this, tr("Choose Mod"), QString(), + tr("Mod Archive").append(QString(" (%1)").arg(extensions.join(" ")))); + } - if (fileName.length() == 0) { + if (fileName.isEmpty()) { return; } else { m_OrganizerCore.installMod(fileName); @@ -1692,7 +1716,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; @@ -1805,33 +1829,6 @@ void MainWindow::setESPListSorting(int index) } } - -bool MainWindow::setCurrentProfile(int index) -{ - QComboBox *profilesBox = findChild("profileBox"); - if (index >= profilesBox->count()) { - return false; - } else { - profilesBox->setCurrentIndex(index); - return true; - } -} - -bool MainWindow::setCurrentProfile(const QString &name) -{ - QComboBox *profilesBox = findChild("profileBox"); - for (int i = 0; i < profilesBox->count(); ++i) { - if (QString::compare(profilesBox->itemText(i), name, Qt::CaseInsensitive) == 0) { - profilesBox->setCurrentIndex(i); - return true; - } - } - // profile not valid - profilesBox->setCurrentIndex(1); - return false; -} - - void MainWindow::refresher_progress(int percent) { if (percent == 100) { @@ -1864,7 +1861,7 @@ void MainWindow::modorder_changed() } m_OrganizerCore.refreshBSAList(); m_OrganizerCore.currentProfile()->writeModlist(); - m_OrganizerCore.saveArchiveList(); + saveArchiveList(); m_OrganizerCore.directoryStructure()->getFileRegister()->sortOrigins(); { // refresh selection @@ -1881,11 +1878,11 @@ 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, static_cast(modName)); if (posList.count() == 1) { ui->modList->scrollTo(posList.at(0)); } @@ -2150,7 +2147,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(); } } } @@ -2261,7 +2258,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->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); @@ -2277,7 +2274,7 @@ 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_OrganizerCore.doAfterLogin(boost::bind(&MainWindow::endorseMod, this, mod)); NexusInterface::instance()->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); @@ -2304,7 +2301,7 @@ 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_OrganizerCore.doAfterLogin([&] () { this->unendorse_clicked(); }); NexusInterface::instance()->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); @@ -2312,14 +2309,14 @@ void MainWindow::unendorse_clicked() } } -void MainWindow::loginFailed(const QString &message) +void MainWindow::loginFailed(const QString&) { statusBar()->hide(); } 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) @@ -2356,7 +2353,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, modInfo->saveMeta(); ModInfoDialog dialog(modInfo, m_OrganizerCore.directoryStructure(), modInfo->hasFlag(ModInfo::FLAG_FOREIGN), this); connect(&dialog, SIGNAL(nexusLinkActivated(QString)), this, SLOT(nexusLinkActivated(QString))); - connect(&dialog, SIGNAL(downloadRequest(QString)), this, SLOT(downloadRequestedNXM(QString))); + connect(&dialog, SIGNAL(downloadRequest(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenNext()), this, SLOT(modOpenNext()), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenPrev()), this, SLOT(modOpenPrev()), Qt::QueuedConnection); @@ -2393,6 +2390,16 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, } } +bool MainWindow::closeWindow() +{ + return close(); +} + +void MainWindow::setWindowEnabled(bool enabled) +{ + setEnabled(enabled); +} + void MainWindow::modOpenNext() { @@ -2495,7 +2502,7 @@ void MainWindow::syncOverwrite() if (syncDialog.exec() == QDialog::Accepted) { syncDialog.apply(QDir::fromNativeSeparators(m_OrganizerCore.settings().getModDirectory())); modInfo->testValid(); - refreshDirectoryStructure(); + m_OrganizerCore.refreshDirectoryStructure(); } } @@ -2530,7 +2537,7 @@ void MainWindow::createModFromOverwrite() shellMove(QStringList(QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"), QStringList(QDir::toNativeSeparators(newMod->absolutePath())), this); - refreshModList(); + m_OrganizerCore.refreshModList(); } void MainWindow::cancelModListEditor() @@ -2742,7 +2749,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); @@ -2776,7 +2783,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); @@ -3297,12 +3304,6 @@ void MainWindow::linkMenu() } } -void MainWindow::downloadSpeed(const QString &serverName, int bytesPerSecond) -{ - m_OrganizerCore.settings().setDownloadSpeed(serverName, bytesPerSecond); -} - - void MainWindow::on_actionSettings_triggered() { QString oldModDirectory(m_OrganizerCore.settings().getModDirectory()); @@ -3400,64 +3401,6 @@ void MainWindow::languageChange(const QString &newLanguage) } -void MainWindow::installDownload(int index) -{ - try { - QString fileName = m_OrganizerCore.downloadManager()->getFilePath(index); - int modID = m_OrganizerCore.downloadManager()->getModID(index); - 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); - - 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 @@ -3704,7 +3647,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); @@ -3832,7 +3775,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))); @@ -4312,7 +4255,7 @@ std::string MainWindow::readFromPipe(HANDLE stdOutRead) return result; } -void MainWindow::processLOOTOut(const std::string &lootOut, std::string &reportURL, std::string &errorMessages, QProgressDialog &dialog) +void MainWindow::processLOOTOut(const std::string &lootOut, std::string &errorMessages, QProgressDialog &dialog) { std::vector lines; boost::split(lines, lootOut, boost::is_any_of("\r\n")); @@ -4347,122 +4290,6 @@ 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(); - } - } 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; -} - void MainWindow::on_bossButton_clicked() { std::string reportURL; @@ -4556,14 +4383,14 @@ void MainWindow::on_bossButton_clicked() // keep processing events so the app doesn't appear dead QCoreApplication::processEvents(); std::string lootOut = readFromPipe(stdOutRead); - processLOOTOut(lootOut, reportURL, errorMessages, dialog); + processLOOTOut(lootOut, errorMessages, dialog); res = ::MsgWaitForMultipleObjects(1, &loot, false, 1000, QS_KEY | QS_MOUSE); } std::string remainder = readFromPipe(stdOutRead).c_str(); if (remainder.length() > 0) { - processLOOTOut(remainder, reportURL, errorMessages, dialog); + processLOOTOut(remainder, errorMessages, dialog); } DWORD exitCode = 0UL; ::GetExitCodeProcess(processHandle, &exitCode); @@ -4720,7 +4547,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 c8cb8152..27d75ba8 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -61,6 +61,7 @@ namespace Ui { class MainWindow; } +class LockedDialog; class QToolButton; class ModListSortProxy; class ModListGroupCategoriesProxy; @@ -79,9 +80,13 @@ public: QWidget *parent = 0); ~MainWindow(); - void storeSettings(QSettings &settings); + void storeSettings(QSettings &settings) override; void readSettings(); + virtual void lock() override; + virtual void unlock() override; + virtual bool unlockClicked() override; + bool addProfile(); void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives); void refreshDataTree(); @@ -92,9 +97,6 @@ public: void setModListSorting(int index); void setESPListSorting(int index); - bool setCurrentProfile(int index); - bool setCurrentProfile(const QString &name); - void createFirstProfile(); bool saveArchiveList(); @@ -106,11 +108,7 @@ public: void createStdoutPipe(HANDLE *stdOutRead, HANDLE *stdOutWrite); 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 processLOOTOut(const std::string &lootOut, std::string &errorMessages, QProgressDialog &dialog); void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo); @@ -120,11 +118,11 @@ public: virtual void disconnectPlugins(); - virtual bool close(); - virtual void setEnabled(bool enabled); - void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab); + virtual bool closeWindow(); + virtual void setWindowEnabled(bool enabled); + public slots: void displayColumnSelection(const QPoint &pos); @@ -161,8 +159,6 @@ protected: private: - void refreshModList(bool saveChanges = true); - void actionToToolButton(QAction *&sourceAction); void updateToolBar(); @@ -172,13 +168,10 @@ 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(); + void installMod(QString fileName = ""); QList findFileInfos(const QString &path, const std::function &filter) const; @@ -275,6 +268,8 @@ private: QProgressBar *m_RefreshProgress; bool m_Refreshing; + QStringList m_DefaultArchives; + QAbstractItemModel *m_ModListGroupingProxy; ModListSortProxy *m_ModListSortProxy; @@ -322,6 +317,8 @@ private: bool m_DidUpdateMasterList; + LockedDialog *m_LockDialog { nullptr }; + private slots: void showMessage(const QString &message); @@ -363,7 +360,6 @@ private slots: void linkMenu(); void languageChange(const QString &newLanguage); - void modStatusChanged(unsigned int index); void saveSelectionChanged(QListWidgetItem *newItem); void windowTutorialFinished(const QString &windowName); @@ -385,7 +381,6 @@ private slots: void linkClicked(const QString &url); - void installDownload(int index); void updateAvailable(); void motdReceived(const QString &motd); @@ -402,7 +397,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); @@ -436,7 +431,7 @@ private slots: void startExeAction(); - void checkBSAList(const QStringList &defaultArchives); + void checkBSAList(); void updateProblemsButton(); @@ -464,8 +459,6 @@ private slots: */ void allowListResize(); - void downloadSpeed(const QString &serverName, int bytesPerSecond); - void toolBar_customContextMenuRequested(const QPoint &point); void removeFromToolbar(); void overwriteClosed(int); @@ -479,7 +472,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.cpp b/src/modinfo.cpp index 189e67b2..d773823a 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -476,6 +476,7 @@ ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStruc , m_Path(path.absolutePath()) , m_MetaInfoChanged(false) , m_EndorsedState(ENDORSED_UNKNOWN) + , m_NexusBridge() { testValid(); m_CreationTime = QFileInfo(path.absolutePath()).created(); diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index b4006097..d68fe8fe 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -34,35 +34,34 @@ using namespace MOShared; NexusBridge::NexusBridge(const QString &subModule) : m_Interface(NexusInterface::instance()) - , m_Url(MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())) + , m_Url() // lazy initialized , m_SubModule(subModule) { } - void NexusBridge::requestDescription(int modID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestDescription(modID, this, userData, m_SubModule, m_Url)); + m_RequestIDs.insert(m_Interface->requestDescription(modID, this, userData, m_SubModule, url())); } void NexusBridge::requestFiles(int modID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestFiles(modID, this, userData, m_SubModule, m_Url)); + m_RequestIDs.insert(m_Interface->requestFiles(modID, this, userData, m_SubModule, url())); } void NexusBridge::requestFileInfo(int modID, int fileID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestFileInfo(modID, fileID, this, userData, m_SubModule, m_Url)); + m_RequestIDs.insert(m_Interface->requestFileInfo(modID, fileID, this, userData, m_SubModule, url())); } void NexusBridge::requestDownloadURL(int modID, int fileID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestDownloadURL(modID, fileID, this, userData, m_SubModule, m_Url)); + m_RequestIDs.insert(m_Interface->requestDownloadURL(modID, fileID, this, userData, m_SubModule, url())); } void NexusBridge::requestToggleEndorsement(int modID, bool endorse, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestToggleEndorsement(modID, endorse, this, userData, m_SubModule, m_Url)); + m_RequestIDs.insert(m_Interface->requestToggleEndorsement(modID, endorse, this, userData, m_SubModule, url())); } void NexusBridge::nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID) @@ -138,6 +137,13 @@ void NexusBridge::nxmRequestFailed(int modID, int fileID, QVariant userData, int } } +QString NexusBridge::url() { + if (m_Url.isEmpty()) { + m_Url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()); + } + return m_Url; +} + QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0); diff --git a/src/nexusinterface.h b/src/nexusinterface.h index af2f8c75..28accd3d 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -107,6 +107,10 @@ public slots: void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID); void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage); +private: + + QString url(); + private: NexusInterface *m_Interface; diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 18b47707..18568dad 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -22,6 +22,7 @@ along with Mod Organizer. If not, see . #include "report.h" #include "utility.h" #include "selfupdater.h" +#include "settings.h" #include "persistentcookiejar.h" #include #include @@ -48,8 +49,9 @@ 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)); + QDir::fromNativeSeparators(Settings::instance().getCacheDirectory() + "/nexus_cookies.dat"))); } NXMAccessManager::~NXMAccessManager() diff --git a/src/organizer.pro b/src/organizer.pro index 98dbcd4a..a16e396d 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -284,7 +284,8 @@ CONFIG(debug, debug|release) { #QMAKE_CXXFLAGS_WARN_ON -= -W3 #QMAKE_CXXFLAGS_WARN_ON += -W4 -QMAKE_CXXFLAGS += /wd4100 -wd4127 -wd4512 -wd4189 +QMAKE_CXXFLAGS -= -w34100 -w34189 +QMAKE_CXXFLAGS += -wd4100 -wd4127 -wd4512 -wd4189 CONFIG += embed_manifest_exe @@ -346,7 +347,7 @@ SRCDIR ~= s,/,$$QMAKE_DIR_SEP,g DSTDIR ~= s,/,$$QMAKE_DIR_SEP,g QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.exe) $$quote($$DSTDIR) $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.exe) $$quote($$DSTDIR) $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.pdb) $$quote($$DSTDIR) $$escape_expand(\\n) QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\stylesheets) $$quote($$DSTDIR)\\stylesheets $$escape_expand(\\n) QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\tutorials) $$quote($$DSTDIR)\\tutorials $$escape_expand(\\n) QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\*.qm) $$quote($$DSTDIR)\\translations $$escape_expand(\\n) diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 68989c8c..751cc010 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -141,7 +141,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() @@ -170,12 +170,11 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, SLOT(directory_refreshed())); connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, SLOT(removeOrigin(QString))); + connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList())); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginSuccessful(bool)), this, SLOT(loginSuccessful(bool))); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); - ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign()); - // make directory refresher run in a separate thread m_RefresherThread.start(); m_DirectoryRefresher.moveToThread(&m_RefresherThread); @@ -214,7 +213,9 @@ void OrganizerCore::storeSettings() if (m_UserInterface != nullptr) { m_UserInterface->storeSettings(settings); } - settings.setValue("selected_profile", m_CurrentProfile->getName().toUtf8().constData()); + if (m_CurrentProfile != nullptr) { + settings.setValue("selected_profile", m_CurrentProfile->getName().toUtf8().constData()); + } settings.setValue("ask_for_nexuspw", m_AskForNexusPW); settings.remove("customExecutables"); @@ -238,9 +239,6 @@ void OrganizerCore::storeSettings() } settings.endArray(); - QComboBox *executableBox = findChild("executablesListBox"); - settings.setValue("selected_executable", executableBox->currentIndex()); - FileDialogMemory::save(settings); settings.sync(); @@ -287,6 +285,10 @@ void OrganizerCore::updateExecutablesList(QSettings &settings) } settings.endArray(); + + + // TODO this has nothing to do with executables list move to an appropriate function! + ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign()); } void OrganizerCore::setUserInterface(IUserInterface *userInterface, QWidget *widget) @@ -295,16 +297,17 @@ void OrganizerCore::setUserInterface(IUserInterface *userInterface, QWidget *wid m_UserInterface = userInterface; - connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex,int)), widget, SLOT(modorder_changed())); - connect(&m_ModList, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); - connect(&m_ModList, SIGNAL(modRenamed(QString,QString)), widget, SLOT(modRenamed(QString,QString))); - connect(&m_ModList, SIGNAL(modUninstalled(QString)), widget, SLOT(modRemoved(QString))); - connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), widget, SLOT(modlistChanged(QModelIndex, int))); - connect(&m_ModList, SIGNAL(removeSelectedMods()), widget, SLOT(removeMod_clicked())); - connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget, SLOT(displayColumnSelection(QPoint))); - connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget, SLOT(fileMoved(QString, QString, QString))); - connect(&m_DownloadManager, SIGNAL(downloadAdded()), widget, SLOT(scrollToBottom())); - connect(&m_DownloadManager, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); + if (widget != nullptr) { + connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex,int)), widget, SLOT(modorder_changed())); + connect(&m_ModList, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); + connect(&m_ModList, SIGNAL(modRenamed(QString,QString)), widget, SLOT(modRenamed(QString,QString))); + connect(&m_ModList, SIGNAL(modUninstalled(QString)), widget, SLOT(modRemoved(QString))); + connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), widget, SLOT(modlistChanged(QModelIndex, int))); + connect(&m_ModList, SIGNAL(removeSelectedMods()), widget, SLOT(removeMod_clicked())); + connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget, SLOT(displayColumnSelection(QPoint))); + connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget, SLOT(fileMoved(QString, QString, QString))); + connect(&m_DownloadManager, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); + } m_InstallationManager.setParentWidget(widget); m_Updater.setUserInterface(widget); @@ -423,15 +426,29 @@ void OrganizerCore::removeOrigin(const QString &name) refreshLists(); } +void OrganizerCore::downloadSpeed(const QString &serverName, int bytesPerSecond) +{ + m_Settings.setDownloadSpeed(serverName, bytesPerSecond); +} + InstallationManager *OrganizerCore::installationManager() { return &m_InstallationManager; } -void OrganizerCore::setCurrentProfile(Profile *profile) { +void OrganizerCore::setCurrentProfile(const QString &profileName) +{ + QString profileDir = qApp->property("dataPath").toString() + "/" + ToQString(AppConfig::profilesPath()) + "/" + profileName; + + Profile *newProfile = new Profile(QDir(profileDir)); + delete m_CurrentProfile; - m_CurrentProfile = profile; - m_ModList.setProfile(profile); + m_CurrentProfile = newProfile; + m_ModList.setProfile(newProfile); + + connect(m_CurrentProfile, SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint))); + + refreshDirectoryStructure(); } MOBase::IGameInfo &OrganizerCore::gameInfo() const @@ -512,7 +529,7 @@ bool OrganizerCore::removeMod(MOBase::IModInterface *mod) } } -void OrganizerCore::modDataChanged(MOBase::IModInterface *mod) +void OrganizerCore::modDataChanged(MOBase::IModInterface*) { refreshModList(false); } @@ -579,6 +596,63 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName) return nullptr; } +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) { @@ -687,10 +761,10 @@ 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(); + m_UserInterface->closeWindow(); } else { if (m_UserInterface != nullptr) { - m_UserInterface->setEnabled(false); + m_UserInterface->setWindowEnabled(false); } // re-enable the locked dialog because what'd be the point otherwise? dialog->setEnabled(true); @@ -738,7 +812,7 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &argument ::CloseHandle(processHandle); if (m_UserInterface != nullptr) { - m_UserInterface->setEnabled(true); + m_UserInterface->setWindowEnabled(true); } refreshDirectoryStructure(); // need to remove our stored load order because it may be outdated if a foreign tool changed the @@ -807,16 +881,118 @@ 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 != nullptr) { + 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); + } + + std::vector::iterator current, end; + m_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(); + } + } 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 +bool OrganizerCore::waitForProcessOrJob(HANDLE handle, LPDWORD exitCode) { if (m_UserInterface != nullptr) { - return m_UserInterface->waitForProcessOrJob(handle, exitCode); + m_UserInterface->lock(); + ON_BLOCK_EXIT([&] () { m_UserInterface->unlock(); }); + } + + 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) + && ((m_UserInterface == nullptr) || !m_UserInterface->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 OrganizerCore::onAboutToRun(const std::function &func) @@ -905,7 +1081,7 @@ void OrganizerCore::refreshBSAList() } if (m_UserInterface != nullptr) { - m_UserInterface->updateBSAList(); + m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives); } m_ArchivesInit = true; @@ -922,7 +1098,6 @@ void OrganizerCore::refreshLists() void OrganizerCore::updateModActiveState(int index, bool active) { ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - QDir dir(modInfo->absolutePath()); foreach (const QString &esm, dir.entryList(QStringList("*.esm"), QDir::Files)) { m_PluginList.enableESP(esm, active); @@ -977,7 +1152,7 @@ void OrganizerCore::updateModInDirectoryStructure(unsigned int index, ModInfo::P void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply) { if (m_PluginContainer != nullptr) { - for (IPluginModPage *modPage : m_PluginContainer->plugins()) { + for (IPluginModPage *modPage : m_PluginContainer->plugins()) { ModRepositoryFileInfo *fileInfo = new ModRepositoryFileInfo(); if (modPage->handlesDownload(url, reply->url(), *fileInfo)) { fileInfo->repository = modPage->name(); @@ -1167,7 +1342,7 @@ void OrganizerCore::loginFailedUpdate(const QString &message) 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 b362a002..17b1d2dc 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -81,7 +81,7 @@ public: void setExecutablesDialog(const ExecutablesList &executablesList) { m_ExecutablesList = executablesList; } Profile *currentProfile() { return m_CurrentProfile; } - void setCurrentProfile(Profile *profile); + void setCurrentProfile(const QString &profileName); void setExecutablesList(const ExecutablesList &executablesList); @@ -95,7 +95,6 @@ public: bool isArchivesInit() const { return m_ArchivesInit; } bool saveCurrentLists(); - void savePluginList(); void prepareStart(); @@ -105,50 +104,45 @@ public: void refreshDirectoryStructure(); void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo); - void requestDownload(const QUrl &url, QNetworkReply *reply); - - void doAfterLogin(std::function &function) { m_PostLoginTasks.append(function); } + void doAfterLogin(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 = ""); + HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID); - void modStatusChanged(unsigned int index); - - void loginSuccessful(bool necessary); void loginSuccessfulUpdate(bool necessary); - void loginFailed(const QString &message); void loginFailedUpdate(const QString &message); public: - virtual MOBase::IGameInfo &gameInfo() const; - virtual MOBase::IModRepositoryBridge *createNexusBridge() const; - virtual QString profileName() const; - virtual QString profilePath() const; - virtual QString downloadsPath() const; - virtual MOBase::VersionInfo appVersion() const; - virtual MOBase::IModInterface *getMod(const QString &name); - virtual MOBase::IModInterface *createMod(MOBase::GuessedValue &name); - virtual bool removeMod(MOBase::IModInterface *mod); - virtual void modDataChanged(MOBase::IModInterface *mod); - virtual QVariant pluginSetting(const QString &pluginName, const QString &key) const; - virtual void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value); - virtual QVariant persistent(const QString &pluginName, const QString &key, const QVariant &def) const; - virtual void setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync); - virtual QString pluginDataPath() const; - virtual MOBase::IModInterface *installMod(const QString &fileName); - virtual QString resolvePath(const QString &fileName) const; - virtual QStringList listDirectories(const QString &directoryName) const; - virtual QStringList findFiles(const QString &path, const std::function &filter) const; - virtual QStringList getFileOrigins(const QString &fileName) const; - virtual QList findFileInfos(const QString &path, const std::function &filter) const; - virtual DownloadManager *downloadManager(); - virtual PluginList *pluginList(); - virtual ModList *modList(); - virtual HANDLE startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile); - virtual bool waitForApplication(HANDLE handle, LPDWORD exitCode) const; - virtual bool onModInstalled(const std::function &func); - virtual bool onAboutToRun(const std::function &func); - virtual bool onFinishedRun(const std::function &func); - virtual void refreshModList(bool saveChanges = true); + MOBase::IGameInfo &gameInfo() const; + MOBase::IModRepositoryBridge *createNexusBridge() const; + QString profileName() const; + QString profilePath() const; + QString downloadsPath() const; + MOBase::VersionInfo appVersion() const; + MOBase::IModInterface *getMod(const QString &name); + MOBase::IModInterface *createMod(MOBase::GuessedValue &name); + bool removeMod(MOBase::IModInterface *mod); + void modDataChanged(MOBase::IModInterface *mod); + QVariant pluginSetting(const QString &pluginName, const QString &key) const; + void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value); + 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; + MOBase::IModInterface *installMod(const QString &fileName); + QString resolvePath(const QString &fileName) const; + QStringList listDirectories(const QString &directoryName) const; + QStringList findFiles(const QString &path, const std::function &filter) const; + QStringList getFileOrigins(const QString &fileName) const; + QList findFileInfos(const QString &path, const std::function &filter) const; + DownloadManager *downloadManager(); + PluginList *pluginList(); + ModList *modList(); + HANDLE startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile); + bool waitForProcessOrJob(HANDLE processHandle, LPDWORD exitCode = nullptr); + bool onModInstalled(const std::function &func); + bool onAboutToRun(const std::function &func); + bool onFinishedRun(const std::function &func); + void refreshModList(bool saveChanges = true); public: // IPluginDiagnose interface @@ -163,15 +157,25 @@ public slots: void profileRefresh(); void externalMessage(const QString &message); + void savePluginList(); + void refreshLists(); + void installDownload(int downloadIndex); + + void modStatusChanged(unsigned int index); + void requestDownload(const QUrl &url, QNetworkReply *reply); + void downloadRequestedNXM(const QString &url); + + bool nexusLogin(); + 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: @@ -179,17 +183,16 @@ private: bool queryLogin(QString &username, QString &password); - 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); private slots: void directory_refreshed(); - void downloadRequestedNXM(const QString &url); void downloadRequested(QNetworkReply *reply, int modID, const QString &fileName); void removeOrigin(const QString &name); + void downloadSpeed(const QString &serverName, int bytesPerSecond); + void loginSuccessful(bool necessary); + void loginFailed(const QString &message); private: diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 07a006f5..61470a1a 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -95,7 +95,7 @@ HANDLE OrganizerProxy::startApplication(const QString &executable, const QString bool OrganizerProxy::waitForApplication(HANDLE handle, LPDWORD exitCode) const { - return m_Proxied->waitForApplication(handle, exitCode); + return m_Proxied->waitForProcessOrJob(handle, exitCode); } bool OrganizerProxy::onAboutToRun(const std::function &func) diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index bd96828e..92073c12 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -1,11 +1,9 @@ #include "plugincontainer.h" #include "organizerproxy.h" #include "report.h" -#include #include #include #include -#include #include #include #include @@ -24,6 +22,7 @@ namespace bf = boost::fusion; PluginContainer::PluginContainer(OrganizerCore *organizer) : m_Organizer(organizer) + , m_UserInterface(nullptr) { } @@ -33,6 +32,17 @@ void PluginContainer::setUserInterface(IUserInterface *userInterface, QWidget *w for (IPluginProxy *proxy : bf::at_key(m_Plugins)) { proxy->setParentWidget(widget); } + + if (userInterface != nullptr) { + for (IPluginModPage *modPage : bf::at_key(m_Plugins)) { + userInterface->registerModPage(modPage); + } + + for (IPluginTool *tool : bf::at_key(m_Plugins)) { + userInterface->registerPluginTool(tool); + } + } + m_UserInterface = userInterface; } @@ -69,7 +79,7 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName) { { // generic treatment for all plugins IPlugin *pluginObj = qobject_cast(plugin); - if (pluginObj == NULL) { + if (pluginObj == nullptr) { qDebug("not an IPlugin"); return false; } @@ -90,7 +100,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 +115,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; } } @@ -240,7 +248,7 @@ void PluginContainer::loadPlugins() loadCheck.open(QIODevice::WriteOnly); - QString pluginPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())) + "/" + ToQString(AppConfig::pluginPath()); + QString pluginPath = qApp->applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()); qDebug("looking for plugins in %s", QDir::toNativeSeparators(pluginPath).toUtf8().constData()); QDirIterator iter(pluginPath, QDir::Files | QDir::NoDotAndDotDot); diff --git a/src/plugincontainer.h b/src/plugincontainer.h index 213b4154..d0a101ad 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,7 +53,7 @@ public: MOBase::IPluginGame *managedGame(const QString &name) const; template - std::vector plugins() const { + std::vector plugins() { return boost::fusion::at_key(m_Plugins); } @@ -60,26 +76,10 @@ signals: private: bool verifyPlugin(MOBase::IPlugin *plugin); - void registerPluginTool(MOBase::IPluginTool *tool); - void registerModPage(MOBase::IPluginModPage *modPage); void registerGame(MOBase::IPluginGame *game); 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: @@ -99,4 +99,5 @@ private: QFile m_PluginsCheck; }; + #endif // PLUGINCONTAINER_H diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index b2d02cd2..2b1fcca1 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -166,7 +166,7 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD originName = modInfo->name(); } - m_ESPs.push_back(ESPInfo(filename, forceEnabled, current->getFileTime(), originName, ToQString(current->getFullPath()), hasIni)); + m_ESPs.push_back(ESPInfo(filename, forceEnabled, originName, ToQString(current->getFullPath()), hasIni)); } catch (const std::exception &e) { reportError(tr("failed to update esp info for file %1 (source id: %2), error: %3").arg(filename).arg(current->getOrigin(archive)).arg(e.what())); } @@ -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, const QString &originName, const QString &fullPath, bool hasIni) : m_Name(name), m_FullPath(fullPath), m_Enabled(enabled), m_ForceEnabled(enabled), diff --git a/src/pluginlist.h b/src/pluginlist.h index 729c6f8b..933a38ee 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -272,7 +272,7 @@ private: struct ESPInfo { - ESPInfo(const QString &name, bool enabled, FILETIME time, const QString &originName, const QString &fullPath, bool hasIni); + ESPInfo(const QString &name, bool enabled, const QString &originName, const QString &fullPath, bool hasIni); QString m_Name; QString m_FullPath; bool m_Enabled; diff --git a/src/profile.cpp b/src/profile.cpp index 6e9d8f0f..37ed94cc 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -59,7 +59,8 @@ Profile::Profile(const QString &name, bool useDefaultSettings) : m_SaveTimer(NULL) { initTimer(); - QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())); + QString profilesDir = qApp->property("datapath").toString() + "/" + ToQString(AppConfig::profilesPath()); +qDebug("pd %s", qPrintable(profilesDir)); QDir profileBase(profilesDir); QString fixedName = name; @@ -78,6 +79,7 @@ Profile::Profile(const QString &name, bool useDefaultSettings) touchFile("modlist.txt"); touchFile("archives.txt"); +qDebug("fp %s", qPrintable(fullPath)); GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings); } catch (...) { // clean up in case of an error @@ -88,8 +90,8 @@ Profile::Profile(const QString &name, bool useDefaultSettings) } -Profile::Profile(const QDir& directory) - : m_Directory(directory), m_SaveTimer(NULL) +Profile::Profile(const QDir &directory) + : m_Directory(directory), m_SaveTimer(nullptr) { initTimer(); if (!QFile::exists(m_Directory.filePath("modlist.txt"))) { diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 803b2cfa..0f75e392 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -56,8 +56,12 @@ template T resolveFunction(QLibrary &lib, const char *name) SelfUpdater::SelfUpdater(NexusInterface *nexusInterface) - : m_Parent(nullptr), m_Interface(nexusInterface), m_UpdateRequestID(-1), - m_Reply(NULL), m_Progress(nullptr), m_Attempts(3) + : m_Parent(nullptr) + , m_Interface(nexusInterface) + , m_UpdateRequestID(-1) + , m_Reply(nullptr) + , m_Progress(nullptr) + , m_Attempts(3) { m_Progress.setMaximum(100); @@ -90,7 +94,7 @@ SelfUpdater::~SelfUpdater() void SelfUpdater::setUserInterface(QWidget *widget) { - m_Progress.setParent(widget); + m_Progress.setVisible(false); m_Parent = widget; } @@ -130,6 +134,16 @@ void SelfUpdater::startUpdate() } +void SelfUpdater::showProgress() +{ + m_Progress.setModal(true); + m_Progress.setParent(m_Parent, Qt::Dialog); + m_Progress.show(); + m_Progress.setValue(0); + m_Progress.setWindowTitle(tr("Update")); + m_Progress.setLabelText(tr("Download in progress")); +} + void SelfUpdater::download(const QString &downloadLink, const QString &fileName) { QNetworkAccessManager *accessManager = m_Interface->getAccessManager(); @@ -139,11 +153,7 @@ void SelfUpdater::download(const QString &downloadLink, const QString &fileName) m_Reply = accessManager->get(request); m_UpdateFile.setFileName(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory()).append("\\").append(fileName))); m_UpdateFile.open(QIODevice::WriteOnly); - m_Progress.setModal(true); - m_Progress.show(); - m_Progress.setValue(0); - m_Progress.setWindowTitle(tr("Update")); - m_Progress.setLabelText(tr("Download in progress")); + showProgress(); connect(m_Reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64))); connect(m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished())); diff --git a/src/selfupdater.h b/src/selfupdater.h index 3490b58a..c376234a 100644 --- a/src/selfupdater.h +++ b/src/selfupdater.h @@ -119,6 +119,7 @@ private: void updateProgressFile(LPCWSTR fileName); void report7ZipError(LPCWSTR errorMessage); QString retrieveNews(const QString &description); + void showProgress(); private slots: diff --git a/src/settings.cpp b/src/settings.cpp index 274e5979..418c5661 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -57,13 +57,13 @@ private: 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::s_Instance = nullptr; -Settings::Settings() - : m_Settings(ToQString(GameInfo::instance().getIniFilename()), QSettings::IniFormat) +Settings::Settings(const QSettings &config) + : m_Settings(config.fileName(), config.format()) { - if (s_Instance != NULL) { + if (s_Instance != nullptr) { throw std::runtime_error("second instance of \"Settings\" created"); } else { s_Instance = this; @@ -73,13 +73,13 @@ Settings::Settings() Settings::~Settings() { - s_Instance = NULL; + s_Instance = nullptr; } Settings &Settings::instance() { - if (s_Instance == NULL) { + if (s_Instance == nullptr) { throw std::runtime_error("no instance of \"Settings\""); } return *s_Instance; @@ -110,9 +110,9 @@ void Settings::registerAsNXMHandler(bool force) 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); + HINSTANCE res = ::ShellExecuteW(nullptr, L"open", nxmPath.c_str(), parameters.c_str(), nullptr, SW_SHOWNORMAL); if ((int)res <= 32) { - QMessageBox::critical(NULL, tr("Failed"), + QMessageBox::critical(nullptr, tr("Failed"), tr("Sorry, failed to start the helper application")); } } @@ -181,10 +181,9 @@ QString Settings::getSteamAppID() const QString Settings::getDownloadDirectory() const { - return QDir::toNativeSeparators(m_Settings.value("Settings/download_directory", ToQString(GameInfo::instance().getDownloadDir())).toString()); + return QDir::toNativeSeparators(m_Settings.value("Settings/download_directory", qApp->applicationDirPath() + "/downloads").toString()); } - void Settings::setDownloadSpeed(const QString &serverName, int bytesPerSecond) { m_Settings.beginGroup("Servers"); @@ -221,12 +220,14 @@ std::map Settings::getPreferredServers() QString Settings::getCacheDirectory() const { - return QDir::toNativeSeparators(m_Settings.value("Settings/cache_directory", ToQString(GameInfo::instance().getCacheDir())).toString()); + return QDir::toNativeSeparators(m_Settings.value("Settings/cache_directory", + qApp->applicationDirPath() + "/webcache").toString()); } QString Settings::getModDirectory() const { - return QDir::toNativeSeparators(m_Settings.value("Settings/mod_directory", ToQString(GameInfo::instance().getModsDir())).toString()); + return QDir::toNativeSeparators(m_Settings.value("Settings/mod_directory", + qApp->applicationDirPath() + "/mods").toString()); } QString Settings::getNMMVersion() const @@ -647,7 +648,7 @@ void Settings::query(QWidget *parent) { // advanced settings if ((QDir::fromNativeSeparators(modDirEdit->text()) != QDir::fromNativeSeparators(getModDirectory())) && - (QMessageBox::question(NULL, tr("Confirm"), tr("Changing the mod directory affects all your profiles! " + (QMessageBox::question(nullptr, 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)) { diff --git a/src/settings.h b/src/settings.h index 5d398f49..cba8392e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -43,7 +43,7 @@ public: /** * @brief constructor **/ - Settings(); + Settings(const QSettings &config); virtual ~Settings(); diff --git a/src/shared/appconfig.inc b/src/shared/appconfig.inc index 080d3655..5d9d9f8e 100644 --- a/src/shared/appconfig.inc +++ b/src/shared/appconfig.inc @@ -1,5 +1,6 @@ APPPARAM(std::wstring, translationPrefix, L"organizer") APPPARAM(std::wstring, pluginPath, L"plugins") +APPPARAM(std::wstring, profilesPath, L"profiles") APPPARAM(std::wstring, stylesheetsPath, L"stylesheets") APPPARAM(std::wstring, profileTweakIni, L"profile_tweaks.ini") APPPARAM(std::wstring, logFile, L"ModOrganizer.log") diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index 5439efff..1a815701 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -27,12 +27,12 @@ along with Mod Organizer. If not, see . #include "skyriminfo.h" #include "util.h" +#include +#include #include #include #include -#include -#include namespace MOShared { @@ -49,7 +49,7 @@ GameInfo::GameInfo(const std::wstring &moDirectory, const std::wstring &moDataDi void GameInfo::cleanup() { delete GameInfo::s_Instance; - GameInfo::s_Instance = NULL; + GameInfo::s_Instance = nullptr; } @@ -126,7 +126,7 @@ bool GameInfo::init(const std::wstring &moDirectory, const std::wstring &moDataD GameInfo &GameInfo::instance() { - assert(s_Instance != NULL); + assert(s_Instance != nullptr && "gameinfo not yet initialized"); return *s_Instance; } @@ -151,18 +151,6 @@ std::wstring GameInfo::getIniFilename() const } -std::wstring GameInfo::getDownloadDir() const -{ - return m_OrganizerDirectory + L"\\downloads"; -} - - -std::wstring GameInfo::getCacheDir() const -{ - return m_OrganizerDirectory + L"\\webcache"; -} - - std::wstring GameInfo::getOverwriteDir() const { return m_OrganizerDirectory + L"\\overwrite"; diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index c11679f3..a9da0b97 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -109,8 +109,6 @@ public: virtual std::wstring getProfilesDir() const; virtual std::wstring getIniFilename() const; - virtual std::wstring getDownloadDir() const; - virtual std::wstring getCacheDir() const; virtual std::wstring getOverwriteDir() const; virtual std::wstring getLogDir() const; virtual std::wstring getLootDir() const; diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 1203e3ed..7f70c097 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -259,7 +259,7 @@ void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults) } 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)); + throw windows_error(std::string("failed to copy ini file: ") + ToString(source, false) + " to " + ToString(target, false)); } } } -- cgit v1.3.1 From 99de80e7224f2491fb7518e32f195ad6a912b624 Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 28 Nov 2014 11:19:20 +0100 Subject: replaced all uses of NULL with nullptr fixed a few placed where NULL was used as a number or boolean --- src/ModOrganizer.pro | 1 - src/activatemodsdialog.cpp | 4 +- src/bbcode.h | 2 +- src/browserdialog.cpp | 16 ++-- src/categories.cpp | 6 +- src/directoryrefresher.cpp | 8 +- src/downloadlistwidget.cpp | 8 +- src/downloadlistwidgetcompact.cpp | 12 +-- src/downloadmanager.cpp | 32 ++++---- src/downloadmanager.h | 2 +- src/dummybsa.cpp | 4 +- src/editexecutablesdialog.cpp | 10 +-- src/executableslist.cpp | 6 +- src/genericicondelegate.h | 2 +- src/helper.cpp | 2 +- src/installationmanager.cpp | 28 +++---- src/installationmanager.h | 4 +- src/json.cpp | 2 +- src/loadmechanism.cpp | 8 +- src/lockeddialog.cpp | 4 +- src/logbuffer.cpp | 2 +- src/main.cpp | 28 +++---- src/mainwindow.cpp | 160 +++++++++++++++++++------------------- src/messagedialog.cpp | 6 +- src/modinfo.cpp | 2 +- src/modinfodialog.cpp | 18 ++--- src/modlist.cpp | 38 ++++----- src/modlist.h | 2 +- src/modlistsortproxy.cpp | 6 +- src/motddialog.cpp | 2 +- src/nexusinterface.cpp | 22 +++--- src/noeditdelegate.cpp | 2 +- src/noeditdelegate.h | 2 +- src/nxmaccessmanager.cpp | 18 ++--- src/organizercore.cpp | 22 +++--- src/organizerproxy.h | 2 +- src/overwriteinfodialog.cpp | 6 +- src/pdll.h | 110 +++++++++++++------------- src/plugincontainer.cpp | 8 +- src/pluginlist.cpp | 22 +++--- src/pluginlist.h | 2 +- src/pluginlistsortproxy.cpp | 4 +- src/problemsdialog.cpp | 2 +- src/profile.cpp | 14 ++-- src/profilesdialog.cpp | 8 +- src/report.cpp | 2 +- src/savegameinfowidget.cpp | 6 +- src/selectiondialog.cpp | 4 +- src/selfupdater.cpp | 11 ++- src/settingsdialog.cpp | 2 +- src/shared/directoryentry.cpp | 56 ++++++------- src/shared/directoryentry.h | 10 +-- src/shared/error_report.cpp | 12 +-- src/shared/fallout3info.cpp | 12 +-- src/shared/falloutnvinfo.cpp | 12 +-- src/shared/gameinfo.cpp | 14 ++-- src/shared/inject.cpp | 16 ++-- src/shared/leaktrace.cpp | 2 +- src/shared/oblivioninfo.cpp | 14 ++-- src/shared/skyriminfo.cpp | 20 ++--- src/shared/util.cpp | 16 ++-- src/shared/windows_error.cpp | 4 +- src/spawn.cpp | 18 ++--- src/syncoverwritedialog.cpp | 10 +-- src/viewmarkingscrollbar.cpp | 2 +- 65 files changed, 455 insertions(+), 457 deletions(-) (limited to 'src/profile.cpp') diff --git a/src/ModOrganizer.pro b/src/ModOrganizer.pro index a8f7f53e..98f6f730 100644 --- a/src/ModOrganizer.pro +++ b/src/ModOrganizer.pro @@ -12,7 +12,6 @@ SUBDIRS = bsatk \ nxmhandler \ BossDummy \ pythonRunner \ - loot_cli \ esptk pythonRunner.depends = uibase diff --git a/src/activatemodsdialog.cpp b/src/activatemodsdialog.cpp index be6eef54..fd56ae3e 100644 --- a/src/activatemodsdialog.cpp +++ b/src/activatemodsdialog.cpp @@ -72,7 +72,7 @@ std::set ActivateModsDialog::getModsToActivate() for (int row = 0; row < modsTable->rowCount(); ++row) { QComboBox *comboBox = dynamic_cast(modsTable->cellWidget(row, 1)); - if (comboBox != NULL) { + if (comboBox != nullptr) { result.insert(comboBox->currentText()); } } @@ -87,7 +87,7 @@ std::set ActivateModsDialog::getESPsToActivate() for (int row = 0; row < modsTable->rowCount(); ++row) { QComboBox *comboBox = dynamic_cast(modsTable->cellWidget(row, 1)); - if (comboBox != NULL) { + if (comboBox != nullptr) { QLabel *espName = dynamic_cast(modsTable->cellWidget(row, 0)); result.insert(espName->text()); diff --git a/src/bbcode.h b/src/bbcode.h index f88e9a6c..708dfe71 100644 --- a/src/bbcode.h +++ b/src/bbcode.h @@ -29,7 +29,7 @@ namespace BBCode { /** * @brief convert a string with BB Code-Tags to HTML * @param input the input string with BB tags - * @param replaceOccured if not NULL, this parameter will be set to true if any bb tags were replaced + * @param replaceOccured if not nullptr, this parameter will be set to true if any bb tags were replaced * @return the same string in html representation **/ QString convertToHTML(const QString &input); diff --git a/src/browserdialog.cpp b/src/browserdialog.cpp index 82cd8d49..1401b941 100644 --- a/src/browserdialog.cpp +++ b/src/browserdialog.cpp @@ -115,7 +115,7 @@ BrowserView *BrowserDialog::getCurrentView() void BrowserDialog::urlChanged(const QUrl &url) { BrowserView *currentView = getCurrentView(); - if (currentView != NULL) { + if (currentView != nullptr) { ui->backBtn->setEnabled(currentView->history()->canGoBack()); ui->fwdBtn->setEnabled(currentView->history()->canGoForward()); } @@ -192,12 +192,12 @@ void BrowserDialog::unsupportedContent(QNetworkReply *reply) { try { QWebPage *page = qobject_cast(sender()); - if (page == NULL) { + if (page == nullptr) { qCritical("sender not a page"); return; } BrowserView *view = qobject_cast(page->view()); - if (view == NULL) { + if (view == nullptr) { qCritical("no view?"); return; } @@ -231,7 +231,7 @@ void BrowserDialog::tabCloseRequested(int index) void BrowserDialog::on_backBtn_clicked() { BrowserView *currentView = getCurrentView(); - if (currentView != NULL) { + if (currentView != nullptr) { currentView->back(); } } @@ -239,7 +239,7 @@ void BrowserDialog::on_backBtn_clicked() void BrowserDialog::on_fwdBtn_clicked() { BrowserView *currentView = getCurrentView(); - if (currentView != NULL) { + if (currentView != nullptr) { currentView->forward(); } } @@ -254,7 +254,7 @@ void BrowserDialog::startSearch() void BrowserDialog::on_searchEdit_returnPressed() { BrowserView *currentView = getCurrentView(); - if (currentView != NULL) { + if (currentView != nullptr) { currentView->findText(ui->searchEdit->text(), QWebPage::FindWrapsAroundDocument); } } @@ -267,7 +267,7 @@ void BrowserDialog::on_refreshBtn_clicked() void BrowserDialog::on_browserTabWidget_currentChanged(int index) { BrowserView *currentView = qobject_cast(ui->browserTabWidget->widget(index)); - if (currentView != NULL) { + if (currentView != nullptr) { ui->backBtn->setEnabled(currentView->history()->canGoBack()); ui->fwdBtn->setEnabled(currentView->history()->canGoForward()); } @@ -276,7 +276,7 @@ void BrowserDialog::on_browserTabWidget_currentChanged(int index) void BrowserDialog::on_urlEdit_returnPressed() { QWebView *currentView = getCurrentView(); - if (currentView != NULL) { + if (currentView != nullptr) { currentView->setUrl(QUrl(ui->urlEdit->text())); } } diff --git a/src/categories.cpp b/src/categories.cpp index 57e18a28..1d5fdefe 100644 --- a/src/categories.cpp +++ b/src/categories.cpp @@ -32,7 +32,7 @@ using namespace MOBase; using namespace MOShared; -CategoryFactory* CategoryFactory::s_Instance = NULL; +CategoryFactory* CategoryFactory::s_Instance = nullptr; QString CategoryFactory::categoriesFilePath() @@ -93,7 +93,7 @@ CategoryFactory::CategoryFactory() CategoryFactory &CategoryFactory::instance() { - if (s_Instance == NULL) { + if (s_Instance == nullptr) { s_Instance = new CategoryFactory; } return *s_Instance; @@ -133,7 +133,7 @@ void CategoryFactory::setParents() void CategoryFactory::cleanup() { delete s_Instance; - s_Instance = NULL; + s_Instance = nullptr; } diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp index 24fda501..45692fdf 100644 --- a/src/directoryrefresher.cpp +++ b/src/directoryrefresher.cpp @@ -31,7 +31,7 @@ using namespace MOShared; DirectoryRefresher::DirectoryRefresher() - : m_DirectoryStructure(NULL) + : m_DirectoryStructure(nullptr) { } @@ -44,7 +44,7 @@ DirectoryEntry *DirectoryRefresher::getDirectoryStructure() { QMutexLocker locker(&m_RefreshLock); DirectoryEntry *result = m_DirectoryStructure; - m_DirectoryStructure = NULL; + m_DirectoryStructure = nullptr; return result; } @@ -104,7 +104,7 @@ void DirectoryRefresher::addModFilesToStructure(DirectoryEntry *directoryStructu foreach (const QString &filename, stealFiles) { QFileInfo fileInfo(filename); FileEntry::Ptr file = directoryStructure->findFile(ToWString(fileInfo.fileName())); - if (file.get() != NULL) { + if (file.get() != nullptr) { if (file->getOrigin() == 0) { // replace data as the origin on this bsa file->removeOrigin(0); @@ -134,7 +134,7 @@ void DirectoryRefresher::refresh() delete m_DirectoryStructure; - m_DirectoryStructure = new DirectoryEntry(L"data", NULL, 0); + m_DirectoryStructure = new DirectoryEntry(L"data", nullptr, 0); std::wstring dataDirectory = GameInfo::instance().getGameDirectory() + L"\\data"; m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0); diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index 9d276de9..2a398bc0 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -255,7 +255,7 @@ void DownloadListWidgetDelegate::issueResume() void DownloadListWidgetDelegate::issueDeleteAll() { - if (QMessageBox::question(NULL, tr("Are you sure?"), + if (QMessageBox::question(nullptr, tr("Are you sure?"), tr("This will remove all finished downloads from this list and from disk."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit removeDownload(-1, true); @@ -264,7 +264,7 @@ void DownloadListWidgetDelegate::issueDeleteAll() void DownloadListWidgetDelegate::issueDeleteCompleted() { - if (QMessageBox::question(NULL, tr("Are you sure?"), + if (QMessageBox::question(nullptr, tr("Are you sure?"), tr("This will remove all installed downloads from this list and from disk."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit removeDownload(-2, true); @@ -273,7 +273,7 @@ void DownloadListWidgetDelegate::issueDeleteCompleted() void DownloadListWidgetDelegate::issueRemoveFromViewAll() { - if (QMessageBox::question(NULL, tr("Are you sure?"), + if (QMessageBox::question(nullptr, tr("Are you sure?"), tr("This will remove all finished downloads from this list (but NOT from disk)."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit removeDownload(-1, false); @@ -282,7 +282,7 @@ void DownloadListWidgetDelegate::issueRemoveFromViewAll() void DownloadListWidgetDelegate::issueRemoveFromViewCompleted() { - if (QMessageBox::question(NULL, tr("Are you sure?"), + if (QMessageBox::question(nullptr, tr("Are you sure?"), tr("This will remove all installed downloads from this list (but NOT from disk)."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit removeDownload(-2, false); diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index 21a690ab..a298494a 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -90,7 +90,7 @@ void DownloadListWidgetCompactDelegate::paintPendingDownload(int downloadIndex) { std::pair nexusids = m_Manager->getPendingDownload(downloadIndex); m_NameLabel->setText(tr("< mod %1 file %2 >").arg(nexusids.first).arg(nexusids.second)); - if (m_SizeLabel != NULL) { + if (m_SizeLabel != nullptr) { m_SizeLabel->setText("???"); } m_DoneLabel->setVisible(true); @@ -110,7 +110,7 @@ void DownloadListWidgetCompactDelegate::paintRegularDownload(int downloadIndex) DownloadManager::DownloadState state = m_Manager->getState(downloadIndex); - if ((m_SizeLabel != NULL) && (state >= DownloadManager::STATE_READY)) { + if ((m_SizeLabel != nullptr) && (state >= DownloadManager::STATE_READY)) { m_SizeLabel->setText(QString::number(m_Manager->getFileSize(downloadIndex) / 1048576)); } @@ -242,7 +242,7 @@ void DownloadListWidgetCompactDelegate::issueResume() void DownloadListWidgetCompactDelegate::issueDeleteAll() { - if (QMessageBox::question(NULL, tr("Are you sure?"), + if (QMessageBox::question(nullptr, tr("Are you sure?"), tr("This will remove all finished downloads from this list and from disk."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit removeDownload(-1, true); @@ -251,7 +251,7 @@ void DownloadListWidgetCompactDelegate::issueDeleteAll() void DownloadListWidgetCompactDelegate::issueDeleteCompleted() { - if (QMessageBox::question(NULL, tr("Are you sure?"), + if (QMessageBox::question(nullptr, tr("Are you sure?"), tr("This will remove all installed downloads from this list and from disk."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit removeDownload(-2, true); @@ -260,7 +260,7 @@ void DownloadListWidgetCompactDelegate::issueDeleteCompleted() void DownloadListWidgetCompactDelegate::issueRemoveFromViewAll() { - if (QMessageBox::question(NULL, tr("Are you sure?"), + if (QMessageBox::question(nullptr, tr("Are you sure?"), tr("This will permanently remove all finished downloads from this list (but NOT from disk)."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit removeDownload(-1, false); @@ -269,7 +269,7 @@ void DownloadListWidgetCompactDelegate::issueRemoveFromViewAll() void DownloadListWidgetCompactDelegate::issueRemoveFromViewCompleted() { - if (QMessageBox::question(NULL, tr("Are you sure?"), + if (QMessageBox::question(nullptr, tr("Are you sure?"), tr("This will permanently remove all installed downloads from this list (but NOT from disk)."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit removeDownload(-2, false); diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index d785a939..1d141656 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -75,7 +75,7 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(con QString metaFileName = filePath + ".meta"; QSettings metaFile(metaFileName, QSettings::IniFormat); if (!showHidden && metaFile.value("removed", false).toBool()) { - return NULL; + return nullptr; } else { info->m_Hidden = metaFile.value("removed", false).toBool(); } @@ -308,7 +308,7 @@ void DownloadManager::refreshList() QString fileName = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + file; DownloadInfo *info = DownloadInfo::createFromMeta(fileName, m_ShowHidden); - if (info != NULL) { + if (info != nullptr) { m_ActiveDownloads.push_front(info); } } @@ -368,7 +368,7 @@ bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, } if (QFile::exists(m_OutputDirectory + "/" + baseName) && - (QMessageBox::question(NULL, tr("Download again?"), tr("A file with the same name has already been downloaded. " + (QMessageBox::question(nullptr, tr("Download again?"), tr("A file with the same name has already been downloaded. " "Do you want to download it again? The new file will receive a different name."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)) { removePending(modID, fileID); @@ -453,7 +453,7 @@ void DownloadManager::addNXMDownload(const QString &url) qDebug("add nxm download: %s", qPrintable(url)); if (nxmInfo.game().compare(managedGame, Qt::CaseInsensitive) != 0) { qDebug("download requested for wrong game (game: %s, url: %s)", qPrintable(managedGame), qPrintable(nxmInfo.game())); - QMessageBox::information(NULL, tr("Wrong Game"), tr("The download link is for a mod for \"%1\" but this instance of MO " + QMessageBox::information(nullptr, tr("Wrong Game"), tr("The download link is for a mod for \"%1\" but this instance of MO " "has been set up for \"%2\".").arg(nxmInfo.game()).arg(managedGame), QMessageBox::Ok); return; } @@ -608,7 +608,7 @@ void DownloadManager::pauseDownload(int index) DownloadInfo *info = m_ActiveDownloads.at(index); if (info->m_State == STATE_DOWNLOADING) { - if ((info->m_Reply != NULL) && (info->m_Reply->isRunning())) { + if ((info->m_Reply != nullptr) && (info->m_Reply->isRunning())) { setState(info, STATE_PAUSING); } else { setState(info, STATE_PAUSED); @@ -664,7 +664,7 @@ DownloadManager::DownloadInfo *DownloadManager::downloadInfoByID(unsigned int id if (iter != m_ActiveDownloads.end()) { return *iter; } else { - return NULL; + return nullptr; } } @@ -694,8 +694,8 @@ void DownloadManager::queryInfo(int index) if (info->m_FileInfo->modID < 0) { QString modIDString; while (modIDString.isEmpty()) { - modIDString = QInputDialog::getText(NULL, tr("Please enter the nexus mod id"), tr("Mod ID:"), QLineEdit::Normal, - QString(), NULL, 0, Qt::ImhFormattedNumbersOnly); + modIDString = QInputDialog::getText(nullptr, tr("Please enter the nexus mod id"), tr("Mod ID:"), QLineEdit::Normal, + QString(), nullptr, 0, Qt::ImhFormattedNumbersOnly); if (modIDString.isNull()) { // canceled return; @@ -704,7 +704,7 @@ void DownloadManager::queryInfo(int index) modIDString.clear(); } } - info->m_FileInfo->modID = modIDString.toInt(NULL, 10); + info->m_FileInfo->modID = modIDString.toInt(nullptr, 10); } } info->m_ReQueried = true; @@ -962,13 +962,13 @@ DownloadManager::DownloadInfo *DownloadManager::findDownload(QObject *reply, int // reverse search as newer, thus more relevant, downloads are at the end for (int i = m_ActiveDownloads.size() - 1; i >= 0; --i) { if (m_ActiveDownloads[i]->m_Reply == reply) { - if (index != NULL) { + if (index != nullptr) { *index = i; } return m_ActiveDownloads[i]; } } - return NULL; + return nullptr; } @@ -1007,7 +1007,7 @@ void DownloadManager::downloadReadyRead() { try { DownloadInfo *info = findDownload(this->sender()); - if (info != NULL) { + if (info != nullptr) { info->m_Output.write(info->m_Reply->readAll()); } } catch (const std::bad_alloc&) { @@ -1059,7 +1059,7 @@ void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant r QVariantMap result = resultData.toMap(); DownloadInfo *info = downloadInfoByID(userData.toInt()); - if (info == NULL) return; + if (info == nullptr) return; info->m_FileInfo->categoryID = result["category_id"].toInt(); info->m_FileInfo->modName = result["name"].toString().trimmed(); info->m_FileInfo->newestVersion.parse(result["version"].toString()); @@ -1104,7 +1104,7 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD } DownloadInfo *info = downloadInfoByID(userData.toInt()); - if (info == NULL) return; + if (info == nullptr) return; QVariantList result = resultData.toList(); @@ -1323,7 +1323,7 @@ void DownloadManager::downloadFinished() int index = 0; DownloadInfo *info = findDownload(this->sender(), &index); - if (info != NULL) { + if (info != nullptr) { QNetworkReply *reply = info->m_Reply; QByteArray data; if (reply->isOpen()) { @@ -1437,7 +1437,7 @@ void DownloadManager::metaDataChanged() int index = 0; DownloadInfo *info = findDownload(this->sender(), &index); - if (info != NULL) { + if (info != nullptr) { QString newName = getFileNameFromNetworkReply(info->m_Reply); if (!newName.isEmpty() && (newName != info->m_FileName)) { info->setName(getDownloadFileName(newName), true); diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 95288884..52da27b8 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -439,7 +439,7 @@ private: bool addDownload(const QStringList &URLs, int modID, int fileID, const MOBase::ModRepositoryFileInfo *fileInfo); // important: the caller has to lock the list-mutex, otherwise the DownloadInfo-pointer might get invalidated at any time - DownloadInfo *findDownload(QObject *reply, int *index = NULL) const; + DownloadInfo *findDownload(QObject *reply, int *index = nullptr) const; void removeFile(int index, bool deleteFile); diff --git a/src/dummybsa.cpp b/src/dummybsa.cpp index df757139..49c6ee53 100644 --- a/src/dummybsa.cpp +++ b/src/dummybsa.cpp @@ -65,7 +65,7 @@ static unsigned long long genHash(const char* fileName) fileNameLower[i] = '\0'; char* ext = strrchr(fileNameLower, '.'); - if (ext == NULL) { + if (ext == nullptr) { ext = fileNameLower + strlen(fileNameLower); } @@ -117,7 +117,7 @@ static unsigned long long genHash(const char* fileName) unsigned char *fileNameLowerU = reinterpret_cast(fileNameLower); char* ext = strrchr(fileNameLower, '.'); - if (ext == NULL) { + if (ext == nullptr) { ext = fileNameLower + strlen(fileNameLower); } unsigned char *extU = reinterpret_cast(ext); diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp index 3cc749d6..162c6a83 100644 --- a/src/editexecutablesdialog.cpp +++ b/src/editexecutablesdialog.cpp @@ -30,7 +30,7 @@ using namespace MOShared; EditExecutablesDialog::EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent) : TutorableDialog("EditExecutables", parent), - ui(new Ui::EditExecutablesDialog), m_CurrentItem(NULL), m_ExecutablesList(executablesList) + ui(new Ui::EditExecutablesDialog), m_CurrentItem(nullptr), m_ExecutablesList(executablesList) { ui->setupUi(this); @@ -86,7 +86,7 @@ void EditExecutablesDialog::resetInput() ui->appIDOverwriteEdit->clear(); ui->overwriteAppIDBox->setChecked(false); ui->closeCheckBox->setChecked(false); - m_CurrentItem = NULL; + m_CurrentItem = nullptr; } @@ -128,7 +128,7 @@ void EditExecutablesDialog::on_browseButton_clicked() { // try to find java automatically std::wstring binaryNameW = ToWString(binaryName); WCHAR buffer[MAX_PATH]; - if (::FindExecutableW(binaryNameW.c_str(), NULL, buffer) > (HINSTANCE)32) { + if (::FindExecutableW(binaryNameW.c_str(), nullptr, buffer) > (HINSTANCE)32) { DWORD binaryType = 0UL; if (!::GetBinaryTypeW(binaryNameW.c_str(), &binaryType)) { qDebug("failed to determine binary type of \"%ls\": %lu", binaryNameW.c_str(), ::GetLastError()); @@ -204,7 +204,7 @@ void EditExecutablesDialog::on_titleEdit_textChanged(const QString &arg1) bool EditExecutablesDialog::executableChanged() { - if (m_CurrentItem != NULL) { + if (m_CurrentItem != nullptr) { const Executable &selectedExecutable = m_CurrentItem->data(Qt::UserRole).value(); return selectedExecutable.m_Arguments != ui->argumentsEdit->text() @@ -220,7 +220,7 @@ bool EditExecutablesDialog::executableChanged() void EditExecutablesDialog::on_executablesListBox_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { - if (current == NULL) { + if (current == nullptr) { resetInput(); return; } diff --git a/src/executableslist.cpp b/src/executableslist.cpp index c46cea10..03f2b7c9 100644 --- a/src/executableslist.cpp +++ b/src/executableslist.cpp @@ -130,7 +130,7 @@ Executable *ExecutablesList::findExe(const QString &title) return &*iter; } } - return NULL; + return nullptr; } @@ -144,7 +144,7 @@ bool ExecutablesList::titleExists(const QString &title) const void ExecutablesList::addExecutable(const Executable &executable) { Executable *existingExe = findExe(executable.m_Title); - if (existingExe != NULL) { + if (existingExe != nullptr) { *existingExe = executable; } else { m_Executables.push_back(executable); @@ -158,7 +158,7 @@ void ExecutablesList::addExecutable(const QString &title, const QString &executa { QFileInfo file(executableName); Executable *existingExe = findExe(title); - if (existingExe != NULL) { + if (existingExe != nullptr) { existingExe->m_Title = title; existingExe->m_CloseMO = closeMO; existingExe->m_BinaryInfo = file; diff --git a/src/genericicondelegate.h b/src/genericicondelegate.h index 8bc75e27..9842d09a 100644 --- a/src/genericicondelegate.h +++ b/src/genericicondelegate.h @@ -20,7 +20,7 @@ public: * of the view, the delegate will turn off this behaviour if the column is smaller than "compactSize" * @param compactSize see explanation of logicalIndex */ - GenericIconDelegate(QObject *parent = NULL, int role = Qt::UserRole + 1, int logicalIndex = -1, int compactSize = 150); + GenericIconDelegate(QObject *parent = nullptr, int role = Qt::UserRole + 1, int logicalIndex = -1, int compactSize = 150); public slots: void columnResized(int logicalIndex, int oldSize, int newSize); private: diff --git a/src/helper.cpp b/src/helper.cpp index d0825d10..f47a468a 100644 --- a/src/helper.cpp +++ b/src/helper.cpp @@ -40,7 +40,7 @@ bool helperExec(LPCWSTR moDirectory, LPCWSTR commandLine) execInfo.cbSize = sizeof(SHELLEXECUTEINFOW); execInfo.fMask = SEE_MASK_NOCLOSEPROCESS; - execInfo.hwnd = NULL; + execInfo.hwnd = nullptr; execInfo.lpVerb = L"runas"; execInfo.lpFile = fileName; execInfo.lpParameters = commandLine; diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 25fe06a4..6ab051b2 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -60,7 +60,7 @@ typedef Archive* (*CreateArchiveType)(); template T resolveFunction(QLibrary &lib, const char *name) { T temp = reinterpret_cast(lib.resolve(name)); - if (temp == NULL) { + if (temp == nullptr) { throw std::runtime_error(QObject::tr("invalid 7-zip32.dll: %1").arg(lib.errorString()).toLatin1().constData()); } return temp; @@ -94,7 +94,7 @@ InstallationManager::~InstallationManager() void InstallationManager::setParentWidget(QWidget *widget) { - m_InstallationProgress.setParent(widget); + m_InstallationProgress.setParent(widget, Qt::Dialog); for (IPluginInstaller *installer : m_Installers) { installer->setParentWidget(widget); } @@ -103,7 +103,7 @@ void InstallationManager::setParentWidget(QWidget *widget) void InstallationManager::queryPassword(LPSTR password) { - QString result = QInputDialog::getText(NULL, tr("Password required"), tr("Password"), QLineEdit::Password); + QString result = QInputDialog::getText(nullptr, tr("Password required"), tr("Password"), QLineEdit::Password); strncpy(password, result.toLocal8Bit().constData(), MAX_PASSWORD_LENGTH); } @@ -231,10 +231,10 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool const wchar_t *targetFile = data[i]->getFileName(); if (flatten) { targetFile = wcsrchr(data[i]->getFileName(), '\\'); - if (targetFile == NULL) { + if (targetFile == nullptr) { targetFile = wcsrchr(data[i]->getFileName(), '/'); } - if (targetFile == NULL) { + if (targetFile == nullptr) { qCritical("failed to find backslash in %ls", data[i]->getFileName()); continue; } else { @@ -383,7 +383,7 @@ DirectoryTree::Node *InstallationManager::getSimpleArchiveBase(DirectoryTree *da currentNode = *currentNode->nodesBegin(); } else { qDebug("not a simple archive"); - return NULL; + return nullptr; } } } @@ -675,7 +675,7 @@ bool InstallationManager::install(const QString &fileName, GuessedValue } ON_BLOCK_EXIT(std::bind(&InstallationManager::postInstallCleanup, this)); - QScopedPointer filesTree(archiveOpen ? createFilesTree() : NULL); + QScopedPointer filesTree(archiveOpen ? createFilesTree() : nullptr); IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED; std::sort(m_Installers.begin(), m_Installers.end(), [] (IPluginInstaller *LHS, IPluginInstaller *RHS) { @@ -700,8 +700,8 @@ bool InstallationManager::install(const QString &fileName, GuessedValue try { { // simple case IPluginInstallerSimple *installerSimple = dynamic_cast(installer); - if ((installerSimple != NULL) && - (filesTree != NULL) && (installer->isArchiveSupported(*filesTree))) { + if ((installerSimple != nullptr) && + (filesTree != nullptr) && (installer->isArchiveSupported(*filesTree))) { installResult = installerSimple->install(modName, *filesTree, version, modID); if (installResult == IPluginInstaller::RESULT_SUCCESS) { mapToArchive(filesTree.data()); @@ -715,9 +715,9 @@ bool InstallationManager::install(const QString &fileName, GuessedValue { // custom case IPluginInstallerCustom *installerCustom = dynamic_cast(installer); - if ((installerCustom != NULL) && - (((filesTree != NULL) && installer->isArchiveSupported(*filesTree)) || - ((filesTree == NULL) && installerCustom->isArchiveSupported(fileName)))) { + if ((installerCustom != nullptr) && + (((filesTree != nullptr) && installer->isArchiveSupported(*filesTree)) || + ((filesTree == nullptr) && installerCustom->isArchiveSupported(fileName)))) { std::set installerExtensions = installerCustom->supportedExtensions(); if (installerExtensions.find(fileInfo.suffix()) != installerExtensions.end()) { installResult = installerCustom->install(modName, fileName, version, modID); @@ -738,7 +738,7 @@ bool InstallationManager::install(const QString &fileName, GuessedValue } break; case IPluginInstaller::RESULT_SUCCESS: case IPluginInstaller::RESULT_SUCCESSCANCEL: { - if (filesTree != NULL) { + if (filesTree != nullptr) { DirectoryTree::node_iterator iniTweakNode = filesTree->nodeFind(DirectoryTreeInformation("INI Tweaks")); hasIniTweaks = (iniTweakNode != filesTree->nodesEnd()) && ((*iniTweakNode)->numLeafs() != 0); @@ -796,7 +796,7 @@ void InstallationManager::registerInstaller(IPluginInstaller *installer) m_Installers.push_back(installer); installer->setInstallationManager(this); IPluginInstallerCustom *installerCustom = dynamic_cast(installer); - if (installerCustom != NULL) { + if (installerCustom != nullptr) { std::set extensions = installerCustom->supportedExtensions(); m_SupportedExtensions.insert(extensions.begin(), extensions.end()); } diff --git a/src/installationmanager.h b/src/installationmanager.h index cd20f0dd..4750a148 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -143,7 +143,7 @@ public: * @param merge if this value is not null, the value will be set to whether the use chose to merge or replace * @return true if we can proceed with the installation, false if the user canceled or in case of an unrecoverable error */ - virtual bool testOverwrite(MOBase::GuessedValue &modName, bool *merge = NULL) const; + virtual bool testOverwrite(MOBase::GuessedValue &modName, bool *merge = nullptr) const; private: @@ -168,7 +168,7 @@ private: bool isSimpleArchiveTopLayer(const MOBase::DirectoryTree::Node *node, bool bainStyle); MOBase::DirectoryTree::Node *getSimpleArchiveBase(MOBase::DirectoryTree *dataTree); - //bool testOverwrite(const QString &modsDirectory, MOBase::GuessedValue &modName, bool *merge = NULL); + //bool testOverwrite(const QString &modsDirectory, MOBase::GuessedValue &modName, bool *merge = nullptr); bool doInstall(MOBase::GuessedValue &modName, int modID, const QString &version, const QString &newestVersion, int categoryID); diff --git a/src/json.cpp b/src/json.cpp index a280ba34..05a2665a 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -409,7 +409,7 @@ namespace QtJson { bool ok; if (numberStr.contains('.')) { - return QVariant(numberStr.toDouble(NULL)); + return QVariant(numberStr.toDouble(nullptr)); } else if (numberStr.startsWith('-')) { int i = numberStr.toInt(&ok); if (!ok) { diff --git a/src/loadmechanism.cpp b/src/loadmechanism.cpp index 9d7e4e28..711a7f1a 100644 --- a/src/loadmechanism.cpp +++ b/src/loadmechanism.cpp @@ -141,7 +141,7 @@ void LoadMechanism::deactivateScriptExtender() removeHintFile(pluginsDir); } catch (const std::exception &e) { - QMessageBox::critical(NULL, QObject::tr("Failed to deactivate script extender loading"), e.what()); + QMessageBox::critical(nullptr, QObject::tr("Failed to deactivate script extender loading"), e.what()); } } @@ -172,7 +172,7 @@ void LoadMechanism::deactivateProxyDLL() QDir dir(gameDirectory); removeHintFile(dir); } catch (const std::exception &e) { - QMessageBox::critical(NULL, QObject::tr("Failed to deactivate proxy-dll loading"), e.what()); + QMessageBox::critical(nullptr, QObject::tr("Failed to deactivate proxy-dll loading"), e.what()); } } @@ -211,7 +211,7 @@ void LoadMechanism::activateScriptExtender() } writeHintFile(pluginsDir); } catch (const std::exception &e) { - QMessageBox::critical(NULL, QObject::tr("Failed to set up script extender loading"), e.what()); + QMessageBox::critical(nullptr, QObject::tr("Failed to set up script extender loading"), e.what()); } } @@ -265,7 +265,7 @@ void LoadMechanism::activateProxyDLL() } writeHintFile(QDir(gameDirectory)); } catch (const std::exception &e) { - QMessageBox::critical(NULL, QObject::tr("Failed to set up proxy-dll loading"), e.what()); + QMessageBox::critical(nullptr, QObject::tr("Failed to set up proxy-dll loading"), e.what()); } } diff --git a/src/lockeddialog.cpp b/src/lockeddialog.cpp index c688fff6..907e3c0a 100644 --- a/src/lockeddialog.cpp +++ b/src/lockeddialog.cpp @@ -31,7 +31,7 @@ LockedDialog::LockedDialog(QWidget *parent, const QString &text, bool unlockButt this->setWindowFlags(this->windowFlags() | Qt::ToolTip | Qt::FramelessWindowHint); - if (parent != NULL) { + if (parent != nullptr) { QPoint position = parent->mapToGlobal(QPoint(parent->width() / 2, parent->height() / 2)); position.rx() -= this->width() / 2; position.ry() -= this->height() / 2; @@ -61,7 +61,7 @@ void LockedDialog::setProcessName(const QString &name) void LockedDialog::resizeEvent(QResizeEvent *event) { QWidget *par = parentWidget(); - if (par != NULL) { + if (par != nullptr) { QPoint position = par->mapToGlobal(QPoint(par->width() / 2, par->height() / 2)); position.rx() -= event->size().width() / 2; position.ry() -= event->size().height() / 2; diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp index 17417bf9..635ce13e 100644 --- a/src/logbuffer.cpp +++ b/src/logbuffer.cpp @@ -30,7 +30,7 @@ QMutex LogBuffer::s_Mutex; LogBuffer::LogBuffer(int messageCount, QtMsgType minMsgType, const QString &outputFileName) - : QAbstractItemModel(NULL), m_OutFileName(outputFileName), m_ShutDown(false), + : QAbstractItemModel(nullptr), m_OutFileName(outputFileName), m_ShutDown(false), m_MinMsgType(minMsgType), m_NumMessages(0) { m_Messages.resize(messageCount); diff --git a/src/main.cpp b/src/main.cpp index 3f0360c2..a3ddac20 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -99,12 +99,12 @@ bool bootstrap() // verify the hook-dll exists QString dllName = qApp->applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName()); - if (::GetModuleHandleW(ToWString(dllName).c_str()) != NULL) { + if (::GetModuleHandleW(ToWString(dllName).c_str()) != nullptr) { throw std::runtime_error("hook.dll already loaded! You can't start Mod Organizer from within itself (not even indirectly)"); } HMODULE dllMod = ::LoadLibraryW(ToWString(dllName).c_str()); - if (dllMod == NULL) { + if (dllMod == nullptr) { throw windows_error("hook.dll is missing or invalid"); } ::FreeLibrary(dllMod); @@ -135,7 +135,7 @@ LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs FuncMiniDumpWriteDump funcDump = (FuncMiniDumpWriteDump)::GetProcAddress(dbgDLL, "MiniDumpWriteDump"); if (funcDump) { - if (QMessageBox::question(NULL, QObject::tr("Woops"), + if (QMessageBox::question(nullptr, QObject::tr("Woops"), QObject::tr("ModOrganizer has crashed! Should a diagnostic file be created? If you send me this file " "(%1) to sherb@gmx.net, the bug is a lot more likely to be fixed. " "Please include a short description of what you were doing when the crash happened").arg(qApp->applicationFilePath().append(".dmp")), @@ -144,14 +144,14 @@ LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs std::wstring dumpName = ToWString(qApp->applicationFilePath().append(".dmp")); HANDLE dumpFile = ::CreateFile(dumpName.c_str(), - GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (dumpFile != INVALID_HANDLE_VALUE) { _MINIDUMP_EXCEPTION_INFORMATION exceptionInfo; exceptionInfo.ThreadId = ::GetCurrentThreadId(); exceptionInfo.ExceptionPointers = exceptionPtrs; - exceptionInfo.ClientPointers = NULL; + exceptionInfo.ClientPointers = false; - BOOL success = funcDump(::GetCurrentProcess(), ::GetCurrentProcessId(), dumpFile, MiniDumpNormal, &exceptionInfo, NULL, NULL); + BOOL success = funcDump(::GetCurrentProcess(), ::GetCurrentProcessId(), dumpFile, MiniDumpNormal, &exceptionInfo, nullptr, nullptr); ::FlushFileBuffers(dumpFile); ::CloseHandle(dumpFile); @@ -174,7 +174,7 @@ LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs _snprintf(errorBuffer, errorLen, "dbghelp.dll not found"); } - QMessageBox::critical(NULL, QObject::tr("Woops"), + QMessageBox::critical(nullptr, QObject::tr("Woops"), QObject::tr("ModOrganizer has crashed! Unfortunately I was not able to write a diagnostic file: %1").arg(errorBuffer)); return result; } @@ -191,14 +191,14 @@ bool HaveWriteAccess(const std::wstring &path) const static SECURITY_INFORMATION requestedFileInformation = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; DWORD length = 0; - if (!::GetFileSecurityW(path.c_str(), requestedFileInformation, NULL, NULL, &length) + if (!::GetFileSecurityW(path.c_str(), requestedFileInformation, nullptr, 0UL, &length) && (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { std::string tempBuffer; tempBuffer.reserve(length); PSECURITY_DESCRIPTOR security = (PSECURITY_DESCRIPTOR)tempBuffer.data(); if (security && ::GetFileSecurity(path.c_str(), requestedFileInformation, security, length, &length)) { - HANDLE token = NULL; + HANDLE token = nullptr; const static DWORD tokenDesiredAccess = TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | STANDARD_RIGHTS_READ; if (!::OpenThreadToken(::GetCurrentThread(), tokenDesiredAccess, TRUE, &token)) { if (!::OpenProcessToken(::GetCurrentProcess(), tokenDesiredAccess, &token)) { @@ -206,7 +206,7 @@ bool HaveWriteAccess(const std::wstring &path) } } - HANDLE impersonatedToken = NULL; + HANDLE impersonatedToken = nullptr; if (::DuplicateToken(token, SecurityImpersonation, &impersonatedToken)) { GENERIC_MAPPING mapping = { 0xFFFFFFFF }; mapping.GenericRead = FILE_GENERIC_READ; @@ -297,7 +297,7 @@ int main(int argc, char *argv[]) if (!HaveWriteAccess(ToWString(application.applicationDirPath()))) { QStringList arguments = application.arguments(); arguments.pop_front(); - ::ShellExecuteW( NULL + ::ShellExecuteW( nullptr , L"runas" , ToWString(QString("\"%1\"").arg(QCoreApplication::applicationFilePath())).c_str() , ToWString(arguments.join(" ")).c_str() @@ -348,7 +348,7 @@ int main(int argc, char *argv[]) instance.sendMessage(arguments.at(1)); return 0; } else if (arguments.size() == 1) { - QMessageBox::information(NULL, QObject::tr("Mod Organizer"), QObject::tr("An instance of Mod Organizer is already running")); + QMessageBox::information(nullptr, QObject::tr("Mod Organizer"), QObject::tr("An instance of Mod Organizer is already running")); return 0; } } // we continue for the primary instance OR if MO has been called with parameters @@ -399,7 +399,7 @@ int main(int argc, char *argv[]) } else { gamePath = QDir::cleanPath(selection.getChoiceData().toString()); if (gamePath.isEmpty()) { - gamePath = QFileDialog::getExistingDirectory(NULL, QObject::tr("Please select the game to manage"), QString(), + gamePath = QFileDialog::getExistingDirectory(nullptr, QObject::tr("Please select the game to manage"), QString(), QFileDialog::ShowDirsOnly); } } @@ -426,7 +426,7 @@ int main(int argc, char *argv[]) if (!settings.contains("game_edition")) { std::vector editions = GameInfo::instance().getSteamVariants(); if (editions.size() > 1) { - SelectionDialog selection(QObject::tr("Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!)"), NULL); + SelectionDialog selection(QObject::tr("Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!)"), nullptr); int index = 0; for (auto iter = editions.begin(); iter != editions.end(); ++iter) { selection.addChoice(ToQString(*iter), "", index++); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d4a7f4fc..0fdbdfad 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -336,7 +336,7 @@ MainWindow::~MainWindow() void MainWindow::disconnectPlugins() { - if (ui->actionTool->menu() != NULL) { + if (ui->actionTool->menu() != nullptr) { ui->actionTool->menu()->clear(); } } @@ -432,11 +432,11 @@ static QModelIndex mapToModel(const QAbstractItemModel *targetModel, QModelIndex QModelIndex result = idx; const QAbstractItemModel *model = idx.model(); while (model != targetModel) { - if (model == NULL) { + if (model == nullptr) { return QModelIndex(); } const QAbstractProxyModel *proxyModel = qobject_cast(model); - if (proxyModel == NULL) { + if (proxyModel == nullptr) { return QModelIndex(); } result = proxyModel->mapToSource(result); @@ -484,7 +484,7 @@ void MainWindow::updateToolBar() QWidget *widget = ui->toolBar->widgetForAction(ui->actionTool); QToolButton *toolBtn = qobject_cast(widget); - if (toolBtn->menu() == NULL) { + if (toolBtn->menu() == nullptr) { actionToToolButton(ui->actionTool); } @@ -600,7 +600,7 @@ void MainWindow::createHelpWidget() { QToolButton *toolBtn = qobject_cast(ui->toolBar->widgetForAction(ui->actionHelp)); QMenu *buttonMenu = toolBtn->menu(); - if (buttonMenu == NULL) { + if (buttonMenu == nullptr) { return; } buttonMenu->clear(); @@ -838,14 +838,14 @@ SaveGameGamebryo *MainWindow::getSaveGame(QListWidgetItem *item) return saveGame; } catch (const std::exception &e) { reportError(tr("failed to read savegame: %1").arg(e.what())); - return NULL; + return nullptr; } } void MainWindow::displaySaveGameInfo(const SaveGameGamebryo *save, QPoint pos) { - if (m_CurrentSaveView == NULL) { + if (m_CurrentSaveView == nullptr) { m_CurrentSaveView = new SaveGameInfoWidgetGamebryo(save, m_OrganizerCore.pluginList(), this); } else { m_CurrentSaveView->setSave(save); @@ -874,11 +874,11 @@ void MainWindow::displaySaveGameInfo(const SaveGameGamebryo *save, QPoint pos) void MainWindow::saveSelectionChanged(QListWidgetItem *newItem) { - if (newItem == NULL) { + if (newItem == nullptr) { hideSaveGameInfo(); - } else if ((m_CurrentSaveView == NULL) || (newItem != m_CurrentSaveView->property("displayItem").value())) { + } else if ((m_CurrentSaveView == nullptr) || (newItem != m_CurrentSaveView->property("displayItem").value())) { const SaveGameGamebryo *save = getSaveGame(newItem); - if (save != NULL) { + if (save != nullptr) { displaySaveGameInfo(save, QCursor::pos()); m_CurrentSaveView->setProperty("displayItem", qVariantFromValue((void*)newItem)); } @@ -889,10 +889,10 @@ void MainWindow::saveSelectionChanged(QListWidgetItem *newItem) void MainWindow::hideSaveGameInfo() { - if (m_CurrentSaveView != NULL) { + if (m_CurrentSaveView != nullptr) { disconnect(m_CurrentSaveView, SIGNAL(closeSaveInfo()), this, SLOT(hideSaveGameInfo())); m_CurrentSaveView->deleteLater(); - m_CurrentSaveView = NULL; + m_CurrentSaveView = nullptr; } } @@ -928,7 +928,7 @@ void MainWindow::modPagePluginInvoke() m_IntegratedBrowser.setWindowTitle(plugin->displayName()); m_IntegratedBrowser.openUrl(plugin->pageURL()); } else { - ::ShellExecuteW(NULL, L"open", ToWString(plugin->pageURL().toString()).c_str(), NULL, NULL, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", ToWString(plugin->pageURL().toString()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } } @@ -948,7 +948,7 @@ void MainWindow::registerPluginTool(IPluginTool *tool) void MainWindow::registerModPage(IPluginModPage *modPage) { // turn the browser action into a drop-down menu if necessary - if (ui->actionNexus->menu() == NULL) { + if (ui->actionNexus->menu() == nullptr) { QAction *nexusAction = ui->actionNexus; // TODO: use a different icon for nexus! ui->actionNexus = new QAction(nexusAction->icon(), tr("Browse Mod Page"), ui->toolBar); @@ -973,7 +973,7 @@ void MainWindow::registerModPage(IPluginModPage *modPage) void MainWindow::startExeAction() { QAction *action = qobject_cast(sender()); - if (action != NULL) { + if (action != nullptr) { Executable selectedExecutable = action->data().value(); m_OrganizerCore.spawnBinary( selectedExecutable.m_BinaryInfo, @@ -1016,7 +1016,7 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) m_OldProfileIndex = index; if ((previousIndex != -1) && - (m_OrganizerCore.currentProfile() != NULL) && + (m_OrganizerCore.currentProfile() != nullptr) && m_OrganizerCore.currentProfile()->exists()) { m_OrganizerCore.saveCurrentLists(); } @@ -1155,7 +1155,7 @@ void MainWindow::expandDataTreeItem(QTreeWidgetItem *item) std::wstring virtualPath = (path + L"\\").substr(6) + ToWString(item->text(0)); DirectoryEntry *dir = m_OrganizerCore.directoryStructure()->findSubDirectoryRecursive(virtualPath); - if (dir != NULL) { + if (dir != nullptr) { updateTo(item, path, *dir, conflictsOnly); } else { qWarning("failed to update view of %ls", path.c_str()); @@ -1227,7 +1227,7 @@ extern QPixmap qt_pixmapFromWinHICON(HICON icon); QIcon MainWindow::iconForExecutable(const QString &filePath) { HICON winIcon; - UINT res = ::ExtractIconExW(ToWString(filePath).c_str(), 0, &winIcon, NULL, 1); + UINT res = ::ExtractIconExW(ToWString(filePath).c_str(), 0, &winIcon, nullptr, 1); if (res == 1) { QIcon result = QIcon(qt_pixmapFromWinHICON(winIcon)); ::DestroyIcon(winIcon); @@ -1397,7 +1397,7 @@ void MainWindow::updateBSAList(const QStringList &defaultArchives, const QString modName = modInfo->name(); } QList items = ui->bsaList->findItems(modName, Qt::MatchFixedString); - QTreeWidgetItem *subItem = NULL; + QTreeWidgetItem *subItem = nullptr; if (items.length() > 0) { subItem = items.at(0); } else { @@ -1648,14 +1648,14 @@ static HRESULT CreateShortcut(LPCWSTR targetFileName, LPCWSTR arguments, LPCWSTR currentDirectory) { HRESULT result = E_INVALIDARG; - if ((targetFileName != NULL) && (wcslen(targetFileName) > 0) && - (arguments != NULL) && - (linkFileName != NULL) && (strlen(linkFileName) > 0) && - (description != NULL) && - (currentDirectory != NULL)) { + if ((targetFileName != nullptr) && (wcslen(targetFileName) > 0) && + (arguments != nullptr) && + (linkFileName != nullptr) && (strlen(linkFileName) > 0) && + (description != nullptr) && + (currentDirectory != nullptr)) { IShellLink* shellLink; - result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + result = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&shellLink); if (!SUCCEEDED(result)) { @@ -1755,19 +1755,19 @@ void MainWindow::helpTriggered() void MainWindow::wikiTriggered() { -// ::ShellExecuteW(NULL, L"open", L"http://issue.tannin.eu/tbg/wiki/Modorganizer%3AMainPage", NULL, NULL, SW_SHOWNORMAL); - ::ShellExecuteW(NULL, L"open", L"http://wiki.step-project.com/Guide:Mod_Organizer", NULL, NULL, SW_SHOWNORMAL); +// ::ShellExecuteW(nullptr, L"open", L"http://issue.tannin.eu/tbg/wiki/Modorganizer%3AMainPage", nullptr, nullptr, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", L"http://wiki.step-project.com/Guide:Mod_Organizer", nullptr, nullptr, SW_SHOWNORMAL); } void MainWindow::issueTriggered() { - ::ShellExecuteW(NULL, L"open", L"http://issue.tannin.eu/tbg", NULL, NULL, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", L"http://issue.tannin.eu/tbg", nullptr, nullptr, SW_SHOWNORMAL); } void MainWindow::tutorialTriggered() { QAction *tutorialAction = qobject_cast(sender()); - if (tutorialAction != NULL) { + if (tutorialAction != nullptr) { if (QMessageBox::question(this, tr("Start Tutorial?"), tr("You're about to start a tutorial. For technical reasons it's not possible to end " "the tutorial early. Continue?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { @@ -1870,7 +1870,7 @@ void MainWindow::modorder_changed() ModInfo::Ptr modInfo = ModInfo::getByIndex(current.data(Qt::UserRole + 1).toInt()); modInfo->doConflictCheck(); m_OrganizerCore.modList()->setOverwriteMarkers(modInfo->getModOverwrite(), modInfo->getModOverwritten()); - if (m_ModListSortProxy != NULL) { + if (m_ModListSortProxy != nullptr) { m_ModListSortProxy->invalidate(); } ui->modList->verticalScrollBar()->repaint(); @@ -2007,7 +2007,7 @@ void MainWindow::modlistChanged(int) void MainWindow::fileMoved(const QString &filePath, const QString &oldOriginName, const QString &newOriginName) { const FileEntry::Ptr filePtr = m_OrganizerCore.directoryStructure()->findFile(ToWString(filePath)); - if (filePtr.get() != NULL) { + if (filePtr.get() != nullptr) { try { if (m_OrganizerCore.directoryStructure()->originExists(ToWString(newOriginName))) { FilesOrigin &newOrigin = m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(newOriginName)); @@ -2035,7 +2035,7 @@ QTreeWidgetItem *MainWindow::addFilterItem(QTreeWidgetItem *root, const QString QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(name)); item->setData(0, Qt::UserRole, categoryID); item->setData(0, Qt::UserRole + 1, type); - if (root != NULL) { + if (root != nullptr) { root->addChild(item); } else { ui->categoriesList->addTopLevelItem(item); @@ -2046,7 +2046,7 @@ QTreeWidgetItem *MainWindow::addFilterItem(QTreeWidgetItem *root, const QString void MainWindow::addContentFilters() { for (unsigned i = 0; i < ModInfo::NUM_CONTENT_TYPES; ++i) { - addFilterItem(NULL, tr("").arg(ModInfo::getContentTypeName(i)), i, ModListSortProxy::TYPE_CONTENT); + addFilterItem(nullptr, tr("").arg(ModInfo::getContentTypeName(i)), i, ModListSortProxy::TYPE_CONTENT); } } @@ -2079,14 +2079,14 @@ void MainWindow::refreshFilters() } ui->categoriesList->clear(); - addFilterItem(NULL, tr(""), CategoryFactory::CATEGORY_SPECIAL_CHECKED, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(NULL, tr(""), CategoryFactory::CATEGORY_SPECIAL_UNCHECKED, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(NULL, tr(""), CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(NULL, tr(""), CategoryFactory::CATEGORY_SPECIAL_MANAGED, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(NULL, tr(""), CategoryFactory::CATEGORY_SPECIAL_UNMANAGED, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(NULL, tr(""), CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(NULL, tr(""), CategoryFactory::CATEGORY_SPECIAL_CONFLICT, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(NULL, tr(""), CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED, ModListSortProxy::TYPE_SPECIAL); + addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_CHECKED, ModListSortProxy::TYPE_SPECIAL); + addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_UNCHECKED, ModListSortProxy::TYPE_SPECIAL); + addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE, ModListSortProxy::TYPE_SPECIAL); + addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_MANAGED, ModListSortProxy::TYPE_SPECIAL); + addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_UNMANAGED, ModListSortProxy::TYPE_SPECIAL); + addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY, ModListSortProxy::TYPE_SPECIAL); + addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_CONFLICT, ModListSortProxy::TYPE_SPECIAL); + addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED, ModListSortProxy::TYPE_SPECIAL); addContentFilters(); @@ -2103,7 +2103,7 @@ void MainWindow::refreshFilters() } } - addCategoryFilters(NULL, categoriesUsed, 0); + addCategoryFilters(nullptr, categoriesUsed, 0); foreach (const QString &item, selectedItems) { QList matches = ui->categoriesList->findItems(item, Qt::MatchFixedString | Qt::MatchRecursive); @@ -2166,7 +2166,7 @@ void MainWindow::modlistSelectionChanged(const QModelIndex ¤t, const QMode } else { m_OrganizerCore.modList()->setOverwriteMarkers(std::set(), std::set()); } - if ((m_ModListSortProxy != NULL) + if ((m_ModListSortProxy != nullptr) && !m_ModListSortProxy->beingInvalidated()) { m_ModListSortProxy->invalidate(); } @@ -2322,7 +2322,7 @@ void MainWindow::windowTutorialFinished(const QString &windowName) void MainWindow::overwriteClosed(int) { OverwriteInfoDialog *dialog = this->findChild("__overwriteDialog"); - if (dialog != NULL) { + if (dialog != nullptr) { m_OrganizerCore.modList()->modInfoChanged(dialog->modInfo()); dialog->deleteLater(); } @@ -2336,7 +2336,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { QDialog *dialog = this->findChild("__overwriteDialog"); try { - if (dialog == NULL) { + if (dialog == nullptr) { dialog = new OverwriteInfoDialog(modInfo, this); dialog->setObjectName("__overwriteDialog"); } else { @@ -2483,7 +2483,7 @@ void MainWindow::openExplorer_clicked() { ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); - ::ShellExecuteW(NULL, L"explore", ToWString(modInfo->absolutePath()).c_str(), NULL, NULL, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } void MainWindow::information_clicked() @@ -2523,13 +2523,13 @@ void MainWindow::createModFromOverwrite() } } - if (m_OrganizerCore.getMod(name) != NULL) { + if (m_OrganizerCore.getMod(name) != nullptr) { reportError(tr("A mod with this name already exists")); return; } IModInterface *newMod = m_OrganizerCore.createMod(name); - if (newMod == NULL) { + if (newMod == nullptr) { return; } @@ -2610,11 +2610,11 @@ void MainWindow::replaceCategoriesFromMenu(QMenu *menu, int modRow) { ModInfo::Ptr modInfo = ModInfo::getByIndex(modRow); foreach (QAction* action, menu->actions()) { - if (action->menu() != NULL) { + if (action->menu() != nullptr) { replaceCategoriesFromMenu(action->menu(), modRow); } else { QWidgetAction *widgetAction = qobject_cast(action); - if (widgetAction != NULL) { + if (widgetAction != nullptr) { QCheckBox *checkbox = qobject_cast(widgetAction->defaultWidget()); modInfo->setCategory(widgetAction->data().toInt(), checkbox->isChecked()); } @@ -2627,11 +2627,11 @@ void MainWindow::addRemoveCategoriesFromMenu(QMenu *menu, int modRow, int refere if (referenceRow != -1 && referenceRow != modRow) { ModInfo::Ptr editedModInfo = ModInfo::getByIndex(referenceRow); foreach (QAction* action, menu->actions()) { - if (action->menu() != NULL) { + if (action->menu() != nullptr) { addRemoveCategoriesFromMenu(action->menu(), modRow, referenceRow); } else { QWidgetAction *widgetAction = qobject_cast(action); - if (widgetAction != NULL) { + if (widgetAction != nullptr) { QCheckBox *checkbox = qobject_cast(widgetAction->defaultWidget()); int categoryId = widgetAction->data().toInt(); bool checkedBefore = editedModInfo->categorySet(categoryId); @@ -2651,7 +2651,7 @@ void MainWindow::addRemoveCategoriesFromMenu(QMenu *menu, int modRow, int refere void MainWindow::addRemoveCategories_MenuHandler() { QMenu *menu = qobject_cast(sender()); - if (menu == NULL) { + if (menu == nullptr) { qCritical("not a menu?"); return; } @@ -2688,7 +2688,7 @@ void MainWindow::addRemoveCategories_MenuHandler() { void MainWindow::replaceCategories_MenuHandler() { QMenu *menu = qobject_cast(sender()); - if (menu == NULL) { + if (menu == nullptr) { qCritical("not a menu?"); return; } @@ -2726,14 +2726,14 @@ void MainWindow::replaceCategories_MenuHandler() { void MainWindow::savePrimaryCategory() { QMenu *menu = qobject_cast(sender()); - if (menu == NULL) { + if (menu == nullptr) { qCritical("not a menu?"); return; } foreach (QAction* action, menu->actions()) { QWidgetAction *widgetAction = qobject_cast(action); - if (widgetAction != NULL) { + if (widgetAction != nullptr) { QRadioButton *btn = qobject_cast(widgetAction->defaultWidget()); if (btn->isChecked()) { QModelIndexList selected = ui->modList->selectionModel()->selectedRows(); @@ -2854,7 +2854,7 @@ void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInf void MainWindow::addPrimaryCategoryCandidates() { QMenu *menu = qobject_cast(sender()); - if (menu == NULL) { + if (menu == nullptr) { qCritical("not a menu?"); return; } @@ -2866,7 +2866,7 @@ void MainWindow::addPrimaryCategoryCandidates() void MainWindow::enableVisibleMods() { - if (QMessageBox::question(NULL, tr("Confirm"), tr("Really enable all visible mods?"), + if (QMessageBox::question(nullptr, tr("Confirm"), tr("Really enable all visible mods?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { m_ModListSortProxy->enableAllVisible(); } @@ -2874,7 +2874,7 @@ void MainWindow::enableVisibleMods() void MainWindow::disableVisibleMods() { - if (QMessageBox::question(NULL, tr("Confirm"), tr("Really disable all visible mods?"), + if (QMessageBox::question(nullptr, tr("Confirm"), tr("Really disable all visible mods?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { m_ModListSortProxy->disableAllVisible(); } @@ -2967,7 +2967,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) m_ContextIdx = mapToModel(m_OrganizerCore.modList(), modList->indexAt(pos)); m_ContextRow = m_ContextIdx.row(); - QMenu *menu = NULL; + QMenu *menu = nullptr; QMenu *allMods = modListContextMenu(); if (m_ContextRow == -1) { // no selection @@ -3121,7 +3121,7 @@ void MainWindow::fixMods_clicked() { QListWidgetItem *selectedItem = ui->savegameList->currentItem(); - if (selectedItem == NULL) + if (selectedItem == nullptr) return; // if required, parse the save game @@ -3345,20 +3345,20 @@ void MainWindow::on_actionSettings_triggered() void MainWindow::on_actionNexus_triggered() { - ::ShellExecuteW(NULL, L"open", GameInfo::instance().getNexusPage(false).c_str(), NULL, NULL, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", GameInfo::instance().getNexusPage(false).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } void MainWindow::nexusLinkActivated(const QString &link) { - ::ShellExecuteW(NULL, L"open", ToWString(link).c_str(), NULL, NULL, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", ToWString(link).c_str(), nullptr, nullptr, SW_SHOWNORMAL); ui->tabWidget->setCurrentIndex(4); } void MainWindow::linkClicked(const QString &url) { - ::ShellExecuteW(NULL, L"open", ToWString(url).c_str(), NULL, NULL, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", ToWString(url).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } @@ -3470,7 +3470,7 @@ int MainWindow::getBinaryExecuteInfo(const QFileInfo &targetInfo, { // try to find java automatically WCHAR buffer[MAX_PATH]; - if (::FindExecutableW(targetPathW.c_str(), NULL, buffer) > (HINSTANCE)32) { + if (::FindExecutableW(targetPathW.c_str(), nullptr, buffer) > (HINSTANCE)32) { DWORD binaryType = 0UL; if (!::GetBinaryTypeW(targetPathW.c_str(), &binaryType)) { qDebug("failed to determine binary type of \"%ls\": %lu", targetPathW.c_str(), ::GetLastError()); @@ -3507,7 +3507,7 @@ int MainWindow::getBinaryExecuteInfo(const QFileInfo &targetInfo, void MainWindow::addAsExecutable() { - if (m_ContextItem != NULL) { + if (m_ContextItem != nullptr) { QFileInfo targetInfo(m_ContextItem->data(0, Qt::UserRole).toString()); QFileInfo binaryInfo; QString arguments; @@ -3605,9 +3605,9 @@ void MainWindow::previewDataFile() offset = fileName.indexOf("/", offset); fileName = fileName.mid(offset + 1); - const FileEntry::Ptr file = m_OrganizerCore.directoryStructure()->searchFile(ToWString(fileName), NULL); + const FileEntry::Ptr file = m_OrganizerCore.directoryStructure()->searchFile(ToWString(fileName), nullptr); - if (file.get() == NULL) { + if (file.get() == nullptr) { reportError(tr("file not found: %1").arg(fileName)); return; } @@ -3641,7 +3641,7 @@ void MainWindow::previewDataFile() void MainWindow::openDataFile() { - if (m_ContextItem != NULL) { + if (m_ContextItem != nullptr) { QFileInfo targetInfo(m_ContextItem->data(0, Qt::UserRole).toString()); QFileInfo binaryInfo; QString arguments; @@ -3650,7 +3650,7 @@ void MainWindow::openDataFile() 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); + ::ShellExecuteW(nullptr, L"open", ToWString(targetInfo.absoluteFilePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } break; default: { // nop @@ -3702,7 +3702,7 @@ void MainWindow::on_dataTree_customContextMenuRequested(const QPoint &pos) m_ContextItem = dataTree->itemAt(pos.x(), pos.y()); QMenu menu; - if ((m_ContextItem != NULL) && (m_ContextItem->childCount() == 0)) { + if ((m_ContextItem != nullptr) && (m_ContextItem->childCount() == 0)) { menu.addAction(tr("Open/Execute"), this, SLOT(openDataFile())); menu.addAction(tr("Add as Executable"), this, SLOT(addAsExecutable())); @@ -3984,9 +3984,9 @@ void MainWindow::displayColumnSelection(const QPoint &pos) int i = 1; foreach (const QAction *action, menu.actions()) { const QWidgetAction *widgetAction = qobject_cast(action); - if (widgetAction != NULL) { + if (widgetAction != nullptr) { const QCheckBox *checkBox = qobject_cast(widgetAction->defaultWidget()); - if (checkBox != NULL) { + if (checkBox != nullptr) { ui->modList->header()->setSectionHidden(i, !checkBox->isChecked()); } } @@ -4109,7 +4109,7 @@ void MainWindow::removeFromToolbar() void MainWindow::toolBar_customContextMenuRequested(const QPoint &point) { QAction *action = ui->toolBar->actionAt(point); - if (action != NULL) { + if (action != nullptr) { if (action->objectName().startsWith("custom_")) { m_ContextAction = action; QMenu menu; @@ -4161,10 +4161,10 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) void MainWindow::on_groupCombo_currentIndexChanged(int index) { - if (m_ModListSortProxy == NULL) { + if (m_ModListSortProxy == nullptr) { return; } - QAbstractProxyModel *newModel = NULL; + QAbstractProxyModel *newModel = nullptr; switch (index) { case 1: { newModel = new QtGroupingProxy(m_OrganizerCore.modList(), QModelIndex(), ModList::COL_CATEGORY, Qt::UserRole, @@ -4176,11 +4176,11 @@ void MainWindow::on_groupCombo_currentIndexChanged(int index) Qt::UserRole + 2); } break; default: { - newModel = NULL; + newModel = nullptr; } break; } - if (newModel != NULL) { + if (newModel != nullptr) { #ifdef TEST_MODELS new ModelTest(newModel, this); #endif // TEST_MODELS @@ -4220,7 +4220,7 @@ void MainWindow::createStdoutPipe(HANDLE *stdOutRead, HANDLE *stdOutWrite) SECURITY_ATTRIBUTES secAttributes; secAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); secAttributes.bInheritHandle = TRUE; - secAttributes.lpSecurityDescriptor = NULL; + secAttributes.lpSecurityDescriptor = nullptr; if (!::CreatePipe(stdOutRead, stdOutWrite, &secAttributes, 0)) { qCritical("failed to create stdout reroute"); @@ -4242,7 +4242,7 @@ std::string MainWindow::readFromPipe(HANDLE stdOutRead) DWORD read = 1; while (read > 0) { - if (!::ReadFile(stdOutRead, buffer, chunkSize, &read, NULL)) { + if (!::ReadFile(stdOutRead, buffer, chunkSize, &read, nullptr)) { break; } if (read > 0) { diff --git a/src/messagedialog.cpp b/src/messagedialog.cpp index 36902bbf..90b0a9d3 100644 --- a/src/messagedialog.cpp +++ b/src/messagedialog.cpp @@ -52,7 +52,7 @@ MessageDialog::MessageDialog(const QString &text, QWidget *reference) : this->setFocusPolicy(Qt::NoFocus); this->setAttribute(Qt::WA_ShowWithoutActivating); QTimer::singleShot(1000 + (text.length() * 40), this, SLOT(hide())); - if (reference != NULL) { + if (reference != nullptr) { QPoint position = reference->mapToGlobal(QPoint(reference->width() / 2, reference->height())); position.rx() -= this->width() / 2; position.ry() -= this->height() + 5; @@ -70,7 +70,7 @@ MessageDialog::~MessageDialog() void MessageDialog::resizeEvent(QResizeEvent *event) { QWidget *par = parentWidget(); - if (par != NULL) { + if (par != nullptr) { QPoint position = par->mapToGlobal(QPoint(par->width() / 2, par->height())); position.rx() -= event->size().width() / 2; position.ry() -= event->size().height() + 5; @@ -83,7 +83,7 @@ void MessageDialog::showMessage(const QString &text, QWidget *reference, bool br { qDebug("%s", qPrintable(text)); if (reference != nullptr) { - if (bringToFront || (qApp->activeWindow() != NULL)) { + if (bringToFront || (qApp->activeWindow() != nullptr)) { MessageDialog *dialog = new MessageDialog(text, reference); dialog->show(); reference->activateWindow(); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index d773823a..aa831e04 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -639,7 +639,7 @@ void ModInfoRegular::nxmRequestFailed(int, int, QVariant userData, const QString if (userData.canConvert() && (userData.toInt() == 1)) { fullMessage += "\nNexus will reject endorsements within 15 Minutes of a failed attempt, the error message may be misleading."; } - if (QApplication::activeWindow() != NULL) { + if (QApplication::activeWindow() != nullptr) { MessageDialog::showMessage(fullMessage, QApplication::activeWindow()); } emit modDetailsUpdated(false); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 9133b166..0dfdd1f4 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -63,8 +63,8 @@ bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS) ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, bool unmanaged, QWidget *parent) : TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_ModInfo(modInfo), m_ThumbnailMapper(this), m_RequestStarted(false), - m_DeleteAction(NULL), m_RenameAction(NULL), m_OpenAction(NULL), - m_Directory(directory), m_Origin(NULL) + m_DeleteAction(nullptr), m_RenameAction(nullptr), m_OpenAction(nullptr), + m_Directory(directory), m_Origin(nullptr) { ui->setupUi(this); this->setWindowTitle(modInfo->name()); @@ -90,7 +90,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo if (directory->originExists(ToWString(modInfo->name()))) { m_Origin = &directory->getOriginByName(ToWString(modInfo->name())); if (m_Origin->isDisabled()) { - m_Origin = NULL; + m_Origin = nullptr; } } @@ -115,7 +115,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } initINITweaks(); - ui->tabWidget->setTabEnabled(TAB_CONFLICTS, m_Origin != NULL); + ui->tabWidget->setTabEnabled(TAB_CONFLICTS, m_Origin != nullptr); if (ui->tabWidget->currentIndex() == TAB_NEXUS) { activateNexusTab(); @@ -248,7 +248,7 @@ void ModInfoDialog::refreshLists() ui->overwriteTree->clear(); ui->overwrittenTree->clear(); - if (m_Origin != NULL) { + if (m_Origin != nullptr) { std::vector files = m_Origin->getFiles(); for (auto iter = files.begin(); iter != files.end(); ++iter) { QString relativeName = QDir::fromNativeSeparators(ToQString((*iter)->getRelativePath())); @@ -692,7 +692,7 @@ void ModInfoDialog::linkClicked(const QUrl &url) this->close(); emit nexusLinkActivated(url.toString()); } else { - ::ShellExecuteW(NULL, L"open", ToWString(url.toString()).c_str(), NULL, NULL, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", ToWString(url.toString()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } } @@ -981,7 +981,7 @@ void ModInfoDialog::openFile(const QModelIndex &index) { QString fileName = m_FileSystemModel->filePath(index); - HINSTANCE res = ::ShellExecuteW(NULL, L"open", ToWString(fileName).c_str(), NULL, NULL, SW_SHOW); + HINSTANCE res = ::ShellExecuteW(nullptr, L"open", ToWString(fileName).c_str(), nullptr, nullptr, SW_SHOW); if ((int)res <= 32) { qCritical("failed to invoke %s: %d", fileName.toUtf8().constData(), res); } @@ -1065,7 +1065,7 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) void ModInfoDialog::on_categoriesTree_itemChanged(QTreeWidgetItem *item, int) { QTreeWidgetItem *parent = item->parent(); - while ((parent != NULL) && ((parent->flags() & Qt::ItemIsUserCheckable) != 0) && (parent->checkState(0) == Qt::Unchecked)) { + while ((parent != nullptr) && ((parent->flags() & Qt::ItemIsUserCheckable) != 0) && (parent->checkState(0) == Qt::Unchecked)) { parent->setCheckState(0, Qt::Checked); parent = parent->parent(); } @@ -1184,7 +1184,7 @@ void ModInfoDialog::on_overwriteTree_customContextMenuRequested(const QPoint &po { m_ConflictsContextItem = ui->overwriteTree->itemAt(pos.x(), pos.y()); - if (m_ConflictsContextItem != NULL) { + if (m_ConflictsContextItem != nullptr) { // offer to hide/unhide file, but not for files from archives if (!m_ConflictsContextItem->data(1, Qt::UserRole + 2).toBool()) { QMenu menu; diff --git a/src/modlist.cpp b/src/modlist.cpp index fb8df15e..1bd7b0f0 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -50,8 +50,8 @@ using namespace MOBase; ModList::ModList(QObject *parent) : QAbstractItemModel(parent) - , m_Profile(NULL) - , m_NexusInterface(NULL) + , m_Profile(nullptr) + , m_NexusInterface(nullptr) , m_Modified(false) , m_FontMetrics(QFont()) , m_DropOnItems(false) @@ -177,7 +177,7 @@ QString ModList::contentsToToolTip(const std::vector &content QVariant ModList::data(const QModelIndex &modelIndex, int role) const { - if (m_Profile == NULL) return QVariant(); + if (m_Profile == nullptr) return QVariant(); if (!modelIndex.isValid()) return QVariant(); unsigned int modIndex = modelIndex.row(); int column = modelIndex.column(); @@ -410,7 +410,7 @@ bool ModList::renameMod(int index, const QString &newName) { QString nameFixed = newName; if (!fixDirectoryName(nameFixed) || nameFixed.isEmpty()) { - MessageDialog::showMessage(tr("Invalid name"), NULL); + MessageDialog::showMessage(tr("Invalid name"), nullptr); return false; } @@ -433,7 +433,7 @@ bool ModList::renameMod(int index, const QString &newName) bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) { - if (m_Profile == NULL) return false; + if (m_Profile == nullptr) return false; if (static_cast(index.row()) >= ModInfo::getNumMods()) { return false; @@ -591,7 +591,7 @@ QStringList ModList::mimeTypes() const void ModList::changeModPriority(std::vector sourceIndices, int newPriority) { - if (m_Profile == NULL) return; + if (m_Profile == nullptr) return; emit layoutAboutToBeChanged(); Profile *profile = m_Profile; @@ -625,7 +625,7 @@ void ModList::changeModPriority(std::vector sourceIndices, int newPriority) void ModList::changeModPriority(int sourceIndex, int newPriority) { - if (m_Profile == NULL) return; + if (m_Profile == nullptr) return; emit layoutAboutToBeChanged(); m_Profile->setModPriority(sourceIndex, newPriority); @@ -839,7 +839,7 @@ bool ModList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int return true; } - if (m_Profile == NULL) return false; + if (m_Profile == nullptr) return false; if (mimeData->hasUrls()) { return dropURLs(mimeData, row, parent); } else { @@ -854,7 +854,7 @@ void ModList::removeRowForce(int row) if (static_cast(row) >= ModInfo::getNumMods()) { return; } - if (m_Profile == NULL) return; + if (m_Profile == nullptr) return; m_Profile->setModEnabled(row, false); @@ -882,7 +882,7 @@ void ModList::removeRow(int row, const QModelIndex&) if (static_cast(row) >= ModInfo::getNumMods()) { return; } - if (m_Profile == NULL) return; + if (m_Profile == nullptr) return; m_Profile->setModEnabled(row, false); @@ -992,28 +992,28 @@ bool ModList::eventFilter(QObject *obj, QEvent *event) if (event->type() == QEvent::ContextMenu) { QContextMenuEvent *contextEvent = static_cast(event); QWidget *object = qobject_cast(obj); - if ((object != NULL) && (contextEvent->reason() == QContextMenuEvent::Mouse)) { + if ((object != nullptr) && (contextEvent->reason() == QContextMenuEvent::Mouse)) { emit requestColumnSelect(object->mapToGlobal(contextEvent->pos())); return true; } - } else if ((event->type() == QEvent::KeyPress) && (m_Profile != NULL)) { + } else if ((event->type() == QEvent::KeyPress) && (m_Profile != nullptr)) { QAbstractItemView *itemView = qobject_cast(obj); QKeyEvent *keyEvent = static_cast(event); - if ((itemView != NULL) + if ((itemView != nullptr) && (keyEvent->modifiers() == Qt::ControlModifier) && ((keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down))) { QItemSelectionModel *selectionModel = itemView->selectionModel(); const QAbstractProxyModel *proxyModel = qobject_cast(selectionModel->model()); - const QSortFilterProxyModel *filterModel = NULL; - while ((filterModel == NULL) && (proxyModel != NULL)) { + const QSortFilterProxyModel *filterModel = nullptr; + while ((filterModel == nullptr) && (proxyModel != nullptr)) { filterModel = qobject_cast(proxyModel); - if (filterModel == NULL) { + if (filterModel == nullptr) { proxyModel = qobject_cast(proxyModel->sourceModel()); } } - if (filterModel == NULL) { + if (filterModel == nullptr) { return true; } int diff = -1; @@ -1028,7 +1028,7 @@ bool ModList::eventFilter(QObject *obj, QEvent *event) } } foreach (QModelIndex idx, rows) { - if (filterModel != NULL) { + if (filterModel != nullptr) { idx = filterModel->mapToSource(idx); } int newPriority = m_Profile->getModPriority(idx.row()) + diff; @@ -1054,7 +1054,7 @@ bool ModList::eventFilter(QObject *obj, QEvent *event) QModelIndex minRow, maxRow; foreach (QModelIndex idx, selectionModel->selectedRows()) { - if (proxyModel != NULL) { + if (proxyModel != nullptr) { idx = proxyModel->mapToSource(idx); } if (!minRow.isValid() || (idx.row() < minRow.row())) { diff --git a/src/modlist.h b/src/modlist.h index cf52b2ec..8dcab2fa 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -73,7 +73,7 @@ public: * @brief constructor * @todo ensure this view works without a profile set, otherwise there are intransparent dependencies on the initialisation order **/ - ModList(QObject *parent = NULL); + ModList(QObject *parent = nullptr); ~ModList(); diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index e132b71e..d16f8353 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -81,7 +81,7 @@ Qt::ItemFlags ModListSortProxy::flags(const QModelIndex &modelIndex) const void ModListSortProxy::enableAllVisible() { - if (m_Profile == NULL) return; + if (m_Profile == nullptr) return; for (int i = 0; i < this->rowCount(); ++i) { int modID = mapToSource(index(i, 0)).data(Qt::UserRole + 1).toInt(); @@ -92,7 +92,7 @@ void ModListSortProxy::enableAllVisible() void ModListSortProxy::disableAllVisible() { - if (m_Profile == NULL) return; + if (m_Profile == nullptr) return; for (int i = 0; i < this->rowCount(); ++i) { int modID = mapToSource(index(i, 0)).data(Qt::UserRole + 1).toInt(); @@ -313,7 +313,7 @@ void ModListSortProxy::setFilterMode(ModListSortProxy::FilterMode mode) bool ModListSortProxy::filterAcceptsRow(int row, const QModelIndex &parent) const { - if (m_Profile == NULL) { + if (m_Profile == nullptr) { return false; } diff --git a/src/motddialog.cpp b/src/motddialog.cpp index 9fa2f005..59ace4d1 100644 --- a/src/motddialog.cpp +++ b/src/motddialog.cpp @@ -45,5 +45,5 @@ void MotDDialog::on_okButton_clicked() void MotDDialog::linkClicked(const QUrl &url) { - ::ShellExecuteW(NULL, L"open", MOBase::ToWString(url.toString()).c_str(), NULL, NULL, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", MOBase::ToWString(url.toString()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index d68fe8fe..bd40921f 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -218,8 +218,8 @@ void NexusInterface::interpretNexusFileName(const QString &fileName, QString &mo QString r3Highlight(fileName); r3Highlight.insert(result.position(3) + result.length(3), "* ").insert(result.position(3), " *"); - selection.addChoice(candidate.c_str(), r3Highlight, strtol(candidate.c_str(), NULL, 10)); - selection.addChoice(candidate2.c_str() + offset, r2Highlight, abs(strtol(candidate2.c_str() + offset, NULL, 10))); + selection.addChoice(candidate.c_str(), r3Highlight, strtol(candidate.c_str(), nullptr, 10)); + selection.addChoice(candidate2.c_str() + offset, r2Highlight, abs(strtol(candidate2.c_str() + offset, nullptr, 10))); if (selection.exec() == QDialog::Accepted) { modID = selection.getChoiceData().toInt(); } else { @@ -229,7 +229,7 @@ void NexusInterface::interpretNexusFileName(const QString &fileName, QString &mo modID = -1; } } else { - modID = strtol(candidate.c_str(), NULL, 10); + modID = strtol(candidate.c_str(), nullptr, 10); } qDebug("mod id guessed: %s -> %d", qPrintable(fileName), modID); } else if (std::tr1::regex_search(fileNameUTF8.constData(), result, simpleexp)) { @@ -542,7 +542,7 @@ void NexusInterface::requestFinished() void NexusInterface::requestError(QNetworkReply::NetworkError) { QNetworkReply *reply = qobject_cast(sender()); - if (reply == NULL) { + if (reply == nullptr) { qWarning("invalid sender type"); return; } @@ -555,7 +555,7 @@ void NexusInterface::requestError(QNetworkReply::NetworkError) void NexusInterface::requestTimeout() { QTimer *timer = qobject_cast(sender()); - if (timer == NULL) { + if (timer == nullptr) { qWarning("invalid sender type"); return; } @@ -577,10 +577,10 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , int nexusGameId) : m_ModID(modID) , m_FileID(0) - , m_Reply(NULL) + , m_Reply(nullptr) , m_Type(type) , m_UserData(userData) - , m_Timeout(NULL) + , m_Timeout(nullptr) , m_Reroute(false) , m_ID(s_NextID.fetchAndAddAcquire(1)) , m_URL(url) @@ -598,10 +598,10 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(std::vector modIDList : m_ModID(-1) , m_ModIDList(modIDList) , m_FileID(0) - , m_Reply(NULL) + , m_Reply(nullptr) , m_Type(type) , m_UserData(userData) - , m_Timeout(NULL) + , m_Timeout(nullptr) , m_Reroute(false) , m_ID(s_NextID.fetchAndAddAcquire(1)) , m_URL(url) @@ -619,10 +619,10 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , int nexusGameId) : m_ModID(modID) , m_FileID(fileID) - , m_Reply(NULL) + , m_Reply(nullptr) , m_Type(type) , m_UserData(userData) - , m_Timeout(NULL) + , m_Timeout(nullptr) , m_Reroute(false) , m_ID(s_NextID.fetchAndAddAcquire(1)) , m_URL(url) diff --git a/src/noeditdelegate.cpp b/src/noeditdelegate.cpp index ab8a4880..d5147b9f 100644 --- a/src/noeditdelegate.cpp +++ b/src/noeditdelegate.cpp @@ -6,5 +6,5 @@ NoEditDelegate::NoEditDelegate(QObject *parent) } QWidget *NoEditDelegate::createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const { - return NULL; + return nullptr; } diff --git a/src/noeditdelegate.h b/src/noeditdelegate.h index d0ba2f68..6fd5ba76 100644 --- a/src/noeditdelegate.h +++ b/src/noeditdelegate.h @@ -5,7 +5,7 @@ class NoEditDelegate: public QStyledItemDelegate { public: - NoEditDelegate(QObject *parent = NULL); + NoEditDelegate(QObject *parent = nullptr); virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 18568dad..db314be6 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -44,7 +44,7 @@ using namespace MOShared; NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) : QNetworkAccessManager(parent) - , m_LoginReply(NULL) + , m_LoginReply(nullptr) , m_ProgressDialog() , m_MOVersion(moVersion) , m_LoginAttempted(false) @@ -56,9 +56,9 @@ NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) NXMAccessManager::~NXMAccessManager() { - if (m_LoginReply != NULL) { + if (m_LoginReply != nullptr) { m_LoginReply->deleteLater(); - m_LoginReply = NULL; + m_LoginReply = nullptr; } } @@ -104,13 +104,13 @@ bool NXMAccessManager::loggedIn() const bool NXMAccessManager::loginWaiting() const { - return m_LoginReply != NULL; + return m_LoginReply != nullptr; } void NXMAccessManager::login(const QString &username, const QString &password) { - if (m_LoginReply != NULL) { + if (m_LoginReply != nullptr) { return; } @@ -168,7 +168,7 @@ void NXMAccessManager::loginTimeout() { emit loginFailed(tr("timeout")); m_LoginReply->deleteLater(); - m_LoginReply = NULL; + m_LoginReply = nullptr; m_LoginAttempted = false; // this usually means we might have usccess later m_LoginTimeout.stop(); m_Username.clear(); @@ -180,10 +180,10 @@ void NXMAccessManager::loginError(QNetworkReply::NetworkError) { m_ProgressDialog.hide(); m_LoginTimeout.stop(); - if (m_LoginReply != NULL) { + if (m_LoginReply != nullptr) { emit loginFailed(m_LoginReply->errorString()); m_LoginReply->deleteLater(); - m_LoginReply = NULL; + m_LoginReply = nullptr; } else { emit loginFailed(tr("Unknown error")); } @@ -216,7 +216,7 @@ void NXMAccessManager::loginFinished() m_LoginTimeout.stop(); m_LoginReply->deleteLater(); - m_LoginReply = NULL; + m_LoginReply = nullptr; m_Username.clear(); m_Password.clear(); } diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 751cc010..fe032eba 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -91,7 +91,7 @@ static bool testForSteam() if (processIDs[i] != 0) { HANDLE process = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIDs[i]); - if (process != NULL) { + if (process != nullptr) { HMODULE module; DWORD ignore; @@ -463,7 +463,7 @@ MOBase::IModRepositoryBridge *OrganizerCore::createNexusBridge() const QString OrganizerCore::profileName() const { - if (m_CurrentProfile != NULL) { + if (m_CurrentProfile != nullptr) { return m_CurrentProfile->getName(); } else { return ""; @@ -472,7 +472,7 @@ QString OrganizerCore::profileName() const QString OrganizerCore::profilePath() const { - if (m_CurrentProfile != NULL) { + if (m_CurrentProfile != nullptr) { return m_CurrentProfile->getPath(); } else { return ""; @@ -493,7 +493,7 @@ MOBase::IModInterface *OrganizerCore::getMod(const QString &name) { unsigned int index = ModInfo::getIndex(name); if (index == UINT_MAX) { - return NULL; + return nullptr; } else { return ModInfo::getByIndex(index).data(); } @@ -502,7 +502,7 @@ MOBase::IModInterface *OrganizerCore::getMod(const QString &name) MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) { if (!m_InstallationManager.testOverwrite(name)) { - return NULL; + return nullptr; } m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); @@ -670,7 +670,7 @@ QStringList OrganizerCore::listDirectories(const QString &directoryName) const { QStringList result; DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(directoryName)); - if (dir != NULL) { + if (dir != nullptr) { std::vector::iterator current, end; dir->getSubDirectories(current, end); for (; current != end; ++current) { @@ -684,7 +684,7 @@ QStringList OrganizerCore::findFiles(const QString &path, const std::functionfindSubDirectoryRecursive(ToWString(path)); - if (dir != NULL) { + if (dir != nullptr) { std::vector files = dir->getFiles(); foreach (FileEntry::Ptr file, files) { if (filter(ToQString(file->getFullPath()))) { @@ -700,9 +700,9 @@ QStringList OrganizerCore::findFiles(const QString &path, const std::functionsearchFile(ToWString(QFileInfo(fileName).fileName()), NULL); + const FileEntry::Ptr file = m_DirectoryStructure->searchFile(ToWString(QFileInfo(fileName).fileName()), nullptr); - if (file.get() != NULL) { + if (file.get() != nullptr) { result.append(ToQString(m_DirectoryStructure->getOriginByID(file->getOrigin()).getName())); foreach (int i, file->getAlternatives()) { result.append(ToQString(m_DirectoryStructure->getOriginByID(i).getName())); @@ -717,7 +717,7 @@ QList OrganizerCore::findFileInfos(const QString & { QList result; DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path)); - if (dir != NULL) { + if (dir != nullptr) { std::vector files = dir->getFiles(); foreach (FileEntry::Ptr file, files) { IOrganizer::FileInfo info; @@ -987,7 +987,7 @@ bool OrganizerCore::waitForProcessOrJob(HANDLE handle, LPDWORD exitCode) res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); } - if (exitCode != NULL) { + if (exitCode != nullptr) { ::GetExitCodeProcess(processHandle, exitCode); } ::CloseHandle(processHandle); diff --git a/src/organizerproxy.h b/src/organizerproxy.h index 625e86aa..aa2a7fab 100644 --- a/src/organizerproxy.h +++ b/src/organizerproxy.h @@ -38,7 +38,7 @@ public: virtual MOBase::IPluginList *pluginList(); virtual MOBase::IModList *modList(); virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); - virtual bool waitForApplication(HANDLE handle, LPDWORD exitCode = NULL) const; + virtual bool waitForApplication(HANDLE handle, LPDWORD exitCode = nullptr) const; virtual void refreshModList(bool saveChanges); virtual bool onAboutToRun(const std::function &func); diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp index d06922bc..e956b025 100644 --- a/src/overwriteinfodialog.cpp +++ b/src/overwriteinfodialog.cpp @@ -71,8 +71,8 @@ private: OverwriteInfoDialog::OverwriteInfoDialog(ModInfo::Ptr modInfo, QWidget *parent) - : QDialog(parent), ui(new Ui::OverwriteInfoDialog), m_FileSystemModel(NULL), - m_DeleteAction(NULL), m_RenameAction(NULL), m_OpenAction(NULL) + : QDialog(parent), ui(new Ui::OverwriteInfoDialog), m_FileSystemModel(nullptr), + m_DeleteAction(nullptr), m_RenameAction(nullptr), m_OpenAction(nullptr) { ui->setupUi(this); @@ -185,7 +185,7 @@ void OverwriteInfoDialog::openFile(const QModelIndex &index) { QString fileName = m_FileSystemModel->filePath(index); - HINSTANCE res = ::ShellExecuteW(NULL, L"open", ToWString(fileName).c_str(), NULL, NULL, SW_SHOW); + HINSTANCE res = ::ShellExecuteW(nullptr, L"open", ToWString(fileName).c_str(), nullptr, nullptr, SW_SHOW); if ((int)res <= 32) { qCritical("failed to invoke %s: %d", fileName.toUtf8().constData(), res); } diff --git a/src/pdll.h b/src/pdll.h index 6e1d06f3..22d74557 100644 --- a/src/pdll.h +++ b/src/pdll.h @@ -26,7 +26,7 @@ //check for dll handle //if this is the first call to the function then try to load it //if not then if the function was loaded successfully make a call to it -//otherwise return a NULL cast to the return parameter. +//otherwise return a nullptr cast to the return parameter. #define DECLARE_FUNCTION0(CallType, retVal, FuncName) \ typedef retVal (CallType* TYPE_##FuncName)(); \ @@ -38,17 +38,17 @@ { \ if (FUNC_LOADED != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED;\ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(); \ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ } \ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ } #define DECLARE_FUNCTION1(CallType,retVal, FuncName, Param1) \ @@ -61,17 +61,17 @@ { \ if (FUNC_LOADED != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED;\ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(p1); \ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ } \ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ } #define DECLARE_FUNCTION2(CallType,retVal, FuncName, Param1, Param2) \ @@ -84,17 +84,17 @@ {\ if (FUNC_LOADED != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED;\ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(p1, p2); \ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ } \ else\ - return (retVal)NULL; \ + return (retVal)nullptr; \ } #define DECLARE_FUNCTION3(CallType,retVal, FuncName, Param1, Param2, Param3) \ @@ -107,17 +107,17 @@ {\ if (FUNC_LOADED != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED; \ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(p1, p2, p3);\ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ } \ else\ - return (retVal)NULL; \ + return (retVal)nullptr; \ } #define DECLARE_FUNCTION4(CallType,retVal, FuncName, Param1, Param2, Param3, Param4) \ @@ -130,17 +130,17 @@ {\ if (FUNC_LOADED != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED;\ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(p1, p2, p3, p4);\ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ } \ else\ - return (retVal)NULL; \ + return (retVal)nullptr; \ } #define DECLARE_FUNCTION5(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5) \ @@ -153,17 +153,17 @@ {\ if (FUNC_LOADED != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED;\ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(p1, p2, p3, p4, p5);\ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ } \ else\ - return (retVal)NULL; \ + return (retVal)nullptr; \ } #define DECLARE_FUNCTION6(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5, Param6) \ @@ -176,17 +176,17 @@ {\ if (FUNC_LOADED != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED;\ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(p1, p2, p3, p4, p5, p6);\ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ } \ else\ - return (retVal)NULL; \ + return (retVal)nullptr; \ } #define DECLARE_FUNCTION7(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5, Param6, Param7) \ @@ -199,17 +199,17 @@ {\ if (FUNC_LOADED != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED;\ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(p1, p2, p3, p4, p5, p6, p7);\ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ } \ else\ - return (retVal)NULL; \ + return (retVal)nullptr; \ } #define DECLARE_FUNCTION8(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8) \ @@ -222,17 +222,17 @@ {\ if (FUNC_LOADED != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED;\ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(p1, p2, p3, p4, p5, p6, p7, p8);\ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ }\ else\ - return (retVal)NULL; \ + return (retVal)nullptr; \ } #define DECLARE_FUNCTION9(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9) \ @@ -245,17 +245,17 @@ {\ if (FUNC_NAME != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED;\ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(p1, p2, p3, p4, p5, p6, p7, p8, p9);\ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ }\ else\ - return (retVal)NULL; \ + return (retVal)nullptr; \ } #define DECLARE_FUNCTION10(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10) \ @@ -268,17 +268,17 @@ {\ if (FUNC_LOADED != m_is##FuncName) \ {\ - m_##FuncName = NULL; \ + m_##FuncName = nullptr; \ m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ m_is##FuncName = FUNC_LOADED;\ }\ - if (NULL != m_##FuncName) \ + if (nullptr != m_##FuncName) \ return m_##FuncName(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);\ else \ - return (retVal)NULL; \ + return (retVal)nullptr; \ }\ else \ - return (retVal)NULL;\ + return (retVal)nullptr;\ } //declare constructors and LoadFunctions @@ -299,12 +299,12 @@ public: PDLL() { - m_dllHandle = NULL; - m_dllName = NULL; + m_dllHandle = nullptr; + m_dllName = nullptr; m_refCount = 0; } - //A NULL here means the name has already been set + //A nullptr here means the name has already been set void LoadDll(LPCTSTR name, bool showMsg = true) { if (name) @@ -313,7 +313,7 @@ public: //try to load m_dllHandle = LoadLibrary(m_dllName); - if (m_dllHandle == NULL && showMsg) + if (m_dllHandle == nullptr && showMsg) { std::ostringstream message; message << "failed to load dll: " << ::GetLastError(); @@ -332,7 +332,7 @@ public: if (m_dllName) { delete []m_dllName; - m_dllName = NULL; + m_dllName = nullptr; } //They may be setting this null (e.g., uninitialize) @@ -357,8 +357,8 @@ public: if (m_refCount == 1 && m_dllName) //if this is first time, load the DLL { //we are assuming the name is already set - LoadDll(NULL, showMsg); - retVal = (m_dllHandle != NULL); + LoadDll(nullptr, showMsg); + retVal = (m_dllHandle != nullptr); } return retVal; } @@ -377,10 +377,10 @@ public: if (m_dllHandle) { FreeLibrary(m_dllHandle); - m_dllHandle = NULL; + m_dllHandle = nullptr; } - SetDllName(NULL); //clear out the name & free memory + SetDllName(nullptr); //clear out the name & free memory } } @@ -394,7 +394,7 @@ public: if (m_dllName) { delete [] m_dllName; - m_dllName = NULL; + m_dllName = nullptr; } } diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 92073c12..bbcd3b6a 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -59,7 +59,7 @@ QStringList PluginContainer::pluginFileNames() const bool PluginContainer::verifyPlugin(IPlugin *plugin) { - if (plugin == NULL) { + if (plugin == nullptr) { return false; } else if (!plugin->init(new OrganizerProxy(m_Organizer, plugin->name()))) { qWarning("plugin failed to initialize"); @@ -89,7 +89,7 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName) { // diagnosis plugins IPluginDiagnose *diagnose = qobject_cast(plugin); - if (diagnose != NULL) { + if (diagnose != nullptr) { bf::at_key(m_Plugins).push_back(diagnose); m_DiagnosisConnections.push_back( diagnose->onInvalidated([&] () { emit diagnosisUpdate(); }) @@ -142,7 +142,7 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName) foreach (const QString &pluginName, pluginNames) { try { QObject *proxiedPlugin = proxy->instantiate(pluginName); - if (proxiedPlugin != NULL) { + if (proxiedPlugin != nullptr) { if (registerPlugin(proxiedPlugin, pluginName)) { qDebug("loaded plugin \"%s\"", qPrintable(pluginName)); } else { @@ -264,7 +264,7 @@ void PluginContainer::loadPlugins() QString pluginName = iter.filePath(); if (QLibrary::isLibrary(pluginName)) { QPluginLoader *pluginLoader = new QPluginLoader(pluginName, this); - if (pluginLoader->instance() == NULL) { + if (pluginLoader->instance() == nullptr) { m_FailedPlugins.push_back(pluginName); qCritical("failed to load plugin %s: %s", qPrintable(pluginName), qPrintable(pluginLoader->errorString())); diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 2b1fcca1..f0352e8a 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -88,7 +88,7 @@ PluginList::PluginList(QObject *parent) m_Utf8Codec = QTextCodec::codecForName("utf-8"); m_LocalCodec = QTextCodec::codecForName("Windows-1252"); - if (m_LocalCodec == NULL) { + if (m_LocalCodec == nullptr) { qCritical("required 8-bit string-encoding not supported."); m_LocalCodec = m_Utf8Codec; } @@ -142,7 +142,7 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD std::vector files = baseDirectory.getFiles(); for (auto iter = files.begin(); iter != files.end(); ++iter) { FileEntry::Ptr current = *iter; - if (current.get() == NULL) { + if (current.get() == nullptr) { continue; } QString filename = ToQString(current->getName()); @@ -157,7 +157,7 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD FilesOrigin &origin = baseDirectory.getOriginByID(current->getOrigin(archive)); QString iniPath = QFileInfo(filename).baseName() + ".ini"; - bool hasIni = baseDirectory.findFile(ToWString(iniPath)).get() != NULL; + bool hasIni = baseDirectory.findFile(ToWString(iniPath)).get() != nullptr; QString originName = ToQString(origin.getName()); unsigned int modIndex = ModInfo::getIndex(originName); @@ -243,7 +243,7 @@ void PluginList::enableESP(const QString &name, bool enable) void PluginList::enableAll() { - if (QMessageBox::question(NULL, tr("Confirm"), tr("Really enable all plugins?"), + if (QMessageBox::question(nullptr, tr("Confirm"), tr("Really enable all plugins?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { for (std::vector::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) { iter->m_Enabled = true; @@ -255,7 +255,7 @@ void PluginList::enableAll() void PluginList::disableAll() { - if (QMessageBox::question(NULL, tr("Confirm"), tr("Really disable all plugins?"), + if (QMessageBox::question(nullptr, tr("Confirm"), tr("Really disable all plugins?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { for (std::vector::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) { if (!iter->m_ForceEnabled) { @@ -515,14 +515,14 @@ bool PluginList::saveLoadOrder(DirectoryEntry &directoryStructure) for (std::vector::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) { std::wstring espName = ToWString(iter->m_Name); const FileEntry::Ptr fileEntry = directoryStructure.findFile(espName); - if (fileEntry.get() != NULL) { + if (fileEntry.get() != nullptr) { QString fileName; bool archive = false; int originid = fileEntry->getOrigin(archive); fileName = QString("%1\\%2").arg(QDir::toNativeSeparators(ToQString(directoryStructure.getOriginByID(originid).getPath()))).arg(iter->m_Name); HANDLE file = ::CreateFile(ToWString(fileName).c_str(), GENERIC_READ | GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (file == INVALID_HANDLE_VALUE) { if (::GetLastError() == ERROR_SHARING_VIOLATION) { // file is locked, probably the game is running @@ -541,7 +541,7 @@ bool PluginList::saveLoadOrder(DirectoryEntry &directoryStructure) newWriteTime.dwHighDateTime = (DWORD)(temp >> 32); iter->m_Time = newWriteTime; fileEntry->setFileTime(newWriteTime); - if (!::SetFileTime(file, NULL, NULL, &newWriteTime)) { + if (!::SetFileTime(file, nullptr, nullptr, &newWriteTime)) { throw windows_error(QObject::tr("failed to set file time %1").arg(fileName).toUtf8().constData()); } @@ -1102,7 +1102,7 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) if (event->type() == QEvent::KeyPress) { QAbstractItemView *itemView = qobject_cast(obj); - if (itemView == NULL) { + if (itemView == nullptr) { return QObject::eventFilter(obj, event); } @@ -1112,7 +1112,7 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) ((keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down))) { QItemSelectionModel *selectionModel = itemView->selectionModel(); const QSortFilterProxyModel *proxyModel = qobject_cast(selectionModel->model()); - if (proxyModel != NULL) { + if (proxyModel != nullptr) { int diff = -1; if (((keyEvent->key() == Qt::Key_Up) && (proxyModel->sortOrder() == Qt::DescendingOrder)) || ((keyEvent->key() == Qt::Key_Down) && (proxyModel->sortOrder() == Qt::AscendingOrder))) { @@ -1147,7 +1147,7 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) QModelIndex minRow, maxRow; foreach (QModelIndex idx, selectionModel->selectedRows()) { - if (proxyModel != NULL) { + if (proxyModel != nullptr) { idx = proxyModel->mapToSource(idx); } if (!minRow.isValid() || (idx.row() < minRow.row())) { diff --git a/src/pluginlist.h b/src/pluginlist.h index 933a38ee..da644ad3 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -96,7 +96,7 @@ public: * * @param parent parent object **/ - PluginList(QObject *parent = NULL); + PluginList(QObject *parent = nullptr); ~PluginList(); diff --git a/src/pluginlistsortproxy.cpp b/src/pluginlistsortproxy.cpp index 02d99b36..dd236f4c 100644 --- a/src/pluginlistsortproxy.cpp +++ b/src/pluginlistsortproxy.cpp @@ -86,9 +86,9 @@ void PluginListSortProxy::displayColumnSelection(const QPoint &pos) m_EnabledColumns.reset(); foreach (const QAction *action, menu.actions()) { const QWidgetAction *widgetAction = qobject_cast(action); - if (widgetAction != NULL) { + if (widgetAction != nullptr) { const QCheckBox *checkBox = qobject_cast(widgetAction->defaultWidget()); - if (checkBox != NULL) { + if (checkBox != nullptr) { m_EnabledColumns.set(i, checkBox->checkState() == Qt::Checked); } } diff --git a/src/problemsdialog.cpp b/src/problemsdialog.cpp index edb34f39..d8af6a62 100644 --- a/src/problemsdialog.cpp +++ b/src/problemsdialog.cpp @@ -73,5 +73,5 @@ void ProblemsDialog::startFix() void ProblemsDialog::urlClicked(const QUrl &url) { - ::ShellExecuteW(NULL, L"open", ToWString(url.toString()).c_str(), NULL, NULL, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", ToWString(url.toString()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } diff --git a/src/profile.cpp b/src/profile.cpp index 37ed94cc..a6b8bf00 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -42,7 +42,7 @@ using namespace MOBase; using namespace MOShared; Profile::Profile() - : m_SaveTimer(NULL) + : m_SaveTimer(nullptr) { initTimer(); } @@ -56,7 +56,7 @@ void Profile::touchFile(QString fileName) } Profile::Profile(const QString &name, bool useDefaultSettings) - : m_SaveTimer(NULL) + : m_SaveTimer(nullptr) { initTimer(); QString profilesDir = qApp->property("datapath").toString() + "/" + ToQString(AppConfig::profilesPath()); @@ -108,7 +108,7 @@ Profile::Profile(const QDir &directory) Profile::Profile(const Profile& reference) - : m_Directory(reference.m_Directory), m_SaveTimer(NULL) + : m_Directory(reference.m_Directory), m_SaveTimer(nullptr) { initTimer(); refreshModStatus(); @@ -553,7 +553,7 @@ void Profile::mergeTweak(const QString &tweakName, const QString &tweakedIni) co for (std::vector::iterator iter = sections.begin(); iter != sections.end(); ++iter) { // retrieve the names of all keys - size = ::GetPrivateProfileStringW(iter->c_str(), NULL, NULL, buffer.data(), + size = ::GetPrivateProfileStringW(iter->c_str(), nullptr, nullptr, buffer.data(), bufferSize, tweakNameW.c_str()); if (size == bufferSize - 2) { throw MyException(QString("Buffer too small. Please report this as a bug. " @@ -566,7 +566,7 @@ void Profile::mergeTweak(const QString &tweakName, const QString &tweakedIni) co keyIter != keys.end(); ++keyIter) { //TODO this treats everything as strings but how could I differentiate the type? ::GetPrivateProfileStringW(iter->c_str(), keyIter->c_str(), - NULL, buffer.data(), bufferSize, ToWString(tweakName).c_str()); + nullptr, buffer.data(), bufferSize, ToWString(tweakName).c_str()); ::WritePrivateProfileStringW(iter->c_str(), keyIter->c_str(), buffer.data(), tweakedIniW.c_str()); } @@ -586,7 +586,7 @@ void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const bool Profile::invalidationActive(bool *supported) const { if (GameInfo::instance().requiresBSAInvalidation()) { - if (supported != NULL) { + if (supported != nullptr) { *supported = true; } wchar_t buffer[1024]; @@ -597,7 +597,7 @@ bool Profile::invalidationActive(bool *supported) const if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), L"", buffer, 1024, iniFileName.c_str()) == 0) { if (errno != 0x02) { - if (supported != NULL) { + if (supported != nullptr) { *supported = false; } return false; diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp index 3fdad880..26891b3c 100644 --- a/src/profilesdialog.cpp +++ b/src/profilesdialog.cpp @@ -177,7 +177,7 @@ void ProfilesDialog::on_removeProfileButton_clicked() Profile::Ptr currentProfile = profilesList->currentItem()->data(Qt::UserRole).value(); QString profilePath; - if (currentProfile.get() == NULL) { + if (currentProfile.get() == nullptr) { profilePath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())) + "/" + 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. " @@ -190,7 +190,7 @@ void ProfilesDialog::on_removeProfileButton_clicked() profilePath = currentProfile->getPath(); } QListWidgetItem* item = profilesList->takeItem(profilesList->currentRow()); - if (item != NULL) { + if (item != nullptr) { delete item; } if (!shellDelete(QStringList(profilePath))) { @@ -231,7 +231,7 @@ void ProfilesDialog::on_invalidationBox_stateChanged(int state) QListWidget *profilesList = findChild("profilesList"); QListWidgetItem *currentItem = profilesList->currentItem(); - if (currentItem == NULL) { + if (currentItem == nullptr) { return; } if (!ui->invalidationBox->isEnabled()) { @@ -263,7 +263,7 @@ void ProfilesDialog::on_profilesList_currentItemChanged(QListWidgetItem *current QPushButton *transferButton = findChild("transferButton"); QPushButton *renameButton = findChild("renameButton"); - if (current != NULL) { + if (current != nullptr) { if (!current->data(Qt::UserRole).isValid()) return; const Profile::Ptr currentProfile = current->data(Qt::UserRole).value(); diff --git a/src/report.cpp b/src/report.cpp index 5cf36078..cca511a9 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -33,7 +33,7 @@ void reportError(QString message) QMessageBox messageBox(QMessageBox::Warning, QObject::tr("Error"), message, QMessageBox::Ok); messageBox.exec(); } else { - ::MessageBoxW(NULL, ToWString(message).c_str(), ToWString(QObject::tr("Error")).c_str(), MB_ICONERROR | MB_OK); + ::MessageBoxW(nullptr, ToWString(message).c_str(), ToWString(QObject::tr("Error")).c_str(), MB_ICONERROR | MB_OK); } } diff --git a/src/savegameinfowidget.cpp b/src/savegameinfowidget.cpp index 093b794c..7da89747 100644 --- a/src/savegameinfowidget.cpp +++ b/src/savegameinfowidget.cpp @@ -46,9 +46,9 @@ void SaveGameInfoWidget::setSave(const SaveGame *saveGame) ui->levelLabel->setText(QString("%1").arg(saveGame->pcLevel())); ui->dateLabel->setText(MOBase::ToString(saveGame->creationTime())); ui->screenshotLabel->setPixmap(QPixmap::fromImage(saveGame->screenshot())); - if (ui->gameFrame->layout() != NULL) { - QLayoutItem *item = NULL; - while ((item = ui->gameFrame->layout()->takeAt(0)) != NULL) { + if (ui->gameFrame->layout() != nullptr) { + QLayoutItem *item = nullptr; + while ((item = ui->gameFrame->layout()->takeAt(0)) != nullptr) { delete item->widget(); delete item; } diff --git a/src/selectiondialog.cpp b/src/selectiondialog.cpp index b4df8a1f..1b284928 100644 --- a/src/selectiondialog.cpp +++ b/src/selectiondialog.cpp @@ -23,7 +23,7 @@ along with Mod Organizer. If not, see . #include SelectionDialog::SelectionDialog(const QString &description, QWidget *parent) - : QDialog(parent), ui(new Ui::SelectionDialog), m_Choice(NULL), m_ValidateByData(false) + : QDialog(parent), ui(new Ui::SelectionDialog), m_Choice(nullptr), m_ValidateByData(false) { ui->setupUi(this); @@ -58,7 +58,7 @@ QVariant SelectionDialog::getChoiceData() QString SelectionDialog::getChoiceString() { - if ((m_Choice == NULL) || + if ((m_Choice == nullptr) || (m_ValidateByData && !m_Choice->property("data").isValid())) { return QString(); } else { diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 0f75e392..0c222ebb 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -48,7 +48,7 @@ typedef Archive* (*CreateArchiveType)(); template T resolveFunction(QLibrary &lib, const char *name) { T temp = reinterpret_cast(lib.resolve(name)); - if (temp == NULL) { + if (temp == nullptr) { throw std::runtime_error(QObject::tr("invalid 7-zip32.dll: %1").arg(lib.errorString()).toLatin1().constData()); } return temp; @@ -94,7 +94,6 @@ SelfUpdater::~SelfUpdater() void SelfUpdater::setUserInterface(QWidget *widget) { - m_Progress.setVisible(false); m_Parent = widget; } @@ -163,7 +162,7 @@ void SelfUpdater::download(const QString &downloadLink, const QString &fileName) void SelfUpdater::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - if (m_Reply != NULL) { + if (m_Reply != nullptr) { if (m_Canceled) { m_Reply->abort(); } else { @@ -177,7 +176,7 @@ void SelfUpdater::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) void SelfUpdater::downloadReadyRead() { - if (m_Reply != NULL) { + if (m_Reply != nullptr) { m_UpdateFile.write(m_Reply->readAll()); } } @@ -187,7 +186,7 @@ void SelfUpdater::downloadFinished() { int error = QNetworkReply::NoError; - if (m_Reply != NULL) { + if (m_Reply != nullptr) { m_UpdateFile.write(m_Reply->readAll()); error = m_Reply->error(); @@ -199,7 +198,7 @@ void SelfUpdater::downloadFinished() m_Progress.hide(); m_Reply->close(); m_Reply->deleteLater(); - m_Reply = NULL; + m_Reply = nullptr; } m_UpdateFile.close(); diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index d1947b5a..765858f5 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -125,7 +125,7 @@ void SettingsDialog::on_resetDialogsButton_clicked() void SettingsDialog::storeSettings(QListWidgetItem *pluginItem) { - if (pluginItem != NULL) { + if (pluginItem != nullptr) { QMap settings = pluginItem->data(Qt::UserRole + 1).toMap(); for (int i = 0; i < ui->pluginSettingsList->topLevelItemCount(); ++i) { diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 0adf0812..58bf5ae6 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -232,8 +232,8 @@ 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_LastAccessed = time(nullptr); + if (m_Parent != nullptr) { m_Parent->propagateOrigin(origin); } if (m_Origin == -1) { @@ -290,8 +290,8 @@ bool FileEntry::removeOrigin(int origin) // now we need to update the file time... std::wstring filePath = getFullPath(); HANDLE file = ::CreateFile(filePath.c_str(), GENERIC_READ | GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (!::GetFileTime(file, NULL, NULL, &m_FileTime)) { + 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (!::GetFileTime(file, nullptr, nullptr, &m_FileTime)) { // maybe this file is in a bsa, but there is no easy way to find out which. User should refresh // the view to find out m_Archive = L"bsa?"; @@ -324,13 +324,13 @@ static bool ByOriginPriority(DirectoryEntry *entry, int LHS, int RHS) FileEntry::FileEntry() - : m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(NULL), m_LastAccessed(time(NULL)) + : m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr), m_LastAccessed(time(nullptr)) { LEAK_TRACE; } FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) - : m_Index(index), m_Name(name), m_Origin(-1), m_Parent(parent), m_Archive(L""), m_LastAccessed(time(NULL)) + : m_Index(index), m_Name(name), m_Origin(-1), m_Parent(parent), m_Archive(L""), m_LastAccessed(time(nullptr)) { LEAK_TRACE; } @@ -351,7 +351,7 @@ void FileEntry::sortOrigins() bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) const { - if (parent == NULL) { + if (parent == nullptr) { return false; } else { // don't append the topmost parent because it is the virtual data-root @@ -480,7 +480,7 @@ void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &di void DirectoryEntry::propagateOrigin(int origin) { m_Origins.insert(origin); - if (m_Parent != NULL) { + if (m_Parent != nullptr) { m_Parent->propagateOrigin(origin); } } @@ -515,13 +515,13 @@ void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOf _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE - bufferOffset, L"\\*"); - HANDLE searchHandle = NULL; + HANDLE searchHandle = nullptr; if (SupportOptimizedFind()) { - searchHandle = ::FindFirstFileExW(buffer, FindExInfoBasic, &findData, FindExSearchNameMatch, NULL, + searchHandle = ::FindFirstFileExW(buffer, FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH); } else { - searchHandle = ::FindFirstFileExW(buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0); + searchHandle = ::FindFirstFileExW(buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, nullptr, 0); } if (searchHandle != INVALID_HANDLE_VALUE) { @@ -572,7 +572,7 @@ void DirectoryEntry::removeFile(const std::wstring &filePath, int *origin) std::wstring dirName = filePath.substr(0, pos); std::wstring rest = filePath.substr(pos + 1); DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); - if (entry != NULL) { + if (entry != nullptr) { entry->removeFile(rest, origin); } } @@ -608,7 +608,7 @@ void DirectoryEntry::removeDir(const std::wstring &path) std::wstring dirName = path.substr(0, pos); std::wstring rest = path.substr(pos + 1); DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); - if (entry != NULL) { + if (entry != nullptr) { entry->removeDir(rest); } } @@ -707,12 +707,12 @@ FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const int DirectoryEntry::getOrigin(const std::wstring &path, bool &archive) { - const DirectoryEntry *directory = NULL; + const DirectoryEntry *directory = nullptr; const FileEntry::Ptr file = searchFile(path, &directory); - if (file.get() != NULL) { + if (file.get() != nullptr) { return file->getOrigin(archive); } else { - if (directory != NULL) { + if (directory != nullptr) { return directory->anyOrigin(); } else { return -1; @@ -732,13 +732,13 @@ std::vector DirectoryEntry::getFiles() const const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const DirectoryEntry **directory) const { - if (directory != NULL) { - *directory = NULL; + if (directory != nullptr) { + *directory = nullptr; } if ((path.length() == 0) || (path == L"*")) { // no file name -> the path ended on a (back-)slash - if (directory != NULL) { + if (directory != nullptr) { *directory = this; } return FileEntry::Ptr(); @@ -751,9 +751,9 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const auto iter = m_Files.find(ToLower(path)); if (iter != m_Files.end()) { return m_FileRegister->getFile(iter->second); - } else if (directory != NULL) { + } else if (directory != nullptr) { DirectoryEntry *temp = findSubDirectory(path); - if (temp != NULL) { + if (temp != nullptr) { *directory = temp; } } @@ -761,7 +761,7 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const // file is in in a subdirectory, recurse into the matching subdirectory std::wstring pathComponent = path.substr(0, len); DirectoryEntry *temp = findSubDirectory(pathComponent); - if (temp != NULL) { + if (temp != nullptr) { return temp->searchFile(path.substr(len + 1), directory); } } @@ -776,7 +776,7 @@ DirectoryEntry *DirectoryEntry::findSubDirectory(const std::wstring &name) const return *iter; } } - return NULL; + return nullptr; } @@ -808,7 +808,7 @@ DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool c new DirectoryEntry(name, this, originID, m_FileRegister, m_OriginConnection)); return *iter; } else { - return NULL; + return nullptr; } } @@ -825,8 +825,8 @@ DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive(const std::wstring &pat return getSubDirectory(path, create); } else { DirectoryEntry *nextChild = getSubDirectory(path.substr(0, pos), create, originID); - if (nextChild == NULL) { - return NULL; + if (nextChild == nullptr) { + return nullptr; } else { return nextChild->getSubDirectoryRecursive(path.substr(pos + 1), create, originID); } @@ -888,7 +888,7 @@ void FileRegister::unregisterFile(FileEntry::Ptr file) } // unregister from directory - if (file->getParent() != NULL) { + if (file->getParent() != nullptr) { file->getParent()->removeFile(file->getIndex()); } } @@ -944,7 +944,7 @@ void FileRegister::removeOriginMulti(std::set indices, int ori // 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) { + if (file->getParent() != nullptr) { parents.insert(file->getParent()); } } diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index d588ab02..197062ef 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -250,12 +250,12 @@ public: /** retrieve a file in this directory by name. * @param name name of the file - * @return fileentry object for the file or NULL if no file matches + * @return fileentry object for the file or nullptr if no file matches */ const FileEntry::Ptr findFile(const std::wstring &name) const; /** search through this directory and all subdirectories for a file by the specified name (relative path). - if directory is not NULL, the referenced variable will be set to the path containing the file */ + if directory is not nullptr, the referenced variable will be set to the path containing the file */ const FileEntry::Ptr searchFile(const std::wstring &path, const DirectoryEntry **directory) const; void insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime); @@ -263,7 +263,7 @@ public: void removeFile(FileEntry::Index index); // remove the specified file from the tree. This can be a path leading to a file in a subdirectory - void removeFile(const std::wstring &filePath, int *origin = NULL); + void removeFile(const std::wstring &filePath, int *origin = nullptr); /** * @brief remove the specified directory @@ -274,9 +274,9 @@ public: void remove(const std::wstring &fileName, int *origin) { auto iter = m_Files.find(ToLower(fileName)); if (iter != m_Files.end()) { - if (origin != NULL) { + if (origin != nullptr) { FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); - if (entry.get() != NULL) { + if (entry.get() != nullptr) { bool ignore; *origin = entry->getOrigin(ignore); } diff --git a/src/shared/error_report.cpp b/src/shared/error_report.cpp index 02ff2d56..c9277c1b 100644 --- a/src/shared/error_report.cpp +++ b/src/shared/error_report.cpp @@ -35,7 +35,7 @@ void reportError(LPCSTR format, ...) vsnprintf(buffer, 1024, format, argList); va_end(argList); - MessageBoxA(NULL, buffer, "Error", MB_OK | MB_ICONERROR); + MessageBoxA(nullptr, buffer, "Error", MB_OK | MB_ICONERROR); } void reportError(LPCWSTR format, ...) @@ -50,18 +50,18 @@ void reportError(LPCWSTR format, ...) _vsnwprintf(buffer, 1024, format, argList); va_end(argList); - MessageBoxW(NULL, buffer, L"Error", MB_OK | MB_ICONERROR); + MessageBoxW(nullptr, buffer, L"Error", MB_OK | MB_ICONERROR); } std::string getCurrentErrorStringA() { - LPSTR buffer = NULL; + LPSTR buffer = nullptr; DWORD errorCode = ::GetLastError(); if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, NULL) == 0) { + nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, nullptr) == 0) { ::SetLastError(errorCode); return std::string(); } else { @@ -78,12 +78,12 @@ std::string getCurrentErrorStringA() std::wstring getCurrentErrorStringW() { - LPWSTR buffer = NULL; + LPWSTR buffer = nullptr; DWORD errorCode = ::GetLastError(); if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&buffer, 0, NULL) == 0) { + nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&buffer, 0, nullptr) == 0) { ::SetLastError(errorCode); return std::wstring(); } else { diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index 82b285b7..6c420044 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -60,7 +60,7 @@ std::wstring Fallout3Info::getRegPathStatic() WCHAR temp[MAX_PATH]; DWORD bufferSize = MAX_PATH; - if (::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { + if (::RegQueryValueExW(key, L"Installed Path", nullptr, nullptr, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { return std::wstring(temp); } else { return std::wstring(); @@ -74,9 +74,9 @@ std::wstring Fallout3Info::getInvalidationBSA() bool Fallout3Info::isInvalidationBSA(const std::wstring &bsaName) { - static LPCWSTR invalidation[] = { L"Fallout - AI!.bsa", L"Fallout - Invalidation.bsa", NULL }; + static LPCWSTR invalidation[] = { L"Fallout - AI!.bsa", L"Fallout - Invalidation.bsa", nullptr }; - for (int i = 0; invalidation[i] != NULL; ++i) { + for (int i = 0; invalidation[i] != nullptr; ++i) { if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { return true; } @@ -203,7 +203,7 @@ void Fallout3Info::createProfile(const std::wstring &directory, bool useDefaults 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, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE file = ::CreateFileW(target.str().c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); ::CloseHandle(file); } } @@ -256,9 +256,9 @@ void Fallout3Info::repairProfile(const std::wstring &directory) bool Fallout3Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*) { - static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", NULL }; + static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", nullptr }; - for (int i = 0; profileFiles[i] != NULL; ++i) { + for (int i = 0; profileFiles[i] != nullptr; ++i) { if (_wcsicmp(fileName, profileFiles[i]) == 0) { return true; } diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index 178ce8b4..f589124a 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -61,7 +61,7 @@ std::wstring FalloutNVInfo::getRegPathStatic() WCHAR temp[MAX_PATH]; DWORD bufferSize = MAX_PATH; - if (::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { + if (::RegQueryValueExW(key, L"Installed Path", nullptr, nullptr, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { return std::wstring(temp); } else { return std::wstring(); @@ -75,9 +75,9 @@ std::wstring FalloutNVInfo::getInvalidationBSA() bool FalloutNVInfo::isInvalidationBSA(const std::wstring &bsaName) { - static LPCWSTR invalidation[] = { L"Fallout - AI!.bsa", L"Fallout - Invalidation.bsa", NULL }; + static LPCWSTR invalidation[] = { L"Fallout - AI!.bsa", L"Fallout - Invalidation.bsa", nullptr }; - for (int i = 0; invalidation[i] != NULL; ++i) { + for (int i = 0; invalidation[i] != nullptr; ++i) { if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { return true; } @@ -170,7 +170,7 @@ void FalloutNVInfo::createProfile(const std::wstring &directory, bool useDefault 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, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE file = ::CreateFileW(target.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); ::CloseHandle(file); } } @@ -247,9 +247,9 @@ void FalloutNVInfo::repairProfile(const std::wstring &directory) bool FalloutNVInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) { - static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", NULL }; + static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", nullptr }; - for (int i = 0; profileFiles[i] != NULL; ++i) { + for (int i = 0; profileFiles[i] != nullptr; ++i) { if (_wcsicmp(fileName, profileFiles[i]) == 0) { return true; } diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index 1a815701..3727ea49 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -37,7 +37,7 @@ along with Mod Organizer. If not, see . namespace MOShared { -GameInfo* GameInfo::s_Instance = NULL; +GameInfo* GameInfo::s_Instance = nullptr; GameInfo::GameInfo(const std::wstring &moDirectory, const std::wstring &moDataDirectory, const std::wstring &gameDirectory) @@ -62,14 +62,14 @@ void GameInfo::identifyMyGamesDirectory(const std::wstring &file) m_MyGamesDirectory.clear(); // a) this is the way it should work. get the configured My Documents\My Games directory - if (::SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, myDocuments) == S_OK) { + if (::SHGetFolderPathW(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_CURRENT, myDocuments) == S_OK) { m_MyGamesDirectory = std::wstring(myDocuments) + L"\\My Games"; } // b) if there is no directory there, look in the default directory if (m_MyGamesDirectory.empty() || !FileExists(m_MyGamesDirectory + L"\\" + file)) { - if (::SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_DEFAULT, myDocuments) == S_OK) { + if (::SHGetFolderPathW(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_DEFAULT, myDocuments) == S_OK) { std::wstring fromDefault = std::wstring(myDocuments) + L"\\My Games"; if (FileExists(fromDefault + L"\\" + file)) { m_MyGamesDirectory = fromDefault; @@ -99,13 +99,13 @@ bool GameInfo::identifyGame(const std::wstring &moDirectory, const std::wstring s_Instance = new SkyrimInfo(moDirectory, moDataDirectory, searchPath); } - return s_Instance != NULL; + return s_Instance != nullptr; } bool GameInfo::init(const std::wstring &moDirectory, const std::wstring &moDataDirectory, const std::wstring &gamePath) { - if (s_Instance == NULL) { + if (s_Instance == nullptr) { if (gamePath.length() == 0) { // search upward in the directory until a recognized game-binary is found std::wstring searchPath(moDirectory); @@ -191,7 +191,7 @@ std::wstring GameInfo::getLocalAppFolder() const wchar_t localAppFolder[MAX_PATH]; memset(localAppFolder, '\0', MAX_PATH * sizeof(wchar_t)); - if (::SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, localAppFolder) == S_OK) { + if (::SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, localAppFolder) == S_OK) { return localAppFolder; } else { // fallback: try the registry @@ -212,7 +212,7 @@ std::wstring GameInfo::getSpecialPath(LPCWSTR name) const WCHAR temp[MAX_PATH]; DWORD bufferSize = MAX_PATH; - errorcode = ::RegQueryValueExW(key, name, NULL, NULL, (LPBYTE)temp, &bufferSize); + errorcode = ::RegQueryValueExW(key, name, nullptr, nullptr, (LPBYTE)temp, &bufferSize); if (errorcode != ERROR_SUCCESS) { throw windows_error((boost::format("failed to look up special folder (%1%)") % ToString(name, true)).str(), errorcode); } diff --git a/src/shared/inject.cpp b/src/shared/inject.cpp index 5a111c4a..143652c2 100644 --- a/src/shared/inject.cpp +++ b/src/shared/inject.cpp @@ -57,13 +57,13 @@ void injectDLL(HANDLE processHandle, HANDLE threadHandle, const std::string &dll _snprintf(parameters.initstr, 5, "Init"); //this is the name of thie initialisation function we want to call in the target process HMODULE k32mod = ::LoadLibrary(__TEXT("kernel32.dll")); - TLoadLibraryType loadLibraryFunc = NULL; - TGetProcAddressType getProcAddressFunc = NULL; + TLoadLibraryType loadLibraryFunc = nullptr; + TGetProcAddressType getProcAddressFunc = nullptr; // ansi binaries - if (k32mod != NULL) { + if (k32mod != nullptr) { loadLibraryFunc = reinterpret_cast(::GetProcAddress(k32mod, "LoadLibraryA")); getProcAddressFunc = reinterpret_cast(::GetProcAddress(k32mod, "GetProcAddress")); - if ((loadLibraryFunc == NULL) || (getProcAddressFunc == NULL)) { + if ((loadLibraryFunc == nullptr) || (getProcAddressFunc == nullptr)) { throw windows_error("failed to determine address for required functions"); } } else { @@ -71,8 +71,8 @@ void injectDLL(HANDLE processHandle, HANDLE threadHandle, const std::string &dll } // allocate memory in the target process and write the parameter-block there - LPVOID remoteMem = ::VirtualAllocEx(processHandle, NULL, sizeof(TParameters), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (remoteMem == NULL) { + LPVOID remoteMem = ::VirtualAllocEx(processHandle, nullptr, sizeof(TParameters), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (remoteMem == nullptr) { throw windows_error("failed to allocate memory in target process"); } SIZE_T written; @@ -104,8 +104,8 @@ void injectDLL(HANDLE processHandle, HANDLE threadHandle, const std::string &dll }; // reserve memory for the stub - PBYTE stubRemote = reinterpret_cast(::VirtualAllocEx(processHandle, NULL, sizeof(stubLocal), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)); - if (stubRemote == NULL) { + PBYTE stubRemote = reinterpret_cast(::VirtualAllocEx(processHandle, nullptr, sizeof(stubLocal), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)); + if (stubRemote == nullptr) { throw windows_error("failed to allocate memory for stub"); } TParameters *remoteParams = reinterpret_cast(remoteMem); diff --git a/src/shared/leaktrace.cpp b/src/shared/leaktrace.cpp index 729eb42e..46ff7924 100644 --- a/src/shared/leaktrace.cpp +++ b/src/shared/leaktrace.cpp @@ -22,7 +22,7 @@ void initDbgIfNecessary() ::SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); firstCall = false; } - if (!::SymInitialize(process, NULL, TRUE)) { + if (!::SymInitialize(process, nullptr, TRUE)) { printf("failed to initialize symbols: %d", ::GetLastError()); } initialized.insert(::GetCurrentProcessId()); diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index 89a795a5..586bcaef 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -61,7 +61,7 @@ std::wstring OblivionInfo::getRegPathStatic() WCHAR temp[MAX_PATH]; DWORD bufferSize = MAX_PATH; - if (::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { + if (::RegQueryValueExW(key, L"Installed Path", nullptr, nullptr, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { return std::wstring(temp); } else { return std::wstring(); @@ -76,9 +76,9 @@ std::wstring OblivionInfo::getInvalidationBSA() bool OblivionInfo::isInvalidationBSA(const std::wstring &bsaName) { static LPCWSTR invalidation[] = { L"Oblivion - Invalidation.bsa", L"ArchiveInvalidationInvalidated!.bsa", - L"BSARedirection.bsa", NULL }; + L"BSARedirection.bsa", nullptr }; - for (int i = 0; invalidation[i] != NULL; ++i) { + for (int i = 0; invalidation[i] != nullptr; ++i) { if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { return true; } @@ -158,7 +158,7 @@ void OblivionInfo::createProfile(const std::wstring &directory, bool useDefaults 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, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE file = ::CreateFileW(target.str().c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); ::CloseHandle(file); } } @@ -194,7 +194,7 @@ void OblivionInfo::createProfile(const std::wstring &directory, bool useDefaults 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, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL) == INVALID_HANDLE_VALUE) && + 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)); } @@ -240,9 +240,9 @@ void OblivionInfo::repairProfile(const std::wstring &directory) bool OblivionInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) { - static LPCWSTR profileFiles[] = { L"oblivion.ini", L"oblivionprefs.ini", L"plugins.txt", NULL }; + static LPCWSTR profileFiles[] = { L"oblivion.ini", L"oblivionprefs.ini", L"plugins.txt", nullptr }; - for (int i = 0; profileFiles[i] != NULL; ++i) { + for (int i = 0; profileFiles[i] != nullptr; ++i) { if (_wcsicmp(fileName, profileFiles[i]) == 0) { return true; } diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 7f70c097..1d00c4f3 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -39,7 +39,7 @@ SkyrimInfo::SkyrimInfo(const std::wstring &moDirectory, const std::wstring &moDa identifyMyGamesDirectory(L"skyrim"); wchar_t appDataPath[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataPath))) { + if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, appDataPath))) { m_AppData = appDataPath; } } @@ -69,7 +69,7 @@ std::wstring SkyrimInfo::getRegPathStatic() WCHAR temp[MAX_PATH]; DWORD bufferSize = MAX_PATH; - if (::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { + if (::RegQueryValueExW(key, L"Installed Path", nullptr, nullptr, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { return std::wstring(temp); } else { return std::wstring(); @@ -84,9 +84,9 @@ std::wstring SkyrimInfo::getInvalidationBSA() bool SkyrimInfo::isInvalidationBSA(const std::wstring &bsaName) { - static LPCWSTR invalidation[] = { L"Skyrim - Invalidation.bsa", NULL }; + static LPCWSTR invalidation[] = { L"Skyrim - Invalidation.bsa", nullptr }; - for (int i = 0; invalidation[i] != NULL; ++i) { + for (int i = 0; invalidation[i] != nullptr; ++i) { if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { return true; } @@ -229,7 +229,7 @@ void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults) 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, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE file = ::CreateFileW(target.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); ::CloseHandle(file); } } @@ -237,7 +237,7 @@ void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults) 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, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE file = ::CreateFileW(target.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); ::CloseHandle(file); } } @@ -272,7 +272,7 @@ void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults) 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, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) == INVALID_HANDLE_VALUE) { + 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)); } } @@ -289,16 +289,16 @@ void SkyrimInfo::repairProfile(const std::wstring &directory) bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) { - static LPCWSTR profileFiles[] = { L"skyrim.ini", L"skyrimprefs.ini", L"loadorder.txt", NULL }; + static LPCWSTR profileFiles[] = { L"skyrim.ini", L"skyrimprefs.ini", L"loadorder.txt", nullptr }; - for (int i = 0; profileFiles[i] != NULL; ++i) { + for (int i = 0; profileFiles[i] != nullptr; ++i) { if (_wcsicmp(fileName, profileFiles[i]) == 0) { return true; } } if ((_wcsicmp(fileName, L"plugins.txt") == 0) && - (m_AppData.empty() || (StrStrIW(fullPath, m_AppData.c_str()) != NULL))) { + (m_AppData.empty() || (StrStrIW(fullPath, m_AppData.c_str()) != nullptr))) { return true; } diff --git a/src/shared/util.cpp b/src/shared/util.cpp index d4a77929..df3a8bad 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -34,7 +34,7 @@ bool FileExists(const std::string &filename) { WIN32_FIND_DATAA findData; ZeroMemory(&findData, sizeof(WIN32_FIND_DATAA)); - HANDLE search = ::FindFirstFileExA(filename.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0); + HANDLE search = ::FindFirstFileExA(filename.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, nullptr, 0); if (search == INVALID_HANDLE_VALUE) { return false; } else { @@ -47,7 +47,7 @@ bool FileExists(const std::wstring &filename) { WIN32_FIND_DATAW findData; ZeroMemory(&findData, sizeof(WIN32_FIND_DATAW)); - HANDLE search = ::FindFirstFileExW(filename.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0); + HANDLE search = ::FindFirstFileExW(filename.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, nullptr, 0); if (search == INVALID_HANDLE_VALUE) { return false; } else { @@ -67,14 +67,14 @@ std::string ToString(const std::wstring &source, bool utf8) { std::string result; UINT codepage = utf8 ? CP_UTF8 : GetACP(); - int sizeRequired = ::WideCharToMultiByte(codepage, 0, &source[0], -1, NULL, 0, NULL, NULL); + int sizeRequired = ::WideCharToMultiByte(codepage, 0, &source[0], -1, nullptr, 0, nullptr, nullptr); if (sizeRequired == 0) { throw windows_error("failed to convert string to multibyte"); } // the size returned by WideCharToMultiByte contains zero termination IF -1 is specified for the length. // we don't want that \0 in the string because then the length field would be wrong. Because madness result.resize(sizeRequired - 1, '\0'); - ::WideCharToMultiByte(codepage, 0, &source[0], (int)source.size(), &result[0], sizeRequired, NULL, NULL); + ::WideCharToMultiByte(codepage, 0, &source[0], (int)source.size(), &result[0], sizeRequired, nullptr, nullptr); return result; } @@ -82,7 +82,7 @@ std::wstring ToWString(const std::string &source, bool utf8) { std::wstring result; UINT codepage = utf8 ? CP_UTF8 : GetACP(); - int sizeRequired = ::MultiByteToWideChar(codepage, 0, &source[0], (int)source.size(), NULL, 0); + int sizeRequired = ::MultiByteToWideChar(codepage, 0, &source[0], (int)source.size(), nullptr, 0); if (sizeRequired == 0) { throw windows_error("failed to convert string to wide character"); } @@ -131,7 +131,7 @@ VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName) throw windows_error("failed to determine file version info"); } - void *versionInfoPtr = NULL; + void *versionInfoPtr = nullptr; UINT versionInfoLength = 0; if (!::VerQueryValue(buffer, L"\\", &versionInfoPtr, &versionInfoLength)) { throw windows_error("failed to determine file version"); @@ -160,14 +160,14 @@ std::string GetStack() ::SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); firstCall = false; } - if (!::SymInitialize(process, NULL, TRUE)) { + if (!::SymInitialize(process, nullptr, TRUE)) { log("failed to initialize symbols: %d", ::GetLastError()); } initialized.insert(::GetCurrentProcessId()); } LPVOID stack[32]; - WORD frames = ::CaptureStackBackTrace(0, 100, stack, NULL); + WORD frames = ::CaptureStackBackTrace(0, 100, stack, nullptr); char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; diff --git a/src/shared/windows_error.cpp b/src/shared/windows_error.cpp index 647a5bfb..97a58a20 100644 --- a/src/shared/windows_error.cpp +++ b/src/shared/windows_error.cpp @@ -27,13 +27,13 @@ std::string windows_error::constructMessage(const std::string& input, int inErro std::ostringstream finalMessage; finalMessage << input; - LPSTR buffer = NULL; + LPSTR buffer = nullptr; DWORD errorCode = inErrorCode != -1 ? inErrorCode : ::GetLastError(); // TODO: the message is not english? if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, NULL) == 0) { + nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, nullptr) == 0) { finalMessage << " (errorcode " << errorCode << ")"; } else { LPSTR lastChar = buffer + strlen(buffer) - 2; diff --git a/src/spawn.cpp b/src/spawn.cpp index cd7e202e..96c219fc 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -55,7 +55,7 @@ bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, bool sus } si.cb = sizeof(si); int length = wcslen(binary) + wcslen(arguments) + 4; - wchar_t *commandLine = NULL; + wchar_t *commandLine = nullptr; if (arguments[0] != L'\0') { commandLine = new wchar_t[length]; _snwprintf(commandLine, length, L"\"%ls\" %ls", binary, arguments); @@ -84,12 +84,12 @@ bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, bool sus } PROCESS_INFORMATION pi; - BOOL success = ::CreateProcess(NULL, + BOOL success = ::CreateProcess(nullptr, commandLine, - NULL, NULL, // no special process or thread attributes + nullptr, nullptr, // no special process or thread attributes inheritHandles, // inherit handles if we plan to use stdout or stderr reroute CREATE_BREAKAWAY_FROM_JOB | (suspended ? CREATE_SUSPENDED : 0), // create suspended so I have time to inject the DLL - NULL, // same environment as parent + nullptr, // same environment as parent currentDirectory, // current directory &si, &pi // startup and process information ); @@ -119,12 +119,12 @@ HANDLE startBinary(const QFileInfo &binary, { JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo; - ::QueryInformationJobObject(NULL, JobObjectExtendedLimitInformation, &jobInfo, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), NULL); + ::QueryInformationJobObject(nullptr, JobObjectExtendedLimitInformation, &jobInfo, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), nullptr); jobInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK; - HANDLE jobObject = ::CreateJobObject(NULL, NULL); + HANDLE jobObject = ::CreateJobObject(nullptr, nullptr); - if (jobObject == NULL) { + if (jobObject == nullptr) { qWarning("failed to create job object: %lu", ::GetLastError()); } else { ::SetInformationJobObject(jobObject, JobObjectExtendedLimitInformation, &jobInfo, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); @@ -144,7 +144,7 @@ HANDLE startBinary(const QFileInfo &binary, if (e.getErrorCode() == ERROR_ELEVATION_REQUIRED) { // TODO: check if this is really correct. Are all settings updated that the secondary instance may use? - if (QMessageBox::question(NULL, QObject::tr("Elevation required"), + if (QMessageBox::question(nullptr, QObject::tr("Elevation required"), QObject::tr("This process requires elevation to run.\n" "This is a potential security risk so I highly advice you to investigate if\n" "\"%1\"\n" @@ -153,7 +153,7 @@ HANDLE startBinary(const QFileInfo &binary, "(you will be asked if you want to allow ModOrganizer.exe to make changes to the system)").arg( QDir::toNativeSeparators(binary.absoluteFilePath())), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - ::ShellExecuteW(NULL, L"runas", ToWString(QCoreApplication::applicationFilePath()).c_str(), + ::ShellExecuteW(nullptr, L"runas", ToWString(QCoreApplication::applicationFilePath()).c_str(), (std::wstring(L"\"") + binaryName + L"\" " + ToWString(arguments)).c_str(), currentDirectoryName.c_str(), SW_SHOWNORMAL); return INVALID_HANDLE_VALUE; } else { diff --git a/src/syncoverwritedialog.cpp b/src/syncoverwritedialog.cpp index 380719d8..adeac9b8 100644 --- a/src/syncoverwritedialog.cpp +++ b/src/syncoverwritedialog.cpp @@ -82,18 +82,18 @@ void SyncOverwriteDialog::readTree(const QString &path, DirectoryEntry *director if (fileInfo.isDir()) { DirectoryEntry *subDir = directoryStructure->findSubDirectory(ToWString(file)); - if (subDir != NULL) { + if (subDir != nullptr) { readTree(fileInfo.absoluteFilePath(), subDir, newItem); } else { qCritical("no directory structure for %s?", file.toUtf8().constData()); delete newItem; - newItem = NULL; + newItem = nullptr; } } else { const FileEntry::Ptr entry = directoryStructure->findFile(ToWString(file)); QComboBox* combo = new QComboBox(ui->syncTree); combo->addItem(tr(""), -1); - if (entry.get() != NULL) { + if (entry.get() != nullptr) { bool ignore; int origin = entry->getOrigin(ignore); addToComboBox(combo, ToQString(m_DirectoryStructure->getOriginByID(origin).getName()), origin); @@ -107,7 +107,7 @@ void SyncOverwriteDialog::readTree(const QString &path, DirectoryEntry *director } ui->syncTree->setItemWidget(newItem, 1, combo); } - if (newItem != NULL) { + if (newItem != nullptr) { subTree->addChild(newItem); } } @@ -137,7 +137,7 @@ void SyncOverwriteDialog::applyTo(QTreeWidgetItem *item, const QString &path, co applyTo(child, filePath, modDirectory); } else { QComboBox *comboBox = qobject_cast(ui->syncTree->itemWidget(child, 1)); - if (comboBox != NULL) { + if (comboBox != nullptr) { int originID = comboBox->itemData(comboBox->currentIndex(), Qt::UserRole).toInt(); if (originID != -1) { FilesOrigin &origin = m_DirectoryStructure->getOriginByID(originID); diff --git a/src/viewmarkingscrollbar.cpp b/src/viewmarkingscrollbar.cpp index f1b1ba34..2452d0e3 100644 --- a/src/viewmarkingscrollbar.cpp +++ b/src/viewmarkingscrollbar.cpp @@ -14,7 +14,7 @@ ViewMarkingScrollBar::ViewMarkingScrollBar(QAbstractItemModel *model, QWidget *p void ViewMarkingScrollBar::paintEvent(QPaintEvent *event) { - if (m_Model == NULL) { + if (m_Model == nullptr) { return; } QScrollBar::paintEvent(event); -- 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/profile.cpp') 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 From 8aa6dc4c7dcf4139cd0144207a327013bd3cb1dc Mon Sep 17 00:00:00 2001 From: Tannin Date: Thu, 29 Jan 2015 19:26:42 +0100 Subject: extended the game-plugin interface --- src/dlls.manifest.debug.qt5 | 2 + src/dlls.manifest.qt5 | 2 + src/dummybsa.cpp | 244 ------------------------------------------- src/dummybsa.h | 64 ------------ src/loadmechanism.cpp | 91 ++++++++-------- src/main.cpp | 28 ++--- src/mainwindow.cpp | 119 ++++++++++----------- src/mainwindow.h | 2 - src/modinfo.cpp | 11 +- src/organizer.pro | 4 +- src/organizercore.cpp | 75 +++++-------- src/organizercore.h | 1 - src/pluginlist.cpp | 23 ++-- src/profile.cpp | 124 +++++----------------- src/profile.h | 15 +-- src/profilesdialog.cpp | 27 +++-- src/profilesdialog.h | 3 +- src/selectiondialog.cpp | 23 +++- src/selectiondialog.h | 5 +- src/shared/fallout3info.cpp | 46 +------- src/shared/fallout3info.h | 45 +++++--- src/shared/falloutnvinfo.cpp | 63 ----------- src/shared/falloutnvinfo.h | 43 +++++--- src/shared/gameinfo.h | 55 +--------- src/shared/oblivioninfo.cpp | 48 +-------- src/shared/oblivioninfo.h | 45 +++++--- src/shared/skyriminfo.cpp | 56 +--------- src/shared/skyriminfo.h | 16 --- 28 files changed, 338 insertions(+), 942 deletions(-) delete mode 100644 src/dummybsa.cpp delete mode 100644 src/dummybsa.h (limited to 'src/profile.cpp') diff --git a/src/dlls.manifest.debug.qt5 b/src/dlls.manifest.debug.qt5 index e49b33b8..59497baa 100644 --- a/src/dlls.manifest.debug.qt5 +++ b/src/dlls.manifest.debug.qt5 @@ -23,5 +23,7 @@ + + diff --git a/src/dlls.manifest.qt5 b/src/dlls.manifest.qt5 index b8f2d467..76206f21 100644 --- a/src/dlls.manifest.qt5 +++ b/src/dlls.manifest.qt5 @@ -23,5 +23,7 @@ + + \ No newline at end of file diff --git a/src/dummybsa.cpp b/src/dummybsa.cpp deleted file mode 100644 index 49c6ee53..00000000 --- a/src/dummybsa.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* -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 "dummybsa.h" -#include -#include -#define WIN32_LEAN_AND_MEAN -#include - - -static void writeUlong(unsigned char* buffer, int offset, unsigned long value) -{ - union { - unsigned long ulValue; - unsigned char cValue[4]; - }; - ulValue = value; - memcpy(buffer + offset, cValue, 4); -} - -static void writeUlonglong(unsigned char* buffer, int offset, unsigned long long value) -{ - union { - unsigned long long ullValue; - unsigned char cValue[8]; - }; - ullValue = value; - memcpy(buffer + offset, cValue, 8); -} -/* -static unsigned long long genHashInt(const char* pos, const char* end) -{ - unsigned long long hash2 = 0; - for (; pos < end; ++pos) { - hash2 *= 0x1003f; - hash2 += *pos; - } - return hash2; -} - - -static unsigned long long genHash(const char* fileName) -{ - char fileNameLower[MAX_PATH + 1]; - int i = 0; - for (; i < MAX_PATH && fileName[i] != '\0'; ++i) { - fileNameLower[i] = tolower(fileName[i]); - } - fileNameLower[i] = '\0'; - - char* ext = strrchr(fileNameLower, '.'); - if (ext == nullptr) { - ext = fileNameLower + strlen(fileNameLower); - } - - int length = ext - fileNameLower; - - unsigned long long hash = 0ULL; - - if (length > 0) { - hash = *(ext - 1) | - ((length > 2 ? *(ext - 2) : 0) << 8) | - (length << 16) | - (fileNameLower[0] << 24); - } - - if (strcmp(ext + 1, "dds") == 0) { - hash |= 0x8080; - } // the rest is not supported currently - - hash |= genHashInt(fileNameLower + 1, ext - 2) << 0x32; - hash |= genHashInt(ext, ext + strlen(ext)) << 0x32; - - return hash; -}*/ - - -static unsigned long genHashInt(const unsigned char *pos, const unsigned char *end) -{ - unsigned long hash = 0; - for (; pos < end; ++pos) { - hash *= 0x1003f; - hash += *pos; - } - return hash; -} - - -static unsigned long long genHash(const char* fileName) -{ - char fileNameLower[MAX_PATH + 1]; - int i = 0; - for (; i < MAX_PATH && fileName[i] != '\0'; ++i) { - fileNameLower[i] = static_cast(tolower(fileName[i])); - if (fileNameLower[i] == '\\') { - fileNameLower[i] = '/'; - } - } - fileNameLower[i] = '\0'; - - unsigned char *fileNameLowerU = reinterpret_cast(fileNameLower); - - char* ext = strrchr(fileNameLower, '.'); - if (ext == nullptr) { - ext = fileNameLower + strlen(fileNameLower); - } - unsigned char *extU = reinterpret_cast(ext); - - int length = ext - fileNameLower; - - unsigned long long hash = 0ULL; - - if (length > 0) { - hash = *(extU - 1) | - ((length > 2 ? *(ext - 2) : 0) << 8) | - (length << 16) | - (fileNameLowerU[0] << 24); - } - - if (strlen(ext) > 0) { - if (strcmp(ext + 1, "kf") == 0) { - hash |= 0x80; - } else if (strcmp(ext + 1, "nif") == 0) { - hash |= 0x8000; - } else if (strcmp(ext + 1, "dds") == 0) { - hash |= 0x8080; - } else if (strcmp(ext + 1, "wav") == 0) { - hash |= 0x80000000; - } - - unsigned long long temp = static_cast(genHashInt( - fileNameLowerU + 1, extU - 2)); - temp += static_cast(genHashInt( - extU, extU + strlen(ext))); - - hash |= (temp & 0xFFFFFFFF) << 32; - } - return hash; -} - - -DummyBSA::DummyBSA() - : m_Version(MOShared::GameInfo::instance().getBSAVersion()), m_FolderName(""), m_FileName("dummy.dds"), - m_TotalFileNameLength(0) -{ -} - - -void DummyBSA::writeHeader(QFile& file) -{ - unsigned char header[] = { - 'B', 'S', 'A', '\0', // magic string - 0xDE, 0xAD, 0xBE, 0xEF, // version - insert later - 0x24, 0x00, 0x00, 0x00, // offset to folder recors. header size is static - 0xDE, 0xAD, 0xBE, 0xEF, // archive flags - insert later - 0x01, 0x00, 0x00, 0x00, // folder count - 0x01, 0x00, 0x00, 0x00, // file count - 0xDE, 0xAD, 0xBE, 0xEF, // total folder names length - insert later - 0xDE, 0xAD, 0xBE, 0xEF, // total file names length - insert later - 0xDE, 0xAD, 0xBE, 0xEF // file flags - insert later - }; - - writeUlong(header, 4, MOShared::GameInfo::instance().getBSAVersion()); - writeUlong(header, 12, 0x01 | 0x02); // has directories and has files. - writeUlong(header, 24, m_FolderName.length() + 1); // empty folder name - writeUlong(header, 28, m_TotalFileNameLength); // single character file name - - writeUlong(header, 32, 2); // has dds - - file.write(reinterpret_cast(header), sizeof(header)); -} - - -void DummyBSA::writeFolderRecord(QFile& file, const std::string& folderName) -{ - unsigned char folderRecord[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // folder hash - 0x01, 0x00, 0x00, 0x00, // file count - 0xDE, 0xAD, 0xBE, 0xEF, // offset to folder name - }; - // we'd usually have to sort folders be the hash value generated here - writeUlonglong(folderRecord, 0, genHash(folderName.c_str())); - writeUlong( folderRecord, 12, 0x34 + m_TotalFileNameLength); // TODO: this should be calculated properly - - file.write(reinterpret_cast(folderRecord), sizeof(folderRecord)); -} - - -void DummyBSA::writeFileRecord(QFile& file, const std::string& fileName) -{ - unsigned char fileRecord[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // file name hash - 0xDE, 0xAD, 0xBE, 0xEF, // size - 0xDE, 0xAD, 0xBE, 0xEF, // offset to file data - }; - - // we'd usually have to sort files by the value generated here - writeUlonglong(fileRecord, 0, genHash(fileName.c_str())); - writeUlong( fileRecord, 8, 0); - writeUlong( fileRecord, 12, 0x44 + (fileName.length() + 1) + 4); // after this record we expect the filename and 4 bytes of file size - - file.write(reinterpret_cast(fileRecord), sizeof(fileRecord)); -} - - -void DummyBSA::writeFileRecordBlocks(QFile& file, const std::string& folderName) -{ - file.write(folderName.c_str(), folderName.length() + 1); - - writeFileRecord(file, m_FileName); -} - - -void DummyBSA::write(const QString& fileName) -{ - QFile file(fileName); - file.open(QIODevice::WriteOnly); - - m_TotalFileNameLength = m_FileName.length() + 1; - - writeHeader(file); - writeFolderRecord(file, m_FolderName); - writeFileRecordBlocks(file, m_FolderName); - file.write(m_FileName.c_str() , m_FileName.length() + 1); - char fileSize[] = { 0x00, 0x00, 0x00, 0x00 }; - file.write(fileSize, sizeof(fileSize)); - file.close(); -} diff --git a/src/dummybsa.h b/src/dummybsa.h deleted file mode 100644 index 0d231393..00000000 --- a/src/dummybsa.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -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 DUMMYBSA_H -#define DUMMYBSA_H - -#include -#include - -/** - * @brief Class for creating a dummy bsa used for archive invalidation - **/ -class DummyBSA -{ - -public: - - /** - * @brief constructor - * - **/ - DummyBSA(); - - /** - * @brief write to the specified file - * - * @param fileName name of the file to write to - **/ - void write(const QString& fileName); - -private: - - void writeHeader(QFile& file); - void writeFolderRecord(QFile& file, const std::string& folderName); - void writeFileRecord(QFile& file, const std::string& fileName); - void writeFileRecordBlocks(QFile& file, const std::string& folderName); - -private: - - unsigned long m_Version; - std::string m_FolderName; - std::string m_FileName; - unsigned long m_TotalFileNameLength; - -}; - - -#endif // DUMMYBSA_H diff --git a/src/loadmechanism.cpp b/src/loadmechanism.cpp index 711a7f1a..913d018c 100644 --- a/src/loadmechanism.cpp +++ b/src/loadmechanism.cpp @@ -20,7 +20,8 @@ along with Mod Organizer. If not, see . #include "loadmechanism.h" #include "utility.h" #include "util.h" -#include +#include +#include #include #include #include @@ -28,6 +29,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include using namespace MOBase; @@ -49,7 +51,7 @@ void LoadMechanism::writeHintFile(const QDir &targetDirectory) if (!hintFile.open(QIODevice::WriteOnly)) { throw MyException(QObject::tr("failed to open %1: %2").arg(hintFilePath).arg(hintFile.errorString())); } - hintFile.write(ToString(GameInfo::instance().getOrganizerDirectory(), true).c_str()); + hintFile.write(qApp->applicationDirPath().toUtf8().constData()); hintFile.close(); } @@ -62,38 +64,31 @@ void LoadMechanism::removeHintFile(QDir &targetDirectory) bool LoadMechanism::isDirectLoadingSupported() { - if (GameInfo::instance().getType() == GameInfo::TYPE_OBLIVION) { + IPluginGame *game = qApp->property("managed_game").value(); + if (game->gameName().compare("oblivion", Qt::CaseInsensitive)) { // oblivion can be loaded directly if it's not the steam variant - QString gamePath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())); - QDir gameDir(gamePath); - // test if this is the steam version of oblivion - if (QFile(gameDir.absoluteFilePath("steam_api.dll")).exists()) { - return false; - } + return !game->gameDirectory().exists("steam_api.dll"); + } else { + // all other games work afaik + return true; } - - // all other games work afaik - return true; } - bool LoadMechanism::isScriptExtenderSupported() { - QString targetPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())); - if ((QFile(targetPath.mid(0).append("/").append(ToQString(GameInfo::instance().getSEName())).append("_loader.exe")).exists()) || - (QFile(targetPath.mid(0).append("/").append(ToQString(GameInfo::instance().getSEName())).append("_steam_loader.dll")).exists())) { - return true; - } else { - return false; - } -} + IPluginGame *game = qApp->property("managed_game").value(); + ScriptExtender *extender = game->feature(); + // test if there even is an extender for the managed game and if so whether it's installed + return (extender != nullptr) + && (game->gameDirectory().exists(extender->name() + "_loader.exe") + || game->gameDirectory().exists(extender->name() + "_steam_loader.dll")); +} bool LoadMechanism::isProxyDLLSupported() { - QString targetPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())); - targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget())); - return QFile(targetPath).exists(); + IPluginGame *game = qApp->property("managed_game").value(); + return game->gameDirectory().exists(QString::fromStdWString(AppConfig::proxyDLLTarget())); } @@ -124,12 +119,13 @@ bool LoadMechanism::hashIdentical(const QString &fileNameLHS, const QString &fil void LoadMechanism::deactivateScriptExtender() { try { - QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())); - - QString pluginsDirPath = gameDirectory; - pluginsDirPath.append("/data/").append(ToQString(GameInfo::instance().getSEName())).append("/plugins"); + IPluginGame *game = qApp->property("managed_game").value(); + ScriptExtender *extender = game->feature(); + if (extender == nullptr) { + throw MyException(QObject::tr("game doesn't support a script extender")); + } - QDir pluginsDir(pluginsDirPath); + QDir pluginsDir(game->gameDirectory().absolutePath() + "/data/" + extender->name() + "/plugins"); QString hookDLLName = ToQString(AppConfig::hookDLLName()); if (QFile(pluginsDir.absoluteFilePath(hookDLLName)).exists()) { @@ -149,14 +145,13 @@ void LoadMechanism::deactivateScriptExtender() void LoadMechanism::deactivateProxyDLL() { try { - QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())); + IPluginGame *game = qApp->property("managed_game").value(); - QString targetPath = gameDirectory; - targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget())); + QString targetPath = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLTarget())); QFile targetDLL(targetPath); if (targetDLL.exists()) { - QString origFile = gameDirectory.mid(0).append(ToQString(AppConfig::proxyDLLOrig())); + QString origFile = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLOrig())); // determine if a proxy-dll is installed // this is a very crude way of making this decision but it should be good enough if ((targetDLL.size() < 24576) && (QFile(origFile).exists())) { @@ -169,8 +164,7 @@ void LoadMechanism::deactivateProxyDLL() } } - QDir dir(gameDirectory); - removeHintFile(dir); + removeHintFile(game->gameDirectory()); } catch (const std::exception &e) { QMessageBox::critical(nullptr, QObject::tr("Failed to deactivate proxy-dll loading"), e.what()); } @@ -180,19 +174,20 @@ void LoadMechanism::deactivateProxyDLL() void LoadMechanism::activateScriptExtender() { try { - QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())); - - QString pluginsDirPath = gameDirectory; - pluginsDirPath.append("/data/").append(ToQString(GameInfo::instance().getSEName())).append("/plugins"); + IPluginGame *game = qApp->property("managed_game").value(); + ScriptExtender *extender = game->feature(); + if (extender == nullptr) { + throw MyException(QObject::tr("game doesn't support a script extender")); + } - QDir pluginsDir(pluginsDirPath); + QDir pluginsDir(game->gameDirectory().absolutePath() + "/data/" + extender->name() + "/plugins"); if (!pluginsDir.exists()) { pluginsDir.mkpath("."); } QString targetPath = pluginsDir.absoluteFilePath(ToQString(AppConfig::hookDLLName())); - QString hookDLLPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/").append(ToQString(AppConfig::hookDLLName())); + QString hookDLLPath = qApp->applicationDirPath() + "/" + QString::fromStdWString(AppConfig::hookDLLName()); QFile dllFile(targetPath); @@ -219,17 +214,16 @@ void LoadMechanism::activateScriptExtender() void LoadMechanism::activateProxyDLL() { try { - QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())); + IPluginGame *game = qApp->property("managed_game").value(); + + QString targetPath = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLTarget())); - QString targetPath = gameDirectory; - targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget())); QFile targetDLL(targetPath); if (!targetDLL.exists()) { return; } - QString sourcePath = QDir::fromNativeSeparators( - ToQString(GameInfo::instance().getOrganizerDirectory())).append("/").append(ToQString(AppConfig::proxyDLLSource())); + QString sourcePath = qApp->applicationDirPath() + "/" + ToQString(AppConfig::proxyDLLSource()); // this is a very crude way of making this decision but it should be good enough if (targetDLL.size() < 24576) { @@ -246,8 +240,7 @@ void LoadMechanism::activateProxyDLL() } else { // no proxy dll installed yet. move the original and insert proxy-dll - QString origFile = gameDirectory; - origFile.append("/").append(ToQString(AppConfig::proxyDLLOrig())); + QString origFile = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLOrig())); if (QFile(origFile).exists()) { // orig-file exists. this may happen if the steam-api was updated or the user messed with the @@ -263,7 +256,7 @@ void LoadMechanism::activateProxyDLL() throw MyException(QObject::tr("Failed to copy %1 to %2").arg(sourcePath).arg(targetPath)); } } - writeHintFile(QDir(gameDirectory)); + writeHintFile(game->gameDirectory()); } catch (const std::exception &e) { QMessageBox::critical(nullptr, QObject::tr("Failed to set up proxy-dll loading"), e.what()); } diff --git a/src/main.cpp b/src/main.cpp index a048d50d..8507d725 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -406,29 +406,17 @@ int main(int argc, char *argv[]) reportError(QObject::tr("No game identified in \"%1\". The directory is required to contain " "the game binary and its launcher.").arg(gamePath)); } - SelectionDialog selection(QObject::tr("Please select the game to manage"), nullptr); - - { // add options - QString skyrimPath = ToQString(SkyrimInfo::getRegPathStatic()); - QString falloutNVPath = ToQString(FalloutNVInfo::getRegPathStatic()); - QString fallout3Path = ToQString(Fallout3Info::getRegPathStatic()); - QString oblivionPath = ToQString(OblivionInfo::getRegPathStatic()); - if (skyrimPath.length() != 0) { - selection.addChoice(QString("Skyrim"), skyrimPath, skyrimPath); - } - if (falloutNVPath.length() != 0) { - selection.addChoice(QString("Fallout NV"), falloutNVPath, falloutNVPath); - } - if (fallout3Path.length() != 0) { - selection.addChoice(QString("Fallout 3"), fallout3Path, fallout3Path); - } - if (oblivionPath.length() != 0) { - selection.addChoice(QString("Oblivion"), oblivionPath, oblivionPath); - } + SelectionDialog selection(QObject::tr("Please select the game to manage"), nullptr, QSize(32, 32)); - selection.addChoice(QString("Browse..."), QString(), QString()); + for (const IPluginGame * const game : pluginContainer.plugins()) { + if (game->isInstalled()) { + QString path = game->gameDirectory().absolutePath(); + selection.addChoice(game->gameIcon(), game->gameName(), path, path); + } } + selection.addChoice(QString("Browse..."), QString(), QString()); + if (selection.exec() == QDialog::Rejected) { gamePath = ""; done = true; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 50a07b75..1c54d250 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -60,13 +60,15 @@ along with Mod Organizer. If not, see . #include "aboutdialog.h" #include "safewritefile.h" #include "organizerproxy.h" -#include "nxmaccessmanager.h +#include "nxmaccessmanager.h" #include #include #include #include #include +#include #include +#include #include #include #include @@ -139,7 +141,7 @@ using namespace MOShared; MainWindow::MainWindow(const QString &exeName - : QMainWindow(parent), ui(new Ui::MainWindow), m_Tutorial(this, "MainWindow"), + , QSettings &initSettings , OrganizerCore &organizerCore , PluginContainer &pluginContainer , QWidget *parent) @@ -148,7 +150,7 @@ MainWindow::MainWindow(const QString &exeName , m_WasVisible(false) , m_Tutorial(this, "MainWindow") , m_ExeName(exeName) - , m_OldProfileIndex(-1), + , m_OldProfileIndex(-1) , m_ModListGroupingProxy(nullptr) , m_ModListSortProxy(nullptr) , m_OldExecutableIndex(-1) @@ -191,7 +193,7 @@ MainWindow::MainWindow(const QString &exeName updateToolBar(); - languageChange(m_Settings.language()); + TaskProgressManager::instance().tryCreateTaskbar(); // set up mod list m_ModListSortProxy = m_OrganizerCore.createModListProxyModel(); @@ -335,7 +337,7 @@ MainWindow::MainWindow(const QString &exeName // refresh profiles so the current profile can be activated refreshProfiles(false); - ui->profileBox->setCurrentText(m_OrganizerCore.currentProfile()->getName()); + ui->profileBox->setCurrentText(m_OrganizerCore.currentProfile()->name()); refreshExecutablesList(); updateToolBar(); @@ -1036,9 +1038,9 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) } if (ui->profileBox->currentIndex() == 0) { - ProfilesDialog(m_GamePath, m_OrganizerCore.managedGame(), this).exec(); + ProfilesDialog(m_GamePath, this).exec(); while (!refreshProfiles()) { - ProfilesDialog(m_GamePath, m_OrganizerCore.managedGame(), this).exec(); + ProfilesDialog(m_GamePath, this).exec(); } ui->profileBox->setCurrentIndex(previousIndex); } else { @@ -1213,25 +1215,6 @@ bool MainWindow::refreshProfiles(bool selectProfile) } -#if QT_VERSION >= 0x050000 -extern QPixmap qt_pixmapFromWinHICON(HICON icon); -#else -#define qt_pixmapFromWinHICON(icon) QPixmap::fromWinHICON(icon) -#endif - -QIcon MainWindow::iconForExecutable(const QString &filePath) -{ - HICON winIcon; - UINT res = ::ExtractIconExW(ToWString(filePath).c_str(), 0, &winIcon, nullptr, 1); - if (res == 1) { - QIcon result = QIcon(qt_pixmapFromWinHICON(winIcon)); - ::DestroyIcon(winIcon); - return result; - } else { - return QIcon(":/MO/gui/executable"); - } -} - void MainWindow::refreshExecutablesList() { QComboBox* executablesList = findChild("executablesListBox"); @@ -1285,12 +1268,12 @@ void MainWindow::refreshSaveList() QDir savesDir; if (m_OrganizerCore.currentProfile()->localSavesEnabled()) { - savesDir.setPath(m_OrganizerCore.currentProfile()->getPath() + "/saves"); + savesDir.setPath(m_OrganizerCore.currentProfile()->absolutePath() + "/saves"); } else { wchar_t path[MAX_PATH]; ::GetPrivateProfileStringW(L"General", L"SLocalSavePath", L"Saves", path, MAX_PATH, - (ToWString(m_OrganizerCore.currentProfile()->getPath()) + L"\\" + GameInfo::instance().getIniFileNames().at(0)).c_str()); + (ToWString(m_OrganizerCore.currentProfile()->absolutePath()) + L"\\" + GameInfo::instance().getIniFileNames().at(0)).c_str()); savesDir.setPath(m_OrganizerCore.managedGame()->documentsDirectory().absoluteFilePath(QString::fromWCharArray(path))); } @@ -1417,42 +1400,46 @@ void MainWindow::updateBSAList(const QStringList &defaultArchives, const QString void MainWindow::checkBSAList() { - ui->bsaList->blockSignals(true); + DataArchives *archives = m_OrganizerCore.managedGame()->feature(); - QStringList defaultArchives = m_OrganizerCore.defaultArchiveList(); + if (archives != nullptr) { + ui->bsaList->blockSignals(true); - bool warning = false; + QStringList defaultArchives = archives->archives(m_OrganizerCore.currentProfile()); - for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) { - bool modWarning = false; - QTreeWidgetItem *tlItem = ui->bsaList->topLevelItem(i); - for (int j = 0; j < tlItem->childCount(); ++j) { - QTreeWidgetItem *item = tlItem->child(j); - QString filename = item->text(0); - item->setIcon(0, QIcon()); - item->setToolTip(0, QString()); + bool warning = false; - if (item->checkState(0) == Qt::Unchecked) { - if (defaultArchives.contains(filename)) { - item->setIcon(0, QIcon(":/MO/gui/warning")); - item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!")); - modWarning = true; + for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) { + bool modWarning = false; + QTreeWidgetItem *tlItem = ui->bsaList->topLevelItem(i); + for (int j = 0; j < tlItem->childCount(); ++j) { + QTreeWidgetItem *item = tlItem->child(j); + QString filename = item->text(0); + item->setIcon(0, QIcon()); + item->setToolTip(0, QString()); + + if (item->checkState(0) == Qt::Unchecked) { + if (defaultArchives.contains(filename)) { + item->setIcon(0, QIcon(":/MO/gui/warning")); + item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!")); + modWarning = true; + } } } + if (modWarning) { + ui->bsaList->expandItem(ui->bsaList->topLevelItem(i)); + warning = true; + } } - if (modWarning) { - ui->bsaList->expandItem(ui->bsaList->topLevelItem(i)); - warning = true; + + if (warning) { + ui->tabWidget->setTabIcon(1, QIcon(":/MO/gui/warning")); + } else { + ui->tabWidget->setTabIcon(1, QIcon()); } - } - if (warning) { - ui->tabWidget->setTabIcon(1, QIcon(":/MO/gui/warning")); - } else { - ui->tabWidget->setTabIcon(1, QIcon()); + ui->bsaList->blockSignals(false); } - - ui->bsaList->blockSignals(false); } @@ -1790,7 +1777,7 @@ void MainWindow::on_actionAdd_Profile_triggered() { bool repeat = true; while (repeat) { - ProfilesDialog profilesDialog(m_GamePath, m_OrganizerCore.managedGame(), this); + ProfilesDialog profilesDialog(m_GamePath, this); profilesDialog.exec(); if (refreshProfiles() && !profilesDialog.failed()) { repeat = false; @@ -2333,10 +2320,10 @@ void MainWindow::overwriteClosed(int) void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) { - if (!m_OrganizerCore.modList()->modInfoAboutToChange(modInfo)) { - qDebug("A different mod information dialog is open. If this is incorrect, please restart MO"); - return; - } + if (!m_OrganizerCore.modList()->modInfoAboutToChange(modInfo)) { + qDebug("A different mod information dialog is open. If this is incorrect, please restart MO"); + return; + } std::vector flags = modInfo->getFlags(); if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { QDialog *dialog = this->findChild("__overwriteDialog"); @@ -3379,7 +3366,7 @@ void MainWindow::installTranslator(const QString &name) QString fileName = name + "_" + m_CurrentLanguage; if (!translator->load(fileName, qApp->applicationDirPath() + "/translations")) { if ((m_CurrentLanguage != "en-US") && (m_CurrentLanguage != "en_US")) { - qWarning("localization file %s not found", qPrintable(fileName)); + qDebug("localization file %s not found", qPrintable(fileName)); } // we don't actually expect localization files for english } @@ -3481,10 +3468,10 @@ int MainWindow::getBinaryExecuteInfo(const QFileInfo &targetInfo, QFileInfo &bin { // try to find java automatically WCHAR buffer[MAX_PATH]; - if (::FindExecutableW(targetPathW.c_str(), nullptr, buffer) > (HINSTANCE)32) { - DWORD binaryType = 0UL; - if (!::GetBinaryTypeW(buffer, &binaryType)) { - qDebug("failed to determine binary type of \"%ls\": %lu", buffer, ::GetLastError()); + if (::FindExecutableW(targetPathW.c_str(), nullptr, buffer) > (HINSTANCE)32) { + DWORD binaryType = 0UL; + if (!::GetBinaryTypeW(buffer, &binaryType)) { + qDebug("failed to determine binary type of \"%ls\": %lu", buffer, ::GetLastError()); } else if (binaryType == SCS_32BIT_BINARY) { binaryPath = ToQString(buffer); } @@ -3659,7 +3646,7 @@ void MainWindow::openDataFile() QString arguments; switch (getBinaryExecuteInfo(targetInfo, binaryInfo, arguments)) { case 1: { - m_OrganizerCore.spawnBinaryDirect(binaryInfo, arguments, m_OrganizerCore.currentProfile()->getName(), targetInfo.absolutePath(), ""); + m_OrganizerCore.spawnBinaryDirect(binaryInfo, arguments, m_OrganizerCore.currentProfile()->name(), targetInfo.absolutePath(), ""); } break; case 2: { ::ShellExecuteW(nullptr, L"open", ToWString(targetInfo.absoluteFilePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); @@ -4339,7 +4326,7 @@ void MainWindow::on_bossButton_clicked() createStdoutPipe(&stdOutRead, &stdOutWrite); HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"), parameters.join(" "), - m_OrganizerCore.currentProfile()->getName(), + m_OrganizerCore.currentProfile()->name(), m_OrganizerCore.settings().logLevel(), qApp->applicationDirPath() + "/loot", true, diff --git a/src/mainwindow.h b/src/mainwindow.h index b9f93cd5..d0e3aaf5 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -229,8 +229,6 @@ private: bool errorReported(QString &logFile); - QIcon iconForExecutable(const QString &filePath); - void updateESPLock(bool locked); static void setupNetworkProxy(bool activate); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index b1d9bed4..3b8d6b92 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -28,8 +28,10 @@ along with Mod Organizer. If not, see . #include "messagedialog.h" #include +#include #include #include +#include #include @@ -862,7 +864,13 @@ std::vector ModInfoRegular::getContents() const if (dir.entryList(QStringList() << "*.esp" << "*.esm").size() > 0) { result.push_back(CONTENT_PLUGIN); } - QString sePluginPath = ToQString(GameInfo::instance().getSEName()) + "/plugins"; + + ScriptExtender *extender = qApp->property("managed_game").value()->feature(); + + if (extender != nullptr) { + QString sePluginPath = extender->name() + "/plugins"; + if (dir.exists(sePluginPath)) result.push_back(CONTENT_SKSE); + } if (dir.exists("textures")) result.push_back(CONTENT_TEXTURE); if (dir.exists("meshes")) result.push_back(CONTENT_MESH); if (dir.exists("interface") @@ -870,7 +878,6 @@ std::vector ModInfoRegular::getContents() const if (dir.exists("music")) result.push_back(CONTENT_MUSIC); if (dir.exists("sound")) result.push_back(CONTENT_SOUND); if (dir.exists("scripts")) result.push_back(CONTENT_SCRIPT); - if (dir.exists(sePluginPath)) result.push_back(CONTENT_SKSE); if (dir.exists("strings")) result.push_back(CONTENT_STRING); if (dir.exists("SkyProc Patchers")) result.push_back(CONTENT_SKYPROC); return result; diff --git a/src/organizer.pro b/src/organizer.pro index b67fb3e5..9300f348 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -57,7 +57,6 @@ SOURCES += \ filedialogmemory.cpp \ executableslist.cpp \ editexecutablesdialog.cpp \ - dummybsa.cpp \ downloadmanager.cpp \ downloadlistwidgetcompact.cpp \ downloadlistwidget.cpp \ @@ -137,7 +136,6 @@ HEADERS += \ filedialogmemory.h \ executableslist.h \ editexecutablesdialog.h \ - dummybsa.h \ downloadmanager.h \ downloadlistwidgetcompact.h \ downloadlistwidget.h \ @@ -257,7 +255,7 @@ LIBS += -L"E:/Visual Leak Detector/lib/Win32" #DEFINES += TEST_MODELS -INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk "$${LOOTPATH}" "$${BOOSTPATH}" +INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk ../plugins/gamefeatures "$${LOOTPATH}" "$${BOOSTPATH}" LIBS += -L"$${BOOSTPATH}/stage/lib" diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 936e5529..0ea3ccb1 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -11,7 +11,9 @@ #include "spawn.h" #include "safewritefile.h" #include "syncoverwritedialog.h" +#include "nxmaccessmanager.h" #include +#include #include #include #include @@ -184,7 +186,7 @@ void OrganizerCore::storeSettings() m_UserInterface->storeSettings(settings); } if (m_CurrentProfile != nullptr) { - settings.setValue("selected_profile", m_CurrentProfile->getName().toUtf8().constData()); + settings.setValue("selected_profile", m_CurrentProfile->name().toUtf8().constData()); } settings.setValue("ask_for_nexuspw", m_AskForNexusPW); @@ -368,6 +370,7 @@ void OrganizerCore::setManagedGame(const QString &gameName) m_GameName = gameName; if (m_PluginContainer != nullptr) { m_GamePlugin = m_PluginContainer->managedGame(m_GameName); + qApp->setProperty("managed_game", QVariant::fromValue(m_GamePlugin)); emit managedGameChanged(m_GamePlugin); } } @@ -473,7 +476,6 @@ InstallationManager *OrganizerCore::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); } @@ -506,7 +508,7 @@ MOBase::IModRepositoryBridge *OrganizerCore::createNexusBridge() const QString OrganizerCore::profileName() const { if (m_CurrentProfile != nullptr) { - return m_CurrentProfile->getName(); + return m_CurrentProfile->name(); } else { return ""; } @@ -515,7 +517,7 @@ QString OrganizerCore::profileName() const QString OrganizerCore::profilePath() const { if (m_CurrentProfile != nullptr) { - return m_CurrentProfile->getPath(); + return m_CurrentProfile->absolutePath(); } else { return ""; } @@ -809,7 +811,7 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &argument dialog->show(); ON_BLOCK_EXIT([&] () { dialog->hide(); dialog->deleteLater(); }); - HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->getName(), currentDirectory, steamAppID); + HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->name(), currentDirectory, steamAppID); if (processHandle != INVALID_HANDLE_VALUE) { if (closeAfterStart && (m_UserInterface != nullptr)) { m_UserInterface->closeWindow(); @@ -938,7 +940,7 @@ HANDLE OrganizerCore::startApplication(const QString &executable, const QStringL QString profileName = profile; if (profile.length() == 0) { if (m_CurrentProfile != nullptr) { - profileName = m_CurrentProfile->getName(); + profileName = m_CurrentProfile->name(); } else { throw MyException(tr("No profile set")); } @@ -1081,7 +1083,7 @@ void OrganizerCore::refreshESPList() // clear list try { - m_PluginList.refresh(m_CurrentProfile->getName(), + m_PluginList.refresh(m_CurrentProfile->name(), *m_DirectoryStructure, m_CurrentProfile->getPluginsFileName(), m_CurrentProfile->getLoadOrderFileName(), @@ -1091,55 +1093,34 @@ 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 < result.count(); ++i) { - result[i] = result[i].trimmed(); - } - - return result; -} - void OrganizerCore::refreshBSAList() { - m_ArchivesInit = false; + DataArchives *archives = m_GamePlugin->feature(); - m_DefaultArchives = defaultArchiveList(); + if (archives != nullptr) { + m_ArchivesInit = false; - wchar_t buffer[256]; - std::wstring iniFileName = ToWString(QDir::toNativeSeparators(m_CurrentProfile->getIniFileName())); - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - L"", buffer, 256, iniFileName.c_str()) != 0) { - m_DefaultArchives = ToQString(buffer).split(','); - } else { - std::vector vanillaBSAs = GameInfo::instance().getVanillaBSAs(); - for (auto iter = vanillaBSAs.begin(); iter != vanillaBSAs.end(); ++iter) { - m_DefaultArchives.append(ToQString(*iter)); + // default archives are the ones enabled outside MO. if the list can't be found (which might + // happen if ini files are missing) use hard-coded defaults (preferrably the same the game would use) + m_DefaultArchives = archives->archives(m_CurrentProfile); + if (m_DefaultArchives.length() == 0) { + m_DefaultArchives = archives->vanillaArchives(); } - } - m_ActiveArchives.clear(); + m_ActiveArchives.clear(); - auto iter = enabledArchives(); - m_ActiveArchives = toStringList(iter.begin(), iter.end()); - if (m_ActiveArchives.isEmpty()) { - m_ActiveArchives = m_DefaultArchives; - } + auto iter = enabledArchives(); + m_ActiveArchives = toStringList(iter.begin(), iter.end()); + if (m_ActiveArchives.isEmpty()) { + m_ActiveArchives = m_DefaultArchives; + } - if (m_UserInterface != nullptr) { - m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives); - } + if (m_UserInterface != nullptr) { + m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives); + } - m_ArchivesInit = true; + m_ArchivesInit = true; + } } void OrganizerCore::refreshLists() diff --git a/src/organizercore.h b/src/organizercore.h index 4d9b0a7c..3191e2a9 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -155,7 +155,6 @@ public: bool onAboutToRun(const std::function &func); bool onFinishedRun(const std::function &func); void refreshModList(bool saveChanges = true); - QStringList defaultArchiveList(); public: // IPluginDiagnose interface diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index ad177247..3c7af59f 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -26,6 +26,7 @@ along with Mod Organizer. If not, see . #include "modinfo.h" #include #include +#include #include #include @@ -126,16 +127,19 @@ QString PluginList::getColumnToolTip(int column) } -void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseDirectory, - const QString &pluginsFile, const QString &loadOrderFile, - const QString &lockedOrderFile) +void PluginList::refresh(const QString &profileName + , const DirectoryEntry &baseDirectory + , const QString &pluginsFile + , const QString &loadOrderFile + , const QString &lockedOrderFile) { ChangeBracket layoutChange(this); m_ESPsByName.clear(); m_ESPsByPriority.clear(); m_ESPs.clear(); - std::vector primaryPlugins = GameInfo::instance().getPrimaryPlugins(); + + QStringList primaryPlugins = qApp->property("managed_game").value()->getPrimaryPlugins(); m_CurrentProfile = profileName; @@ -150,7 +154,7 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD if ((extension == "esp") || (extension == "esm")) { bool forceEnabled = Settings::instance().forceEnableCoreFiles() && - std::find(primaryPlugins.begin(), primaryPlugins.end(), ToWString(filename.toLower())) != primaryPlugins.end(); + std::find(primaryPlugins.begin(), primaryPlugins.end(), filename.toLower()) != primaryPlugins.end(); bool archive = false; try { @@ -321,11 +325,10 @@ bool PluginList::readLoadOrder(const QString &fileName) int priority = 0; - std::vector primaryPlugins = GameInfo::instance().getPrimaryPlugins(); - for (std::vector::iterator iter = primaryPlugins.begin(); - iter != primaryPlugins.end(); ++iter) { - if (availableESPs.find(ToQString(*iter)) != availableESPs.end()) { - m_ESPLoadOrder[ToQString(*iter)] = priority++; + QStringList primaryPlugins = qApp->property("managed_game").value()->getPrimaryPlugins(); + for (const QString &plugin : primaryPlugins) { + if (availableESPs.find(plugin) != availableESPs.end()) { + m_ESPLoadOrder[plugin] = priority++; } } diff --git a/src/profile.cpp b/src/profile.cpp index dd6814fd..5d31533a 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -21,7 +21,6 @@ along with Mod Organizer. If not, see . #include "report.h" #include "gameinfo.h" #include "windows_error.h" -#include "dummybsa.h" #include "modinfo.h" #include "safewritefile.h" #include @@ -29,6 +28,8 @@ along with Mod Organizer. If not, see . #include #include #include +#include +#include #include #include #include @@ -58,6 +59,7 @@ void Profile::touchFile(QString fileName) Profile::Profile(const QString &name, IPluginGame *gamePlugin, bool useDefaultSettings) : m_SaveTimer(nullptr) + , m_GamePlugin(gamePlugin) { initTimer(); QString profilesDir = qApp->property("dataPath").toString() + "/" + ToQString(AppConfig::profilesPath()); @@ -120,8 +122,9 @@ Profile::Profile(const QDir &directory, IPluginGame *gamePlugin) } -Profile::Profile(const Profile& reference) - : m_Directory(reference.m_Directory), m_SaveTimer(nullptr) +Profile::Profile(const Profile &reference) + : m_Directory(reference.m_Directory) + , m_SaveTimer(nullptr) { initTimer(); refreshModStatus(); @@ -590,35 +593,22 @@ void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const bool Profile::invalidationActive(bool *supported) const { - if (GameInfo::instance().requiresBSAInvalidation()) { + IPluginGame *gamePlugin = qApp->property("managed_game").value(); + + BSAInvalidation *invalidation = gamePlugin->feature(); + DataArchives *dataArchives = gamePlugin->feature(); + + if ((invalidation != nullptr) && (dataArchives != nullptr)) { if (supported != nullptr) { *supported = true; } - wchar_t buffer[1024]; - std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); - // epic ms fail: GetPrivateProfileString uses errno (for whatever reason) to signal a fail since the return value - // has a different meaning (number of bytes copied). HOWEVER, it will not set errno to 0 if NO error occured - errno = 0; - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - L"", buffer, 1024, iniFileName.c_str()) == 0) { - if (errno != 0x02) { - if (supported != nullptr) { - *supported = false; - } - return false; - } else { - QString errorMessage = tr("failed to parse ini file (%1)").arg(ToQString(iniFileName)); - throw windows_error(errorMessage.toUtf8().constData()); - } - } - QStringList archives = ToQString(buffer).split(','); - for (int i = 0; i < archives.count(); ++i) { - QString bsaName = archives.at(i).trimmed(); - if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) { + for (const QString &archive : dataArchives->archives(this)) { + if (invalidation->isInvalidationBSA(archive)) { return true; } } + return false; } else { *supported = false; } @@ -626,85 +616,26 @@ bool Profile::invalidationActive(bool *supported) const } -void Profile::deactivateInvalidation() const +void Profile::deactivateInvalidation() { - if (GameInfo::instance().requiresBSAInvalidation()) { - wchar_t buffer[1024]; - std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); - errno = 0; - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - L"", buffer, 1024, iniFileName.c_str()) == 0) { - if (errno == 0x02) { - QString errorMessage = tr("failed to parse ini file (%1): %2").arg(QDir::toNativeSeparators(getIniFileName())).arg(::GetLastError()); - throw windows_error(errorMessage.toUtf8().constData()); - } else { - return; - } - } - QStringList archives = ToQString(buffer).split(", "); - - for (int i = 0; i < archives.count();) { - QString bsaName = archives.at(i).trimmed(); - if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) { - archives.removeAt(i); - } else { - ++i; - } - } + IPluginGame *gamePlugin = qApp->property("managed_game").value(); - // just to be safe... - ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); + BSAInvalidation *invalidation = gamePlugin->feature(); - if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"0", iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"ArchiveInvalidation.txt", iniFileName.c_str())) { - QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); - throw windows_error(errorMessage.toUtf8().constData()); - } + if (invalidation != nullptr) { + invalidation->deactivate(this); } } -void Profile::activateInvalidation(const QString& dataDirectory) const +void Profile::activateInvalidation() { - if (GameInfo::instance().requiresBSAInvalidation()) { - wchar_t buffer[1024]; - std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName())); - errno = 0; - if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - L"", buffer, 1024, iniFileName.c_str()) == 0) { - if (errno == 0x02) { - throw windows_error("failed to parse ini file"); - } else { - // ignore. shouldn't have gotten here anyway - return; - } - } - QStringList archives = ToQString(buffer).split(", "); - - QString invalidationBSA = ToQString(GameInfo::instance().getInvalidationBSA()); + IPluginGame *gamePlugin = qApp->property("managed_game").value(); - if (!archives.contains(invalidationBSA)) { - archives.insert(0, invalidationBSA); - } - - // just to be safe... - ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); - - if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), - ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", iniFileName.c_str()) || - !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"", iniFileName.c_str())) { - QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); - throw windows_error(errorMessage.toUtf8().constData()); - } + BSAInvalidation *invalidation = gamePlugin->feature(); - QString bsaFile = dataDirectory + "/" + invalidationBSA; - if (!QFile::exists(bsaFile)) { - DummyBSA bsa; - bsa.write(bsaFile); - } + if (invalidation != nullptr) { + invalidation->activate(this); } } @@ -784,7 +715,7 @@ QString Profile::getProfileTweaks() const return QDir::cleanPath(m_Directory.absoluteFilePath(ToQString(AppConfig::profileTweakIni()))); } -QString Profile::getPath() const +QString Profile::absolutePath() const { return QDir::cleanPath(m_Directory.absolutePath()); } @@ -792,6 +723,7 @@ QString Profile::getPath() const void Profile::rename(const QString &newName) { QDir profileDir(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath())); - profileDir.rename(getName(), newName); + profileDir.rename(name(), newName); m_Directory = profileDir.absoluteFilePath(newName); } + diff --git a/src/profile.h b/src/profile.h index 34efd4b2..5eaa1ea1 100644 --- a/src/profile.h +++ b/src/profile.h @@ -22,7 +22,7 @@ along with Mod Organizer. If not, see . #include "modinfo.h" - +#include #include #include #include @@ -38,7 +38,7 @@ namespace MOBase { /** * @brief represents a profile **/ -class Profile : public QObject +class Profile : public QObject, public MOBase::IProfile { Q_OBJECT @@ -112,7 +112,7 @@ public: /** * @brief deactivate archive invalidation if it was active **/ - void deactivateInvalidation() const; + void deactivateInvalidation(); /** * @brief activate archive invalidation @@ -121,7 +121,7 @@ public: * @todo passing the data directory as a parameter is useless, the function should * be able to query it from GameInfo **/ - void activateInvalidation(const QString &dataDirectory) const; + void activateInvalidation(); /** * @return true if this profile uses local save games @@ -139,7 +139,7 @@ public: /** * @return name of the profile (this is identical to its directory name) **/ - QString getName() const { return m_Directory.dirName(); } + QString name() const { return m_Directory.dirName(); } /** * @return the path of the plugins file in this profile @@ -188,7 +188,7 @@ public: /** * @return path to this profile **/ - QString getPath() const; + QString absolutePath() const; void rename(const QString &newName); @@ -315,12 +315,15 @@ private: QDir m_Directory; + MOBase::IPluginGame *m_GamePlugin; + mutable QByteArray m_LastModlistHash; std::vector m_ModStatus; std::vector m_ModIndexByPriority; unsigned int m_NumRegularMods; QTimer *m_SaveTimer; + }; Q_DECLARE_METATYPE(Profile) diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp index c6731041..44ee96e9 100644 --- a/src/profilesdialog.cpp +++ b/src/profilesdialog.cpp @@ -24,7 +24,9 @@ along with Mod Organizer. If not, see . #include "transfersavesdialog.h" #include "profileinputdialog.h" #include "mainwindow.h" -#include +#include "aboutdialog.h" +#include +#include #include #include #include @@ -40,10 +42,9 @@ using namespace MOShared; Q_DECLARE_METATYPE(Profile::Ptr) -ProfilesDialog::ProfilesDialog(const QString &gamePath, MOBase::IPluginGame *gamePlugin, QWidget *parent) +ProfilesDialog::ProfilesDialog(const QString &gamePath, QWidget *parent) : TutorableDialog("Profiles", parent) , ui(new Ui::ProfilesDialog) - , m_GamePlugin(gamePlugin) , m_GamePath(gamePath) , m_FailState(false) { @@ -61,7 +62,11 @@ ProfilesDialog::ProfilesDialog(const QString &gamePath, MOBase::IPluginGame *gam } QCheckBox *invalidationBox = findChild("invalidationBox"); - if (!GameInfo::instance().requiresBSAInvalidation()) { + + IPluginGame *game = qApp->property("managed_game").value(); + BSAInvalidation *invalidation = game->feature(); + + if (invalidation == nullptr) { invalidationBox->setToolTip(tr("Archive invalidation isn't required for this game.")); invalidationBox->setEnabled(false); } @@ -97,7 +102,7 @@ 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, m_GamePlugin)))); + newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(new Profile(profileDir, qApp->property("managed_game").value())))); m_FailState = false; } catch (const std::exception& e) { reportError(tr("failed to create profile: %1").arg(e.what())); @@ -109,7 +114,7 @@ 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, m_GamePlugin, useDefaultSettings)))); + newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(new Profile(name, qApp->property("managed_game").value(), useDefaultSettings)))); profilesList->addItem(newItem); m_FailState = false; } catch (const std::exception&) { @@ -123,7 +128,7 @@ 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, m_GamePlugin)))); + newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(Profile::createPtrFrom(name, reference, qApp->property("managed_game").value())))); profilesList->addItem(newItem); m_FailState = false; } catch (const std::exception&) { @@ -190,7 +195,7 @@ void ProfilesDialog::on_removeProfileButton_clicked() } else { // on destruction, the profile object would write the profile.ini file again, so // we have to get rid of the it before deleting the directory - profilePath = currentProfile->getPath(); + profilePath = currentProfile->absolutePath(); } QListWidgetItem* item = profilesList->takeItem(profilesList->currentRow()); if (item != nullptr) { @@ -216,7 +221,7 @@ void ProfilesDialog::on_renameButton_clicked() while (!valid) { bool ok = false; name = QInputDialog::getText(this, tr("Rename Profile"), tr("New Name"), - QLineEdit::Normal, currentProfile->getName(), + QLineEdit::Normal, currentProfile->name(), &ok); valid = fixDirectoryName(name); if (!ok) { @@ -249,7 +254,7 @@ void ProfilesDialog::on_invalidationBox_stateChanged(int state) if (state == Qt::Unchecked) { currentProfile->deactivateInvalidation(); } else { - currentProfile->activateInvalidation(m_GamePath + "/data"); + currentProfile->activateInvalidation(); } } catch (const std::exception &e) { reportError(tr("failed to change archive invalidation state: %1").arg(e.what())); @@ -318,6 +323,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, m_GamePlugin, this); + TransferSavesDialog transferDialog(*currentProfile, qApp->property("managed_game").value(), this); transferDialog.exec(); } diff --git a/src/profilesdialog.h b/src/profilesdialog.h index 18b3932f..5cb5a449 100644 --- a/src/profilesdialog.h +++ b/src/profilesdialog.h @@ -50,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, MOBase::IPluginGame *gamePlugin, QWidget *parent = 0); + explicit ProfilesDialog(const QString &gamePath, QWidget *parent = 0); ~ProfilesDialog(); /** @@ -91,7 +91,6 @@ private slots: private: Ui::ProfilesDialog *ui; - MOBase::IPluginGame *m_GamePlugin; QString m_GamePath; QListWidget *m_ProfilesList; bool m_FailState; diff --git a/src/selectiondialog.cpp b/src/selectiondialog.cpp index 1b284928..aae95f56 100644 --- a/src/selectiondialog.cpp +++ b/src/selectiondialog.cpp @@ -22,8 +22,8 @@ along with Mod Organizer. If not, see . #include -SelectionDialog::SelectionDialog(const QString &description, QWidget *parent) - : QDialog(parent), ui(new Ui::SelectionDialog), m_Choice(nullptr), m_ValidateByData(false) +SelectionDialog::SelectionDialog(const QString &description, QWidget *parent, const QSize &iconSize) + : QDialog(parent), ui(new Ui::SelectionDialog), m_Choice(nullptr), m_ValidateByData(false), m_IconSize(iconSize) { ui->setupUi(this); @@ -35,10 +35,24 @@ SelectionDialog::~SelectionDialog() delete ui; } - void SelectionDialog::addChoice(const QString &buttonText, const QString &description, const QVariant &data) { - QCommandLinkButton *button = new QCommandLinkButton(buttonText, description, ui->buttonBox); + QAbstractButton *button = new QCommandLinkButton(buttonText, description, ui->buttonBox); + if (m_IconSize.isValid()) { + button->setIconSize(m_IconSize); + } + button->setProperty("data", data); + ui->buttonBox->addButton(button, QDialogButtonBox::AcceptRole); + if (data.isValid()) m_ValidateByData = true; +} + +void SelectionDialog::addChoice(const QIcon &icon, const QString &buttonText, const QString &description, const QVariant &data) +{ + QAbstractButton *button = new QCommandLinkButton(buttonText, description, ui->buttonBox); + if (m_IconSize.isValid()) { + button->setIconSize(m_IconSize); + } + button->setIcon(icon); button->setProperty("data", data); ui->buttonBox->addButton(button, QDialogButtonBox::AcceptRole); if (data.isValid()) m_ValidateByData = true; @@ -49,7 +63,6 @@ int SelectionDialog::numChoices() const return ui->buttonBox->findChildren(QString()).count(); } - QVariant SelectionDialog::getChoiceData() { return m_Choice->property("data"); diff --git a/src/selectiondialog.h b/src/selectiondialog.h index fe148859..43ba9767 100644 --- a/src/selectiondialog.h +++ b/src/selectiondialog.h @@ -33,7 +33,7 @@ class SelectionDialog : public QDialog public: - explicit SelectionDialog(const QString &description, QWidget *parent = 0); + explicit SelectionDialog(const QString &description, QWidget *parent = 0, const QSize &iconSize = QSize()); ~SelectionDialog(); @@ -46,6 +46,8 @@ public: */ void addChoice(const QString &buttonText, const QString &description, const QVariant &data); + void addChoice(const QIcon &icon, const QString &buttonText, const QString &description, const QVariant &data); + int numChoices() const; QVariant getChoiceData(); @@ -62,6 +64,7 @@ private: Ui::SelectionDialog *ui; QAbstractButton *m_Choice; bool m_ValidateByData; + QSize m_IconSize; }; diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index c8688cc4..37238a2a 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -42,11 +42,6 @@ bool Fallout3Info::identifyGame(const std::wstring &searchPath) FileExists(searchPath, L"FalloutLauncher.exe"); } -unsigned long Fallout3Info::getBSAVersion() -{ - return 0x68; -} - std::wstring Fallout3Info::getRegPathStatic() { HKEY key; @@ -67,37 +62,6 @@ std::wstring Fallout3Info::getRegPathStatic() } } -std::wstring Fallout3Info::getInvalidationBSA() -{ - return L"Fallout - Invalidation.bsa"; -} - -bool Fallout3Info::isInvalidationBSA(const std::wstring &bsaName) -{ - static LPCWSTR invalidation[] = { L"Fallout - AI!.bsa", L"Fallout - Invalidation.bsa", nullptr }; - - for (int i = 0; invalidation[i] != nullptr; ++i) { - if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { - return true; - } - } - return false; -} - -std::vector Fallout3Info::getPrimaryPlugins() -{ - return boost::assign::list_of(L"fallout3.esm"); -} - -std::vector Fallout3Info::getVanillaBSAs() -{ - return boost::assign::list_of (L"Fallout - Textures.bsa") - (L"Fallout - Meshes.bsa") - (L"Fallout - Voices.bsa") - (L"Fallout - Sound.bsa") - (L"Fallout - MenuVoices.bsa") - (L"Fallout - Misc.bsa"); -} std::vector Fallout3Info::getDLCPlugins() { @@ -125,20 +89,14 @@ std::wstring Fallout3Info::getReferenceDataFile() } -std::wstring Fallout3Info::getOMODExt() -{ - return L"fomod"; -} + std::vector Fallout3Info::getSteamVariants() const { return boost::assign::list_of(L"Regular")(L"Game Of The Year"); } -std::wstring Fallout3Info::getSEName() -{ - return L"fose"; -} + std::wstring Fallout3Info::getNexusPage(bool nmmScheme) { diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h index 6a211d4f..1f3a381d 100644 --- a/src/shared/fallout3info.h +++ b/src/shared/fallout3info.h @@ -35,8 +35,6 @@ public: virtual ~Fallout3Info() {} - virtual unsigned long getBSAVersion(); - static std::wstring getRegPathStatic(); virtual std::wstring getRegPath() { return getRegPathStatic(); } virtual std::wstring getBinaryName() { return L"Fallout3.exe"; } @@ -45,14 +43,38 @@ public: virtual std::wstring getGameName() const { return L"Fallout 3"; } virtual std::wstring getGameShortName() const { return L"Fallout3"; } - - virtual std::wstring getInvalidationBSA(); - - virtual bool isInvalidationBSA(const std::wstring &bsaName); - - virtual std::vector getPrimaryPlugins(); - - virtual std::vector getVanillaBSAs(); +/* + virtual std::wstring getInvalidationBSA() + { + return L"Fallout - Invalidation.bsa"; + } + + virtual bool isInvalidationBSA(const std::wstring &bsaName) + { + static LPCWSTR invalidation[] = { L"Fallout - AI!.bsa", L"Fallout - Invalidation.bsa", nullptr }; + + for (int i = 0; invalidation[i] != nullptr; ++i) { + if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { + return true; + } + } + return false; + } + + virtual std::vector getVanillaBSAs() + { + return boost::assign::list_of (L"Fallout - Textures.bsa") + (L"Fallout - Meshes.bsa") + (L"Fallout - Voices.bsa") + (L"Fallout - Sound.bsa") + (L"Fallout - MenuVoices.bsa") + (L"Fallout - Misc.bsa"); + } + + virtual std::vector getPrimaryPlugins() + { + return boost::assign::list_of(L"fallout3.esm"); + }*/ virtual std::vector getDLCPlugins(); virtual std::vector getSavegameAttachmentExtensions(); @@ -60,12 +82,9 @@ public: virtual std::vector getIniFileNames(); virtual std::wstring getReferenceDataFile(); - virtual std::wstring getOMODExt(); virtual std::vector getSteamVariants() const; - virtual std::wstring getSEName(); - virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); } diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index 8e701887..1203bd25 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -43,11 +43,6 @@ bool FalloutNVInfo::identifyGame(const std::wstring &searchPath) FileExists(searchPath, L"FalloutNVLauncher.exe"); } -unsigned long FalloutNVInfo::getBSAVersion() -{ - return 0x68; -} - std::wstring FalloutNVInfo::getRegPathStatic() { HKEY key; @@ -68,38 +63,6 @@ std::wstring FalloutNVInfo::getRegPathStatic() } } -std::wstring FalloutNVInfo::getInvalidationBSA() -{ - return L"Fallout - Invalidation.bsa"; -} - -bool FalloutNVInfo::isInvalidationBSA(const std::wstring &bsaName) -{ - static LPCWSTR invalidation[] = { L"Fallout - AI!.bsa", L"Fallout - Invalidation.bsa", nullptr }; - - for (int i = 0; invalidation[i] != nullptr; ++i) { - if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { - return true; - } - } - return false; -} - -std::vector FalloutNVInfo::getPrimaryPlugins() -{ - return boost::assign::list_of(L"falloutnv.esm"); -} - -std::vector FalloutNVInfo::getVanillaBSAs() -{ - return boost::assign::list_of (L"Fallout - Textures.bsa") - (L"Fallout - Textures2.bsa") - (L"Fallout - Meshes.bsa") - (L"Fallout - Voices1.bsa") - (L"Fallout - Sound.bsa") - (L"Fallout - Misc.bsa"); -} - std::vector FalloutNVInfo::getDLCPlugins() { return boost::assign::list_of (L"DeadMoney.esm") @@ -129,19 +92,6 @@ std::wstring FalloutNVInfo::getReferenceDataFile() return L"Fallout - Meshes.bsa"; } - -std::wstring FalloutNVInfo::getOMODExt() -{ - return L"fomod"; -} - - -std::wstring FalloutNVInfo::getSEName() -{ - return L"nvse"; -} - - std::wstring FalloutNVInfo::getNexusPage(bool nmmScheme) { if (nmmScheme) { @@ -176,17 +126,4 @@ bool FalloutNVInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) return false; } -/* -std::vector FalloutNVInfo::getExecutables() -{ - std::vector result; - result.push_back(ExecutableInfo(L"NVSE", L"nvse_loader.exe", L"", L"", DEFAULT_CLOSE)); - result.push_back(ExecutableInfo(L"New Vegas", L"falloutnv.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"FalloutNVLauncher.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/falloutnvinfo.h b/src/shared/falloutnvinfo.h index 513b7218..e1a614d2 100644 --- a/src/shared/falloutnvinfo.h +++ b/src/shared/falloutnvinfo.h @@ -35,8 +35,6 @@ public: virtual ~FalloutNVInfo() {} - virtual unsigned long getBSAVersion(); - static std::wstring getRegPathStatic(); virtual std::wstring getRegPath() { return getRegPathStatic(); } virtual std::wstring getBinaryName() { return L"FalloutNV.exe"; } @@ -48,13 +46,37 @@ public: // virtual bool requiresSteam() const { return true; } - virtual std::wstring getInvalidationBSA(); - - virtual bool isInvalidationBSA(const std::wstring &bsaName); - - virtual std::vector getPrimaryPlugins(); - - virtual std::vector getVanillaBSAs(); +/* virtual std::wstring getInvalidationBSA() + { + return L"Fallout - Invalidation.bsa"; + } + + virtual bool isInvalidationBSA(const std::wstring &bsaName) + { + static LPCWSTR invalidation[] = { L"Fallout - AI!.bsa", L"Fallout - Invalidation.bsa", nullptr }; + + for (int i = 0; invalidation[i] != nullptr; ++i) { + if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { + return true; + } + } + return false; + } + + virtual std::vector getVanillaBSAs() + { + return boost::assign::list_of (L"Fallout - Textures.bsa") + (L"Fallout - Textures2.bsa") + (L"Fallout - Meshes.bsa") + (L"Fallout - Voices1.bsa") + (L"Fallout - Sound.bsa") + (L"Fallout - Misc.bsa"); + } + + virtual std::vector getPrimaryPlugins() + { + return boost::assign::list_of(L"falloutnv.esm"); + }*/ virtual std::vector getDLCPlugins(); virtual std::vector getSavegameAttachmentExtensions(); @@ -62,9 +84,6 @@ public: virtual std::vector getIniFileNames(); virtual std::wstring getReferenceDataFile(); - virtual std::wstring getOMODExt(); - - virtual std::wstring getSEName(); virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index dbb935f6..77ed4d3c 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -28,32 +28,7 @@ along with Mod Organizer. If not, see . #include namespace MOShared { -/* -enum CloseMOStyle { - DEFAULT_CLOSE, - DEFAULT_STAY, - NEVER_CLOSE -}; -struct ExecutableInfo { - - ExecutableInfo(const std::wstring &aTitle, const std::wstring &aBinary, - const std::wstring &aArguments, const std::wstring &aWorkingDirectory, CloseMOStyle aCloseMO) - : title(aTitle), binary(aBinary), arguments(aArguments), workingDirectory(aWorkingDirectory), - closeMO(aCloseMO), steamAppID(L"") {} - ExecutableInfo(const std::wstring &aTitle, const std::wstring &aBinary, - const std::wstring &aArguments, const std::wstring &aWorkingDirectory, - CloseMOStyle aCloseMO, const std::wstring &aSteamAppID) - : title(aTitle), binary(aBinary), arguments(aArguments), workingDirectory(aWorkingDirectory), - closeMO(aCloseMO), steamAppID(aSteamAppID) {} - std::wstring title; - std::wstring binary; - std::wstring arguments; - std::wstring workingDirectory; - CloseMOStyle closeMO; - std::wstring steamAppID; -}; -*/ /** Class to manage information that depends on the used game type. The intention is to keep @@ -86,8 +61,6 @@ public: virtual std::wstring getRegPath() = 0; virtual std::wstring getBinaryName() = 0; - virtual unsigned long getBSAVersion() = 0; - virtual GameInfo::Type getType() = 0; virtual std::wstring getGameName() const = 0; @@ -98,30 +71,8 @@ public: 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; - // get absolute path to the directory where omo stores its profiles - virtual std::wstring getProfilesDir() const; - - virtual std::wstring getIniFilename() const; - virtual std::wstring getOverwriteDir() const; - virtual std::wstring getLogDir() const; - virtual std::wstring getLootDir() const; - virtual std::wstring getTutorialDir() const;*/ - - virtual bool requiresBSAInvalidation() const { return true; } - virtual bool requiresSteam() const; - - virtual std::wstring getInvalidationBSA() = 0; - virtual bool isInvalidationBSA(const std::wstring &bsaName) = 0; - - // the key in the game's ini-file that defines the list of bsas to load - virtual std::wstring archiveListKey() = 0; - - virtual std::vector getPrimaryPlugins() = 0; - - virtual std::vector getVanillaBSAs() = 0; + virtual bool requiresSteam() const; // get a list of file extensions for additional files belonging to a save game virtual std::vector getSavegameAttachmentExtensions() = 0; @@ -134,12 +85,8 @@ public: virtual std::wstring getReferenceDataFile() = 0; - virtual std::wstring getOMODExt() = 0; - virtual std::vector getSteamVariants() const; - virtual std::wstring getSEName() = 0; - virtual std::wstring getNexusPage(bool nmmScheme = true) = 0; virtual std::wstring getNexusInfoUrl() = 0; virtual int getNexusModID() = 0; diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index c945dcf4..16748f58 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -43,11 +43,6 @@ bool OblivionInfo::identifyGame(const std::wstring &searchPath) FileExists(searchPath, L"OblivionLauncher.exe"); } -unsigned long OblivionInfo::getBSAVersion() -{ - return 0x67; -} - std::wstring OblivionInfo::getRegPathStatic() { HKEY key; @@ -68,39 +63,14 @@ std::wstring OblivionInfo::getRegPathStatic() } } -std::wstring OblivionInfo::getInvalidationBSA() -{ - return L"Oblivion - Invalidation.bsa"; -} -bool OblivionInfo::isInvalidationBSA(const std::wstring &bsaName) -{ - static LPCWSTR invalidation[] = { L"Oblivion - Invalidation.bsa", L"ArchiveInvalidationInvalidated!.bsa", - L"BSARedirection.bsa", nullptr }; - for (int i = 0; invalidation[i] != nullptr; ++i) { - if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { - return true; - } - } - return false; -} -std::vector OblivionInfo::getPrimaryPlugins() -{ - return boost::assign::list_of(L"oblivion.esm"); -} -std::vector OblivionInfo::getVanillaBSAs() -{ - return boost::assign::list_of(L"Oblivion - Meshes.bsa") - (L"Oblivion - Textures - Compressed.bsa") - (L"Oblivion - Sounds.bsa") - (L"Oblivion - Voices1.bsa") - (L"Oblivion - Voices2.bsa") - (L"Oblivion - Misc.bsa"); -} + + + std::vector OblivionInfo::getDLCPlugins() @@ -131,10 +101,7 @@ std::vector OblivionInfo::getIniFileNames() } -std::wstring OblivionInfo::getSEName() -{ - return L"obse"; -} + std::wstring OblivionInfo::getNexusPage(bool nmmScheme) @@ -175,11 +142,4 @@ std::wstring OblivionInfo::getReferenceDataFile() return L"Oblivion - Meshes.bsa"; } - -std::wstring OblivionInfo::getOMODExt() -{ - return L"omod"; -} - - } // namespace MOShared diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h index a93f510e..87ba26ff 100644 --- a/src/shared/oblivioninfo.h +++ b/src/shared/oblivioninfo.h @@ -33,8 +33,6 @@ public: virtual ~OblivionInfo() {} - virtual unsigned long getBSAVersion(); - static std::wstring getRegPathStatic(); virtual std::wstring getRegPath() { return getRegPathStatic(); } virtual std::wstring getBinaryName() { return L"Oblivion.exe"; } @@ -43,14 +41,40 @@ public: virtual std::wstring getGameName() const { return L"Oblivion"; } virtual std::wstring getGameShortName() const { return L"Oblivion"; } +/* + virtual std::wstring getInvalidationBSA() + { + return L"Oblivion - Invalidation.bsa"; + } + + virtual bool isInvalidationBSA(const std::wstring &bsaName) + { + static LPCWSTR invalidation[] = { L"Oblivion - Invalidation.bsa", L"ArchiveInvalidationInvalidated!.bsa", + L"BSARedirection.bsa", nullptr }; + + for (int i = 0; invalidation[i] != nullptr; ++i) { + if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { + return true; + } + } + return false; + } + + virtual std::vector getVanillaBSAs() + { + return boost::assign::list_of(L"Oblivion - Meshes.bsa") + (L"Oblivion - Textures - Compressed.bsa") + (L"Oblivion - Sounds.bsa") + (L"Oblivion - Voices1.bsa") + (L"Oblivion - Voices2.bsa") + (L"Oblivion - Misc.bsa"); + } + + virtual std::vector getPrimaryPlugins() + { + return boost::assign::list_of(L"oblivion.esm"); + }*/ - virtual std::wstring getInvalidationBSA(); - - virtual bool isInvalidationBSA(const std::wstring &bsaName); - - virtual std::vector getPrimaryPlugins(); - - virtual std::vector getVanillaBSAs(); virtual std::vector getDLCPlugins(); virtual std::vector getSavegameAttachmentExtensions(); @@ -58,9 +82,6 @@ public: virtual std::vector getIniFileNames(); virtual std::wstring getReferenceDataFile(); - virtual std::wstring getOMODExt(); - - virtual std::wstring getSEName(); virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index feaf88a3..f66fcef4 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -50,10 +50,7 @@ bool SkyrimInfo::identifyGame(const std::wstring &searchPath) FileExists(searchPath, L"SkyrimLauncher.exe"); } -unsigned long SkyrimInfo::getBSAVersion() -{ - return 0x68; -} + std::wstring SkyrimInfo::getRegPathStatic() @@ -76,25 +73,6 @@ std::wstring SkyrimInfo::getRegPathStatic() } } - -std::wstring SkyrimInfo::getInvalidationBSA() -{ - return L"Skyrim - Invalidation.bsa"; -} - -bool SkyrimInfo::isInvalidationBSA(const std::wstring &bsaName) -{ - static LPCWSTR invalidation[] = { L"Skyrim - Invalidation.bsa", nullptr }; - - for (int i = 0; invalidation[i] != nullptr; ++i) { - if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) { - return true; - } - } - return false; -} - - GameInfo::LoadOrderMechanism SkyrimInfo::getLoadOrderMechanism() const { std::wstring fileName = getGameDirectory() + L"\\TESV.exe"; @@ -113,27 +91,6 @@ GameInfo::LoadOrderMechanism SkyrimInfo::getLoadOrderMechanism() const } } -std::vector SkyrimInfo::getPrimaryPlugins() -{ - return boost::assign::list_of(L"skyrim.esm")(L"update.esm"); -} - -std::vector SkyrimInfo::getVanillaBSAs() -{ - return boost::assign::list_of(L"Skyrim - Misc.bsa") - (L"Skyrim - Shaders.bsa") - (L"Skyrim - Textures.bsa") - (L"HighResTexturePack01.bsa") - (L"HighResTexturePack02.bsa") - (L"HighResTexturePack03.bsa") - (L"Skyrim - Interface.bsa") - (L"Skyrim - Animations.bsa") - (L"Skyrim - Meshes.bsa") - (L"Skyrim - Sounds.bsa") - (L"Skyrim - Voices.bsa") - (L"Skyrim - VoicesExtra.bsa"); -} - std::vector SkyrimInfo::getDLCPlugins() { return boost::assign::list_of (L"Dawnguard.esm") @@ -161,17 +118,6 @@ std::wstring SkyrimInfo::getReferenceDataFile() } -std::wstring SkyrimInfo::getOMODExt() -{ - return L"fomod"; -} - -std::wstring SkyrimInfo::getSEName() -{ - return L"skse"; -} - - std::wstring SkyrimInfo::getNexusPage(bool nmmScheme) { if (nmmScheme) { diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h index bf08fb25..5951f910 100644 --- a/src/shared/skyriminfo.h +++ b/src/shared/skyriminfo.h @@ -35,8 +35,6 @@ public: virtual ~SkyrimInfo() {} - virtual unsigned long getBSAVersion(); - static std::wstring getRegPathStatic(); virtual std::wstring getRegPath() { return getRegPathStatic(); } virtual std::wstring getBinaryName() { return L"TESV.exe"; } @@ -48,15 +46,6 @@ public: virtual LoadOrderMechanism getLoadOrderMechanism() const; - virtual bool requiresBSAInvalidation() const { return true; } - - virtual std::wstring getInvalidationBSA(); - - virtual bool isInvalidationBSA(const std::wstring &bsaName); - - virtual std::vector getPrimaryPlugins(); - - virtual std::vector getVanillaBSAs(); virtual std::vector getDLCPlugins(); virtual std::vector getSavegameAttachmentExtensions(); @@ -65,9 +54,6 @@ public: virtual std::vector getIniFileNames(); virtual std::wstring getReferenceDataFile(); - virtual std::wstring getOMODExt(); - - virtual std::wstring getSEName(); virtual std::wstring getNexusPage(bool nmmScheme = true); @@ -80,8 +66,6 @@ public: virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); - virtual std::wstring archiveListKey() { return L"SResourceArchiveList"; } - private: SkyrimInfo(const std::wstring &moDirectory, const std::wstring &moDataDirectory, const std::wstring &gameDirectory); -- cgit v1.3.1 From d08e0ebab09b0a71c3042cd20b8994ec1c10a525 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sun, 1 Mar 2015 11:28:49 +0100 Subject: - some fixes to how file changes are signaled and delayed --- src/iuserinterface.h | 4 +- src/mainwindow.cpp | 31 ++++------ src/mainwindow.h | 8 ++- src/modlist.cpp | 153 ++++++++++++++++++++++++++---------------------- src/modlist.h | 8 +++ src/modlistview.cpp | 1 + src/modlistview.h | 1 - src/organizercore.cpp | 26 ++++---- src/organizercore.h | 5 ++ src/plugincontainer.cpp | 3 +- src/pluginlist.cpp | 42 +++---------- src/pluginlist.h | 10 +--- src/profile.cpp | 51 ++++------------ src/profile.h | 17 ++---- src/version.rc | 4 +- 15 files changed, 162 insertions(+), 202 deletions(-) (limited to 'src/profile.cpp') diff --git a/src/iuserinterface.h b/src/iuserinterface.h index ff683bce..bcf800b7 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -5,6 +5,7 @@ #include "modinfo.h" #include #include +#include class IUserInterface @@ -27,12 +28,13 @@ public: virtual void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives) = 0; - virtual bool saveArchiveList() = 0; + virtual MOBase::DelayedFileWriterBase &archivesWriter() = 0; virtual void lock() = 0; virtual void unlock() = 0; virtual bool unlockClicked() = 0; + }; #endif // IUSERINTERFACE_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index af654e2f..9aa26db6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -160,6 +160,7 @@ MainWindow::MainWindow(const QString &exeName , m_OrganizerCore(organizerCore) , m_PluginContainer(pluginContainer) , m_DidUpdateMasterList(false) + , m_ArchiveListWriter(std::bind(&MainWindow::saveArchiveList, this)) { ui->setupUi(this); this->setWindowTitle(ToQString(GameInfo::instance().getGameName()) + " Mod Organizer v" + m_OrganizerCore.getVersion().displayString()); @@ -1846,8 +1847,8 @@ void MainWindow::modorder_changed() } } m_OrganizerCore.refreshBSAList(); - m_OrganizerCore.currentProfile()->writeModlist(); - saveArchiveList(); + m_OrganizerCore.currentProfile()->modlistWriter().write(); + m_ArchiveListWriter.write(); m_OrganizerCore.directoryStructure()->getFileRegister()->sortOrigins(); { // refresh selection @@ -1981,13 +1982,6 @@ void MainWindow::modRenamed(const QString &oldName, const QString &newName) } } - -void MainWindow::modlistChanged(int) -{ - m_ModListSortProxy->invalidate(); -} - - void MainWindow::fileMoved(const QString &filePath, const QString &oldOriginName, const QString &newOriginName) { const FileEntry::Ptr filePtr = m_OrganizerCore.directoryStructure()->findFile(ToWString(filePath)); @@ -2139,7 +2133,7 @@ void MainWindow::restoreBackup_clicked() void MainWindow::modlistChanged(const QModelIndex&, int) { - m_OrganizerCore.currentProfile()->writeModlist(); + m_OrganizerCore.currentProfile()->modlistWriter().write(); } void MainWindow::modlistSelectionChanged(const QModelIndex ¤t, const QModelIndex&) @@ -2150,10 +2144,10 @@ void MainWindow::modlistSelectionChanged(const QModelIndex ¤t, const QMode } else { m_OrganizerCore.modList()->setOverwriteMarkers(std::set(), std::set()); } - if ((m_ModListSortProxy != nullptr) +/* if ((m_ModListSortProxy != nullptr) && !m_ModListSortProxy->beingInvalidated()) { m_ModListSortProxy->invalidate(); - } + }*/ ui->modList->verticalScrollBar()->repaint(); } @@ -2727,7 +2721,7 @@ void MainWindow::savePrimaryCategory() } } -bool MainWindow::saveArchiveList() +void MainWindow::saveArchiveList() { if (m_OrganizerCore.isArchivesInit()) { SafeWriteFile archiveFile(m_OrganizerCore.currentProfile()->getArchivesFileName()); @@ -2746,12 +2740,10 @@ bool MainWindow::saveArchiveList() } if (archiveFile.commitIfDifferent(m_ArchiveListHash)) { qDebug("%s saved", qPrintable(QDir::toNativeSeparators(m_OrganizerCore.currentProfile()->getArchivesFileName()))); - return true; } } else { qWarning("archive list not initialised"); } - return false; } void MainWindow::checkModsForUpdates() @@ -3191,7 +3183,7 @@ void MainWindow::fixMods_clicked() } } - m_OrganizerCore.currentProfile()->writeModlist(); + m_OrganizerCore.currentProfile()->modlistWriter().write(); m_OrganizerCore.refreshLists(); std::set espsToActivate = dialog.getESPsToActivate(); @@ -4005,14 +3997,14 @@ void MainWindow::on_bsaList_customContextMenuRequested(const QPoint &pos) void MainWindow::bsaList_itemMoved() { - saveArchiveList(); + m_ArchiveListWriter.write(); m_CheckBSATimer.start(500); } void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int) { - saveArchiveList(); + m_ArchiveListWriter.write(); m_CheckBSATimer.start(500); } @@ -4528,12 +4520,13 @@ void MainWindow::on_restoreButton_clicked() void MainWindow::on_saveModsButton_clicked() { - m_OrganizerCore.currentProfile()->writeModlistNow(true); + m_OrganizerCore.currentProfile()->modlistWriter().writeImmediately(true); QDateTime now = QDateTime::currentDateTime(); if (createBackup(m_OrganizerCore.currentProfile()->getModlistFileName(), now)) { MessageDialog::showMessage(tr("Backup of modlist created"), this); } } + void MainWindow::on_restoreModsButton_clicked() { QString modlistName = m_OrganizerCore.currentProfile()->getModlistFileName(); diff --git a/src/mainwindow.h b/src/mainwindow.h index d0e3aaf5..88419594 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -53,6 +53,7 @@ along with Mod Organizer. If not, see . #include "iuserinterface.h" #include #include +#include #ifndef Q_MOC_RUN #include #endif @@ -97,7 +98,7 @@ public: void setModListSorting(int index); void setESPListSorting(int index); - bool saveArchiveList(); + void saveArchiveList(); void registerPluginTool(MOBase::IPluginTool *tool); void registerModPage(MOBase::IPluginModPage *modPage); @@ -121,6 +122,8 @@ public: virtual bool closeWindow(); virtual void setWindowEnabled(bool enabled); + virtual MOBase::DelayedFileWriterBase &archivesWriter() override { return m_ArchiveListWriter; } + public slots: void displayColumnSelection(const QPoint &pos); @@ -315,6 +318,8 @@ private: LockedDialog *m_LockDialog { nullptr }; + MOBase::DelayedFileWriter m_ArchiveListWriter; + private slots: void showMessage(const QString &message); @@ -389,7 +394,6 @@ private slots: void addPrimaryCategoryCandidates(); void modDetailsUpdated(bool success); - void modlistChanged(int row); void modInstalled(const QString &modName); diff --git a/src/modlist.cpp b/src/modlist.cpp index 250b5caf..c6beee8b 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -458,6 +458,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) emit modlist_changed(index, role); } result = true; + emit dataChanged(index, index); } else if (role == Qt::EditRole) { switch (index.column()) { case COL_NAME: { @@ -469,7 +470,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) if (ok) { m_Profile->setModPriority(modID, newPriority); - emit modlist_changed(index, role); + emit modorder_changed(); result = true; } else { result = false; @@ -859,13 +860,14 @@ void ModList::removeRowForce(int row, const QModelIndex &parent) bool wasEnabled = m_Profile->modEnabled(row); - beginRemoveRows(parent, row, row); m_Profile->setModEnabled(row, false); - m_Profile->cancelWriteModlist(); // don't write modlist while we're changing it + + m_Profile->modlistWriter().cancel(); + beginRemoveRows(parent, row, row); ModInfo::removeMod(row); - m_Profile->refreshModStatus(); // removes the mod from the status list endRemoveRows(); - m_Profile->writeModlist(); // this ensures the modified list gets written back before new mods can be installed + m_Profile->refreshModStatus(); // removes the mod from the status list + m_Profile->modlistWriter().write(); // this ensures the modified list gets written back before new mods can be installed if (wasEnabled) { emit removeOrigin(modInfo->name()); @@ -874,7 +876,6 @@ void ModList::removeRowForce(int row, const QModelIndex &parent) emit modUninstalled(modInfo->getInstallationFile()); } - bool ModList::removeRows(int row, int count, const QModelIndex &parent) { if (static_cast(row) >= ModInfo::getNumMods()) { @@ -903,6 +904,7 @@ bool ModList::removeRows(int row, int count, const QModelIndex &parent) removeRowForce(row + i, parent); } } + return success; } @@ -997,6 +999,76 @@ QString ModList::getColumnToolTip(int column) } +bool ModList::moveSelection(QAbstractItemView *itemView, int direction) +{ + QItemSelectionModel *selectionModel = itemView->selectionModel(); + + const QAbstractProxyModel *proxyModel = qobject_cast(selectionModel->model()); + const QSortFilterProxyModel *filterModel = nullptr; + + while ((filterModel == nullptr) && (proxyModel != nullptr)) { + filterModel = qobject_cast(proxyModel); + if (filterModel == nullptr) { + proxyModel = qobject_cast(proxyModel->sourceModel()); + } + } + if (filterModel == nullptr) { + return true; + } + + int offset = -1; + if (((direction < 0) && (filterModel->sortOrder() == Qt::DescendingOrder)) || + ((direction > 0) && (filterModel->sortOrder() == Qt::AscendingOrder))) { + offset = 1; + } + + QModelIndexList rows = selectionModel->selectedRows(); + if (direction > 0) { + for (int i = 0; i < rows.size() / 2; ++i) { + rows.swap(i, rows.size() - i - 1); + } + } + for (QModelIndex idx : rows) { + if (filterModel != nullptr) { + idx = filterModel->mapToSource(idx); + } + int newPriority = m_Profile->getModPriority(idx.row()) + offset; + if ((newPriority >= 0) && (newPriority < static_cast(m_Profile->numRegularMods()))) { + m_Profile->setModPriority(idx.row(), newPriority); + notifyChange(idx.row()); + } + } + emit modorder_changed(); + return true; +} + +bool ModList::deleteSelection(QAbstractItemView *itemView) +{ + QItemSelectionModel *selectionModel = itemView->selectionModel(); + + QModelIndexList rows = selectionModel->selectedRows(); + if (rows.count() > 1) { + emit removeSelectedMods(); + } else if (rows.count() == 1) { + removeRow(rows[0].data(Qt::UserRole + 1).toInt(), QModelIndex()); + } + return true; +} + +bool ModList::toggleSelection(QAbstractItemView *itemView) +{ + QAbstractItemModel *model = itemView->model(); + QItemSelectionModel *selectionModel = itemView->selectionModel(); + + for (QModelIndex idx : selectionModel->selectedRows()) { + int oldState = idx.data(Qt::CheckStateRole).toInt(); + model->setData(idx, oldState == Qt::Unchecked ? Qt::Checked + : Qt::Unchecked, + Qt::CheckStateRole); + } + return true; +} + bool ModList::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::ContextMenu) { @@ -1010,74 +1082,15 @@ bool ModList::eventFilter(QObject *obj, QEvent *event) } else if ((event->type() == QEvent::KeyPress) && (m_Profile != nullptr)) { QAbstractItemView *itemView = qobject_cast(obj); QKeyEvent *keyEvent = static_cast(event); + if ((itemView != nullptr) && (keyEvent->modifiers() == Qt::ControlModifier) - && ((keyEvent->key() == Qt::Key_Up) - || (keyEvent->key() == Qt::Key_Down))) { - QItemSelectionModel *selectionModel = itemView->selectionModel(); - const QAbstractProxyModel *proxyModel = qobject_cast(selectionModel->model()); - const QSortFilterProxyModel *filterModel = nullptr; - while ((filterModel == nullptr) && (proxyModel != nullptr)) { - filterModel = qobject_cast(proxyModel); - if (filterModel == nullptr) { - proxyModel = qobject_cast(proxyModel->sourceModel()); - } - } - if (filterModel == nullptr) { - return true; - } - int diff = -1; - if (((keyEvent->key() == Qt::Key_Up) && (filterModel->sortOrder() == Qt::DescendingOrder)) || - ((keyEvent->key() == Qt::Key_Down) && (filterModel->sortOrder() == Qt::AscendingOrder))) { - diff = 1; - } - QModelIndexList rows = selectionModel->selectedRows(); - if (keyEvent->key() == Qt::Key_Down) { - for (int i = 0; i < rows.size() / 2; ++i) { - rows.swap(i, rows.size() - i - 1); - } - } - foreach (QModelIndex idx, rows) { - if (filterModel != nullptr) { - idx = filterModel->mapToSource(idx); - } - int newPriority = m_Profile->getModPriority(idx.row()) + diff; - if ((newPriority >= 0) && (newPriority < static_cast(m_Profile->numRegularMods()))) { - m_Profile->setModPriority(idx.row(), newPriority); - notifyChange(idx.row()); - } - } - emit modorder_changed(); - return true; + && ((keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down))) { + return moveSelection(itemView, keyEvent->key() == Qt::Key_Up ? -1 : 1); } else if (keyEvent->key() == Qt::Key_Delete) { - QItemSelectionModel *selectionModel = itemView->selectionModel(); - QModelIndexList rows = selectionModel->selectedRows(); - if (rows.count() > 1) { - emit removeSelectedMods(); - } else if (rows.count() == 1) { - removeRow(rows[0].data(Qt::UserRole + 1).toInt(), QModelIndex()); - } - return true; + return deleteSelection(itemView); } else if (keyEvent->key() == Qt::Key_Space) { - QItemSelectionModel *selectionModel = itemView->selectionModel(); - const QSortFilterProxyModel *proxyModel = qobject_cast(selectionModel->model()); - - QModelIndex minRow, maxRow; - foreach (QModelIndex idx, selectionModel->selectedRows()) { - if (proxyModel != nullptr) { - idx = proxyModel->mapToSource(idx); - } - if (!minRow.isValid() || (idx.row() < minRow.row())) { - minRow = idx; - } - if (!maxRow.isValid() || (idx.row() > maxRow.row())) { - maxRow = idx; - } - int oldState = idx.data(Qt::CheckStateRole).toInt(); - setData(idx, oldState == Qt::Unchecked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); - } - emit dataChanged(minRow, maxRow); - return true; + return toggleSelection(itemView); } } return QObject::eventFilter(obj, event); diff --git a/src/modlist.h b/src/modlist.h index 73a49745..6a10cb3e 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -40,6 +40,8 @@ along with Mod Organizer. If not, see . #include +class QSortFilterProxyModel; + /** * Model presenting an overview of the installed mod * This is used in a view in the main window of MO. It combines general information about @@ -261,6 +263,12 @@ private: ModStates state(unsigned int modIndex) const; + bool moveSelection(QAbstractItemView *itemView, int direction); + + bool deleteSelection(QAbstractItemView *itemView); + + bool toggleSelection(QAbstractItemView *itemView); + private slots: private: diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 50a13a8c..fe2d3e57 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -53,3 +53,4 @@ void ModListView::setModel(QAbstractItemModel *model) QTreeView::setModel(model); setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); } + diff --git a/src/modlistview.h b/src/modlistview.h index 4907225f..6cd114b0 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -19,7 +19,6 @@ public slots: private: ViewMarkingScrollBar *m_Scrollbar; - }; #endif // MODLISTVIEW_H diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 0ea3ccb1..6a5e08d0 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -24,6 +24,7 @@ #include #include #include +#include using namespace MOShared; @@ -126,6 +127,7 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) , m_AskForNexusPW(false) , m_DirectoryUpdate(false) , m_ArchivesInit(false) + , m_PluginListsWriter(std::bind(&OrganizerCore::savePluginList, this)) { m_DownloadManager.setOutputDirectory(m_Settings.getDownloadDirectory()); m_DownloadManager.setPreferredServers(m_Settings.getPreferredServers()); @@ -140,13 +142,14 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, SLOT(directory_refreshed())); connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, SLOT(removeOrigin(QString))); - connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList())); 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*))); + connect(&m_PluginList, &PluginList::writePluginsList, &m_PluginListsWriter, &DelayedFileWriterBase::write); + // make directory refresher run in a separate thread m_RefresherThread.start(); m_DirectoryRefresher.moveToThread(&m_RefresherThread); @@ -319,11 +322,11 @@ void OrganizerCore::setUserInterface(IUserInterface *userInterface, QWidget *wid m_UserInterface = userInterface; if (widget != nullptr) { - connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex,int)), widget, SLOT(modorder_changed())); +// connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), widget, SLOT(modorder_changed())); + connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), widget, SLOT(modlistChanged(QModelIndex, int))); connect(&m_ModList, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); connect(&m_ModList, SIGNAL(modRenamed(QString,QString)), widget, SLOT(modRenamed(QString,QString))); connect(&m_ModList, SIGNAL(modUninstalled(QString)), widget, SLOT(modRemoved(QString))); - connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), widget, SLOT(modlistChanged(QModelIndex, int))); connect(&m_ModList, SIGNAL(removeSelectedMods()), widget, SLOT(removeMod_clicked())); connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget, SLOT(displayColumnSelection(QPoint))); connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget, SLOT(fileMoved(QString, QString, QString))); @@ -920,7 +923,7 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString & // need to make sure all data is saved before we start the application if (m_CurrentProfile != nullptr) { - m_CurrentProfile->writeModlistNow(true); + m_CurrentProfile->modlistWriter().writeImmediately(true); } // TODO: should also pass arguments @@ -1066,7 +1069,7 @@ void OrganizerCore::refreshModList(bool saveChanges) { // don't lose changes! if (saveChanges) { - m_CurrentProfile->writeModlistNow(true); + m_CurrentProfile->modlistWriter().writeImmediately(true); } ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign()); @@ -1079,7 +1082,7 @@ void OrganizerCore::refreshModList(bool saveChanges) void OrganizerCore::refreshESPList() { - m_CurrentProfile->writeModlist(); + m_CurrentProfile->modlistWriter().write(); // clear list try { @@ -1151,8 +1154,7 @@ void OrganizerCore::updateModActiveState(int index, bool active) } m_PluginList.refreshLoadOrder(); // immediately save affected lists - savePluginList(); -// refreshBSAList(); + m_PluginListsWriter.writeImmediately(false); } void OrganizerCore::updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo) @@ -1172,7 +1174,7 @@ void OrganizerCore::updateModInDirectoryStructure(unsigned int index, ModInfo::P // now we need to refresh the bsa list and save it so there is no confusion about what archives are avaiable and active refreshBSAList(); if (m_UserInterface != nullptr) { - m_UserInterface->saveArchiveList(); + m_UserInterface->archivesWriter().write(); } m_DirectoryRefresher.setMods(m_CurrentProfile->getActiveMods(), enabledArchives()); @@ -1257,7 +1259,7 @@ std::set OrganizerCore::enabledArchives() void OrganizerCore::refreshDirectoryStructure() { if (!m_DirectoryUpdate) { - m_CurrentProfile->writeModlistNow(true); + m_CurrentProfile->modlistWriter().writeImmediately(true); m_DirectoryUpdate = true; std::vector > activeModList = m_CurrentProfile->getActiveMods(); @@ -1447,7 +1449,7 @@ bool OrganizerCore::saveCurrentLists() try { savePluginList(); if (m_UserInterface != nullptr) { - m_UserInterface->saveArchiveList(); + m_UserInterface->archivesWriter().write(); } } catch (const std::exception &e) { reportError(tr("failed to save load order: %1").arg(e.what())); @@ -1470,7 +1472,7 @@ void OrganizerCore::prepareStart() { if (m_CurrentProfile == nullptr) { return; } - m_CurrentProfile->writeModlist(); + m_CurrentProfile->modlistWriter().write(); m_CurrentProfile->createTweakedIniFile(); saveCurrentLists(); m_Settings.setupLoadMechanism(); diff --git a/src/organizercore.h b/src/organizercore.h index 3191e2a9..d8a3e232 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -124,6 +125,8 @@ public: void createDefaultProfile(); + MOBase::DelayedFileWriter &pluginsWriter() { return m_PluginListsWriter; } + public: MOBase::IGameInfo &gameInfo() const; MOBase::IModRepositoryBridge *createNexusBridge() const; @@ -255,6 +258,8 @@ private: bool m_DirectoryUpdate; bool m_ArchivesInit; + MOBase::DelayedFileWriter m_PluginListsWriter; + }; #endif // ORGANIZERCORE_H diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 1b06d1a6..6bc9d12b 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -317,7 +317,8 @@ QString PluginContainer::fullDescription(unsigned int key) const { switch (key) { case PROBLEM_PLUGINSNOTLOADED: { - QString result = tr("The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version:") + "
    "; + QString result = tr("The following plugins could not be loaded. The reason may be missing " + "dependencies (i.e. python) or an outdated version:") + "
      "; for (const QString &plugin : m_FailedPlugins) { result += "
    • " + plugin + "
    • "; } diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index f72ab690..f8a27ef4 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -69,23 +69,12 @@ static bool ByPriority(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo static bool ByDate(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) { return QFileInfo(LHS.m_FullPath).lastModified() < QFileInfo(RHS.m_FullPath).lastModified(); -/* QString lhsExtension = LHS.m_Name.right(3).toLower(); - QString rhsExtension = RHS.m_Name.right(3).toLower(); - if (lhsExtension != rhsExtension) { - return lhsExtension == "esm"; - } - - return ::CompareFileTime(&LHS.m_Time, &RHS.m_Time) < 0;*/ } PluginList::PluginList(QObject *parent) : QAbstractItemModel(parent) , m_FontMetrics(QFont()) - , m_SaveTimer(this) { - m_SaveTimer.setSingleShot(true); - connect(&m_SaveTimer, SIGNAL(timeout()), this, SIGNAL(saveTimer())); - m_Utf8Codec = QTextCodec::codecForName("utf-8"); m_LocalCodec = QTextCodec::codecForName("Windows-1252"); @@ -238,7 +227,8 @@ void PluginList::enableESP(const QString &name, bool enable) if (iter != m_ESPsByName.end()) { m_ESPs[iter->second].m_Enabled = enable; - startSaveTime(); + + emit writePluginsList(); } else { reportError(tr("esp not found: %1").arg(name)); } @@ -252,7 +242,7 @@ void PluginList::enableAll() for (std::vector::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) { iter->m_Enabled = true; } - startSaveTime(); + emit writePluginsList(); } } @@ -266,7 +256,7 @@ void PluginList::disableAll() iter->m_Enabled = false; } } - startSaveTime(); + emit writePluginsList(); } } @@ -382,7 +372,7 @@ void PluginList::readEnabledFrom(const QString &fileName) m_ESPs[iter->second].m_Enabled = true; } else { qWarning("plugin %s not found", modName.toUtf8().constData()); - startSaveTime(); + emit writePluginsList(); } } } @@ -503,8 +493,6 @@ void PluginList::saveTo(const QString &pluginFileName } else if (QFile::exists(deleterFileName)) { shellDelete(QStringList() << deleterFileName); } - - m_SaveTimer.stop(); } @@ -581,7 +569,8 @@ void PluginList::lockESPIndex(int index, bool lock) m_LockedOrder.erase(iter); } } - startSaveTime(); +qDebug(__FUNCTION__); + emit writePluginsList(); } @@ -631,7 +620,7 @@ void PluginList::refreshLoadOrder() setPluginPriority(index, temp); m_ESPs[index].m_LoadOrder = iter->first; syncLoadOrder(); - startSaveTime(); + emit writePluginsList(); } } } @@ -897,7 +886,7 @@ bool PluginList::setData(const QModelIndex &modIndex, const QVariant &value, int emit dataChanged(modIndex, modIndex); refreshLoadOrder(); - startSaveTime(); + emit writePluginsList(); result = true; } else if (role == Qt::EditRole) { @@ -1034,21 +1023,8 @@ void PluginList::changePluginPriority(std::vector rows, int newPriority) layoutChange.finish(); refreshLoadOrder(); - - startSaveTime(); -} - - -void PluginList::startSaveTime() -{ - testMasters(); - - if (!m_SaveTimer.isActive()) { - m_SaveTimer.start(2000); - } } - bool PluginList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int, const QModelIndex &parent) { if (action == Qt::IgnoreAction) { diff --git a/src/pluginlist.h b/src/pluginlist.h index e0913840..80fcc25b 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -262,10 +262,7 @@ signals: **/ void esplist_changed(); - /** - * @brief emitted when the plugin list should be saved - */ - void saveTimer(); + void writePluginsList(); private: @@ -310,8 +307,6 @@ private: void setPluginPriority(int row, int &newPriority); void changePluginPriority(std::vector rows, int newPriority); - void startSaveTime(); - void testMasters(); private: @@ -336,14 +331,11 @@ private: QTextCodec *m_Utf8Codec; QTextCodec *m_LocalCodec; - mutable QTimer m_SaveTimer; - SignalRefreshed m_Refreshed; SignalPluginMoved m_PluginMoved; QTemporaryFile m_TempFile; - }; #endif // PLUGINLIST_H diff --git a/src/profile.cpp b/src/profile.cpp index 5d31533a..c1e6e5fc 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -34,6 +34,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #define WIN32_LEAN_AND_MEAN #include @@ -43,10 +44,10 @@ along with Mod Organizer. If not, see . using namespace MOBase; using namespace MOShared; + Profile::Profile() - : m_SaveTimer(nullptr) + : m_ModListWriter(std::bind(&Profile::writeModlistNow, this)) { - initTimer(); } void Profile::touchFile(QString fileName) @@ -58,10 +59,9 @@ void Profile::touchFile(QString fileName) } Profile::Profile(const QString &name, IPluginGame *gamePlugin, bool useDefaultSettings) - : m_SaveTimer(nullptr) + : m_ModListWriter(std::bind(&Profile::writeModlistNow, this)) , m_GamePlugin(gamePlugin) { - initTimer(); QString profilesDir = qApp->property("dataPath").toString() + "/" + ToQString(AppConfig::profilesPath()); QDir profileBase(profilesDir); @@ -100,10 +100,10 @@ Profile::Profile(const QString &name, IPluginGame *gamePlugin, bool useDefaultSe Profile::Profile(const QDir &directory, IPluginGame *gamePlugin) - : m_Directory(directory), m_SaveTimer(nullptr) + : m_Directory(directory) + , m_ModListWriter(std::bind(&Profile::writeModlistNow, this)) { assert(gamePlugin != nullptr); - initTimer(); if (!QFile::exists(m_Directory.filePath("modlist.txt"))) { qWarning("missing modlist.txt in %s", qPrintable(directory.path())); @@ -124,52 +124,24 @@ Profile::Profile(const QDir &directory, IPluginGame *gamePlugin) Profile::Profile(const Profile &reference) : m_Directory(reference.m_Directory) - , m_SaveTimer(nullptr) + , m_ModListWriter(std::bind(&Profile::writeModlistNow, this)) { - initTimer(); refreshModStatus(); } Profile::~Profile() { - writeModlistNow(); -} - - -void Profile::initTimer() -{ - m_SaveTimer = new QTimer(this); - m_SaveTimer->setSingleShot(true); - connect(m_SaveTimer, SIGNAL(timeout()), this, SLOT(writeModlistNow())); + m_ModListWriter.writeImmediately(true); } - bool Profile::exists() const { return m_Directory.exists(); } - -void Profile::writeModlist() const -{ - if (!m_SaveTimer->isActive()) { - m_SaveTimer->start(2000); - } -} - - -void Profile::cancelWriteModlist() const +void Profile::writeModlistNow() { - m_SaveTimer->stop(); -} - - -void Profile::writeModlistNow(bool onlyOnTimer) const -{ - if (onlyOnTimer && !m_SaveTimer->isActive()) return; - - m_SaveTimer->stop(); if (!m_Directory.exists()) return; try { @@ -366,7 +338,7 @@ void Profile::refreshModStatus() file.close(); updateIndices(); if (modStatusModified) { - writeModlist(); + m_ModListWriter.write(); } } @@ -456,7 +428,6 @@ void Profile::setModEnabled(unsigned int index, bool enabled) } } - bool Profile::modEnabled(unsigned int index) const { if (index >= m_ModStatus.size()) { @@ -508,7 +479,7 @@ void Profile::setModPriority(unsigned int index, int &newPriority) m_ModStatus.at(index).m_Priority = newPriorityTemp; updateIndices(); - writeModlist(); + m_ModListWriter.write(); } Profile *Profile::createPtrFrom(const QString &name, const Profile &reference, MOBase::IPluginGame *gamePlugin) diff --git a/src/profile.h b/src/profile.h index 5eaa1ea1..342b6fa0 100644 --- a/src/profile.h +++ b/src/profile.h @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "modinfo.h" #include +#include #include #include #include @@ -89,16 +90,7 @@ public: **/ static Profile *createPtrFrom(const QString &name, const Profile &reference, MOBase::IPluginGame *gamePlugin); - /** - * @brief write out the modlist.txt - **/ - void writeModlist() const; - - /** - * cancel any potential request to write modlist.txt. This is used to ensure no invalid modlist is - * saved while it's being modified - */ - void cancelWriteModlist() const; + MOBase::DelayedFileWriter &modlistWriter() { return m_ModListWriter; } /** * @brief test if this profile uses archive invalidation @@ -283,7 +275,7 @@ signals: public slots: - void writeModlistNow(bool onlyOnTimer = false) const; + void writeModlistNow(); private: @@ -310,6 +302,7 @@ private: void mergeTweak(const QString &tweakName, const QString &tweakedIni) const; void mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const; void touchFile(QString fileName); + void finishChangeStatus() const; private: @@ -322,7 +315,7 @@ private: std::vector m_ModIndexByPriority; unsigned int m_NumRegularMods; - QTimer *m_SaveTimer; + MOBase::DelayedFileWriter m_ModListWriter; }; diff --git a/src/version.rc b/src/version.rc index 68d7cd23..d6fe2f85 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 1,3,0,0 -#define VER_FILEVERSION_STR "1,3,0,0\0" +#define VER_FILEVERSION 1,3,1,0 +#define VER_FILEVERSION_STR "1,3,1,0\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION -- cgit v1.3.1