From 2fb491711188d413f65bdd8193644d25ae03c2c2 Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Sun, 27 Sep 2015 13:29:04 +0100 Subject: Changes to go with cleaned up archive library --- src/SConscript | 6 ++- src/installationmanager.cpp | 113 +++++++++++++++++++++----------------------- src/installationmanager.h | 12 ++--- src/selfupdater.cpp | 50 +++++++------------- src/selfupdater.h | 9 ++-- 5 files changed, 85 insertions(+), 105 deletions(-) diff --git a/src/SConscript b/src/SConscript index 6783fd8b..540b2343 100644 --- a/src/SConscript +++ b/src/SConscript @@ -72,7 +72,11 @@ env.AppendUnique(CPPDEFINES = [ 'QT_MESSAGELOGCONTEXT' ]) -env.AppendUnique(CPPFLAGS = [ '-wd4100', '-wd4127', '-wd4512', '-wd4189' ]) +# Boost produces very long names with msvc truncates. Doesn't seem to cause +# problems. +# Also note to remove the -wd4100 I hacked the boost headers (tagged_argument.hpp) +# appropriately. +env.AppendUnique(CPPFLAGS = [ '-wd4503' ]) env.AppendUnique(LINKFLAGS = [ '/SUBSYSTEM:WINDOWS', diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 75abd750..ead9b3dc 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -81,9 +81,9 @@ InstallationManager::InstallationManager() CreateArchiveType CreateArchiveFunc = resolveFunction(archiveLib, "CreateArchive"); - m_CurrentArchive = CreateArchiveFunc(); - if (!m_CurrentArchive->isValid()) { - throw MyException(getErrorString(m_CurrentArchive->getLastError())); + m_ArchiveHandler = CreateArchiveFunc(); + if (!m_ArchiveHandler->isValid()) { + throw MyException(getErrorString(m_ArchiveHandler->getLastError())); } m_InstallationProgress.setWindowFlags(m_InstallationProgress.windowFlags() & (~Qt::WindowContextHelpButtonHint)); @@ -92,7 +92,7 @@ InstallationManager::InstallationManager() InstallationManager::~InstallationManager() { - delete m_CurrentArchive; + delete m_ArchiveHandler; } void InstallationManager::setParentWidget(QWidget *widget) @@ -104,34 +104,32 @@ void InstallationManager::setParentWidget(QWidget *widget) } -void InstallationManager::queryPassword(LPSTR password) +void InstallationManager::queryPassword(QString *password) { - QString result = QInputDialog::getText(nullptr, tr("Password required"), tr("Password"), QLineEdit::Password); - strncpy(password, result.toLocal8Bit().constData(), MAX_PASSWORD_LENGTH); + *password = QInputDialog::getText(nullptr, tr("Password required"), tr("Password"), QLineEdit::Password); } -void InstallationManager::mapToArchive(const DirectoryTree::Node *node, std::wstring path, FileData * const *data) +void InstallationManager::mapToArchive(const DirectoryTree::Node *node, QString path, FileData * const *data) { if (path.length() > 0) { // when using a long windows path (starting with \\?\) we apparently can have redundant // . components in the path. This wasn't a problem with "regular" path names. - if (path == L".") { + if (path == ".") { path.clear(); } else { - path.append(L"\\"); + path.append("\\"); } } for (DirectoryTree::const_leaf_iterator iter = node->leafsBegin(); iter != node->leafsEnd(); ++iter) { - std::wstring temp = path + iter->getName().toStdWString(); - data[iter->getIndex()]->addOutputFileName(temp.c_str()); + data[iter->getIndex()]->addOutputFileName(path + iter->getName().toQString()); } for (DirectoryTree::const_node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) { - std::wstring temp = path + (*iter)->getData().name.toStdWString(); + QString temp = path + (*iter)->getData().name.toQString(); if ((*iter)->getData().index != -1) { - data[(*iter)->getData().index]->addOutputFileName(temp.c_str()); + data[(*iter)->getData().index]->addOutputFileName(temp); } mapToArchive(*iter, temp, data); } @@ -142,11 +140,9 @@ void InstallationManager::mapToArchive(const DirectoryTree::Node *baseNode) { FileData* const *data; size_t size; - m_CurrentArchive->getFileList(data, size); + m_ArchiveHandler->getFileList(data, size); - std::wstring currentPath; - - mapToArchive(baseNode, currentPath, data); + mapToArchive(baseNode, "", data); } @@ -154,15 +150,15 @@ bool InstallationManager::unpackSingleFile(const QString &fileName) { FileData* const *data; size_t size; - m_CurrentArchive->getFileList(data, size); + m_ArchiveHandler->getFileList(data, size); QString baseName = QFileInfo(fileName).fileName(); bool available = false; for (size_t i = 0; i < size; ++i) { - if (_wcsicmp(data[i]->getFileName(), ToWString(fileName).c_str()) == 0) { + if (data[i]->getFileName().compare(fileName, Qt::CaseInsensitive) == 0) { available = true; - data[i]->addOutputFileName(ToWString(baseName).c_str()); + data[i]->addOutputFileName(baseName); m_TempFilesToDelete.insert(baseName); } } @@ -177,10 +173,10 @@ bool InstallationManager::unpackSingleFile(const QString &fileName) m_InstallationProgress.setWindowModality(Qt::WindowModal); m_InstallationProgress.show(); - bool res = m_CurrentArchive->extract(ToWString(QDir::toNativeSeparators(QDir::tempPath())).c_str(), + bool res = m_ArchiveHandler->extract(QDir::tempPath(), new MethodCallback(this, &InstallationManager::updateProgress), - new MethodCallback(this, &InstallationManager::dummyProgressFile), - new MethodCallback(this, &InstallationManager::report7ZipError)); + nullptr, + new MethodCallback(this, &InstallationManager::report7ZipError)); m_InstallationProgress.hide(); @@ -223,25 +219,30 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool FileData* const *data; size_t size; - m_CurrentArchive->getFileList(data, size); + m_ArchiveHandler->getFileList(data, size); for (size_t i = 0; i < size; ++i) { - if (files.contains(ToQString(data[i]->getFileName()), Qt::CaseInsensitive)) { - const wchar_t *targetFile = data[i]->getFileName(); + //FIXME Use qstring all the way through + if (files.contains(data[i]->getFileName(), Qt::CaseInsensitive)) { + std::wstring temp = data[i]->getFileName().toStdWString(); + wchar_t const * const origFile = temp.c_str(); + const wchar_t *targetFile = origFile; + //Note: I don't think 'flatten' is ever set to true. so this code + //might never be executed if (flatten) { - targetFile = wcsrchr(data[i]->getFileName(), '\\'); + targetFile = wcsrchr(origFile/*data[i]->getFileName()*/, '\\'); if (targetFile == nullptr) { - targetFile = wcsrchr(data[i]->getFileName(), '/'); + targetFile = wcsrchr(origFile/*data[i]->getFileName()*/, '/'); } if (targetFile == nullptr) { - qCritical("failed to find backslash in %ls", data[i]->getFileName()); + qCritical() << "Failed to find backslash in " << data[i]->getFileName(); continue; } else { // skip the slash ++targetFile; } } - data[i]->addOutputFileName(targetFile); + data[i]->addOutputFileName(ToQString(targetFile)); result.append(QDir::tempPath().append("/").append(ToQString(targetFile))); @@ -256,12 +257,12 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool m_InstallationProgress.show(); // unpack only the files we need for the installer - if (!m_CurrentArchive->extract(ToWString(QDir::toNativeSeparators(QDir::tempPath())).c_str(), + if (!m_ArchiveHandler->extract(QDir::tempPath(), new MethodCallback(this, &InstallationManager::updateProgress), - new MethodCallback(this, &InstallationManager::dummyProgressFile), - new MethodCallback(this, &InstallationManager::report7ZipError))) { + nullptr, + new MethodCallback(this, &InstallationManager::report7ZipError))) { m_InstallationProgress.hide(); - throw MyException(QString("extracting failed (%1)").arg(m_CurrentArchive->getLastError())); + throw MyException(QString("extracting failed (%1)").arg(m_ArchiveHandler->getLastError())); } m_InstallationProgress.hide(); @@ -286,7 +287,7 @@ DirectoryTree *InstallationManager::createFilesTree() { FileData* const *data; size_t size; - m_CurrentArchive->getFileList(data, size); + m_ArchiveHandler->getFileList(data, size); QScopedPointer result(new DirectoryTree); @@ -296,7 +297,7 @@ DirectoryTree *InstallationManager::createFilesTree() // grouping the filenames first, but so far there doesn't seem to be an actual performance problem DirectoryTree::Node *currentNode = result.data(); - QString fileName = ToQString(data[i]->getFileName()); + QString fileName = data[i]->getFileName(); QStringList components = fileName.split("\\"); // iterate over all path-components of this filename (including the filename itself) @@ -389,26 +390,22 @@ void InstallationManager::updateProgress(float percentage) { m_InstallationProgress.setValue(static_cast(percentage * 100.0)); if (m_InstallationProgress.wasCanceled()) { - m_CurrentArchive->cancel(); + m_ArchiveHandler->cancel(); m_InstallationProgress.reset(); } } -void InstallationManager::updateProgressFile(LPCWSTR fileName) +void InstallationManager::updateProgressFile(QString const &fileName) { -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) - m_InstallationProgress.setLabelText(QString::fromWCharArray(fileName)); -#else - m_InstallationProgress.setLabelText(QString::fromUtf16(fileName)); -#endif + m_InstallationProgress.setLabelText(fileName); } -void InstallationManager::report7ZipError(LPCWSTR errorMessage) +void InstallationManager::report7ZipError(QString const &errorMessage) { - reportError(QString::fromWCharArray(errorMessage)); - m_CurrentArchive->cancel(); + reportError(errorMessage); + m_ArchiveHandler->cancel(); } @@ -538,15 +535,15 @@ bool InstallationManager::doInstall(GuessedValue &modName, int modID, m_InstallationProgress.setValue(0); m_InstallationProgress.setWindowModality(Qt::WindowModal); m_InstallationProgress.show(); - if (!m_CurrentArchive->extract(ToWString("\\\\?\\" + targetDirectoryNative).c_str(), + if (!m_ArchiveHandler->extract(targetDirectory, new MethodCallback(this, &InstallationManager::updateProgress), - new MethodCallback(this, &InstallationManager::updateProgressFile), - new MethodCallback(this, &InstallationManager::report7ZipError))) { + new MethodCallback(this, &InstallationManager::updateProgressFile), + new MethodCallback(this, &InstallationManager::report7ZipError))) { m_InstallationProgress.hide(); - if (m_CurrentArchive->getLastError() == Archive::ERROR_EXTRACT_CANCELLED) { + if (m_ArchiveHandler->getLastError() == Archive::ERROR_EXTRACT_CANCELLED) { return false; } else { - throw MyException(QString("extracting failed (%1)").arg(m_CurrentArchive->getLastError())); + throw MyException(QString("extracting failed (%1)").arg(m_ArchiveHandler->getLastError())); } } @@ -587,13 +584,13 @@ bool InstallationManager::doInstall(GuessedValue &modName, int modID, bool InstallationManager::wasCancelled() { - return m_CurrentArchive->getLastError() == Archive::ERROR_EXTRACT_CANCELLED; + return m_ArchiveHandler->getLastError() == Archive::ERROR_EXTRACT_CANCELLED; } void InstallationManager::postInstallCleanup() { - m_CurrentArchive->close(); + m_ArchiveHandler->close(); // directories we may want to remove. sorted from longest to shortest to ensure we remove subdirectories first. auto longestFirst = [](const QString &LHS, const QString &RHS) -> bool { @@ -679,13 +676,13 @@ bool InstallationManager::install(const QString &fileName, GuessedValue //If there's an archive already open, close it. This happens with the bundle //installer when it uncompresses a split archive, then finds it has a real archive //to deal with. - m_CurrentArchive->close(); + m_ArchiveHandler->close(); // open the archive and construct the directory tree the installers work on - bool archiveOpen = m_CurrentArchive->open(ToWString(QDir::toNativeSeparators(fileName)).c_str(), - new MethodCallback(this, &InstallationManager::queryPassword)); + bool archiveOpen = m_ArchiveHandler->open(fileName, + new MethodCallback(this, &InstallationManager::queryPassword)); if (!archiveOpen) { - qDebug("integrated archiver can't open %s. errorcode %d", qPrintable(fileName), m_CurrentArchive->getLastError()); + qDebug("integrated archiver can't open %s. errorcode %d", qPrintable(fileName), m_ArchiveHandler->getLastError()); } ON_BLOCK_EXIT(std::bind(&InstallationManager::postInstallCleanup, this)); diff --git a/src/installationmanager.h b/src/installationmanager.h index d4b4f7dc..d73f4653 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -147,12 +147,10 @@ public: private: - void queryPassword(LPSTR password); + void queryPassword(QString *password); void updateProgress(float percentage); - void updateProgressFile(LPCWSTR fileName); - void report7ZipError(LPCWSTR errorMessage); - - void dummyProgressFile(LPCWSTR) {} + void updateProgressFile(const QString &fileName); + void report7ZipError(const QString &errorMessage); MOBase::DirectoryTree *createFilesTree(); @@ -161,7 +159,7 @@ private: void mapToArchive(const MOBase::DirectoryTree::Node *baseNode); // recursive worker function for mapToArchive - void mapToArchive(const MOBase::DirectoryTree::Node *node, std::wstring path, FileData * const *data); + void mapToArchive(const MOBase::DirectoryTree::Node *node, QString path, FileData * const *data); bool unpackSingleFile(const QString &fileName); @@ -205,7 +203,7 @@ private: std::vector m_Installers; std::set m_SupportedExtensions; - Archive *m_CurrentArchive; + Archive *m_ArchiveHandler; QString m_CurrentFile; QProgressDialog m_InstallationProgress; diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index ed34bfc2..724b89db 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -73,9 +73,9 @@ SelfUpdater::SelfUpdater(NexusInterface *nexusInterface) CreateArchiveType CreateArchiveFunc = resolveFunction(archiveLib, "CreateArchive"); - m_CurrentArchive = CreateArchiveFunc(); - if (!m_CurrentArchive->isValid()) { - throw MyException(InstallationManager::getErrorString(m_CurrentArchive->getLastError())); + m_ArchiveHandler = CreateArchiveFunc(); + if (!m_ArchiveHandler->isValid()) { + throw MyException(InstallationManager::getErrorString(m_ArchiveHandler->getLastError())); } connect(&m_Progress, SIGNAL(canceled()), this, SLOT(downloadCancel())); @@ -90,7 +90,7 @@ SelfUpdater::SelfUpdater(NexusInterface *nexusInterface) SelfUpdater::~SelfUpdater() { - delete m_CurrentArchive; + delete m_ArchiveHandler; } void SelfUpdater::setUserInterface(QWidget *widget) @@ -238,26 +238,25 @@ void SelfUpdater::installUpdate() QDir().mkdir(backupPath); // rename files that are currently open so we can unpack the update - if (!m_CurrentArchive->open(ToWString(QDir::toNativeSeparators(m_UpdateFile.fileName())).c_str(), - new MethodCallback(this, &SelfUpdater::queryPassword))) { + if (!m_ArchiveHandler->open(m_UpdateFile.fileName(), nullptr)) { throw MyException(tr("failed to open archive \"%1\": %2") .arg(m_UpdateFile.fileName()) - .arg(InstallationManager::getErrorString(m_CurrentArchive->getLastError()))); + .arg(InstallationManager::getErrorString(m_ArchiveHandler->getLastError()))); } // move all files contained in the archive out of the way, // otherwise we can't overwrite everything FileData* const *data; size_t size; - m_CurrentArchive->getFileList(data, size); + m_ArchiveHandler->getFileList(data, size); for (size_t i = 0; i < size; ++i) { - QString outputName = ToQString(data[i]->getFileName()); + QString outputName = data[i]->getFileName(); if (outputName.startsWith("ModOrganizer\\", Qt::CaseInsensitive)) { outputName = outputName.mid(13); - data[i]->addOutputFileName(ToWString(outputName).c_str()); + data[i]->addOutputFileName(outputName); } else if (outputName != "ModOrganizer") { - data[i]->addOutputFileName(ToWString(outputName).c_str()); + data[i]->addOutputFileName(outputName); } QFileInfo file(mopath + "/" + outputName); if (file.exists() && file.isFile()) { @@ -270,14 +269,14 @@ void SelfUpdater::installUpdate() } // now unpack the archive into the mo directory - if (!m_CurrentArchive->extract(GameInfo::instance().getOrganizerDirectory().c_str(), - new MethodCallback(this, &SelfUpdater::updateProgress), - new MethodCallback(this, &SelfUpdater::updateProgressFile), - new MethodCallback(this, &SelfUpdater::report7ZipError))) { + if (!m_ArchiveHandler->extract(QString::fromStdWString(GameInfo::instance().getOrganizerDirectory()), + nullptr, + nullptr, + new MethodCallback(this, &SelfUpdater::report7ZipError))) { throw std::runtime_error("extracting failed"); } - m_CurrentArchive->close(); + m_ArchiveHandler->close(); m_UpdateFile.remove(); @@ -292,24 +291,9 @@ void SelfUpdater::installUpdate() emit restart(); } -void SelfUpdater::queryPassword(LPSTR) +void SelfUpdater::report7ZipError(QString const &errorMessage) { - // nop -} - -void SelfUpdater::updateProgress(float) -{ - // nop -} - -void SelfUpdater::updateProgressFile(LPCWSTR) -{ - // nop -} - -void SelfUpdater::report7ZipError(LPCWSTR errorMessage) -{ - QMessageBox::critical(m_Parent, tr("Error"), ToQString(errorMessage)); + QMessageBox::critical(m_Parent, tr("Error"), errorMessage); } diff --git a/src/selfupdater.h b/src/selfupdater.h index b5bbc406..ac9bddb3 100644 --- a/src/selfupdater.h +++ b/src/selfupdater.h @@ -65,7 +65,7 @@ public: * @param parent parent widget * @todo passing the nexus interface is unneccessary **/ - SelfUpdater(NexusInterface *nexusInterface); + explicit SelfUpdater(NexusInterface *nexusInterface); virtual ~SelfUpdater(); @@ -115,10 +115,7 @@ private: void download(const QString &downloadLink, const QString &fileName); void installUpdate(); - void queryPassword(LPSTR password); - void updateProgress(float percentage); - void updateProgressFile(LPCWSTR fileName); - void report7ZipError(LPCWSTR errorMessage); + void report7ZipError(const QString &errorMessage); QString retrieveNews(const QString &description); void showProgress(); @@ -142,7 +139,7 @@ private: bool m_Canceled; int m_Attempts; - Archive *m_CurrentArchive; + Archive *m_ArchiveHandler; }; -- cgit v1.3.1 From 9a7b0ee2c78f1807d34e754d1f051c88b55a2f0c Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Sun, 11 Oct 2015 21:12:37 +0100 Subject: Fixes for quotes in module names: Treat the module name from Nexus as HTML and convert into plain text. Allow ' in a module name Also removed the ::tr1:: namespace in the regexs --- src/downloadmanager.cpp | 13 ++++++++++--- src/nexusinterface.cpp | 11 ++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index bc78cdc6..0cc45183 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -37,6 +37,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include @@ -909,10 +910,10 @@ QString DownloadManager::getDownloadFileName(const QString &baseName) const QString DownloadManager::getFileNameFromNetworkReply(QNetworkReply *reply) { if (reply->hasRawHeader("Content-Disposition")) { - std::tr1::regex exp("filename=\"(.*)\""); + std::regex exp("filename=\"(.*)\""); - std::tr1::cmatch result; - if (std::tr1::regex_search(reply->rawHeader("Content-Disposition").constData(), result, exp)) { + std::cmatch result; + if (std::regex_search(reply->rawHeader("Content-Disposition").constData(), result, exp)) { return QString::fromUtf8(result.str(1).c_str()); } } @@ -1128,6 +1129,12 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD if (!info->m_FileInfo->version.isValid()) { info->m_FileInfo->version = info->m_FileInfo->newestVersion; } + //Nexus has HTMLd these so unhtml them if necessary + QTextDocument doc; + doc.setHtml(info->m_FileInfo->modName); + info->m_FileInfo->modName = doc.toPlainText(); + doc.setHtml(info->m_FileInfo->name); + info->m_FileInfo->name = doc.toPlainText(); info->m_FileInfo->fileCategory = convertFileCategory(fileInfo["category_id"].toInt()); info->m_FileInfo->fileTime = matchDate(fileInfo["date"].toString()); info->m_FileInfo->fileID = fileInfo["id"].toInt(); diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index dc027be4..25f3d1b4 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -196,12 +196,13 @@ void NexusInterface::loginCompleted() void NexusInterface::interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query) { - static std::tr1::regex exp("^([a-zA-Z0-9_\\- ]*?)([-_ ][VvRr]?[0-9_]+)?-([1-9][0-9]+).*"); - static std::tr1::regex simpleexp("^([a-zA-Z0-9_]+)"); + //Look for something along the lines of modulename-Vn-m + any old rubbish. + static std::regex exp("^([a-zA-Z0-9_'\"\\- ]*?)([-_ ][VvRr]?[0-9_]+)?-([1-9][0-9]+).*"); + static std::regex simpleexp("^([a-zA-Z0-9_]+)"); QByteArray fileNameUTF8 = fileName.toUtf8(); - std::tr1::cmatch result; - if (std::tr1::regex_search(fileNameUTF8.constData(), result, exp)) { + std::cmatch result; + if (std::regex_search(fileNameUTF8.constData(), result, exp)) { modName = QString::fromUtf8(result[1].str().c_str()); modName = modName.replace('_', ' ').trimmed(); @@ -231,7 +232,7 @@ void NexusInterface::interpretNexusFileName(const QString &fileName, QString &mo 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)) { + } else if (std::regex_search(fileNameUTF8.constData(), result, simpleexp)) { qDebug("simple expression matched, using name only"); modName = QString::fromUtf8(result[1].str().c_str()); modName = modName.replace('_', ' ').trimmed(); -- cgit v1.3.1 From 9d88ca13b9b6a193dfb17a1ee3ea5e5cbed8a7e9 Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Thu, 15 Oct 2015 13:34:01 +0100 Subject: Added functionality to use the web page link in a fomod to supply an optional web page. Also refactored modinfo into multiple files (as the actual types are opaque anyway) and cleaned up the headers a bit I also changed 'visit on nexus' from the menu not to open the downloads tab because it was a little confusing --- src/CMakeLists.txt | 10 + src/installationmanager.cpp | 5 + src/installationmanager.h | 5 +- src/mainwindow.cpp | 21 +- src/mainwindow.h | 1 + src/modinfo.cpp | 787 +--------------------------------------- src/modinfo.h | 561 +--------------------------- src/modinfobackup.cpp | 20 + src/modinfobackup.h | 40 ++ src/modinfoforeign.cpp | 50 +++ src/modinfoforeign.h | 67 ++++ src/modinfooverwrite.cpp | 53 +++ src/modinfooverwrite.h | 59 +++ src/modinforegular.cpp | 571 +++++++++++++++++++++++++++++ src/modinforegular.h | 349 ++++++++++++++++++ src/modinfowithconflictinfo.cpp | 130 +++++++ src/modinfowithconflictinfo.h | 64 ++++ src/organizer.pro | 14 +- 18 files changed, 1479 insertions(+), 1328 deletions(-) create mode 100644 src/modinfobackup.cpp create mode 100644 src/modinfobackup.h create mode 100644 src/modinfoforeign.cpp create mode 100644 src/modinfoforeign.h create mode 100644 src/modinfooverwrite.cpp create mode 100644 src/modinfooverwrite.h create mode 100644 src/modinforegular.cpp create mode 100644 src/modinforegular.h create mode 100644 src/modinfowithconflictinfo.cpp create mode 100644 src/modinfowithconflictinfo.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 22b725de..e8acd89d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,11 @@ SET(organizer_SRCS modlist.cpp modinfodialog.cpp modinfo.cpp + modinfobackup.cpp + modinfoforeign.cpp + modinfooverwrite.cpp + modinforegular.cpp + modinfowithconflictinfo.cpp messagedialog.cpp mainwindow.cpp main.cpp @@ -125,6 +130,11 @@ SET(organizer_HDRS modlist.h modinfodialog.h modinfo.h + modinfobackup.h + modinfoforeign.h + modinfooverwrite.h + modinforegular.h + modinfowithconflictinfo.h messagedialog.h mainwindow.h loghighlighter.h diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index a6af1a7f..3b1703d0 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -100,6 +100,10 @@ void InstallationManager::setParentWidget(QWidget *widget) } } +void InstallationManager::setURL(QString const &url) +{ + m_URL = url; +} void InstallationManager::queryPassword(LPSTR password) { @@ -581,6 +585,7 @@ bool InstallationManager::doInstall(GuessedValue &modName, int modID, } settingsFile.setValue("installationFile", m_CurrentFile); settingsFile.setValue("repository", repository); + settingsFile.setValue("url", m_URL); if (!merge) { // this does not clear the list we have in memory but the mod is going to have to be re-read anyway diff --git a/src/installationmanager.h b/src/installationmanager.h index 111b41f5..e5e5346f 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -59,6 +59,8 @@ public: void setParentWidget(QWidget *widget); + void setURL(const QString &url); + /** * @brief update the directory where mods are to be installed * @param modsDirectory the mod directory @@ -168,8 +170,6 @@ 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 = nullptr); - bool doInstall(MOBase::GuessedValue &modName, int modID, const QString &version, const QString &newestVersion, int categoryID, const QString &repository); @@ -212,6 +212,7 @@ private: std::set m_TempFilesToDelete; + QString m_URL; }; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 26ff4b98..1dca599d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2525,12 +2525,22 @@ void MainWindow::visitOnNexus_clicked() { int modID = m_OrganizerCore.modList()->data(m_OrganizerCore.modList()->index(m_ContextRow, 0), Qt::UserRole).toInt(); if (modID > 0) { - nexusLinkActivated(QString("%1/mods/%2").arg(ToQString(GameInfo::instance().getNexusPage(false))).arg(modID)); + linkClicked(QString("%1/mods/%2").arg(ToQString(GameInfo::instance().getNexusPage(false))).arg(modID)); } else { MessageDialog::showMessage(tr("Nexus ID for this Mod is unknown"), this); } } +void MainWindow::visitWebPage_clicked() +{ + ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); + if (info->getURL() != "") { + linkClicked(info->getURL()); + } else { + MessageDialog::showMessage(tr("Web page for this mod is unknown"), this); + } +} + void MainWindow::openExplorer_clicked() { ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); @@ -3106,7 +3116,14 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addAction(tr("Ignore missing data"), this, SLOT(ignoreMissingData_clicked())); } - menu->addAction(tr("Visit on Nexus"), this, SLOT(visitOnNexus_clicked())); + if (info->getNexusID() > 0) { + menu->addAction(tr("Visit on Nexus"), this, SLOT(visitOnNexus_clicked())); + } + + if (info->getURL() != "") { + menu->addAction(tr("Visit web page"), this, SLOT(visitWebPage_clicked())); + } + menu->addAction(tr("Open in explorer"), this, SLOT(openExplorer_clicked())); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 55ae40b9..3c7f2258 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -361,6 +361,7 @@ private slots: void unendorse_clicked(); void ignoreMissingData_clicked(); void visitOnNexus_clicked(); + void visitWebPage_clicked(); void openExplorer_clicked(); void information_clicked(); // savegame context menu diff --git a/src/modinfo.cpp b/src/modinfo.cpp index e0d888e6..fe5098e8 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -19,29 +19,23 @@ along with Mod Organizer. If not, see . #include "modinfo.h" -#include "utility.h" +#include "modinfobackup.h" +#include "modinforegular.h" +#include "modinfoforeign.h" +#include "modinfooverwrite.h" + #include "installationtester.h" #include "categories.h" -#include "report.h" #include "modinfodialog.h" #include "overwriteinfodialog.h" #include "json.h" -#include "messagedialog.h" #include "filenamestring.h" - -#include -#include -#include -#include -#include +#include "versioninfo.h" #include - #include #include #include -#include - using namespace MOBase; using namespace MOShared; @@ -386,772 +380,3 @@ void ModInfo::testValid() dirIter.next(); } } - - -ModInfoWithConflictInfo::ModInfoWithConflictInfo(DirectoryEntry **directoryStructure) - : m_DirectoryStructure(directoryStructure) {} - -void ModInfoWithConflictInfo::clearCaches() -{ - m_LastConflictCheck = QTime(); -} - -std::vector ModInfoWithConflictInfo::getFlags() const -{ - std::vector result; - switch (isConflicted()) { - case CONFLICT_MIXED: { - result.push_back(ModInfo::FLAG_CONFLICT_MIXED); - } break; - case CONFLICT_OVERWRITE: { - result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITE); - } break; - case CONFLICT_OVERWRITTEN: { - result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITTEN); - } break; - case CONFLICT_REDUNDANT: { - result.push_back(ModInfo::FLAG_CONFLICT_REDUNDANT); - } break; - default: { /* NOP */ } - } - return result; -} - - -void ModInfoWithConflictInfo::doConflictCheck() const -{ - m_OverwriteList.clear(); - m_OverwrittenList.clear(); - - bool providesAnything = false; - - int dataID = 0; - if ((*m_DirectoryStructure)->originExists(L"data")) { - dataID = (*m_DirectoryStructure)->getOriginByName(L"data").getID(); - } - - std::wstring name = ToWString(this->name()); - - m_CurrentConflictState = CONFLICT_NONE; - - if ((*m_DirectoryStructure)->originExists(name)) { - FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); - std::vector files = origin.getFiles(); - // for all files in this origin - for (FileEntry::Ptr file : files) { - const std::vector &alternatives = file->getAlternatives(); - if ((alternatives.size() == 0) || (alternatives[0] == dataID)) { - // no alternatives -> no conflict - providesAnything = true; - } else { - if (file->getOrigin() != origin.getID()) { - FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(file->getOrigin()); - unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName())); - m_OverwrittenList.insert(altIndex); - } else { - providesAnything = true; - } - - // for all non-providing alternative origins - for (int altId : alternatives) { - if ((altId != dataID) && (altId != origin.getID())) { - FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(altId); - unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName())); - if (origin.getPriority() > altOrigin.getPriority()) { - m_OverwriteList.insert(altIndex); - } else { - m_OverwrittenList.insert(altIndex); - } - } - } - } - } - m_LastConflictCheck = QTime::currentTime(); - - if (files.size() != 0) { - if (!providesAnything) - m_CurrentConflictState = CONFLICT_REDUNDANT; - else if (!m_OverwriteList.empty() && !m_OverwrittenList.empty()) - m_CurrentConflictState = CONFLICT_MIXED; - else if (!m_OverwriteList.empty()) - m_CurrentConflictState = CONFLICT_OVERWRITE; - else if (!m_OverwrittenList.empty()) - m_CurrentConflictState = CONFLICT_OVERWRITTEN; - } - } -} - -ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isConflicted() const -{ - // this is costy so cache the result - QTime now = QTime::currentTime(); - if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) { - doConflictCheck(); - } - - return m_CurrentConflictState; -} - - -bool ModInfoWithConflictInfo::isRedundant() const -{ - std::wstring name = ToWString(this->name()); - if ((*m_DirectoryStructure)->originExists(name)) { - FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); - std::vector files = origin.getFiles(); - bool ignore = false; - for (auto iter = files.begin(); iter != files.end(); ++iter) { - if ((*iter)->getOrigin(ignore) == origin.getID()) { - return false; - } - } - return true; - } else { - return false; - } -} - - - -ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStructure) - : ModInfoWithConflictInfo(directoryStructure) - , m_Name(path.dirName()) - , m_Path(path.absolutePath()) - , m_Repository() - , m_MetaInfoChanged(false) - , m_EndorsedState(ENDORSED_UNKNOWN) - , m_NexusBridge() -{ - testValid(); - m_CreationTime = QFileInfo(path.absolutePath()).created(); - // read out the meta-file for information - readMeta(); - - connect(&m_NexusBridge, SIGNAL(descriptionAvailable(int,QVariant,QVariant)) - , this, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant))); - connect(&m_NexusBridge, SIGNAL(endorsementToggled(int,QVariant,QVariant)) - , this, SLOT(nxmEndorsementToggled(int,QVariant,QVariant))); - connect(&m_NexusBridge, SIGNAL(requestFailed(int,int,QVariant,QString)) - , this, SLOT(nxmRequestFailed(int,int,QVariant,QString))); -} - - -ModInfoRegular::~ModInfoRegular() -{ - try { - saveMeta(); - } catch (const std::exception &e) { - qCritical("failed to save meta information for \"%s\": %s", - m_Name.toUtf8().constData(), e.what()); - } -} - -bool ModInfoRegular::isEmpty() const -{ - QDirIterator iter(m_Path, QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); - if (!iter.hasNext()) return true; - iter.next(); - if ((iter.fileName() == "meta.ini") && !iter.hasNext()) return true; - return false; -} - - -void ModInfoRegular::readMeta() -{ - QSettings metaFile(m_Path + "/meta.ini", QSettings::IniFormat); - m_Notes = metaFile.value("notes", "").toString(); - m_NexusID = metaFile.value("modid", -1).toInt(); - m_Version.parse(metaFile.value("version", "").toString()); - m_NewestVersion = metaFile.value("newestVersion", "").toString(); - m_IgnoredVersion = metaFile.value("ignoredVersion", "").toString(); - m_InstallationFile = metaFile.value("installationFile", "").toString(); - m_NexusDescription = metaFile.value("nexusDescription", "").toString(); - m_Repository = metaFile.value("repository", "Nexus").toString(); - m_LastNexusQuery = QDateTime::fromString(metaFile.value("lastNexusQuery", "").toString(), Qt::ISODate); - if (metaFile.contains("endorsed")) { - if (metaFile.value("endorsed").canConvert()) { - switch (metaFile.value("endorsed").toInt()) { - case ENDORSED_FALSE: m_EndorsedState = ENDORSED_FALSE; break; - case ENDORSED_TRUE: m_EndorsedState = ENDORSED_TRUE; break; - case ENDORSED_NEVER: m_EndorsedState = ENDORSED_NEVER; break; - default: m_EndorsedState = ENDORSED_UNKNOWN; break; - } - } else { - m_EndorsedState = metaFile.value("endorsed", false).toBool() ? ENDORSED_TRUE : ENDORSED_FALSE; - } - } - - QString categoriesString = metaFile.value("category", "").toString(); - - QStringList categories = categoriesString.split(',', QString::SkipEmptyParts); - for (QStringList::iterator iter = categories.begin(); iter != categories.end(); ++iter) { - bool ok = false; - int categoryID = iter->toInt(&ok); - if (categoryID < 0) { - // ignore invalid id - continue; - } - if (ok && (categoryID != 0) && (CategoryFactory::instance().categoryExists(categoryID))) { - m_Categories.insert(categoryID); - if (iter == categories.begin()) { - m_PrimaryCategory = categoryID; - } - } - } - - int numFiles = metaFile.beginReadArray("installedFiles"); - for (int i = 0; i < numFiles; ++i) { - metaFile.setArrayIndex(i); - m_InstalledFileIDs.insert(std::make_pair(metaFile.value("modid").toInt(), metaFile.value("fileid").toInt())); - } - metaFile.endArray(); - - m_MetaInfoChanged = false; -} - -void ModInfoRegular::saveMeta() -{ - // only write meta data if the mod directory exists - if (m_MetaInfoChanged && QFile::exists(absolutePath())) { - QSettings metaFile(absolutePath().append("/meta.ini"), QSettings::IniFormat); - if (metaFile.status() == QSettings::NoError) { - std::set temp = m_Categories; - temp.erase(m_PrimaryCategory); - metaFile.setValue("category", QString("%1").arg(m_PrimaryCategory) + "," + SetJoin(temp, ",")); - metaFile.setValue("newestVersion", m_NewestVersion.canonicalString()); - metaFile.setValue("ignoredVersion", m_IgnoredVersion.canonicalString()); - metaFile.setValue("version", m_Version.canonicalString()); - metaFile.setValue("installationFile", m_InstallationFile); - metaFile.setValue("repository", m_Repository); - metaFile.setValue("modid", m_NexusID); - metaFile.setValue("notes", m_Notes); - metaFile.setValue("nexusDescription", m_NexusDescription); - metaFile.setValue("lastNexusQuery", m_LastNexusQuery.toString(Qt::ISODate)); - if (m_EndorsedState != ENDORSED_UNKNOWN) { - metaFile.setValue("endorsed", m_EndorsedState); - } - - metaFile.beginWriteArray("installedFiles"); - int idx = 0; - for (auto iter = m_InstalledFileIDs.begin(); iter != m_InstalledFileIDs.end(); ++iter) { - metaFile.setArrayIndex(idx++); - metaFile.setValue("modid", iter->first); - metaFile.setValue("fileid", iter->second); - } - metaFile.endArray(); - - metaFile.sync(); // sync needs to be called to ensure the file is created - - if (metaFile.status() == QSettings::NoError) { - m_MetaInfoChanged = false; - } else { - reportError(tr("failed to write %1/meta.ini: error %2").arg(absolutePath()).arg(metaFile.status())); - } - } else { - reportError(tr("failed to write %1/meta.ini: error %2").arg(absolutePath()).arg(metaFile.status())); - } - } -} - - -bool ModInfoRegular::updateAvailable() const -{ - if (m_IgnoredVersion.isValid() && (m_IgnoredVersion == m_NewestVersion)) { - return false; - } - return m_NewestVersion.isValid() && (m_Version < m_NewestVersion); -} - - -bool ModInfoRegular::downgradeAvailable() const -{ - if (m_IgnoredVersion.isValid() && (m_IgnoredVersion == m_NewestVersion)) { - return false; - } - return m_NewestVersion.isValid() && (m_NewestVersion < m_Version); -} - - -void ModInfoRegular::nxmDescriptionAvailable(int, QVariant, QVariant resultData) -{ - QVariantMap result = resultData.toMap(); - setNewestVersion(VersionInfo(result["version"].toString())); - setNexusDescription(result["description"].toString()); - - if ((m_EndorsedState != ENDORSED_NEVER) && (result.contains("voted_by_user"))) { - setEndorsedState(result["voted_by_user"].toBool() ? ENDORSED_TRUE : ENDORSED_FALSE); - } - m_LastNexusQuery = QDateTime::currentDateTime(); - //m_MetaInfoChanged = true; - saveMeta(); - emit modDetailsUpdated(true); -} - - -void ModInfoRegular::nxmEndorsementToggled(int, QVariant, QVariant resultData) -{ - m_EndorsedState = resultData.toBool() ? ENDORSED_TRUE : ENDORSED_FALSE; - m_MetaInfoChanged = true; - saveMeta(); - emit modDetailsUpdated(true); -} - - -void ModInfoRegular::nxmRequestFailed(int, int, QVariant userData, const QString &errorMessage) -{ - QString fullMessage = errorMessage; - 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() != nullptr) { - MessageDialog::showMessage(fullMessage, QApplication::activeWindow()); - } - emit modDetailsUpdated(false); -} - - -bool ModInfoRegular::updateNXMInfo() -{ - if (m_NexusID > 0) { - m_NexusBridge.requestDescription(m_NexusID, QVariant()); - return true; - } - return false; -} - - -void ModInfoRegular::setCategory(int categoryID, bool active) -{ - m_MetaInfoChanged = true; - - if (active) { - m_Categories.insert(categoryID); - if (m_PrimaryCategory == -1) { - m_PrimaryCategory = categoryID; - } - } else { - std::set::iterator iter = m_Categories.find(categoryID); - if (iter != m_Categories.end()) { - m_Categories.erase(iter); - } - if (categoryID == m_PrimaryCategory) { - if (m_Categories.size() == 0) { - m_PrimaryCategory = -1; - } else { - m_PrimaryCategory = *(m_Categories.begin()); - } - } - } -} - - -bool ModInfoRegular::setName(const QString &name) -{ - if (name.contains('/') || name.contains('\\')) { - return false; - } - - QString newPath = m_Path.mid(0).replace(m_Path.length() - m_Name.length(), m_Name.length(), name); - QDir modDir(m_Path.mid(0, m_Path.length() - m_Name.length())); - - if (m_Name.compare(name, Qt::CaseInsensitive) == 0) { - QString tempName = name; - tempName.append("_temp"); - while (modDir.exists(tempName)) { - tempName.append("_"); - } - if (!modDir.rename(m_Name, tempName)) { - return false; - } - if (!modDir.rename(tempName, name)) { - qCritical("rename to final name failed after successful rename to intermediate name"); - modDir.rename(tempName, m_Name); - return false; - } - } else { - if (!shellRename(modDir.absoluteFilePath(m_Name), modDir.absoluteFilePath(name))) { - qCritical("failed to rename mod %s (errorcode %d)", - qPrintable(name), ::GetLastError()); - return false; - } - } - - std::map::iterator nameIter = s_ModsByName.find(m_Name); - if (nameIter != s_ModsByName.end()) { - unsigned int index = nameIter->second; - s_ModsByName.erase(nameIter); - - m_Name = name; - m_Path = newPath; - - s_ModsByName[m_Name] = index; - - std::sort(s_Collection.begin(), s_Collection.end(), ByName); - updateIndices(); - } else { // otherwise mod isn't registered yet? - m_Name = name; - m_Path = newPath; - } - - return true; -} - -void ModInfoRegular::setNotes(const QString ¬es) -{ - m_Notes = notes; - m_MetaInfoChanged = true; -} - -void ModInfoRegular::setNexusID(int modID) -{ - m_NexusID = modID; - m_MetaInfoChanged = true; -} - -void ModInfoRegular::setVersion(const VersionInfo &version) -{ - m_Version = version; - m_MetaInfoChanged = true; -} - -void ModInfoRegular::setNewestVersion(const VersionInfo &version) -{ - if (version != m_NewestVersion) { - m_NewestVersion = version; - m_MetaInfoChanged = true; - } -} - -void ModInfoRegular::setNexusDescription(const QString &description) -{ - if (qHash(description) != qHash(m_NexusDescription)) { - m_NexusDescription = description; - m_MetaInfoChanged = true; - } -} - -void ModInfoRegular::setEndorsedState(EEndorsedState endorsedState) -{ - if (endorsedState != m_EndorsedState) { - m_EndorsedState = endorsedState; - m_MetaInfoChanged = true; - } -} - -void ModInfoRegular::setInstallationFile(const QString &fileName) -{ - m_InstallationFile = fileName; - m_MetaInfoChanged = true; -} - -void ModInfoRegular::addNexusCategory(int categoryID) -{ - m_Categories.insert(CategoryFactory::instance().resolveNexusID(categoryID)); -} - -void ModInfoRegular::setIsEndorsed(bool endorsed) -{ - if (m_EndorsedState != ENDORSED_NEVER) { - m_EndorsedState = endorsed ? ENDORSED_TRUE : ENDORSED_FALSE; - m_MetaInfoChanged = true; - } -} - - -void ModInfoRegular::setNeverEndorse() -{ - m_EndorsedState = ENDORSED_NEVER; - m_MetaInfoChanged = true; -} - - -bool ModInfoRegular::remove() -{ - m_MetaInfoChanged = false; - return shellDelete(QStringList(absolutePath()), true); -} - -void ModInfoRegular::endorse(bool doEndorse) -{ - if (doEndorse != (m_EndorsedState == ENDORSED_TRUE)) { - m_NexusBridge.requestToggleEndorsement(getNexusID(), doEndorse, QVariant(1)); - } -} - - -QString ModInfoRegular::absolutePath() const -{ - return m_Path; -} - -void ModInfoRegular::ignoreUpdate(bool ignore) -{ - if (ignore) { - m_IgnoredVersion = m_NewestVersion; - } else { - m_IgnoredVersion.clear(); - } - m_MetaInfoChanged = true; -} - - -std::vector ModInfoRegular::getFlags() const -{ - std::vector result = ModInfoWithConflictInfo::getFlags(); - if ((m_NexusID > 0) && (endorsedState() == ENDORSED_FALSE)) { - result.push_back(ModInfo::FLAG_NOTENDORSED); - } - if (!isValid()) { - result.push_back(ModInfo::FLAG_INVALID); - } - if (m_Notes.length() != 0) { - result.push_back(ModInfo::FLAG_NOTES); - } - return result; -} - - -std::vector ModInfoRegular::getContents() const -{ - QTime now = QTime::currentTime(); - if (m_LastContentCheck.isNull() || (m_LastContentCheck.secsTo(now) > 60)) { - m_CachedContent.clear(); - QDir dir(absolutePath()); - if (dir.entryList(QStringList() << "*.esp" << "*.esm").size() > 0) { - m_CachedContent.push_back(CONTENT_PLUGIN); - } - if (dir.entryList(QStringList() << "*.bsa").size() > 0) { - m_CachedContent.push_back(CONTENT_BSA); - } - - ScriptExtender *extender = qApp->property("managed_game").value()->feature(); - - if (extender != nullptr) { - QString sePluginPath = extender->name() + "/plugins"; - if (dir.exists(sePluginPath)) m_CachedContent.push_back(CONTENT_SKSE); - } - if (dir.exists("textures")) m_CachedContent.push_back(CONTENT_TEXTURE); - if (dir.exists("meshes")) m_CachedContent.push_back(CONTENT_MESH); - if (dir.exists("interface") - || dir.exists("menus")) m_CachedContent.push_back(CONTENT_INTERFACE); - if (dir.exists("music")) m_CachedContent.push_back(CONTENT_MUSIC); - if (dir.exists("sound")) m_CachedContent.push_back(CONTENT_SOUND); - if (dir.exists("scripts")) m_CachedContent.push_back(CONTENT_SCRIPT); - if (dir.exists("strings")) m_CachedContent.push_back(CONTENT_STRING); - if (dir.exists("SkyProc Patchers")) m_CachedContent.push_back(CONTENT_SKYPROC); - - m_LastContentCheck = QTime::currentTime(); - } - - return m_CachedContent; - -} - - -int ModInfoRegular::getHighlight() const -{ - return isValid() ? HIGHLIGHT_NONE: HIGHLIGHT_INVALID; -} - - -QString ModInfoRegular::getDescription() const -{ - if (!isValid()) { - return tr("%1 contains no esp/esm and no asset (textures, meshes, interface, ...) directory").arg(name()); - } else { - const std::set &categories = getCategories(); - std::wostringstream categoryString; - categoryString << ToWString(tr("Categories:
")); - CategoryFactory &categoryFactory = CategoryFactory::instance(); - for (std::set::const_iterator catIter = categories.begin(); - catIter != categories.end(); ++catIter) { - if (catIter != categories.begin()) { - categoryString << " , "; - } - categoryString << "" << ToWString(categoryFactory.getCategoryName(categoryFactory.getCategoryIndex(*catIter))) << ""; - } - - return ToQString(categoryString.str()); - } -} - -QString ModInfoRegular::notes() const -{ - return m_Notes; -} - -QDateTime ModInfoRegular::creationTime() const -{ - return m_CreationTime; -} - -QString ModInfoRegular::getNexusDescription() const -{ - return m_NexusDescription; -} - -QString ModInfoRegular::repository() const -{ - return m_Repository; -} - -ModInfoRegular::EEndorsedState ModInfoRegular::endorsedState() const -{ - return m_EndorsedState; -} - -QDateTime ModInfoRegular::getLastNexusQuery() const -{ - return m_LastNexusQuery; -} - - -QStringList ModInfoRegular::archives() const -{ - QStringList result; - QDir dir(this->absolutePath()); - for (const QString &archive : dir.entryList(QStringList("*.bsa"))) { - result.append(this->absolutePath() + "/" + archive); - } - return result; -} - -void ModInfoRegular::addInstalledFile(int modId, int fileId) -{ - m_InstalledFileIDs.insert(std::make_pair(modId, fileId)); - m_MetaInfoChanged = true; -} - -std::vector ModInfoRegular::getIniTweaks() const -{ - QString metaFileName = absolutePath().append("/meta.ini"); - QSettings metaFile(metaFileName, QSettings::IniFormat); - - std::vector result; - - int numTweaks = metaFile.beginReadArray("INI Tweaks"); - - if (numTweaks != 0) { - qDebug("%d active ini tweaks in %s", - numTweaks, QDir::toNativeSeparators(metaFileName).toUtf8().constData()); - } - - for (int i = 0; i < numTweaks; ++i) { - metaFile.setArrayIndex(i); - QString filename = absolutePath().append("/INI Tweaks/").append(metaFile.value("name").toString()); - result.push_back(filename); - } - metaFile.endArray(); - return result; -} - -std::vector ModInfoBackup::getFlags() const -{ - std::vector result = ModInfoRegular::getFlags(); - result.insert(result.begin(), ModInfo::FLAG_BACKUP); - return result; -} - - -QString ModInfoBackup::getDescription() const -{ - return tr("This is the backup of a mod"); -} - - -ModInfoBackup::ModInfoBackup(const QDir &path, DirectoryEntry **directoryStructure) - : ModInfoRegular(path, directoryStructure) -{ -} - - -ModInfoOverwrite::ModInfoOverwrite() -{ - testValid(); -} - - -bool ModInfoOverwrite::isEmpty() const -{ - QDirIterator iter(absolutePath(), QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); - if (!iter.hasNext()) return true; - iter.next(); - if ((iter.fileName() == "meta.ini") && !iter.hasNext()) return true; - return false; -} - -QString ModInfoOverwrite::absolutePath() const -{ - return QDir::fromNativeSeparators(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::overwritePath())); -} - -std::vector ModInfoOverwrite::getFlags() const -{ - std::vector result; - result.push_back(FLAG_OVERWRITE); - return result; -} - -int ModInfoOverwrite::getHighlight() const -{ - return (isValid() ? HIGHLIGHT_IMPORTANT : HIGHLIGHT_INVALID) | HIGHLIGHT_CENTER; -} - -QString ModInfoOverwrite::getDescription() const -{ - return tr("This pseudo mod contains files from the virtual data tree that got " - "modified (i.e. by the construction kit)"); -} - -QStringList ModInfoOverwrite::archives() const -{ - QStringList result; - QDir dir(this->absolutePath()); - for (const QString &archive : dir.entryList(QStringList("*.bsa"))) { - result.append(this->absolutePath() + "/" + archive); - } - return result; -} - -QString ModInfoForeign::name() const -{ - return m_Name; -} - -QDateTime ModInfoForeign::creationTime() const -{ - return m_CreationTime; -} - -QString ModInfoForeign::absolutePath() const -{ - return QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/data"; -} - -std::vector ModInfoForeign::getFlags() const -{ - std::vector result = ModInfoWithConflictInfo::getFlags(); - result.push_back(FLAG_FOREIGN); - - return result; -} - -int ModInfoForeign::getHighlight() const -{ - return 0; -} - -QString ModInfoForeign::getDescription() const -{ - return tr("This pseudo mod represents content managed outside MO. It isn't modified by MO."); -} - -ModInfoForeign::ModInfoForeign(const QString &referenceFile, const QStringList &archives, - DirectoryEntry **directoryStructure) - : ModInfoWithConflictInfo(directoryStructure) - , m_ReferenceFile(referenceFile) - , m_Archives(archives) -{ - m_CreationTime = QFileInfo(referenceFile).created(); - m_Name = "Unmanaged: " + QFileInfo(m_ReferenceFile).baseName(); -} diff --git a/src/modinfo.h b/src/modinfo.h index da97b09b..7d305c11 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -20,22 +20,23 @@ along with Mod Organizer. If not, see . #ifndef MODINFO_H #define MODINFO_H -#include "nexusinterface.h" -#include -#include +#include "imodinterface.h" +#include "versioninfo.h" -#include +class QDateTime; +class QDir; #include -#include -#include #include -#include +#include +#include + +#include + #include #include #include -#include -using MOBase::ModRepositoryFileInfo; +namespace MOShared { class DirectoryEntry; } /** * @brief Represents meta information about a single mod. @@ -556,6 +557,16 @@ public: */ virtual void doConflictCheck() const {} + /** + * @brief set the URL for a mod + */ + virtual void setURL(QString const &) {} + + /** + * @returns the URL for a mod + */ + virtual QString getURL() const { return ""; } + signals: /** @@ -596,536 +607,4 @@ private: }; -class ModInfoWithConflictInfo : public ModInfo -{ - -public: - - ModInfoWithConflictInfo(MOShared::DirectoryEntry **directoryStructure); - - std::vector getFlags() const; - - /** - * @brief clear all caches held for this mod - */ - virtual void clearCaches(); - - virtual std::set getModOverwrite() { return m_OverwriteList; } - - virtual std::set getModOverwritten() { return m_OverwrittenList; } - - virtual void doConflictCheck() const; - -private: - - enum EConflictType { - CONFLICT_NONE, - CONFLICT_OVERWRITE, - CONFLICT_OVERWRITTEN, - CONFLICT_MIXED, - CONFLICT_REDUNDANT - }; - -private: - - /** - * @return true if there is a conflict for files in this mod - */ - EConflictType isConflicted() const; - - /** - * @return true if this mod is completely replaced by others - */ - bool isRedundant() const; - -private: - - MOShared::DirectoryEntry **m_DirectoryStructure; - - mutable EConflictType m_CurrentConflictState; - mutable QTime m_LastConflictCheck; - - mutable std::set m_OverwriteList; // indices of mods overritten by this mod - mutable std::set m_OverwrittenList; // indices of mods overwriting this mod - -}; - - -/** - * @brief Represents meta information about a single mod. - * - * Represents meta information about a single mod. The class interface is used - * to manage the mod collection - * - **/ -class ModInfoRegular : public ModInfoWithConflictInfo -{ - - Q_OBJECT - - friend class ModInfo; - -public: - - ~ModInfoRegular(); - - virtual bool isRegular() const { return true; } - - virtual bool isEmpty() const; - - /** - * @brief test if there is a newer version of the mod - * - * test if there is a newer version of the mod. This does NOT cause - * information to be retrieved from the nexus, it will only test version information already - * available locally. Use checkAllForUpdate() to update this version information - * - * @return true if there is a newer version - **/ - bool updateAvailable() const; - - /** - * @return true if the current update is being ignored - */ - virtual bool updateIgnored() const { return m_IgnoredVersion == m_NewestVersion; } - - /** - * @brief test if there is a newer version of the mod - * - * test if there is a newer version of the mod. This does NOT cause - * information to be retrieved from the nexus, it will only test version information already - * available locally. Use checkAllForUpdate() to update this version information - * - * @return true if there is a newer version - **/ - bool downgradeAvailable() const; - - /** - * @brief request an update of nexus description for this mod. - * - * This requests mod information from the nexus. This is an asynchronous request, - * so there is no immediate effect of this call. - * - * @return returns true if information for this mod will be updated, false if there is no nexus mod id to use - **/ - bool updateNXMInfo(); - - /** - * @brief assign or unassign the specified category - * - * Every mod can have an arbitrary number of categories assigned to it - * - * @param categoryID id of the category to set - * @param active determines wheter the category is assigned or unassigned - * @note this function does not test whether categoryID actually identifies a valid category - **/ - void setCategory(int categoryID, bool active); - - /** - * @brief set the name of this mod - * - * set the name of this mod. This will also update the name of the - * directory that contains this mod - * - * @param name new name of the mod - * @return true on success, false if the new name can't be used (i.e. because the new - * directory name wouldn't be valid) - **/ - bool setName(const QString &name); - - /** - * @brief change the notes (manually set information) for this mod - * @param notes new notes - */ - void setNotes(const QString ¬es); - - /** - * @brief set/change the nexus mod id of this mod - * - * @param modID the nexus mod id - **/ - void setNexusID(int modID); - - /** - * @brief set the version of this mod - * - * this can be used to overwrite the version of a mod without actually - * updating the mod - * - * @param version the new version to use - **/ - void setVersion(const MOBase::VersionInfo &version); - - /** - * @brief set the newest version of this mod on the nexus - * - * this can be used to overwrite the version of a mod without actually - * updating the mod - * - * @param version the new version to use - * @todo this function should be made obsolete. All queries for mod information should go through - * this class so no public function for this change is required - **/ - void setNewestVersion(const MOBase::VersionInfo &version); - - /** - * @brief changes/updates the nexus description text - * @param description the current description text - */ - virtual void setNexusDescription(const QString &description); - - virtual void setInstallationFile(const QString &fileName); - - /** - * @brief sets the category id from a nexus category id. Conversion to MO id happens internally - * @param categoryID the nexus category id - * @note if a mapping is not possible, the category is set to the default value - */ - virtual void addNexusCategory(int categoryID); - - /** - * @brief sets the new primary category of the mod - * @param categoryID the category to set - */ - virtual void setPrimaryCategory(int categoryID) { m_PrimaryCategory = categoryID; m_MetaInfoChanged = true; } - - /** - * @brief sets the download repository - * @param repository - */ - virtual void setRepository(const QString &repository) { m_Repository = repository; } - - /** - * update the endorsement state for the mod. This only changes the - * buffered state, it does not sync with Nexus - * @param endorsed the new endorsement state - */ - virtual void setIsEndorsed(bool endorsed); - - /** - * set the mod to "i don't intend to endorse". The mod will not show as unendorsed but can still be endorsed - */ - virtual void setNeverEndorse(); - - /** - * @brief delete the mod from the disc. This does not update the global ModInfo structure or indices - * @return true if the mod was successfully removed - **/ - bool remove(); - - /** - * @brief endorse or un-endorse the mod - * @param doEndorse if true, the mod is endorsed, if false, it's un-endorsed. - * @note if doEndorse doesn't differ from the current value, nothing happens. - */ - virtual void endorse(bool doEndorse); - - /** - * @brief getter for the mod name - * - * @return the mod name - **/ - QString name() const { return m_Name; } - - /** - * @brief getter for the mod path - * - * @return the (absolute) path to the mod - **/ - QString absolutePath() const; - - /** - * @brief getter for the newest version number of this mod - * - * @return newest version of the mod - **/ - MOBase::VersionInfo getNewestVersion() const { return m_NewestVersion; } - - /** - * @brief ignore the newest version for updates - */ - void ignoreUpdate(bool ignore); - - /** - * @brief getter for the installation file - * - * @return file used to install this mod from - */ - virtual QString getInstallationFile() const { return m_InstallationFile; } - /** - * @brief getter for the nexus mod id - * - * @return the nexus mod id. may be 0 if the mod id isn't known or doesn't exist - **/ - int getNexusID() const { return m_NexusID; } - - /** - * @return the fixed priority of mods of this type or INT_MIN if the priority of mods - * needs to be user-modifiable - */ - virtual int getFixedPriority() const { return INT_MIN; } - - /** - * @return true if the mod can be updated - */ - virtual bool canBeUpdated() const { return m_NexusID > 0; } - - /** - * @return true if the mod can be enabled/disabled - */ - virtual bool canBeEnabled() const { return true; } - - /** - * @return a list of flags for this mod - */ - virtual std::vector getFlags() const; - - virtual std::vector getContents() const; - - /** - * @return an indicator if and how this mod should be highlighted by the UI - */ - virtual int getHighlight() const; - - /** - * @return list of names of ini tweaks - **/ - std::vector getIniTweaks() const; - - /** - * @return a description about the mod, to be displayed in the ui - */ - virtual QString getDescription() const; - - /** - * @return manually set notes for this mod - */ - virtual QString notes() const; - - /** - * @return time this mod was created (file time of the directory) - */ - virtual QDateTime creationTime() const; - - /** - * @return nexus description of the mod (html) - */ - QString getNexusDescription() const; - - /** - * @return repository from which the file was downloaded - */ - virtual QString repository() const; - - /** - * @return true if the file has been endorsed on nexus - */ - virtual EEndorsedState endorsedState() const; - - /** - * @return last time nexus was queried for infos on this mod - */ - virtual QDateTime getLastNexusQuery() const; - - virtual QStringList archives() const; - - virtual void addInstalledFile(int modId, int fileId); - - /** - * @brief stores meta information back to disk - */ - virtual void saveMeta(); - - void readMeta(); - -private: - - void setEndorsedState(EEndorsedState endorsedState); - -private slots: - - void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData); - void nxmEndorsementToggled(int, QVariant userData, QVariant resultData); - void nxmRequestFailed(int modID, int fileID, QVariant userData, const QString &errorMessage); - -protected: - - ModInfoRegular(const QDir &path, MOShared::DirectoryEntry **directoryStructure); - -private: - - QString m_Name; - QString m_Path; - QString m_InstallationFile; - QString m_Notes; - QString m_NexusDescription; - QString m_Repository; - - QDateTime m_CreationTime; - QDateTime m_LastNexusQuery; - - int m_NexusID; - std::set> m_InstalledFileIDs; - - bool m_MetaInfoChanged; - MOBase::VersionInfo m_NewestVersion; - MOBase::VersionInfo m_IgnoredVersion; - - EEndorsedState m_EndorsedState; - - NexusBridge m_NexusBridge; - - mutable std::vector m_CachedContent; - mutable QTime m_LastContentCheck; - -}; - - -class ModInfoBackup : public ModInfoRegular -{ - - friend class ModInfo; - -public: - - virtual bool updateAvailable() const { return false; } - virtual bool updateIgnored() const { return false; } - virtual bool downgradeAvailable() const { return false; } - virtual bool updateNXMInfo() { return false; } - virtual void setNexusID(int) {} - virtual void endorse(bool) {} - virtual int getFixedPriority() const { return -1; } - virtual void ignoreUpdate(bool) {} - virtual bool canBeUpdated() const { return false; } - virtual bool canBeEnabled() const { return false; } - virtual std::vector getIniTweaks() const { return std::vector(); } - virtual std::vector getFlags() const; - virtual QString getDescription() const; - virtual QDateTime getLastNexusQuery() const { return QDateTime(); } - virtual void getNexusFiles(QList::const_iterator&, - QList::const_iterator&) {} - virtual QString getNexusDescription() const { return QString(); } - - virtual void addInstalledFile(int, int) {} - -private: - - ModInfoBackup(const QDir &path, MOShared::DirectoryEntry **directoryStructure); - -}; - - -class ModInfoOverwrite : public ModInfo -{ - - Q_OBJECT - - friend class ModInfo; - -public: - - virtual bool updateAvailable() const { return false; } - virtual bool updateIgnored() const { return false; } - virtual bool downgradeAvailable() const { return false; } - virtual bool updateNXMInfo() { return false; } - virtual void setCategory(int, bool) {} - virtual bool setName(const QString&) { return false; } - virtual void setNotes(const QString&) {} - virtual void setNexusID(int) {} - virtual void setNewestVersion(const MOBase::VersionInfo&) {} - virtual void ignoreUpdate(bool) {} - virtual void setNexusDescription(const QString&) {} - virtual void setInstallationFile(const QString&) {} - virtual void addNexusCategory(int) {} - virtual void setIsEndorsed(bool) {} - virtual void setNeverEndorse() {} - virtual bool remove() { return false; } - virtual void endorse(bool) {} - virtual bool alwaysEnabled() const { return true; } - virtual bool isEmpty() const; - virtual QString name() const { return "Overwrite"; } - virtual QString notes() const { return ""; } - virtual QDateTime creationTime() const { return QDateTime(); } - virtual QString absolutePath() const; - virtual MOBase::VersionInfo getNewestVersion() const { return ""; } - virtual QString getInstallationFile() const { return ""; } - virtual int getFixedPriority() const { return INT_MAX; } - virtual int getNexusID() const { return -1; } - virtual std::vector getIniTweaks() const { return std::vector(); } - virtual std::vector getFlags() const; - virtual int getHighlight() const; - virtual QString getDescription() const; - virtual QDateTime getLastNexusQuery() const { return QDateTime(); } - virtual QString getNexusDescription() const { return QString(); } - virtual QStringList archives() const; - virtual void addInstalledFile(int, int) {} - -private: - - ModInfoOverwrite(); - -}; - - -class ModInfoForeign : public ModInfoWithConflictInfo -{ - - Q_OBJECT - - friend class ModInfo; - -public: - - virtual bool updateAvailable() const { return false; } - virtual bool updateIgnored() const { return false; } - virtual bool downgradeAvailable() const { return false; } - virtual bool updateNXMInfo() { return false; } - virtual void setCategory(int, bool) {} - virtual bool setName(const QString&) { return false; } - virtual void setNotes(const QString&) {} - virtual void setNexusID(int) {} - virtual void setNewestVersion(const MOBase::VersionInfo&) {} - virtual void ignoreUpdate(bool) {} - virtual void setNexusDescription(const QString&) {} - virtual void setInstallationFile(const QString&) {} - virtual void addNexusCategory(int) {} - virtual void setIsEndorsed(bool) {} - virtual void setNeverEndorse() {} - virtual bool remove() { return false; } - virtual void endorse(bool) {} - virtual bool isEmpty() const { return false; } - virtual QString name() const; - virtual QString internalName() const { return name(); } - virtual QString notes() const { return ""; } - virtual QDateTime creationTime() const; - virtual QString absolutePath() const; - virtual MOBase::VersionInfo getNewestVersion() const { return ""; } - virtual QString getInstallationFile() const { return ""; } - virtual int getNexusID() const { return -1; } - virtual std::vector getIniTweaks() const { return std::vector(); } - virtual std::vector getFlags() const; - virtual int getHighlight() const; - virtual QString getDescription() const; - virtual QDateTime getLastNexusQuery() const { return QDateTime(); } - virtual QString getNexusDescription() const { return QString(); } - virtual int getFixedPriority() const { return INT_MIN; } - virtual QStringList archives() const { return m_Archives; } - virtual QStringList stealFiles() const { return m_Archives + QStringList(m_ReferenceFile); } - virtual bool alwaysEnabled() const { return true; } - virtual void addInstalledFile(int, int) {} - -protected: - - ModInfoForeign(const QString &referenceFile, const QStringList &archives, MOShared::DirectoryEntry **directoryStructure); - -private: - - QString m_Name; - QString m_ReferenceFile; - QStringList m_Archives; - QDateTime m_CreationTime; - int m_Priority; - -}; - #endif // MODINFO_H diff --git a/src/modinfobackup.cpp b/src/modinfobackup.cpp new file mode 100644 index 00000000..a57b84fe --- /dev/null +++ b/src/modinfobackup.cpp @@ -0,0 +1,20 @@ +#include "modinfobackup.h" + +std::vector ModInfoBackup::getFlags() const +{ + std::vector result = ModInfoRegular::getFlags(); + result.insert(result.begin(), ModInfo::FLAG_BACKUP); + return result; +} + + +QString ModInfoBackup::getDescription() const +{ + return tr("This is the backup of a mod"); +} + + +ModInfoBackup::ModInfoBackup(const QDir &path, MOShared::DirectoryEntry **directoryStructure) + : ModInfoRegular(path, directoryStructure) +{ +} diff --git a/src/modinfobackup.h b/src/modinfobackup.h new file mode 100644 index 00000000..adbac783 --- /dev/null +++ b/src/modinfobackup.h @@ -0,0 +1,40 @@ +#ifndef MODINFOBACKUP_H +#define MODINFOBACKUP_H + +#include "modinforegular.h" + +class ModInfoBackup : public ModInfoRegular +{ + + friend class ModInfo; + +public: + + virtual bool updateAvailable() const { return false; } + virtual bool updateIgnored() const { return false; } + virtual bool downgradeAvailable() const { return false; } + virtual bool updateNXMInfo() { return false; } + virtual void setNexusID(int) {} + virtual void endorse(bool) {} + virtual int getFixedPriority() const { return -1; } + virtual void ignoreUpdate(bool) {} + virtual bool canBeUpdated() const { return false; } + virtual bool canBeEnabled() const { return false; } + virtual std::vector getIniTweaks() const { return std::vector(); } + virtual std::vector getFlags() const; + virtual QString getDescription() const; + virtual QDateTime getLastNexusQuery() const { return QDateTime(); } + virtual void getNexusFiles(QList::const_iterator&, + QList::const_iterator&) {} + virtual QString getNexusDescription() const { return QString(); } + + virtual void addInstalledFile(int, int) {} + +private: + + ModInfoBackup(const QDir &path, MOShared::DirectoryEntry **directoryStructure); + +}; + + +#endif // MODINFOBACKUP_H diff --git a/src/modinfoforeign.cpp b/src/modinfoforeign.cpp new file mode 100644 index 00000000..c0e769c9 --- /dev/null +++ b/src/modinfoforeign.cpp @@ -0,0 +1,50 @@ +#include "modinfoforeign.h" + +#include "gameinfo.h" +#include "utility.h" + +using namespace MOBase; +using namespace MOShared; + +QString ModInfoForeign::name() const +{ + return m_Name; +} + +QDateTime ModInfoForeign::creationTime() const +{ + return m_CreationTime; +} + +QString ModInfoForeign::absolutePath() const +{ + return QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/data"; +} + +std::vector ModInfoForeign::getFlags() const +{ + std::vector result = ModInfoWithConflictInfo::getFlags(); + result.push_back(FLAG_FOREIGN); + + return result; +} + +int ModInfoForeign::getHighlight() const +{ + return 0; +} + +QString ModInfoForeign::getDescription() const +{ + return tr("This pseudo mod represents content managed outside MO. It isn't modified by MO."); +} + +ModInfoForeign::ModInfoForeign(const QString &referenceFile, const QStringList &archives, + DirectoryEntry **directoryStructure) + : ModInfoWithConflictInfo(directoryStructure) + , m_ReferenceFile(referenceFile) + , m_Archives(archives) +{ + m_CreationTime = QFileInfo(referenceFile).created(); + m_Name = "Unmanaged: " + QFileInfo(m_ReferenceFile).baseName(); +} diff --git a/src/modinfoforeign.h b/src/modinfoforeign.h new file mode 100644 index 00000000..b8ff2671 --- /dev/null +++ b/src/modinfoforeign.h @@ -0,0 +1,67 @@ +#ifndef MODINFOFOREIGN_H +#define MODINFOFOREIGN_H + +#include "modinfowithconflictinfo.h" + +class ModInfoForeign : public ModInfoWithConflictInfo +{ + + Q_OBJECT + + friend class ModInfo; + +public: + + virtual bool updateAvailable() const { return false; } + virtual bool updateIgnored() const { return false; } + virtual bool downgradeAvailable() const { return false; } + virtual bool updateNXMInfo() { return false; } + virtual void setCategory(int, bool) {} + virtual bool setName(const QString&) { return false; } + virtual void setNotes(const QString&) {} + virtual void setNexusID(int) {} + virtual void setNewestVersion(const MOBase::VersionInfo&) {} + virtual void ignoreUpdate(bool) {} + virtual void setNexusDescription(const QString&) {} + virtual void setInstallationFile(const QString&) {} + virtual void addNexusCategory(int) {} + virtual void setIsEndorsed(bool) {} + virtual void setNeverEndorse() {} + virtual bool remove() { return false; } + virtual void endorse(bool) {} + virtual bool isEmpty() const { return false; } + virtual QString name() const; + virtual QString internalName() const { return name(); } + virtual QString notes() const { return ""; } + virtual QDateTime creationTime() const; + virtual QString absolutePath() const; + virtual MOBase::VersionInfo getNewestVersion() const { return ""; } + virtual QString getInstallationFile() const { return ""; } + virtual int getNexusID() const { return -1; } + virtual std::vector getIniTweaks() const { return std::vector(); } + virtual std::vector getFlags() const; + virtual int getHighlight() const; + virtual QString getDescription() const; + virtual QDateTime getLastNexusQuery() const { return QDateTime(); } + virtual QString getNexusDescription() const { return QString(); } + virtual int getFixedPriority() const { return INT_MIN; } + virtual QStringList archives() const { return m_Archives; } + virtual QStringList stealFiles() const { return m_Archives + QStringList(m_ReferenceFile); } + virtual bool alwaysEnabled() const { return true; } + virtual void addInstalledFile(int, int) {} + +protected: + + ModInfoForeign(const QString &referenceFile, const QStringList &archives, MOShared::DirectoryEntry **directoryStructure); + +private: + + QString m_Name; + QString m_ReferenceFile; + QStringList m_Archives; + QDateTime m_CreationTime; + int m_Priority; + +}; + +#endif // MODINFOFOREIGN_H diff --git a/src/modinfooverwrite.cpp b/src/modinfooverwrite.cpp new file mode 100644 index 00000000..4380a255 --- /dev/null +++ b/src/modinfooverwrite.cpp @@ -0,0 +1,53 @@ +#include "modinfooverwrite.h" + +#include "appconfig.h" + +#include +#include + +ModInfoOverwrite::ModInfoOverwrite() +{ + testValid(); +} + +bool ModInfoOverwrite::isEmpty() const +{ + QDirIterator iter(absolutePath(), QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); + if (!iter.hasNext()) return true; + iter.next(); + if ((iter.fileName() == "meta.ini") && !iter.hasNext()) return true; + return false; +} + +QString ModInfoOverwrite::absolutePath() const +{ + return QDir::fromNativeSeparators(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::overwritePath())); +} + +std::vector ModInfoOverwrite::getFlags() const +{ + std::vector result; + result.push_back(FLAG_OVERWRITE); + return result; +} + +int ModInfoOverwrite::getHighlight() const +{ + return (isValid() ? HIGHLIGHT_IMPORTANT : HIGHLIGHT_INVALID) | HIGHLIGHT_CENTER; +} + +QString ModInfoOverwrite::getDescription() const +{ + return tr("This pseudo mod contains files from the virtual data tree that got " + "modified (i.e. by the construction kit)"); +} + +QStringList ModInfoOverwrite::archives() const +{ + QStringList result; + QDir dir(this->absolutePath()); + for (const QString &archive : dir.entryList(QStringList("*.bsa"))) { + result.append(this->absolutePath() + "/" + archive); + } + return result; +} diff --git a/src/modinfooverwrite.h b/src/modinfooverwrite.h new file mode 100644 index 00000000..9184a90c --- /dev/null +++ b/src/modinfooverwrite.h @@ -0,0 +1,59 @@ +#ifndef MODINFOOVERWRITE_H +#define MODINFOOVERWRITE_H + +#include "modinfo.h" + +#include + +class ModInfoOverwrite : public ModInfo +{ + + Q_OBJECT + + friend class ModInfo; + +public: + + virtual bool updateAvailable() const { return false; } + virtual bool updateIgnored() const { return false; } + virtual bool downgradeAvailable() const { return false; } + virtual bool updateNXMInfo() { return false; } + virtual void setCategory(int, bool) {} + virtual bool setName(const QString&) { return false; } + virtual void setNotes(const QString&) {} + virtual void setNexusID(int) {} + virtual void setNewestVersion(const MOBase::VersionInfo&) {} + virtual void ignoreUpdate(bool) {} + virtual void setNexusDescription(const QString&) {} + virtual void setInstallationFile(const QString&) {} + virtual void addNexusCategory(int) {} + virtual void setIsEndorsed(bool) {} + virtual void setNeverEndorse() {} + virtual bool remove() { return false; } + virtual void endorse(bool) {} + virtual bool alwaysEnabled() const { return true; } + virtual bool isEmpty() const; + virtual QString name() const { return "Overwrite"; } + virtual QString notes() const { return ""; } + virtual QDateTime creationTime() const { return QDateTime(); } + virtual QString absolutePath() const; + virtual MOBase::VersionInfo getNewestVersion() const { return ""; } + virtual QString getInstallationFile() const { return ""; } + virtual int getFixedPriority() const { return INT_MAX; } + virtual int getNexusID() const { return -1; } + virtual std::vector getIniTweaks() const { return std::vector(); } + virtual std::vector getFlags() const; + virtual int getHighlight() const; + virtual QString getDescription() const; + virtual QDateTime getLastNexusQuery() const { return QDateTime(); } + virtual QString getNexusDescription() const { return QString(); } + virtual QStringList archives() const; + virtual void addInstalledFile(int, int) {} + +private: + + ModInfoOverwrite(); + +}; + +#endif // MODINFOOVERWRITE_H diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp new file mode 100644 index 00000000..b1f73a37 --- /dev/null +++ b/src/modinforegular.cpp @@ -0,0 +1,571 @@ +#include "modinforegular.h" + +#include "categories.h" +#include "iplugingame.h" +#include "messagedialog.h" +#include "report.h" +#include "scriptextender.h" + +#include +#include +#include + +#include + +using namespace MOBase; +using namespace MOShared; + +namespace { + //Arguably this should be a class static or we should be using FileString rather + //than QString for the names. Or both. + static bool ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS) + { + return QString::compare(LHS->name(), RHS->name(), Qt::CaseInsensitive) < 0; + } +} + +ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStructure) + : ModInfoWithConflictInfo(directoryStructure) + , m_Name(path.dirName()) + , m_Path(path.absolutePath()) + , m_Repository() + , m_MetaInfoChanged(false) + , m_EndorsedState(ENDORSED_UNKNOWN) + , m_NexusBridge() +{ + testValid(); + m_CreationTime = QFileInfo(path.absolutePath()).created(); + // read out the meta-file for information + readMeta(); + + connect(&m_NexusBridge, SIGNAL(descriptionAvailable(int,QVariant,QVariant)) + , this, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant))); + connect(&m_NexusBridge, SIGNAL(endorsementToggled(int,QVariant,QVariant)) + , this, SLOT(nxmEndorsementToggled(int,QVariant,QVariant))); + connect(&m_NexusBridge, SIGNAL(requestFailed(int,int,QVariant,QString)) + , this, SLOT(nxmRequestFailed(int,int,QVariant,QString))); +} + + +ModInfoRegular::~ModInfoRegular() +{ + try { + saveMeta(); + } catch (const std::exception &e) { + qCritical("failed to save meta information for \"%s\": %s", + m_Name.toUtf8().constData(), e.what()); + } +} + +bool ModInfoRegular::isEmpty() const +{ + QDirIterator iter(m_Path, QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); + if (!iter.hasNext()) return true; + iter.next(); + if ((iter.fileName() == "meta.ini") && !iter.hasNext()) return true; + return false; +} + + +void ModInfoRegular::readMeta() +{ + QSettings metaFile(m_Path + "/meta.ini", QSettings::IniFormat); + m_Notes = metaFile.value("notes", "").toString(); + m_NexusID = metaFile.value("modid", -1).toInt(); + m_Version.parse(metaFile.value("version", "").toString()); + m_NewestVersion = metaFile.value("newestVersion", "").toString(); + m_IgnoredVersion = metaFile.value("ignoredVersion", "").toString(); + m_InstallationFile = metaFile.value("installationFile", "").toString(); + m_NexusDescription = metaFile.value("nexusDescription", "").toString(); + m_Repository = metaFile.value("repository", "Nexus").toString(); + m_URL = metaFile.value("url", "").toString(); + m_LastNexusQuery = QDateTime::fromString(metaFile.value("lastNexusQuery", "").toString(), Qt::ISODate); + if (metaFile.contains("endorsed")) { + if (metaFile.value("endorsed").canConvert()) { + switch (metaFile.value("endorsed").toInt()) { + case ENDORSED_FALSE: m_EndorsedState = ENDORSED_FALSE; break; + case ENDORSED_TRUE: m_EndorsedState = ENDORSED_TRUE; break; + case ENDORSED_NEVER: m_EndorsedState = ENDORSED_NEVER; break; + default: m_EndorsedState = ENDORSED_UNKNOWN; break; + } + } else { + m_EndorsedState = metaFile.value("endorsed", false).toBool() ? ENDORSED_TRUE : ENDORSED_FALSE; + } + } + + QString categoriesString = metaFile.value("category", "").toString(); + + QStringList categories = categoriesString.split(',', QString::SkipEmptyParts); + for (QStringList::iterator iter = categories.begin(); iter != categories.end(); ++iter) { + bool ok = false; + int categoryID = iter->toInt(&ok); + if (categoryID < 0) { + // ignore invalid id + continue; + } + if (ok && (categoryID != 0) && (CategoryFactory::instance().categoryExists(categoryID))) { + m_Categories.insert(categoryID); + if (iter == categories.begin()) { + m_PrimaryCategory = categoryID; + } + } + } + + int numFiles = metaFile.beginReadArray("installedFiles"); + for (int i = 0; i < numFiles; ++i) { + metaFile.setArrayIndex(i); + m_InstalledFileIDs.insert(std::make_pair(metaFile.value("modid").toInt(), metaFile.value("fileid").toInt())); + } + metaFile.endArray(); + + m_MetaInfoChanged = false; +} + +void ModInfoRegular::saveMeta() +{ + // only write meta data if the mod directory exists + if (m_MetaInfoChanged && QFile::exists(absolutePath())) { + QSettings metaFile(absolutePath().append("/meta.ini"), QSettings::IniFormat); + if (metaFile.status() == QSettings::NoError) { + std::set temp = m_Categories; + temp.erase(m_PrimaryCategory); + metaFile.setValue("category", QString("%1").arg(m_PrimaryCategory) + "," + SetJoin(temp, ",")); + metaFile.setValue("newestVersion", m_NewestVersion.canonicalString()); + metaFile.setValue("ignoredVersion", m_IgnoredVersion.canonicalString()); + metaFile.setValue("version", m_Version.canonicalString()); + metaFile.setValue("installationFile", m_InstallationFile); + metaFile.setValue("repository", m_Repository); + metaFile.setValue("modid", m_NexusID); + metaFile.setValue("notes", m_Notes); + metaFile.setValue("nexusDescription", m_NexusDescription); + metaFile.setValue("url", m_URL); + metaFile.setValue("lastNexusQuery", m_LastNexusQuery.toString(Qt::ISODate)); + if (m_EndorsedState != ENDORSED_UNKNOWN) { + metaFile.setValue("endorsed", m_EndorsedState); + } + + metaFile.beginWriteArray("installedFiles"); + int idx = 0; + for (auto iter = m_InstalledFileIDs.begin(); iter != m_InstalledFileIDs.end(); ++iter) { + metaFile.setArrayIndex(idx++); + metaFile.setValue("modid", iter->first); + metaFile.setValue("fileid", iter->second); + } + metaFile.endArray(); + + metaFile.sync(); // sync needs to be called to ensure the file is created + + if (metaFile.status() == QSettings::NoError) { + m_MetaInfoChanged = false; + } else { + reportError(tr("failed to write %1/meta.ini: error %2").arg(absolutePath()).arg(metaFile.status())); + } + } else { + reportError(tr("failed to write %1/meta.ini: error %2").arg(absolutePath()).arg(metaFile.status())); + } + } +} + + +bool ModInfoRegular::updateAvailable() const +{ + if (m_IgnoredVersion.isValid() && (m_IgnoredVersion == m_NewestVersion)) { + return false; + } + return m_NewestVersion.isValid() && (m_Version < m_NewestVersion); +} + + +bool ModInfoRegular::downgradeAvailable() const +{ + if (m_IgnoredVersion.isValid() && (m_IgnoredVersion == m_NewestVersion)) { + return false; + } + return m_NewestVersion.isValid() && (m_NewestVersion < m_Version); +} + + +void ModInfoRegular::nxmDescriptionAvailable(int, QVariant, QVariant resultData) +{ + QVariantMap result = resultData.toMap(); + setNewestVersion(VersionInfo(result["version"].toString())); + setNexusDescription(result["description"].toString()); + + if ((m_EndorsedState != ENDORSED_NEVER) && (result.contains("voted_by_user"))) { + setEndorsedState(result["voted_by_user"].toBool() ? ENDORSED_TRUE : ENDORSED_FALSE); + } + m_LastNexusQuery = QDateTime::currentDateTime(); + //m_MetaInfoChanged = true; + saveMeta(); + emit modDetailsUpdated(true); +} + + +void ModInfoRegular::nxmEndorsementToggled(int, QVariant, QVariant resultData) +{ + m_EndorsedState = resultData.toBool() ? ENDORSED_TRUE : ENDORSED_FALSE; + m_MetaInfoChanged = true; + saveMeta(); + emit modDetailsUpdated(true); +} + + +void ModInfoRegular::nxmRequestFailed(int, int, QVariant userData, const QString &errorMessage) +{ + QString fullMessage = errorMessage; + 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() != nullptr) { + MessageDialog::showMessage(fullMessage, QApplication::activeWindow()); + } + emit modDetailsUpdated(false); +} + + +bool ModInfoRegular::updateNXMInfo() +{ + if (m_NexusID > 0) { + m_NexusBridge.requestDescription(m_NexusID, QVariant()); + return true; + } + return false; +} + + +void ModInfoRegular::setCategory(int categoryID, bool active) +{ + m_MetaInfoChanged = true; + + if (active) { + m_Categories.insert(categoryID); + if (m_PrimaryCategory == -1) { + m_PrimaryCategory = categoryID; + } + } else { + std::set::iterator iter = m_Categories.find(categoryID); + if (iter != m_Categories.end()) { + m_Categories.erase(iter); + } + if (categoryID == m_PrimaryCategory) { + if (m_Categories.size() == 0) { + m_PrimaryCategory = -1; + } else { + m_PrimaryCategory = *(m_Categories.begin()); + } + } + } +} + + +bool ModInfoRegular::setName(const QString &name) +{ + if (name.contains('/') || name.contains('\\')) { + return false; + } + + QString newPath = m_Path.mid(0).replace(m_Path.length() - m_Name.length(), m_Name.length(), name); + QDir modDir(m_Path.mid(0, m_Path.length() - m_Name.length())); + + if (m_Name.compare(name, Qt::CaseInsensitive) == 0) { + QString tempName = name; + tempName.append("_temp"); + while (modDir.exists(tempName)) { + tempName.append("_"); + } + if (!modDir.rename(m_Name, tempName)) { + return false; + } + if (!modDir.rename(tempName, name)) { + qCritical("rename to final name failed after successful rename to intermediate name"); + modDir.rename(tempName, m_Name); + return false; + } + } else { + if (!shellRename(modDir.absoluteFilePath(m_Name), modDir.absoluteFilePath(name))) { + qCritical("failed to rename mod %s (errorcode %d)", + qPrintable(name), ::GetLastError()); + return false; + } + } + + std::map::iterator nameIter = s_ModsByName.find(m_Name); + if (nameIter != s_ModsByName.end()) { + unsigned int index = nameIter->second; + s_ModsByName.erase(nameIter); + + m_Name = name; + m_Path = newPath; + + s_ModsByName[m_Name] = index; + + std::sort(s_Collection.begin(), s_Collection.end(), ByName); + updateIndices(); + } else { // otherwise mod isn't registered yet? + m_Name = name; + m_Path = newPath; + } + + return true; +} + +void ModInfoRegular::setNotes(const QString ¬es) +{ + m_Notes = notes; + m_MetaInfoChanged = true; +} + +void ModInfoRegular::setNexusID(int modID) +{ + m_NexusID = modID; + m_MetaInfoChanged = true; +} + +void ModInfoRegular::setVersion(const VersionInfo &version) +{ + m_Version = version; + m_MetaInfoChanged = true; +} + +void ModInfoRegular::setNewestVersion(const VersionInfo &version) +{ + if (version != m_NewestVersion) { + m_NewestVersion = version; + m_MetaInfoChanged = true; + } +} + +void ModInfoRegular::setNexusDescription(const QString &description) +{ + if (qHash(description) != qHash(m_NexusDescription)) { + m_NexusDescription = description; + m_MetaInfoChanged = true; + } +} + +void ModInfoRegular::setEndorsedState(EEndorsedState endorsedState) +{ + if (endorsedState != m_EndorsedState) { + m_EndorsedState = endorsedState; + m_MetaInfoChanged = true; + } +} + +void ModInfoRegular::setInstallationFile(const QString &fileName) +{ + m_InstallationFile = fileName; + m_MetaInfoChanged = true; +} + +void ModInfoRegular::addNexusCategory(int categoryID) +{ + m_Categories.insert(CategoryFactory::instance().resolveNexusID(categoryID)); +} + +void ModInfoRegular::setIsEndorsed(bool endorsed) +{ + if (m_EndorsedState != ENDORSED_NEVER) { + m_EndorsedState = endorsed ? ENDORSED_TRUE : ENDORSED_FALSE; + m_MetaInfoChanged = true; + } +} + + +void ModInfoRegular::setNeverEndorse() +{ + m_EndorsedState = ENDORSED_NEVER; + m_MetaInfoChanged = true; +} + + +bool ModInfoRegular::remove() +{ + m_MetaInfoChanged = false; + return shellDelete(QStringList(absolutePath()), true); +} + +void ModInfoRegular::endorse(bool doEndorse) +{ + if (doEndorse != (m_EndorsedState == ENDORSED_TRUE)) { + m_NexusBridge.requestToggleEndorsement(getNexusID(), doEndorse, QVariant(1)); + } +} + + +QString ModInfoRegular::absolutePath() const +{ + return m_Path; +} + +void ModInfoRegular::ignoreUpdate(bool ignore) +{ + if (ignore) { + m_IgnoredVersion = m_NewestVersion; + } else { + m_IgnoredVersion.clear(); + } + m_MetaInfoChanged = true; +} + + +std::vector ModInfoRegular::getFlags() const +{ + std::vector result = ModInfoWithConflictInfo::getFlags(); + if ((m_NexusID > 0) && (endorsedState() == ENDORSED_FALSE)) { + result.push_back(ModInfo::FLAG_NOTENDORSED); + } + if (!isValid()) { + result.push_back(ModInfo::FLAG_INVALID); + } + if (m_Notes.length() != 0) { + result.push_back(ModInfo::FLAG_NOTES); + } + return result; +} + + +std::vector ModInfoRegular::getContents() const +{ + QTime now = QTime::currentTime(); + if (m_LastContentCheck.isNull() || (m_LastContentCheck.secsTo(now) > 60)) { + m_CachedContent.clear(); + QDir dir(absolutePath()); + if (dir.entryList(QStringList() << "*.esp" << "*.esm").size() > 0) { + m_CachedContent.push_back(CONTENT_PLUGIN); + } + if (dir.entryList(QStringList() << "*.bsa").size() > 0) { + m_CachedContent.push_back(CONTENT_BSA); + } + + ScriptExtender *extender = qApp->property("managed_game").value()->feature(); + + if (extender != nullptr) { + QString sePluginPath = extender->name() + "/plugins"; + if (dir.exists(sePluginPath)) m_CachedContent.push_back(CONTENT_SKSE); + } + if (dir.exists("textures")) m_CachedContent.push_back(CONTENT_TEXTURE); + if (dir.exists("meshes")) m_CachedContent.push_back(CONTENT_MESH); + if (dir.exists("interface") + || dir.exists("menus")) m_CachedContent.push_back(CONTENT_INTERFACE); + if (dir.exists("music")) m_CachedContent.push_back(CONTENT_MUSIC); + if (dir.exists("sound")) m_CachedContent.push_back(CONTENT_SOUND); + if (dir.exists("scripts")) m_CachedContent.push_back(CONTENT_SCRIPT); + if (dir.exists("strings")) m_CachedContent.push_back(CONTENT_STRING); + if (dir.exists("SkyProc Patchers")) m_CachedContent.push_back(CONTENT_SKYPROC); + + m_LastContentCheck = QTime::currentTime(); + } + + return m_CachedContent; + +} + + +int ModInfoRegular::getHighlight() const +{ + return isValid() ? HIGHLIGHT_NONE: HIGHLIGHT_INVALID; +} + + +QString ModInfoRegular::getDescription() const +{ + if (!isValid()) { + return tr("%1 contains no esp/esm and no asset (textures, meshes, interface, ...) directory").arg(name()); + } else { + const std::set &categories = getCategories(); + std::wostringstream categoryString; + categoryString << ToWString(tr("Categories:
")); + CategoryFactory &categoryFactory = CategoryFactory::instance(); + for (std::set::const_iterator catIter = categories.begin(); + catIter != categories.end(); ++catIter) { + if (catIter != categories.begin()) { + categoryString << " , "; + } + categoryString << "" << ToWString(categoryFactory.getCategoryName(categoryFactory.getCategoryIndex(*catIter))) << ""; + } + + return ToQString(categoryString.str()); + } +} + +QString ModInfoRegular::notes() const +{ + return m_Notes; +} + +QDateTime ModInfoRegular::creationTime() const +{ + return m_CreationTime; +} + +QString ModInfoRegular::getNexusDescription() const +{ + return m_NexusDescription; +} + +QString ModInfoRegular::repository() const +{ + return m_Repository; +} + +ModInfoRegular::EEndorsedState ModInfoRegular::endorsedState() const +{ + return m_EndorsedState; +} + +QDateTime ModInfoRegular::getLastNexusQuery() const +{ + return m_LastNexusQuery; +} + +void ModInfoRegular::setURL(QString const &url) +{ + m_URL = url; + m_MetaInfoChanged = true; +} + +QString ModInfoRegular::getURL() const +{ + return m_URL; +} + + + +QStringList ModInfoRegular::archives() const +{ + QStringList result; + QDir dir(this->absolutePath()); + for (const QString &archive : dir.entryList(QStringList("*.bsa"))) { + result.append(this->absolutePath() + "/" + archive); + } + return result; +} + +void ModInfoRegular::addInstalledFile(int modId, int fileId) +{ + m_InstalledFileIDs.insert(std::make_pair(modId, fileId)); + m_MetaInfoChanged = true; +} + +std::vector ModInfoRegular::getIniTweaks() const +{ + QString metaFileName = absolutePath().append("/meta.ini"); + QSettings metaFile(metaFileName, QSettings::IniFormat); + + std::vector result; + + int numTweaks = metaFile.beginReadArray("INI Tweaks"); + + if (numTweaks != 0) { + qDebug("%d active ini tweaks in %s", + numTweaks, QDir::toNativeSeparators(metaFileName).toUtf8().constData()); + } + + for (int i = 0; i < numTweaks; ++i) { + metaFile.setArrayIndex(i); + QString filename = absolutePath().append("/INI Tweaks/").append(metaFile.value("name").toString()); + result.push_back(filename); + } + metaFile.endArray(); + return result; +} diff --git a/src/modinforegular.h b/src/modinforegular.h new file mode 100644 index 00000000..a94d0363 --- /dev/null +++ b/src/modinforegular.h @@ -0,0 +1,349 @@ +#ifndef MODINFOREGULAR_H +#define MODINFOREGULAR_H + +#include "modinfowithconflictinfo.h" + +#include "nexusinterface.h" + +/** + * @brief Represents meta information about a single mod. + * + * Represents meta information about a single mod. The class interface is used + * to manage the mod collection + * + **/ +class ModInfoRegular : public ModInfoWithConflictInfo +{ + + Q_OBJECT + + friend class ModInfo; + +public: + + ~ModInfoRegular(); + + virtual bool isRegular() const { return true; } + + virtual bool isEmpty() const; + + /** + * @brief test if there is a newer version of the mod + * + * test if there is a newer version of the mod. This does NOT cause + * information to be retrieved from the nexus, it will only test version information already + * available locally. Use checkAllForUpdate() to update this version information + * + * @return true if there is a newer version + **/ + bool updateAvailable() const; + + /** + * @return true if the current update is being ignored + */ + virtual bool updateIgnored() const { return m_IgnoredVersion == m_NewestVersion; } + + /** + * @brief test if there is a newer version of the mod + * + * test if there is a newer version of the mod. This does NOT cause + * information to be retrieved from the nexus, it will only test version information already + * available locally. Use checkAllForUpdate() to update this version information + * + * @return true if there is a newer version + **/ + bool downgradeAvailable() const; + + /** + * @brief request an update of nexus description for this mod. + * + * This requests mod information from the nexus. This is an asynchronous request, + * so there is no immediate effect of this call. + * + * @return returns true if information for this mod will be updated, false if there is no nexus mod id to use + **/ + bool updateNXMInfo(); + + /** + * @brief assign or unassign the specified category + * + * Every mod can have an arbitrary number of categories assigned to it + * + * @param categoryID id of the category to set + * @param active determines wheter the category is assigned or unassigned + * @note this function does not test whether categoryID actually identifies a valid category + **/ + void setCategory(int categoryID, bool active); + + /** + * @brief set the name of this mod + * + * set the name of this mod. This will also update the name of the + * directory that contains this mod + * + * @param name new name of the mod + * @return true on success, false if the new name can't be used (i.e. because the new + * directory name wouldn't be valid) + **/ + bool setName(const QString &name); + + /** + * @brief change the notes (manually set information) for this mod + * @param notes new notes + */ + void setNotes(const QString ¬es); + + /** + * @brief set/change the nexus mod id of this mod + * + * @param modID the nexus mod id + **/ + void setNexusID(int modID); + + /** + * @brief set the version of this mod + * + * this can be used to overwrite the version of a mod without actually + * updating the mod + * + * @param version the new version to use + **/ + void setVersion(const MOBase::VersionInfo &version); + + /** + * @brief set the newest version of this mod on the nexus + * + * this can be used to overwrite the version of a mod without actually + * updating the mod + * + * @param version the new version to use + * @todo this function should be made obsolete. All queries for mod information should go through + * this class so no public function for this change is required + **/ + void setNewestVersion(const MOBase::VersionInfo &version); + + /** + * @brief changes/updates the nexus description text + * @param description the current description text + */ + virtual void setNexusDescription(const QString &description); + + virtual void setInstallationFile(const QString &fileName); + + /** + * @brief sets the category id from a nexus category id. Conversion to MO id happens internally + * @param categoryID the nexus category id + * @note if a mapping is not possible, the category is set to the default value + */ + virtual void addNexusCategory(int categoryID); + + /** + * @brief sets the new primary category of the mod + * @param categoryID the category to set + */ + virtual void setPrimaryCategory(int categoryID) { m_PrimaryCategory = categoryID; m_MetaInfoChanged = true; } + + /** + * @brief sets the download repository + * @param repository + */ + virtual void setRepository(const QString &repository) { m_Repository = repository; } + + /** + * update the endorsement state for the mod. This only changes the + * buffered state, it does not sync with Nexus + * @param endorsed the new endorsement state + */ + virtual void setIsEndorsed(bool endorsed); + + /** + * set the mod to "i don't intend to endorse". The mod will not show as unendorsed but can still be endorsed + */ + virtual void setNeverEndorse(); + + /** + * @brief delete the mod from the disc. This does not update the global ModInfo structure or indices + * @return true if the mod was successfully removed + **/ + bool remove(); + + /** + * @brief endorse or un-endorse the mod + * @param doEndorse if true, the mod is endorsed, if false, it's un-endorsed. + * @note if doEndorse doesn't differ from the current value, nothing happens. + */ + virtual void endorse(bool doEndorse); + + /** + * @brief getter for the mod name + * + * @return the mod name + **/ + QString name() const { return m_Name; } + + /** + * @brief getter for the mod path + * + * @return the (absolute) path to the mod + **/ + QString absolutePath() const; + + /** + * @brief getter for the newest version number of this mod + * + * @return newest version of the mod + **/ + MOBase::VersionInfo getNewestVersion() const { return m_NewestVersion; } + + /** + * @brief ignore the newest version for updates + */ + void ignoreUpdate(bool ignore); + + /** + * @brief getter for the installation file + * + * @return file used to install this mod from + */ + virtual QString getInstallationFile() const { return m_InstallationFile; } + /** + * @brief getter for the nexus mod id + * + * @return the nexus mod id. may be 0 if the mod id isn't known or doesn't exist + **/ + int getNexusID() const { return m_NexusID; } + + /** + * @return the fixed priority of mods of this type or INT_MIN if the priority of mods + * needs to be user-modifiable + */ + virtual int getFixedPriority() const { return INT_MIN; } + + /** + * @return true if the mod can be updated + */ + virtual bool canBeUpdated() const { return m_NexusID > 0; } + + /** + * @return true if the mod can be enabled/disabled + */ + virtual bool canBeEnabled() const { return true; } + + /** + * @return a list of flags for this mod + */ + virtual std::vector getFlags() const; + + virtual std::vector getContents() const; + + /** + * @return an indicator if and how this mod should be highlighted by the UI + */ + virtual int getHighlight() const; + + /** + * @return list of names of ini tweaks + **/ + std::vector getIniTweaks() const; + + /** + * @return a description about the mod, to be displayed in the ui + */ + virtual QString getDescription() const; + + /** + * @return manually set notes for this mod + */ + virtual QString notes() const; + + /** + * @return time this mod was created (file time of the directory) + */ + virtual QDateTime creationTime() const; + + /** + * @return nexus description of the mod (html) + */ + QString getNexusDescription() const; + + /** + * @return repository from which the file was downloaded + */ + virtual QString repository() const; + + /** + * @return true if the file has been endorsed on nexus + */ + virtual EEndorsedState endorsedState() const; + + /** + * @return last time nexus was queried for infos on this mod + */ + virtual QDateTime getLastNexusQuery() const; + + virtual QStringList archives() const; + + virtual void addInstalledFile(int modId, int fileId); + + /** + * @brief stores meta information back to disk + */ + virtual void saveMeta(); + + void readMeta(); + + /** + * @brief set the URL for a mod + */ + virtual void setURL(QString const &); + + /** + * @returns the URL for a mod + */ + virtual QString getURL() const; + +private: + + void setEndorsedState(EEndorsedState endorsedState); + +private slots: + + void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData); + void nxmEndorsementToggled(int, QVariant userData, QVariant resultData); + void nxmRequestFailed(int modID, int fileID, QVariant userData, const QString &errorMessage); + +protected: + + ModInfoRegular(const QDir &path, MOShared::DirectoryEntry **directoryStructure); + +private: + + QString m_Name; + QString m_Path; + QString m_InstallationFile; + QString m_Notes; + QString m_NexusDescription; + QString m_Repository; + QString m_URL; + + QDateTime m_CreationTime; + QDateTime m_LastNexusQuery; + + int m_NexusID; + std::set> m_InstalledFileIDs; + + bool m_MetaInfoChanged; + MOBase::VersionInfo m_NewestVersion; + MOBase::VersionInfo m_IgnoredVersion; + + EEndorsedState m_EndorsedState; + + NexusBridge m_NexusBridge; + + mutable std::vector m_CachedContent; + mutable QTime m_LastContentCheck; + +}; + + +#endif // MODINFOREGULAR_H diff --git a/src/modinfowithconflictinfo.cpp b/src/modinfowithconflictinfo.cpp new file mode 100644 index 00000000..fbf2345f --- /dev/null +++ b/src/modinfowithconflictinfo.cpp @@ -0,0 +1,130 @@ +#include "modinfowithconflictinfo.h" + +#include "directoryentry.h" +#include "utility.h" + +using namespace MOBase; +using namespace MOShared; + +ModInfoWithConflictInfo::ModInfoWithConflictInfo(DirectoryEntry **directoryStructure) + : m_DirectoryStructure(directoryStructure) {} + +void ModInfoWithConflictInfo::clearCaches() +{ + m_LastConflictCheck = QTime(); +} + +std::vector ModInfoWithConflictInfo::getFlags() const +{ + std::vector result; + switch (isConflicted()) { + case CONFLICT_MIXED: { + result.push_back(ModInfo::FLAG_CONFLICT_MIXED); + } break; + case CONFLICT_OVERWRITE: { + result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITE); + } break; + case CONFLICT_OVERWRITTEN: { + result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITTEN); + } break; + case CONFLICT_REDUNDANT: { + result.push_back(ModInfo::FLAG_CONFLICT_REDUNDANT); + } break; + default: { /* NOP */ } + } + return result; +} + + +void ModInfoWithConflictInfo::doConflictCheck() const +{ + m_OverwriteList.clear(); + m_OverwrittenList.clear(); + + bool providesAnything = false; + + int dataID = 0; + if ((*m_DirectoryStructure)->originExists(L"data")) { + dataID = (*m_DirectoryStructure)->getOriginByName(L"data").getID(); + } + + std::wstring name = ToWString(this->name()); + + m_CurrentConflictState = CONFLICT_NONE; + + if ((*m_DirectoryStructure)->originExists(name)) { + FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); + std::vector files = origin.getFiles(); + // for all files in this origin + for (FileEntry::Ptr file : files) { + const std::vector &alternatives = file->getAlternatives(); + if ((alternatives.size() == 0) || (alternatives[0] == dataID)) { + // no alternatives -> no conflict + providesAnything = true; + } else { + if (file->getOrigin() != origin.getID()) { + FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(file->getOrigin()); + unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName())); + m_OverwrittenList.insert(altIndex); + } else { + providesAnything = true; + } + + // for all non-providing alternative origins + for (int altId : alternatives) { + if ((altId != dataID) && (altId != origin.getID())) { + FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(altId); + unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName())); + if (origin.getPriority() > altOrigin.getPriority()) { + m_OverwriteList.insert(altIndex); + } else { + m_OverwrittenList.insert(altIndex); + } + } + } + } + } + m_LastConflictCheck = QTime::currentTime(); + + if (files.size() != 0) { + if (!providesAnything) + m_CurrentConflictState = CONFLICT_REDUNDANT; + else if (!m_OverwriteList.empty() && !m_OverwrittenList.empty()) + m_CurrentConflictState = CONFLICT_MIXED; + else if (!m_OverwriteList.empty()) + m_CurrentConflictState = CONFLICT_OVERWRITE; + else if (!m_OverwrittenList.empty()) + m_CurrentConflictState = CONFLICT_OVERWRITTEN; + } + } +} + +ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isConflicted() const +{ + // this is costy so cache the result + QTime now = QTime::currentTime(); + if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) { + doConflictCheck(); + } + + return m_CurrentConflictState; +} + + +bool ModInfoWithConflictInfo::isRedundant() const +{ + std::wstring name = ToWString(this->name()); + if ((*m_DirectoryStructure)->originExists(name)) { + FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); + std::vector files = origin.getFiles(); + bool ignore = false; + for (auto iter = files.begin(); iter != files.end(); ++iter) { + if ((*iter)->getOrigin(ignore) == origin.getID()) { + return false; + } + } + return true; + } else { + return false; + } +} diff --git a/src/modinfowithconflictinfo.h b/src/modinfowithconflictinfo.h new file mode 100644 index 00000000..be31f20f --- /dev/null +++ b/src/modinfowithconflictinfo.h @@ -0,0 +1,64 @@ +#ifndef MODINFOWITHCONFLICTINFO_H +#define MODINFOWITHCONFLICTINFO_H + +#include "modinfo.h" + +#include + +class ModInfoWithConflictInfo : public ModInfo +{ + +public: + + ModInfoWithConflictInfo(MOShared::DirectoryEntry **directoryStructure); + + std::vector getFlags() const; + + /** + * @brief clear all caches held for this mod + */ + virtual void clearCaches(); + + virtual std::set getModOverwrite() { return m_OverwriteList; } + + virtual std::set getModOverwritten() { return m_OverwrittenList; } + + virtual void doConflictCheck() const; + +private: + + enum EConflictType { + CONFLICT_NONE, + CONFLICT_OVERWRITE, + CONFLICT_OVERWRITTEN, + CONFLICT_MIXED, + CONFLICT_REDUNDANT + }; + +private: + + /** + * @return true if there is a conflict for files in this mod + */ + EConflictType isConflicted() const; + + /** + * @return true if this mod is completely replaced by others + */ + bool isRedundant() const; + +private: + + MOShared::DirectoryEntry **m_DirectoryStructure; + + mutable EConflictType m_CurrentConflictState; + mutable QTime m_LastConflictCheck; + + mutable std::set m_OverwriteList; // indices of mods overritten by this mod + mutable std::set m_OverwrittenList; // indices of mods overwriting this mod + +}; + + + +#endif // MODINFOWITHCONFLICTINFO_H diff --git a/src/organizer.pro b/src/organizer.pro index 261e3c1c..adc606fb 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -91,7 +91,12 @@ SOURCES += \ organizerproxy.cpp \ viewmarkingscrollbar.cpp \ plugincontainer.cpp \ - organizercore.cpp + organizercore.cpp \ + modinfowithconflictinfo.cpp \ + modinforegular.cpp \ + modinfobackup.cpp \ + modinfooverwrite.cpp \ + modinfoforeign.cpp HEADERS += \ @@ -167,7 +172,12 @@ HEADERS += \ viewmarkingscrollbar.h \ plugincontainer.h \ organizercore.h \ - iuserinterface.h + iuserinterface.h \ + modinfowithconflictinfo.h \ + modinforegular.h \ + modinfobackup.h \ + modinfooverwrite.h \ + modinfoforeign.h FORMS += \ transfersavesdialog.ui \ -- cgit v1.3.1 From d4754f4d3ca7451d784e89ae1ddef240666dfdf8 Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Thu, 15 Oct 2015 18:44:39 +0100 Subject: Fix for some nexus fomods having the old version of the mod link and not the new version. Also fixed a lot of const correctness things --- src/mainwindow.cpp | 4 ++- src/shared/SConscript | 10 +++---- src/shared/fallout3info.cpp | 17 ++++++++---- src/shared/fallout3info.h | 31 ++++++++++----------- src/shared/falloutnvinfo.cpp | 17 ++++++++---- src/shared/falloutnvinfo.h | 63 ++++++++++-------------------------------- src/shared/gameinfo.cpp | 13 +++++++++ src/shared/gameinfo.h | 30 +++++++++++--------- src/shared/oblivioninfo.cpp | 31 ++++++++------------- src/shared/oblivioninfo.h | 65 ++++++++++---------------------------------- src/shared/skyriminfo.cpp | 17 ++++++++---- src/shared/skyriminfo.h | 26 ++++++++++-------- 12 files changed, 138 insertions(+), 186 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1dca599d..9982e928 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3120,7 +3120,9 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addAction(tr("Visit on Nexus"), this, SLOT(visitOnNexus_clicked())); } - if (info->getURL() != "") { + if (info->getURL() != "" && + !GameInfo::instance().isValidModURL(info->getNexusID(), + info->getURL().toStdWString())) { menu->addAction(tr("Visit web page"), this, SLOT(visitWebPage_clicked())); } diff --git a/src/shared/SConscript b/src/shared/SConscript index 69a95289..24ca5b19 100644 --- a/src/shared/SConscript +++ b/src/shared/SConscript @@ -1,8 +1,6 @@ -Import('qt_env') +Import('env') -env = qt_env.Clone() - -env.EnableQtModules('Core', 'Gui') +env = env.Clone() env.AppendUnique(CPPDEFINES = [ 'UNICODE', @@ -21,5 +19,5 @@ env.AppendUnique(CPPPATH = [ # Not sure if renaming this helps much as it's static env.StaticLibrary('mo_shared', env.Glob('*.cpp')) -res = env['QT_USED_MODULES'] -Return('res') +#res = env['QT_USED_MODULES'] +#Return('res') diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index 0b369b50..f0776a7d 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -63,7 +63,7 @@ std::wstring Fallout3Info::getRegPathStatic() } -std::vector Fallout3Info::getDLCPlugins() +std::vector Fallout3Info::getDLCPlugins() const { return boost::assign::list_of (L"ThePitt.esm") (L"Anchorage.esm") @@ -73,22 +73,22 @@ std::vector Fallout3Info::getDLCPlugins() ; } -std::vector Fallout3Info::getSavegameAttachmentExtensions() +std::vector Fallout3Info::getSavegameAttachmentExtensions() const { return std::vector(); } -std::vector Fallout3Info::getIniFileNames() +std::vector Fallout3Info::getIniFileNames() const { return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini"); } -std::wstring Fallout3Info::getReferenceDataFile() +std::wstring Fallout3Info::getReferenceDataFile() const { return L"Fallout - Meshes.bsa"; } -std::wstring Fallout3Info::getNexusPage(bool nmmScheme) +std::wstring Fallout3Info::getNexusPage(bool nmmScheme) const { if (nmmScheme) { return L"http://nmm.nexusmods.com/fallout3"; @@ -107,7 +107,7 @@ int Fallout3Info::getNexusModIDStatic() return 16348; } -bool Fallout3Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*) +bool Fallout3Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*) const { static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", nullptr }; @@ -119,4 +119,9 @@ bool Fallout3Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*) return false; } +bool Fallout3Info::isValidModURL(int modID, const std::wstring &url) const +{ + return GameInfo::isValidModURL(modID, url, L"http://fallout3.nexusmods.com"); +} + } // namespace MOShared diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h index 0045581d..0a2b0ebe 100644 --- a/src/shared/fallout3info.h +++ b/src/shared/fallout3info.h @@ -20,7 +20,6 @@ along with Mod Organizer. If not, see . #ifndef FALLOUT3INFO_H #define FALLOUT3INFO_H - #include "gameinfo.h" namespace MOShared { @@ -36,36 +35,34 @@ public: virtual ~Fallout3Info() {} static std::wstring getRegPathStatic(); - virtual std::wstring getRegPath() { return getRegPathStatic(); } - virtual std::wstring getBinaryName() { return L"Fallout3.exe"; } + virtual std::wstring getRegPath() const { return getRegPathStatic(); } + virtual std::wstring getBinaryName() const { return L"Fallout3.exe"; } - virtual GameInfo::Type getType() { return TYPE_FALLOUT3; } + virtual GameInfo::Type getType() const { return TYPE_FALLOUT3; } virtual std::wstring getGameName() const { return L"Fallout 3"; } virtual std::wstring getGameShortName() const { return L"Fallout3"; } - virtual std::vector getDLCPlugins(); - virtual std::vector getSavegameAttachmentExtensions(); + virtual std::vector getDLCPlugins() const; + virtual std::vector getSavegameAttachmentExtensions() const; // file name of this games ini (no path) - virtual std::vector getIniFileNames(); + virtual std::vector getIniFileNames() const; - virtual std::wstring getReferenceDataFile(); + virtual std::wstring getReferenceDataFile() const; - virtual std::wstring getNexusPage(bool nmmScheme = true); + virtual std::wstring getNexusPage(bool nmmScheme = true) const; static std::wstring getNexusInfoUrlStatic(); - virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); } + virtual std::wstring getNexusInfoUrl() const { return getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); - virtual int getNexusModID() { return getNexusModIDStatic(); } - virtual int getNexusGameID() { return 120; } + virtual int getNexusModID() const { return getNexusModIDStatic(); } + virtual int getNexusGameID() const { return 120; } - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); + virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const; - // 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() const { return L"SArchiveList"; } - virtual std::wstring archiveListKey() { return L"SArchiveList"; } + virtual bool isValidModURL(int modID, std::wstring const &url) const; private: diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index 1203bd25..f6de72e3 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -63,7 +63,7 @@ std::wstring FalloutNVInfo::getRegPathStatic() } } -std::vector FalloutNVInfo::getDLCPlugins() +std::vector FalloutNVInfo::getDLCPlugins() const { return boost::assign::list_of (L"DeadMoney.esm") (L"HonestHearts.esm") @@ -77,22 +77,22 @@ std::vector FalloutNVInfo::getDLCPlugins() ; } -std::vector FalloutNVInfo::getSavegameAttachmentExtensions() +std::vector FalloutNVInfo::getSavegameAttachmentExtensions() const { return std::vector(); } -std::vector FalloutNVInfo::getIniFileNames() +std::vector FalloutNVInfo::getIniFileNames() const { return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini"); } -std::wstring FalloutNVInfo::getReferenceDataFile() +std::wstring FalloutNVInfo::getReferenceDataFile() const { return L"Fallout - Meshes.bsa"; } -std::wstring FalloutNVInfo::getNexusPage(bool nmmScheme) +std::wstring FalloutNVInfo::getNexusPage(bool nmmScheme) const { if (nmmScheme) { return L"http://nmm.nexusmods.com/newvegas"; @@ -114,7 +114,7 @@ int FalloutNVInfo::getNexusModIDStatic() } -bool FalloutNVInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) +bool FalloutNVInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) const { static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", nullptr }; @@ -126,4 +126,9 @@ bool FalloutNVInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) return false; } +bool FalloutNVInfo::isValidModURL(int modID, std::wstring const &url) const +{ + return GameInfo::isValidModURL(modID, url, L"http://newvegas.nexusmods.com"); +} + } // namespace MOShared diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h index e1a614d2..d03add9c 100644 --- a/src/shared/falloutnvinfo.h +++ b/src/shared/falloutnvinfo.h @@ -36,69 +36,34 @@ public: virtual ~FalloutNVInfo() {} static std::wstring getRegPathStatic(); - virtual std::wstring getRegPath() { return getRegPathStatic(); } - virtual std::wstring getBinaryName() { return L"FalloutNV.exe"; } + virtual std::wstring getRegPath() const { return getRegPathStatic(); } + virtual std::wstring getBinaryName() const { return L"FalloutNV.exe"; } - virtual GameInfo::Type getType() { return TYPE_FALLOUTNV; } + virtual GameInfo::Type getType() const { return TYPE_FALLOUTNV; } virtual std::wstring getGameName() const { return L"New Vegas"; } virtual std::wstring getGameShortName() const { return L"FalloutNV"; } -// virtual bool requiresSteam() const { return true; } - -/* 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(); + virtual std::vector getDLCPlugins() const; + virtual std::vector getSavegameAttachmentExtensions() const; // file name of this games ini (no path) - virtual std::vector getIniFileNames(); + virtual std::vector getIniFileNames() const; - virtual std::wstring getReferenceDataFile(); + virtual std::wstring getReferenceDataFile() const; - virtual std::wstring getNexusPage(bool nmmScheme = true); + virtual std::wstring getNexusPage(bool nmmScheme = true) const; static std::wstring getNexusInfoUrlStatic(); - virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); } + virtual std::wstring getNexusInfoUrl() const { return getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); - virtual int getNexusModID() { return getNexusModIDStatic(); } - virtual int getNexusGameID() { return 130; } + virtual int getNexusModID() const { return getNexusModIDStatic(); } + virtual int getNexusGameID() const { return 130; } - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); + virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const; - // 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() const { return L"SArchiveList"; } - virtual std::wstring archiveListKey() { return L"SArchiveList"; } + virtual bool isValidModURL(int modID, const std::wstring &url) const; private: diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index 1f25cec1..5338c069 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -86,6 +86,19 @@ void GameInfo::identifyMyGamesDirectory(const std::wstring &file) } } +bool GameInfo::isValidModURL(int modID, const std::wstring &url, const std::wstring &alt) const +{ + std::wostringstream os; + os << getNexusPage(false) << "/mods/" << modID; + if (url == os.str()) { + return true; + } + os.clear(); + os.str(L""); + os << alt << "/mods/" << modID; + return url == os.str(); +} + bool GameInfo::identifyGame(const std::wstring &moDirectory, const std::wstring &moDataDirectory, const std::wstring &searchPath) { diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index a32b6b82..8caa5937 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -56,12 +56,12 @@ public: virtual ~GameInfo() {} - std::wstring getOrganizerDirectory() { return m_OrganizerDirectory; } + std::wstring getOrganizerDirectory() const { return m_OrganizerDirectory; } - virtual std::wstring getRegPath() = 0; - virtual std::wstring getBinaryName() = 0; + virtual std::wstring getRegPath() const = 0; + virtual std::wstring getBinaryName() const = 0; - virtual GameInfo::Type getType() = 0; + virtual GameInfo::Type getType() const = 0; virtual std::wstring getGameName() const = 0; virtual std::wstring getGameShortName() const = 0; @@ -75,22 +75,24 @@ public: virtual bool requiresSteam() const; // get a list of file extensions for additional files belonging to a save game - virtual std::vector getSavegameAttachmentExtensions() = 0; + virtual std::vector getSavegameAttachmentExtensions() const = 0; // get a set of esp/esm files that are part of known dlcs - virtual std::vector getDLCPlugins() = 0; + virtual std::vector getDLCPlugins() const = 0; // file name of this games ini file(s) - virtual std::vector getIniFileNames() = 0; + virtual std::vector getIniFileNames() const = 0; - virtual std::wstring getReferenceDataFile() = 0; + virtual std::wstring getReferenceDataFile() const = 0; - virtual std::wstring getNexusPage(bool nmmScheme = true) = 0; - virtual std::wstring getNexusInfoUrl() = 0; - virtual int getNexusModID() = 0; - virtual int getNexusGameID() = 0; + virtual std::wstring getNexusPage(bool nmmScheme = true) const = 0; + virtual std::wstring getNexusInfoUrl() const = 0; + virtual int getNexusModID() const = 0; + virtual int getNexusGameID() const = 0; - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) = 0; + virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const = 0; + + virtual bool isValidModURL(int modID, std::wstring const &url) const = 0; public: @@ -108,6 +110,8 @@ protected: const std::wstring &getMyGamesDirectory() const { return m_MyGamesDirectory; } void identifyMyGamesDirectory(const std::wstring &file); + bool isValidModURL(int modID, const std::wstring &url, const std::wstring &alt) const; + private: static bool identifyGame(const std::wstring &moDirectory, const std::wstring &moDataDirectory, const std::wstring &searchPath); diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index 16748f58..730c186b 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -63,17 +63,7 @@ std::wstring OblivionInfo::getRegPathStatic() } } - - - - - - - - - - -std::vector OblivionInfo::getDLCPlugins() +std::vector OblivionInfo::getDLCPlugins() const { return boost::assign::list_of (L"DLCShiveringIsles.esp") (L"Knights.esp") @@ -89,22 +79,18 @@ std::vector OblivionInfo::getDLCPlugins() } -std::vector OblivionInfo::getSavegameAttachmentExtensions() +std::vector OblivionInfo::getSavegameAttachmentExtensions() const { return boost::assign::list_of(L"obse"); } -std::vector OblivionInfo::getIniFileNames() +std::vector OblivionInfo::getIniFileNames() const { return boost::assign::list_of(L"oblivion.ini")(L"oblivionprefs.ini"); } - - - - -std::wstring OblivionInfo::getNexusPage(bool nmmScheme) +std::wstring OblivionInfo::getNexusPage(bool nmmScheme) const { if (nmmScheme) { return L"http://nmm.nexusmods.com/oblivion"; @@ -125,7 +111,7 @@ int OblivionInfo::getNexusModIDStatic() return 38277; } -bool OblivionInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) +bool OblivionInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) const { static LPCWSTR profileFiles[] = { L"oblivion.ini", L"oblivionprefs.ini", L"plugins.txt", nullptr }; @@ -137,9 +123,14 @@ bool OblivionInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) return false; } -std::wstring OblivionInfo::getReferenceDataFile() +std::wstring OblivionInfo::getReferenceDataFile() const { return L"Oblivion - Meshes.bsa"; } +bool OblivionInfo::isValidModURL(int modID, const std::wstring &url) const +{ + return GameInfo::isValidModURL(modID, url, L"http://oblivion.nexusmods.com"); +} + } // namespace MOShared diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h index 87ba26ff..1adf9cab 100644 --- a/src/shared/oblivioninfo.h +++ b/src/shared/oblivioninfo.h @@ -34,69 +34,34 @@ public: virtual ~OblivionInfo() {} static std::wstring getRegPathStatic(); - virtual std::wstring getRegPath() { return getRegPathStatic(); } - virtual std::wstring getBinaryName() { return L"Oblivion.exe"; } + virtual std::wstring getRegPath() const { return getRegPathStatic(); } + virtual std::wstring getBinaryName() const { return L"Oblivion.exe"; } - virtual GameInfo::Type getType() { return TYPE_OBLIVION; } + virtual GameInfo::Type getType() const { return TYPE_OBLIVION; } 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::vector getDLCPlugins(); - virtual std::vector getSavegameAttachmentExtensions(); + + virtual std::vector getDLCPlugins() const; + virtual std::vector getSavegameAttachmentExtensions() const; // file name of this games ini (no path) - virtual std::vector getIniFileNames(); + virtual std::vector getIniFileNames() const; - virtual std::wstring getReferenceDataFile(); + virtual std::wstring getReferenceDataFile() const; - virtual std::wstring getNexusPage(bool nmmScheme = true); + virtual std::wstring getNexusPage(bool nmmScheme = true) const; static std::wstring getNexusInfoUrlStatic(); - virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); } + virtual std::wstring getNexusInfoUrl() const { return getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); - virtual int getNexusModID() { return getNexusModIDStatic(); } - virtual int getNexusGameID() { return 101; } + virtual int getNexusModID() const { return getNexusModIDStatic(); } + virtual int getNexusGameID() const { return 101; } - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); + virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const; - // 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() const { return L"SArchiveList"; } - virtual std::wstring archiveListKey() { return L"SArchiveList"; } + virtual bool isValidModURL(int modID, std::wstring const &url) const; private: diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index f66fcef4..7c85fe10 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -91,7 +91,7 @@ GameInfo::LoadOrderMechanism SkyrimInfo::getLoadOrderMechanism() const } } -std::vector SkyrimInfo::getDLCPlugins() +std::vector SkyrimInfo::getDLCPlugins() const { return boost::assign::list_of (L"Dawnguard.esm") (L"Dragonborn.esm") @@ -102,23 +102,23 @@ std::vector SkyrimInfo::getDLCPlugins() ; } -std::vector SkyrimInfo::getSavegameAttachmentExtensions() +std::vector SkyrimInfo::getSavegameAttachmentExtensions() const { return boost::assign::list_of(L"skse"); } -std::vector SkyrimInfo::getIniFileNames() +std::vector SkyrimInfo::getIniFileNames() const { return boost::assign::list_of(L"skyrim.ini")(L"skyrimprefs.ini"); } -std::wstring SkyrimInfo::getReferenceDataFile() +std::wstring SkyrimInfo::getReferenceDataFile() const { return L"Skyrim - Meshes.bsa"; } -std::wstring SkyrimInfo::getNexusPage(bool nmmScheme) +std::wstring SkyrimInfo::getNexusPage(bool nmmScheme) const { if (nmmScheme) { return L"http://nmm.nexusmods.com/skyrim"; @@ -139,7 +139,7 @@ int SkyrimInfo::getNexusModIDStatic() return 1334; } -bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) +bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const { static LPCWSTR profileFiles[] = { L"skyrim.ini", L"skyrimprefs.ini", L"loadorder.txt", nullptr }; @@ -157,5 +157,10 @@ bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPa return false; } +bool SkyrimInfo::isValidModURL(int modID, const std::wstring &url) const +{ + return GameInfo::isValidModURL(modID, url, L"http://skyrim.nexusmods.com"); +} + } // namespace MOShared diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h index 5951f910..e2766688 100644 --- a/src/shared/skyriminfo.h +++ b/src/shared/skyriminfo.h @@ -36,35 +36,37 @@ public: virtual ~SkyrimInfo() {} static std::wstring getRegPathStatic(); - virtual std::wstring getRegPath() { return getRegPathStatic(); } - virtual std::wstring getBinaryName() { return L"TESV.exe"; } + virtual std::wstring getRegPath() const { return getRegPathStatic(); } + virtual std::wstring getBinaryName() const { return L"TESV.exe"; } - virtual GameInfo::Type getType() { return TYPE_SKYRIM; } + virtual GameInfo::Type getType() const { return TYPE_SKYRIM; } virtual std::wstring getGameName() const { return L"Skyrim"; } virtual std::wstring getGameShortName() const { return L"Skyrim"; } virtual LoadOrderMechanism getLoadOrderMechanism() const; - virtual std::vector getDLCPlugins(); + virtual std::vector getDLCPlugins() const; - virtual std::vector getSavegameAttachmentExtensions(); + virtual std::vector getSavegameAttachmentExtensions() const; // file name of this games ini (no path) - virtual std::vector getIniFileNames(); + virtual std::vector getIniFileNames() const; - virtual std::wstring getReferenceDataFile(); + virtual std::wstring getReferenceDataFile() const; - virtual std::wstring getNexusPage(bool nmmScheme = true); + virtual std::wstring getNexusPage(bool nmmScheme = true) const; static std::wstring getNexusInfoUrlStatic(); - virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); } + virtual std::wstring getNexusInfoUrl() const { return getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); - virtual int getNexusModID() { return getNexusModIDStatic(); } + virtual int getNexusModID() const { return getNexusModIDStatic(); } static int getNexusGameIDStatic() { return 110; } - virtual int getNexusGameID() { return getNexusGameIDStatic(); } + virtual int getNexusGameID() const { return getNexusGameIDStatic(); } - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); + virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const; + + virtual bool isValidModURL(int modID, std::wstring const &url) const; private: -- cgit v1.3.1 From 2e78a381ea43533769690af0f58989bcbec36d8e Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Fri, 27 Nov 2015 08:13:21 +0000 Subject: Fix sconstruct issue from main line --- SConstruct | 4 ---- 1 file changed, 4 deletions(-) diff --git a/SConstruct b/SConstruct index eb3da432..5df6f96a 100644 --- a/SConstruct +++ b/SConstruct @@ -50,10 +50,6 @@ def setup_config_variables(): PathVariable.PathIsDir), PathVariable('SEVENZIPPATH', 'Path to 7zip sources', sevenzippath, PathVariable.PathIsDir), - PathVariable('ZLIBPATH', 'Path to zlib install', zlibpath, - PathVariable.PathIsDir). - PathVariable('ZLIBPATH', 'Path to zlib install', zlibpath, - PathVariable.PathIsDir), PathVariable('ZLIBPATH', 'Path to zlib install', zlibpath, PathVariable.PathIsDir) ) -- cgit v1.3.1 From 20442cd4de97db354b304903d616754e8015ce62 Mon Sep 17 00:00:00 2001 From: ReadmeCritic Date: Sun, 29 Nov 2015 19:55:03 -0800 Subject: Update README URLs based on HTTP redirects --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 5965614e..e985a130 100644 --- a/readme.md +++ b/readme.md @@ -20,7 +20,7 @@ Here is a complete list: * https://github.com/TanninOne/modorganizer-lootcli * https://github.com/TanninOne/modorganizer-helper * https://github.com/TanninOne/modorganizer-tool_nmmimport -* https://github.com/TanninOne/modorganizer-tool_pyniedit +* https://github.com/TanninOne/modorganizer-tool_configurator * https://github.com/TanninOne/modorganizer-tool_inieditor * https://github.com/TanninOne/modorganizer-preview_base * https://github.com/TanninOne/modorganizer-diagnose_basic -- cgit v1.3.1