From dfca8be71b15bc13997110cc8777dd5a84a1fde7 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 21 Sep 2013 19:25:33 +0200 Subject: - esp reader now handles invalid files more gracefully - files moved will now also be treated as "deleted" in the old location so a newly created file with that same name is not created in overwrite - introduced a mechanism by which MO can recognize if it crashed before when attempting to load a plugin. That plugin can be blacklisted so it doesn't get loaded again - plugins can now programaticaly change their settings - plugins can now store data persistently without exposing that data as settings - requesting an unset-setting from a plugin is no longer treated as a bug - clarified warning message for when files are in overwrite directory - the proxyPython plugin will now discover if python initialization crashed MO on a previous session and give the user a chance to fix it or disable the plugin - bugfix: GetModuleFileName modified the buffer past the zero termination. While this doesn't violate the API documentation it is different from the regular windows implementation - bugfix: proxy plugins couldn't access the parent widget - bugfix: when moving a file from overwrite to a mod the in-memory file structure wasn't updated - bugfix: name input dialog for profiles allowed names that weren't valid directory names - bugfix: profile dialog wasn't able to delete profiles if the name started or ended in whitespaces - bugfix: The name-cells for plugin settings could be changed (without effect) - removed a few obsolete files from the repository --- src/shared/skyriminfo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/shared/skyriminfo.cpp') diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 80cf4c80..8c6b79ef 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -33,8 +33,8 @@ along with Mod Organizer. If not, see . namespace MOShared { -SkyrimInfo::SkyrimInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory) - : GameInfo(omoDirectory, gameDirectory) +SkyrimInfo::SkyrimInfo(const std::wstring &moDirectory, const std::wstring &gameDirectory) + : GameInfo(moDirectory, gameDirectory) { identifyMyGamesDirectory(L"skyrim"); -- cgit v1.3.1 From 54c7131a5e2fa282369e25344ac190d51676c383 Mon Sep 17 00:00:00 2001 From: Tannin Date: Thu, 10 Oct 2013 19:32:01 +0200 Subject: - new toggle to display hidden downloads - hidden downloads can be un-hidden - the installation manager now more thoroughly cleans up the temporary directory after installation - added SkyrimLauncher.exe to the list of auto-detected executables - bugfix: shutting down MO while downloads where active in some occasions didn't work - bugfix: when canceling the only active download the taskbar icon didn't return to normal --- src/downloadlistwidget.cpp | 20 ++++++++--- src/downloadlistwidget.h | 2 ++ src/downloadlistwidgetcompact.cpp | 20 ++++++++--- src/downloadlistwidgetcompact.h | 2 ++ src/downloadmanager.cpp | 58 ++++++++++++++++++++++++++----- src/downloadmanager.h | 24 ++++++++++++- src/installationmanager.cpp | 72 +++++++++++++-------------------------- src/installationmanager.h | 5 +-- src/main.cpp | 5 ++- src/mainwindow.cpp | 6 ++++ src/mainwindow.h | 1 + src/mainwindow.ui | 9 ++++- src/moapplication.cpp | 6 +++- src/moapplication.h | 60 ++++++++++++++++---------------- src/shared/skyriminfo.cpp | 1 + src/version.rc | 4 +-- 16 files changed, 189 insertions(+), 106 deletions(-) (limited to 'src/shared/skyriminfo.cpp') diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index b657ca69..b4d40799 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -208,6 +208,11 @@ void DownloadListWidgetDelegate::issueRemoveFromView() emit removeDownload(m_ContextRow, false); } +void DownloadListWidgetDelegate::issueRestoreToView() +{ + emit restoreDownload(m_ContextRow); +} + void DownloadListWidgetDelegate::issueCancel() { emit cancelDownload(m_ContextRow); @@ -277,13 +282,18 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel * QMenu menu(m_View); m_ContextRow = qobject_cast(model)->mapToSource(index).row(); DownloadManager::DownloadState state = m_Manager->getState(m_ContextRow); + bool hidden = m_Manager->isHidden(m_ContextRow); if (state >= DownloadManager::STATE_READY) { menu.addAction(tr("Install"), this, SLOT(issueInstall())); if (m_Manager->isInfoIncomplete(m_ContextRow)) { menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfo())); } menu.addAction(tr("Delete"), this, SLOT(issueDelete())); - menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView())); + if (hidden) { + menu.addAction(tr("Un-Hide"), this, SLOT(issueRestoreToView())); + } else { + menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView())); + } } else if (state == DownloadManager::STATE_DOWNLOADING){ menu.addAction(tr("Cancel"), this, SLOT(issueCancel())); menu.addAction(tr("Pause"), this, SLOT(issuePause())); @@ -295,9 +305,11 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel * menu.addSeparator(); menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted())); menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll())); - menu.addSeparator(); - menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted())); - menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll())); + if (!hidden) { + menu.addSeparator(); + menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted())); + menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll())); + } menu.exec(mouseEvent->globalPos()); event->accept(); diff --git a/src/downloadlistwidget.h b/src/downloadlistwidget.h index c80200fb..f5bfdbaa 100644 --- a/src/downloadlistwidget.h +++ b/src/downloadlistwidget.h @@ -65,6 +65,7 @@ signals: void installDownload(int index); void queryInfo(int index); void removeDownload(int index, bool deleteFile); + void restoreDownload(int index); void cancelDownload(int index); void pauseDownload(int index); void resumeDownload(int index); @@ -83,6 +84,7 @@ private slots: void issueInstall(); void issueDelete(); void issueRemoveFromView(); + void issueRestoreToView(); void issueCancel(); void issuePause(); void issueResume(); diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index 8dd6e275..d2a71dd5 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -195,6 +195,11 @@ void DownloadListWidgetCompactDelegate::issueRemoveFromView() emit removeDownload(m_ContextIndex.row(), false); } +void DownloadListWidgetCompactDelegate::issueRestoreToView() +{ + emit restoreDownload(m_ContextIndex.row()); +} + void DownloadListWidgetCompactDelegate::issueCancel() { emit cancelDownload(m_ContextIndex.row()); @@ -265,13 +270,18 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem QMenu menu; m_ContextIndex = qobject_cast(model)->mapToSource(index); DownloadManager::DownloadState state = m_Manager->getState(m_ContextIndex.row()); + bool hidden = m_Manager->isHidden(m_ContextIndex.row()); if (state >= DownloadManager::STATE_READY) { menu.addAction(tr("Install"), this, SLOT(issueInstall())); if (m_Manager->isInfoIncomplete(m_ContextIndex.row())) { menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfo())); } menu.addAction(tr("Delete"), this, SLOT(issueDelete())); - menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView())); + if (hidden) { + menu.addAction(tr("Un-Hide"), this, SLOT(issueRestoreToView())); + } else { + menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView())); + } } else if (state == DownloadManager::STATE_DOWNLOADING){ menu.addAction(tr("Cancel"), this, SLOT(issueCancel())); menu.addAction(tr("Pause"), this, SLOT(issuePause())); @@ -283,9 +293,11 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem menu.addSeparator(); menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted())); menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll())); - menu.addSeparator(); - menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted())); - menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll())); + if (!hidden) { + menu.addSeparator(); + menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted())); + menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll())); + } menu.exec(mouseEvent->globalPos()); event->accept(); diff --git a/src/downloadlistwidgetcompact.h b/src/downloadlistwidgetcompact.h index 78f51840..05d00b9d 100644 --- a/src/downloadlistwidgetcompact.h +++ b/src/downloadlistwidgetcompact.h @@ -65,6 +65,7 @@ signals: void installDownload(int index); void queryInfo(int index); void removeDownload(int index, bool deleteFile); + void restoreDownload(int index); void cancelDownload(int index); void pauseDownload(int index); void resumeDownload(int index); @@ -83,6 +84,7 @@ private slots: void issueInstall(); void issueDelete(); void issueRemoveFromView(); + void issueRestoreToView(); void issueCancel(); void issuePause(); void issueResume(); diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index b965d598..eca5600a 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -69,14 +69,16 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const Ne return info; } -DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(const QString &filePath) +DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(const QString &filePath, bool showHidden) { DownloadInfo *info = new DownloadInfo; QString metaFileName = filePath + ".meta"; QSettings metaFile(metaFileName, QSettings::IniFormat); - if (metaFile.value("removed", false).toBool()) { + if (!showHidden && metaFile.value("removed", false).toBool()) { return NULL; + } else { + info->m_Hidden = metaFile.value("removed", false).toBool(); } QString fileName = QFileInfo(filePath).fileName(); @@ -151,7 +153,7 @@ QString DownloadManager::DownloadInfo::currentURL() DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent) - : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher() + : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false) { connect(&m_DirWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); } @@ -188,13 +190,15 @@ void DownloadManager::pauseAll() ::Sleep(100); bool done = false; + QTime startTime = QTime::currentTime(); // further loops: busy waiting for all downloads to complete. This could be neater... - while (!done) { + while (!done && (startTime.secsTo(QTime::currentTime()) < 5)) { QCoreApplication::processEvents(); done = true; foreach (DownloadInfo *info, m_ActiveDownloads) { if ((info->m_State < STATE_CANCELED) || - (info->m_State != STATE_FETCHINGFILEINFO) || (info->m_State != STATE_FETCHINGMODINFO)) { + (info->m_State == STATE_FETCHINGFILEINFO) || + (info->m_State == STATE_FETCHINGMODINFO)) { done = false; break; } @@ -230,6 +234,12 @@ void DownloadManager::setSupportedExtensions(const QStringList &extensions) refreshList(); } +void DownloadManager::setShowHidden(bool showHidden) +{ + m_ShowHidden = showHidden; + refreshList(); +} + void DownloadManager::refreshList() { // remove finished downloads @@ -267,7 +277,7 @@ void DownloadManager::refreshList() QString fileName = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + file; - DownloadInfo *info = DownloadInfo::createFromMeta(fileName); + DownloadInfo *info = DownloadInfo::createFromMeta(fileName, m_ShowHidden); if (info != NULL) { m_ActiveDownloads.push_front(info); } @@ -437,6 +447,22 @@ void DownloadManager::refreshAlphabeticalTranslation() } +void DownloadManager::restoreDownload(int index) +{ + if ((index < 0) || (index >= m_ActiveDownloads.size())) { + throw MyException(tr("invalid index")); + } + + DownloadInfo *download = m_ActiveDownloads.at(index); + download->m_Hidden = false; + + QString filePath = m_OutputDirectory + "/" + download->m_FileName; + + QSettings metaSettings(filePath.append(".meta"), QSettings::IniFormat); + metaSettings.setValue("removed", false); +} + + void DownloadManager::removeDownload(int index, bool deleteFile) { try { @@ -492,9 +518,13 @@ void DownloadManager::pauseDownload(int index) return; } - if (m_ActiveDownloads.at(index)->m_State == STATE_DOWNLOADING) { - setState(m_ActiveDownloads.at(index), STATE_PAUSING); - qDebug("pausing %d - %s", index, m_ActiveDownloads[index]->m_FileName.toUtf8().constData()); + DownloadInfo *info = m_ActiveDownloads.at(index); + + if (info->m_State == STATE_DOWNLOADING) { + setState(info, STATE_PAUSING); + qDebug("pausing %d - %s", index, info->m_FileName.toUtf8().constData()); + } else if ((info->m_State == STATE_FETCHINGMODINFO) || (info->m_State == STATE_FETCHINGFILEINFO)) { + setState(info, STATE_READY); } } @@ -671,6 +701,14 @@ int DownloadManager::getModID(int index) const return m_ActiveDownloads.at(index)->m_ModID; } +bool DownloadManager::isHidden(int index) const +{ + if ((index < 0) || (index >= m_ActiveDownloads.size())) { + throw MyException(tr("invalid index")); + } + return m_ActiveDownloads.at(index)->m_Hidden; +} + NexusInfo DownloadManager::getNexusInfo(int index) const { @@ -843,6 +881,7 @@ void DownloadManager::createMetaFile(DownloadInfo *info) metaFile.setValue("uninstalled", info->m_State == DownloadManager::STATE_UNINSTALLED); metaFile.setValue("paused", (info->m_State == DownloadManager::STATE_PAUSED) || (info->m_State == DownloadManager::STATE_ERROR)); + metaFile.setValue("removed", info->m_Hidden); // slightly hackish... for (int i = 0; i < m_ActiveDownloads.size(); ++i) { @@ -1110,6 +1149,7 @@ void DownloadManager::downloadFinished() QByteArray data = info->m_Reply->readAll(); info->m_Output.write(data); info->m_Output.close(); + TaskProgressManager::instance().forgetMe(info->m_TaskProgressId); bool error = false; diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 0d49aa35..a42ac073 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -102,8 +102,10 @@ private: NexusInfo m_NexusInfo; + bool m_Hidden; + static DownloadInfo *createNew(const NexusInfo &nexusInfo, int modID, int fileID, const QStringList &URLs); - static DownloadInfo *createFromMeta(const QString &filePath); + static DownloadInfo *createFromMeta(const QString &filePath, bool showHidden); /** * @brief rename the file @@ -168,6 +170,11 @@ public: */ void setSupportedExtensions(const QStringList &extensions); + /** + * @brief sets whether hidden files are to be shown after all + */ + void setShowHidden(bool showHidden); + /** * @brief download from an already open network connection * @@ -263,6 +270,13 @@ public: **/ int getModID(int index) const; + /** + * @brief determine if the specified file is supposed to be hidden + * @param index index of the file to look up + * @return true if the specified file is supposed to be hidden + */ + bool isHidden(int index) const; + /** * @brief retrieve all nexus info of the download specified by index * @@ -352,6 +366,12 @@ public slots: **/ void removeDownload(int index, bool deleteFile); + /** + * @brief restores the specified download to view (which was previously hidden + * @param index index of the download to restore + */ + void restoreDownload(int index); + /** * @brief cancel the specified download. This will lead to the corresponding file to be deleted * @@ -436,6 +456,8 @@ private: std::map m_DownloadFails; + bool m_ShowHidden; + }; #endif // DOWNLOADMANAGER_H diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 87efecf1..6b430eea 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -184,11 +184,7 @@ bool InstallationManager::unpackSingleFile(const QString &fileName) QString InstallationManager::extractFile(const QString &fileName) { if (unpackSingleFile(fileName)) { - QString tempFileName = QDir::tempPath().append("/").append(QFileInfo(fileName).fileName()); - - m_FilesToDelete.insert(tempFileName); - - return tempFileName; + return QDir::tempPath().append("/").append(QFileInfo(fileName).fileName()); } else { return QString(); } @@ -571,49 +567,36 @@ bool InstallationManager::doInstall(GuessedValue &modName, int modID, } -void InstallationManager::openFile(const QString &fileName) +bool InstallationManager::wasCancelled() { - unpackSingleFile(fileName); + return m_CurrentArchive->getLastError() == Archive::ERROR_EXTRACT_CANCELLED; +} - QString tempFileName = QDir::tempPath().append("/").append(QFileInfo(fileName).fileName()); - SHELLEXECUTEINFOW execInfo; - memset(&execInfo, 0, sizeof(SHELLEXECUTEINFOW)); - execInfo.cbSize = sizeof(SHELLEXECUTEINFOW); - execInfo.fMask = SEE_MASK_NOCLOSEPROCESS; - execInfo.lpVerb = L"open"; - std::wstring fileNameW = ToWString(tempFileName); - execInfo.lpFile = fileNameW.c_str(); - execInfo.nShow = SW_SHOWNORMAL; - if (!::ShellExecuteExW(&execInfo)) { - qCritical("failed to spawn %s: %d", tempFileName.toUtf8().constData(), ::GetLastError()); - } +void InstallationManager::postInstallCleanup() const +{ + m_CurrentArchive->close(); - m_FilesToDelete.insert(tempFileName); -} + // 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 { + if (LHS.size() != RHS.size()) return LHS.size() > RHS.size(); + else return LHS < RHS; + }; + std::set> directoriesToRemove(longestFirst); -// copy and pasted from mo_dll -bool EndsWith(LPCWSTR string, LPCWSTR subString) -{ - size_t slen = wcslen(string); - size_t len = wcslen(subString); - if (slen < len) { - return false; + // clean up temp files + // TODO: this doesn't yet remove directories. Also, the files may be left there if this point isn't reached + foreach (const QString &tempFile, m_TempFilesToDelete) { + QFileInfo fileInfo(QDir::tempPath() + "/" + tempFile); + QFile::remove(fileInfo.absoluteFilePath()); + directoriesToRemove.insert(fileInfo.absolutePath()); } - for (size_t i = 0; i < len; ++i) { - if (towlower(string[slen - len + i]) != towlower(subString[i])) { - return false; - } + // try to delete each directory we had temporary files in. the call fails for non-empty directories which is ok + foreach (const QString &dir, directoriesToRemove) { + QDir().rmdir(dir); } - return true; -} - - -bool InstallationManager::wasCancelled() -{ - return m_CurrentArchive->getLastError() == Archive::ERROR_EXTRACT_CANCELLED; } @@ -675,9 +658,7 @@ bool InstallationManager::install(const QString &fileName, GuessedValue bool archiveOpen = m_CurrentArchive->open(ToWString(QDir::toNativeSeparators(fileName)).c_str(), new MethodCallback(this, &InstallationManager::queryPassword)); - ON_BLOCK_EXIT([this] { - this->m_CurrentArchive->close(); - }); + ON_BLOCK_EXIT(std::bind(&InstallationManager::postInstallCleanup, this)); QScopedPointer filesTree(archiveOpen ? createFilesTree() : NULL); IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED; @@ -729,13 +710,6 @@ bool InstallationManager::install(const QString &fileName, GuessedValue qPrintable(installer->name()), e.what()); } - // clean up temp files - // TODO: this doesn't yet remove directories. Also, the files may be left there if this point isn't reached - foreach (const QString &tempFile, m_TempFilesToDelete) { - QFile::remove(QDir::tempPath() + "/" + tempFile); - } - - // act upon the installation result. at this point the files have already been // extracted to the correct location switch (installResult) { diff --git a/src/installationmanager.h b/src/installationmanager.h index 0e43a15d..1c6f9f19 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -168,9 +168,7 @@ private: bool ensureValidModName(MOBase::GuessedValue &name) const; -private slots: - - void openFile(const QString &fileName); + void postInstallCleanup() const; private: @@ -202,7 +200,6 @@ private: QProgressDialog m_InstallationProgress; - std::set m_FilesToDelete; std::set m_TempFilesToDelete; }; diff --git a/src/main.cpp b/src/main.cpp index c4431ac4..7c2a9b0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -433,7 +433,10 @@ int main(int argc, char *argv[]) qDebug("initializing tutorials"); TutorialManager::init(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getTutorialDir())).append("/")); - application.setStyleFile(settings.value("Settings/style", "").toString()); + if (!application.setStyleFile(settings.value("Settings/style", "").toString())) { + // disable invalid stylesheet + settings.setValue("Settings/style", ""); + } int res = 1; { // scope to control lifetime of mainwindow diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 57201db9..33dc75ed 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4265,6 +4265,7 @@ void MainWindow::updateDownloadListDelegate() connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), this, SLOT(installDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(queryInfo(int)), &m_DownloadManager, SLOT(queryInfo(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(removeDownload(int, bool)), &m_DownloadManager, SLOT(removeDownload(int, bool))); + connect(ui->downloadView->itemDelegate(), SIGNAL(restoreDownload(int)), &m_DownloadManager, SLOT(restoreDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(cancelDownload(int)), &m_DownloadManager, SLOT(cancelDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(pauseDownload(int)), &m_DownloadManager, SLOT(pauseDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(resumeDownload(int)), &m_DownloadManager, SLOT(resumeDownload(int))); @@ -4735,3 +4736,8 @@ void MainWindow::on_linkButton_pressed() ui->linkButton->menu()->actions().at(1)->setIcon(linkDesktopFile.exists() ? removeIcon : addIcon); ui->linkButton->menu()->actions().at(2)->setIcon(linkMenuFile.exists() ? removeIcon : addIcon); } + +void MainWindow::on_showHiddenBox_toggled(bool checked) +{ + m_DownloadManager.setShowHidden(checked); +} diff --git a/src/mainwindow.h b/src/mainwindow.h index dfec5f49..ad4ed82e 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -502,6 +502,7 @@ private slots: // ui slots void on_groupCombo_currentIndexChanged(int index); void on_categoriesList_itemSelectionChanged(); void on_linkButton_pressed(); + void on_showHiddenBox_toggled(bool checked); }; #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 923df17c..b29136ff 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -968,7 +968,7 @@ p, li { white-space: pre-wrap; } - + @@ -976,6 +976,13 @@ p, li { white-space: pre-wrap; } + + + + Show Hidden + + + diff --git a/src/moapplication.cpp b/src/moapplication.cpp index efb0b615..ac660c6c 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -32,7 +32,7 @@ MOApplication::MOApplication(int argc, char **argv) } -void MOApplication::setStyleFile(const QString &styleName) +bool MOApplication::setStyleFile(const QString &styleName) { // remove all files from watch QStringList currentWatch = m_StyleWatcher.files(); @@ -42,11 +42,15 @@ void MOApplication::setStyleFile(const QString &styleName) // set new stylesheet or clear it if (styleName.length() != 0) { QString styleSheetName = applicationDirPath() + "/" + MOBase::ToQString(AppConfig::stylesheetsPath()) + "/" + styleName; + if (!QFile::exists(styleSheetName)) { + return false; + } m_StyleWatcher.addPath(styleSheetName); updateStyle(styleSheetName); } else { setStyleSheet(""); } + return true; } diff --git a/src/moapplication.h b/src/moapplication.h index 3d74031d..dd7f5eab 100644 --- a/src/moapplication.h +++ b/src/moapplication.h @@ -17,33 +17,33 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#ifndef MOAPPLICATION_H -#define MOAPPLICATION_H - -#include -#include - - -class MOApplication : public QApplication { -Q_OBJECT -public: - - MOApplication(int argc, char **argv); - - virtual bool notify (QObject *receiver, QEvent *event); - -public slots: - - void setStyleFile(const QString &style); - -private slots: - - void updateStyle(const QString &fileName); - -private: - - QFileSystemWatcher m_StyleWatcher; -}; - - -#endif // MOAPPLICATION_H +#ifndef MOAPPLICATION_H +#define MOAPPLICATION_H + +#include +#include + + +class MOApplication : public QApplication { +Q_OBJECT +public: + + MOApplication(int argc, char **argv); + + virtual bool notify (QObject *receiver, QEvent *event); + +public slots: + + bool setStyleFile(const QString &style); + +private slots: + + void updateStyle(const QString &fileName); + +private: + + QFileSystemWatcher m_StyleWatcher; +}; + + +#endif // MOAPPLICATION_H diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 8c6b79ef..71d791f5 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -295,6 +295,7 @@ std::vector SkyrimInfo::getExecutables() result.push_back(ExecutableInfo(L"SKSE", L"skse_loader.exe", L"", L"", DEFAULT_CLOSE)); result.push_back(ExecutableInfo(L"SBW", L"SBW.exe", L"", L"", DEFAULT_CLOSE)); result.push_back(ExecutableInfo(L"Skyrim", L"TESV.exe", L"", L"", DEFAULT_CLOSE)); + result.push_back(ExecutableInfo(L"Skyrim Launcher", L"SkyrimLauncher.exe", L"", L"", DEFAULT_CLOSE)); result.push_back(ExecutableInfo(L"BOSS", L"BOSS/BOSS.exe", L"", L"", DEFAULT_STAY)); result.push_back(ExecutableInfo(L"Creation Kit", L"CreationKit.exe", L"", L"", DEFAULT_STAY, L"202480")); diff --git a/src/version.rc b/src/version.rc index 16669011..e71bc1b5 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 1,0,4,0 -#define VER_FILEVERSION_STR "1,0,4,0\0" +#define VER_FILEVERSION 1,0,5,0 +#define VER_FILEVERSION_STR "1,0,5,0\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION -- cgit v1.3.1 From 8c0b309bdee2527fab66b00b34171ef18f5055bf Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 25 Oct 2013 14:49:42 +0200 Subject: - Option to choose edition of the game. Currently only relevant for FO3 because FO3 GOTY is its own app - applications that aren't in the executables list can again be started from the command line - references to missing categories are now removed from mods - bugfix: integrated fomod installer didn't update description and picture on some constellations of Windows version and theme --- src/ModOrganizer.pro | 20 ++++++++++---------- src/main.cpp | 21 +++++++++++++++++++++ src/mainwindow.cpp | 2 +- src/modinfo.cpp | 1 - src/modlist.cpp | 14 ++++++++++---- src/settings.cpp | 2 +- src/shared/fallout3info.cpp | 11 +++++++++-- src/shared/fallout3info.h | 3 ++- src/shared/falloutnvinfo.cpp | 2 +- src/shared/falloutnvinfo.h | 2 +- src/shared/gameinfo.cpp | 6 ++++++ src/shared/gameinfo.h | 3 ++- src/shared/oblivioninfo.cpp | 2 +- src/shared/oblivioninfo.h | 2 +- src/shared/skyriminfo.cpp | 2 +- src/shared/skyriminfo.h | 2 +- src/version.rc | 4 ++-- 17 files changed, 70 insertions(+), 29 deletions(-) (limited to 'src/shared/skyriminfo.cpp') diff --git a/src/ModOrganizer.pro b/src/ModOrganizer.pro index c8bfe6df..f35958f8 100644 --- a/src/ModOrganizer.pro +++ b/src/ModOrganizer.pro @@ -3,21 +3,21 @@ CONFIG += ordered SUBDIRS = bsatk \ - shared \ - uibase \ - organizer \ - hookdll \ - archive \ - helper \ + shared \ + uibase \ + organizer \ + hookdll \ + archive \ + helper \ plugins \ proxydll \ nxmhandler \ - BossDummy \ - pythonRunner \ - esptk + BossDummy \ + pythonRunner \ + esptk hookdll.depends = shared -organizer.depends = shared, uibase, plugins +organizer.depends = shared uibase plugins CONFIG(debug, debug|release) { DESTDIR = outputd diff --git a/src/main.cpp b/src/main.cpp index 59a4e837..90cf9e0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -400,6 +400,27 @@ int main(int argc, char *argv[]) settings.setValue("gamePath", gamePath.toUtf8().constData()); } + int edition = 0; + if (settings.contains("game_edition")) { + edition = settings.value("game_edition").toInt(); + } else { + std::vector editions = GameInfo::instance().getSteamVariants(); + if (editions.size() < 2) { + edition = 0; + } else { + SelectionDialog selection(QObject::tr("Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!)"), NULL); + int index = 0; + for (auto iter = editions.begin(); iter != editions.end(); ++iter) { + selection.addChoice(ToQString(*iter), "", index++); + } + if (selection.exec() == QDialog::Rejected) { + return -1; + } else { + settings.setValue("game_edition", selection.getChoiceData().toInt()); + } + } + } + qDebug("managing game at %s", qPrintable(QDir::toNativeSeparators(gamePath))); ExecutablesList executablesList; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 14bb3b59..cf8e355f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2134,7 +2134,7 @@ HANDLE MainWindow::startApplication(const QString &executable, const QStringList } } catch (const std::runtime_error&) { qWarning("\"%s\" not set up as executable", executable.toUtf8().constData()); - return INVALID_HANDLE_VALUE; + binary = QFileInfo(executable); } } diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 8d672675..faf8c95b 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -560,7 +560,6 @@ void ModInfoRegular::addNexusCategory(int categoryID) m_Categories.insert(CategoryFactory::instance().resolveNexusID(categoryID)); } - void ModInfoRegular::setIsEndorsed(bool endorsed) { if (m_EndorsedState != ENDORSED_NEVER) { diff --git a/src/modlist.cpp b/src/modlist.cpp index 18bbfd5a..0fdcf6fa 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -169,10 +169,16 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const int category = modInfo->getPrimaryCategory(); if (category != -1) { CategoryFactory &categoryFactory = CategoryFactory::instance(); - try { - return categoryFactory.getCategoryName(categoryFactory.getCategoryIndex(category)); - } catch (const std::exception &e) { - qCritical("failed to retrieve category name: %s", e.what()); + if (categoryFactory.categoryExists(category)) { + try { + return categoryFactory.getCategoryName(categoryFactory.getCategoryIndex(category)); + } catch (const std::exception &e) { + qCritical("failed to retrieve category name: %s", e.what()); + return QString(); + } + } else { + qWarning("category %d doesn't exist (may have been removed)", category); + modInfo->setCategory(category, false); return QString(); } } else { diff --git a/src/settings.cpp b/src/settings.cpp index 4f301b0c..36a4f1f8 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -159,7 +159,7 @@ bool Settings::automaticLoginEnabled() const QString Settings::getSteamAppID() const { - return m_Settings.value("Settings/app_id", ToQString(GameInfo::instance().getSteamAPPId())).toString(); + return m_Settings.value("Settings/app_id", ToQString(GameInfo::instance().getSteamAPPId(m_Settings.value("game_edition", 0).toInt()))).toString(); } QString Settings::getDownloadDirectory() const diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index 79dd90c9..c673fa1b 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -133,10 +133,17 @@ std::wstring Fallout3Info::getOMODExt() return L"fomod"; } +std::vector Fallout3Info::getSteamVariants() const +{ + return boost::assign::list_of(L"Regular")(L"Game Of The Year"); +} -std::wstring Fallout3Info::getSteamAPPId() +std::wstring Fallout3Info::getSteamAPPId(int variant) const { - return L"22300"; + switch (variant) { + case 1: return L"22370"; + default: return L"22300"; + } } diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h index 95a76471..0fa97d41 100644 --- a/src/shared/fallout3info.h +++ b/src/shared/fallout3info.h @@ -66,7 +66,8 @@ public: virtual std::wstring getReferenceDataFile(); virtual std::wstring getOMODExt(); - virtual std::wstring getSteamAPPId(); + virtual std::vector getSteamVariants() const; + virtual std::wstring getSteamAPPId(int variant = 0) const; virtual std::wstring getSEName(); diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index 6da454ee..7d7f0098 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -136,7 +136,7 @@ std::wstring FalloutNVInfo::getOMODExt() } -std::wstring FalloutNVInfo::getSteamAPPId() +std::wstring FalloutNVInfo::getSteamAPPId(int) const { return L"22380"; } diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h index 2f6cba7b..3960c951 100644 --- a/src/shared/falloutnvinfo.h +++ b/src/shared/falloutnvinfo.h @@ -68,7 +68,7 @@ public: virtual std::wstring getReferenceDataFile(); virtual std::wstring getOMODExt(); - virtual std::wstring getSteamAPPId(); + virtual std::wstring getSteamAPPId(int variant = 0) const; virtual std::wstring getSEName(); diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index f74e52b4..00bb42fd 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -30,6 +30,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include namespace MOShared { @@ -184,6 +185,11 @@ bool GameInfo::requiresSteam() const return FileExists(getGameDirectory().append(L"\\steam_api.dll")); } +std::vector GameInfo::getSteamVariants() const +{ + return boost::assign::list_of(L"Regular"); +} + std::wstring GameInfo::getLocalAppFolder() const { diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index 14f52f05..0221dd1b 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -137,7 +137,8 @@ public: virtual std::wstring getOMODExt() = 0; - virtual std::wstring getSteamAPPId() = 0; + virtual std::vector getSteamVariants() const; + virtual std::wstring getSteamAPPId(int variant = 0) const = 0; virtual std::wstring getSEName() = 0; diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index 7c6d5cbb..b3e65e59 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -243,7 +243,7 @@ std::wstring OblivionInfo::getOMODExt() } -std::wstring OblivionInfo::getSteamAPPId() +std::wstring OblivionInfo::getSteamAPPId(int) const { return L"22330"; } diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h index 13374aa3..6a9f56ca 100644 --- a/src/shared/oblivioninfo.h +++ b/src/shared/oblivioninfo.h @@ -64,7 +64,7 @@ public: virtual std::wstring getReferenceDataFile(); virtual std::wstring getOMODExt(); - virtual std::wstring getSteamAPPId(); + virtual std::wstring getSteamAPPId(int variant = 0) const; virtual std::wstring getSEName(); diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 71d791f5..0a0dd98d 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -169,7 +169,7 @@ std::wstring SkyrimInfo::getOMODExt() } -std::wstring SkyrimInfo::getSteamAPPId() +std::wstring SkyrimInfo::getSteamAPPId(int) const { return L"72850"; } diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h index c5d31ff0..ae5ab81f 100644 --- a/src/shared/skyriminfo.h +++ b/src/shared/skyriminfo.h @@ -71,7 +71,7 @@ public: virtual std::wstring getReferenceDataFile(); virtual std::wstring getOMODExt(); - virtual std::wstring getSteamAPPId(); + virtual std::wstring getSteamAPPId(int variant = 0) const; virtual std::wstring getSEName(); diff --git a/src/version.rc b/src/version.rc index e71bc1b5..55de3798 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 1,0,5,0 -#define VER_FILEVERSION_STR "1,0,5,0\0" +#define VER_FILEVERSION 1,0,7,0 +#define VER_FILEVERSION_STR "1,0,7,0\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION -- cgit v1.3.1 From 469cc2d945afebb1291a825339642b5e95f0e223 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 7 Dec 2013 12:48:18 +0100 Subject: - download manager now saves the file times on nexus, for potential later use in version check - nexus interface now supports 301 redirects - now using the new nexus url format - bugfix: "visit on nexus" used an outdated url scheme and thus caused unnecessary redirection --- src/downloadmanager.cpp | 26 ++++++++++++++++--- src/downloadmanager.h | 5 ++++ src/mainwindow.cpp | 2 +- src/nexusinterface.cpp | 59 ++++++++++++++++++++++++++------------------ src/nexusinterface.h | 7 +++--- src/shared/fallout3info.cpp | 4 +-- src/shared/fallout3info.h | 1 + src/shared/falloutnvinfo.cpp | 4 +-- src/shared/falloutnvinfo.h | 1 + src/shared/gameinfo.h | 1 + src/shared/oblivioninfo.cpp | 4 +-- src/shared/oblivioninfo.h | 1 + src/shared/skyriminfo.cpp | 4 +-- src/shared/skyriminfo.h | 1 + 14 files changed, 80 insertions(+), 40 deletions(-) (limited to 'src/shared/skyriminfo.cpp') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 779b052c..48484d24 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -153,7 +153,8 @@ QString DownloadManager::DownloadInfo::currentURL() DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent) - : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false) + : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false), + m_DateExpression("/Date\\((\\d+)\\)/") { connect(&m_DirWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); } @@ -851,7 +852,6 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) setState(info, STATE_PAUSED); } else { if (bytesTotal > info->m_TotalSize) { - qDebug("file size %s: %lld", qPrintable(info->m_FileName), bytesTotal); info->m_TotalSize = bytesTotal; } int oldProgress = info->m_Progress; @@ -883,6 +883,7 @@ void DownloadManager::createMetaFile(DownloadInfo *info) metaFile.setValue("name", info->m_NexusInfo.m_Name); metaFile.setValue("modName", info->m_NexusInfo.m_ModName); metaFile.setValue("version", info->m_NexusInfo.m_Version); + metaFile.setValue("fileTime", info->m_NexusInfo.m_FileTime); metaFile.setValue("fileCategory", info->m_NexusInfo.m_FileCategory); metaFile.setValue("newestVersion", info->m_NexusInfo.m_NewestVersion); metaFile.setValue("category", info->m_NexusInfo.m_Category); @@ -914,11 +915,9 @@ void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant r DownloadInfo *info = downloadInfoByID(userData.toInt()); if (info == NULL) return; - info->m_NexusInfo.m_Category = result["category_id"].toInt(); info->m_NexusInfo.m_ModName = result["name"].toString().trimmed(); info->m_NexusInfo.m_NewestVersion = result["version"].toString(); - if (info->m_FileID != 0) { setState(info, STATE_READY); } else { @@ -927,6 +926,17 @@ void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant r } +QDateTime DownloadManager::matchDate(const QString &timeString) +{ + if (m_DateExpression.exactMatch(timeString)) { + return QDateTime::fromMSecsSinceEpoch(m_DateExpression.cap(1).toLongLong()); + } else { + qWarning("date not matched: %s", qPrintable(timeString)); + return QDateTime::currentDateTime(); + } +} + + void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultData, int requestID) { std::set::iterator idIter = m_RequestIDs.find(requestID); @@ -960,7 +970,11 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD (fileNameVariant == info->m_FileName) || (fileNameVariant == alternativeLocalName)) { info->m_NexusInfo.m_Name = fileInfo["name"].toString(); info->m_NexusInfo.m_Version = fileInfo["version"].toString(); + if (info->m_NexusInfo.m_Version.isEmpty()) { + info->m_NexusInfo.m_Version = info->m_NexusInfo.m_NewestVersion; + } info->m_NexusInfo.m_FileCategory = fileInfo["category_id"].toInt(); + info->m_NexusInfo.m_FileTime = matchDate(fileInfo["date"].toString()); info->m_FileID = fileInfo["id"].toInt(); found = true; break; @@ -1014,7 +1028,11 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant userD info.m_Name = result["name"].toString(); info.m_Version = result["version"].toString(); + if (info.m_Version.isEmpty()) { + info.m_Version = info.m_NewestVersion; + } info.m_FileName = result["uri"].toString(); + info.m_FileTime = matchDate(result["date"].toString()); if (userData.isValid()) { m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(info), userData.toString())); diff --git a/src/downloadmanager.h b/src/downloadmanager.h index e82ea064..099e6084 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -46,6 +46,7 @@ struct NexusInfo { QString m_NewestVersion; QString m_FileName; QVariantList m_DownloadMap; + QDateTime m_FileTime; bool m_Set; }; Q_DECLARE_METATYPE(NexusInfo) @@ -437,6 +438,8 @@ private: DownloadInfo *downloadInfoByID(unsigned int id); + QDateTime matchDate(const QString &timeString); + private: static const int AUTOMATIC_RETRIES = 3; @@ -458,6 +461,8 @@ private: bool m_ShowHidden; + QRegExp m_DateExpression; + }; #endif // DOWNLOADMANAGER_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d5330017..cca10279 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3214,7 +3214,7 @@ void MainWindow::visitOnNexus_clicked() { int modID = m_ModList.data(m_ModList.index(m_ContextRow, 0), Qt::UserRole).toInt(); if (modID > 0) { - nexusLinkActivated(QString("%1/downloads/file.php?id=%2").arg(ToQString(GameInfo::instance().getNexusPage())).arg(modID)); + nexusLinkActivated(QString("%1/mods/%2").arg(ToQString(GameInfo::instance().getNexusPage())).arg(modID)); } else { MessageDialog::showMessage(tr("Nexus ID for this Mod is unknown"), this); } diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 6a4ae046..6b5cf19c 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -380,29 +380,33 @@ void NexusInterface::nextRequest() info.m_Timeout->setInterval(60000); QString url; - switch (info.m_Type) { - case NXMRequestInfo::TYPE_DESCRIPTION: { - url = QString("%1/Mods/%2/").arg(info.m_URL).arg(info.m_ModID); - } break; - case NXMRequestInfo::TYPE_FILES: { - url = QString("%1/Files/indexfrommod/%2/").arg(info.m_URL).arg(info.m_ModID); - } break; - case NXMRequestInfo::TYPE_FILEINFO: { - url = QString("%1/Files/%2/").arg(info.m_URL).arg(info.m_FileID); - } break; - case NXMRequestInfo::TYPE_DOWNLOADURL: { - url = QString("%1/Files/download/%2").arg(info.m_URL).arg(info.m_FileID); - } break; - case NXMRequestInfo::TYPE_TOGGLEENDORSEMENT: { - url = QString("%1/Mods/toggleendorsement/%2?lvote=%3").arg(info.m_URL).arg(info.m_ModID).arg(!info.m_Endorse); - } break; - case NXMRequestInfo::TYPE_GETUPDATES: { - QString modIDList = VectorJoin(info.m_ModIDList, ","); - modIDList = "[" + modIDList + "]"; - url = QString("%1/Mods/GetUpdates?ModList=%2").arg(info.m_URL).arg(modIDList); - } break; + if (!info.m_Reroute) { + switch (info.m_Type) { + case NXMRequestInfo::TYPE_DESCRIPTION: { + url = QString("%1/Mods/%2/").arg(info.m_URL).arg(info.m_ModID); + } break; + case NXMRequestInfo::TYPE_FILES: { + url = QString("%1/Files/indexfrommod/%2/").arg(info.m_URL).arg(info.m_ModID); + } break; + case NXMRequestInfo::TYPE_FILEINFO: { + url = QString("%1/Files/%2/").arg(info.m_URL).arg(info.m_FileID); + } break; + case NXMRequestInfo::TYPE_DOWNLOADURL: { + url = QString("%1/Files/download/%2").arg(info.m_URL).arg(info.m_FileID); + } break; + case NXMRequestInfo::TYPE_TOGGLEENDORSEMENT: { + url = QString("%1/Mods/toggleendorsement/%2?lvote=%3").arg(info.m_URL).arg(info.m_ModID).arg(!info.m_Endorse); + } break; + case NXMRequestInfo::TYPE_GETUPDATES: { + QString modIDList = VectorJoin(info.m_ModIDList, ","); + modIDList = "[" + modIDList + "]"; + url = QString("%1/Mods/GetUpdates?ModList=%2").arg(info.m_URL).arg(modIDList); + } break; + } + url.append(QString("?game_id=%1").arg(GameInfo::instance().getNexusGameID())); + } else { + url = info.m_URL; } - QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml"); request.setRawHeader("User-Agent", @@ -433,13 +437,21 @@ void NexusInterface::requestFinished(std::list::iterator iter) qWarning("request failed: %s", reply->errorString().toUtf8().constData()); emit nxmRequestFailed(iter->m_ModID, iter->m_UserData, iter->m_ID, reply->errorString()); } else { + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 301) { + // redirect request, return request to queue + iter->m_URL = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString(); + iter->m_Reroute = true; + m_RequestQueue.enqueue(*iter); + nextRequest(); + return; + } QByteArray data = reply->readAll(); if (data.isNull() || data.isEmpty() || (strcmp(data.constData(), "null") == 0)) { QString nexusError(reply->rawHeader("NexusErrorInfo")); if (nexusError.length() == 0) { nexusError = tr("empty response"); } - emit nxmRequestFailed(iter->m_ModID, iter->m_UserData, iter->m_ID, nexusError); } else { bool ok; @@ -509,7 +521,6 @@ void NexusInterface::requestTimeout() qWarning("invalid sender type"); return; } - qWarning("request timeout"); for (std::list::iterator iter = m_ActiveRequest.begin(); iter != m_ActiveRequest.end(); ++iter) { if (iter->m_Timeout == timer) { // this abort causes a "request failed" which cleans up the rest diff --git a/src/nexusinterface.h b/src/nexusinterface.h index da5fe02a..e7a01b0f 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -270,18 +270,19 @@ private: QVariant m_UserData; QTimer *m_Timeout; QString m_URL; + bool m_Reroute; int m_ID; int m_Endorse; NXMRequestInfo(int modID, Type type, QVariant userData, const QString &url) : m_ModID(modID), m_FileID(0), m_Reply(NULL), m_Type(type), m_UserData(userData), - m_Timeout(NULL), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} + m_Timeout(NULL), m_Reroute(false), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} NXMRequestInfo(std::vector modIDList, Type type, QVariant userData, const QString &url) : m_ModID(-1), m_ModIDList(modIDList), m_FileID(0), m_Reply(NULL), m_Type(type), m_UserData(userData), - m_Timeout(NULL), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} + m_Timeout(NULL), m_Reroute(false), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} NXMRequestInfo(int modID, int fileID, Type type, QVariant userData, const QString &url) : m_ModID(modID), m_FileID(fileID), m_Reply(NULL), m_Type(type), m_UserData(userData), - m_Timeout(NULL), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} + m_Timeout(NULL), m_Reroute(false), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} private: static QAtomicInt s_NextID; diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index c673fa1b..864a67be 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -155,13 +155,13 @@ std::wstring Fallout3Info::getSEName() std::wstring Fallout3Info::getNexusPage() { - return L"http://fallout3.nexusmods.com"; + return L"http://www.nexusmods.com/fallout3"; } std::wstring Fallout3Info::getNexusInfoUrlStatic() { - return L"http://fallout3.nexusmods.com"; + return L"http://www.nexusmods.com/fallout3"; } diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h index 0fa97d41..4bcb9d37 100644 --- a/src/shared/fallout3info.h +++ b/src/shared/fallout3info.h @@ -76,6 +76,7 @@ public: virtual std::wstring getNexusInfoUrl() { return Fallout3Info::getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); virtual int getNexusModID() { return Fallout3Info::getNexusModIDStatic(); } + virtual int getNexusGameID() { return 120; } virtual void createProfile(const std::wstring &directory, bool useDefaults); virtual void repairProfile(const std::wstring &directory); diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index 7d7f0098..8df9607d 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -209,13 +209,13 @@ std::wstring FalloutNVInfo::getSEName() std::wstring FalloutNVInfo::getNexusPage() { - return L"http://newvegas.nexusmods.com"; + return L"http://www.nexusmods.com/newvegas"; } std::wstring FalloutNVInfo::getNexusInfoUrlStatic() { - return L"http://newvegas.nexusmods.com"; + return L"http://www.nexusmods.com/newvegas"; } diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h index 3960c951..e25d2e02 100644 --- a/src/shared/falloutnvinfo.h +++ b/src/shared/falloutnvinfo.h @@ -77,6 +77,7 @@ public: virtual std::wstring getNexusInfoUrl() { return FalloutNVInfo::getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); virtual int getNexusModID() { return FalloutNVInfo::getNexusModIDStatic(); } + virtual int getNexusGameID() { return 130; } virtual void createProfile(const std::wstring &directory, bool useDefaults); virtual void repairProfile(const std::wstring &directory); diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index 0221dd1b..d517fc1b 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -145,6 +145,7 @@ public: virtual std::wstring getNexusPage() = 0; virtual std::wstring getNexusInfoUrl() = 0; virtual int getNexusModID() = 0; + virtual int getNexusGameID() = 0; // clone relevant files to the specified directory virtual void createProfile(const std::wstring &directory, bool useDefaults) = 0; diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index b3e65e59..1438de0a 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -191,13 +191,13 @@ std::wstring OblivionInfo::getSEName() std::wstring OblivionInfo::getNexusPage() { - return L"http://oblivion.nexusmods.com"; + return L"http://www.nexusmods.com/oblivion"; } std::wstring OblivionInfo::getNexusInfoUrlStatic() { - return L"http://oblivion.nexusmods.com"; + return L"http://www.nexusmods.com/oblivion"; } diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h index 6a9f56ca..dfa53575 100644 --- a/src/shared/oblivioninfo.h +++ b/src/shared/oblivioninfo.h @@ -73,6 +73,7 @@ public: virtual std::wstring getNexusInfoUrl() { return OblivionInfo::getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); virtual int getNexusModID() { return OblivionInfo::getNexusModIDStatic(); } + virtual int getNexusGameID() { return 101; } virtual void createProfile(const std::wstring &directory, bool useDefaults); virtual void repairProfile(const std::wstring &directory); diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 0a0dd98d..a8b9a433 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -183,13 +183,13 @@ std::wstring SkyrimInfo::getSEName() std::wstring SkyrimInfo::getNexusPage() { - return L"http://skyrim.nexusmods.com"; + return L"http://www.nexusmods.com/skyrim"; } std::wstring SkyrimInfo::getNexusInfoUrlStatic() { - return L"http://skyrim.nexusmods.com"; + return L"http://www.nexusmods.com/skyrim"; } diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h index ae5ab81f..7da523a1 100644 --- a/src/shared/skyriminfo.h +++ b/src/shared/skyriminfo.h @@ -81,6 +81,7 @@ public: virtual std::wstring getNexusInfoUrl() { return SkyrimInfo::getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); virtual int getNexusModID() { return SkyrimInfo::getNexusModIDStatic(); } + virtual int getNexusGameID() { return 110; } virtual void createProfile(const std::wstring &directory, bool useDefaults); virtual void repairProfile(const std::wstring &directory); -- cgit v1.3.1 From 340edcfbcab81ad7b2fac03cb724fb4d947d0d1d Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 7 Dec 2013 17:13:36 +0100 Subject: - returned to default branch - nexus interface will now use the nmm url - nmm importer no longer cancels on a missing non-essential flag --- src/shared/fallout3info.cpp | 4 ++-- src/shared/falloutnvinfo.cpp | 4 ++-- src/shared/oblivioninfo.cpp | 4 ++-- src/shared/skyriminfo.cpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/shared/skyriminfo.cpp') diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index 864a67be..cc90d6fa 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -155,13 +155,13 @@ std::wstring Fallout3Info::getSEName() std::wstring Fallout3Info::getNexusPage() { - return L"http://www.nexusmods.com/fallout3"; + return L"http://nmm.nexusmods.com/fallout3"; } std::wstring Fallout3Info::getNexusInfoUrlStatic() { - return L"http://www.nexusmods.com/fallout3"; + return L"http://nmm.nexusmods.com/fallout3"; } diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index 8df9607d..abff9323 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -209,13 +209,13 @@ std::wstring FalloutNVInfo::getSEName() std::wstring FalloutNVInfo::getNexusPage() { - return L"http://www.nexusmods.com/newvegas"; + return L"http://nmm.nexusmods.com/newvegas"; } std::wstring FalloutNVInfo::getNexusInfoUrlStatic() { - return L"http://www.nexusmods.com/newvegas"; + return L"http://nmm.nexusmods.com/newvegas"; } diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index 1438de0a..c84f8b88 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -191,13 +191,13 @@ std::wstring OblivionInfo::getSEName() std::wstring OblivionInfo::getNexusPage() { - return L"http://www.nexusmods.com/oblivion"; + return L"http://nmm.nexusmods.com/oblivion"; } std::wstring OblivionInfo::getNexusInfoUrlStatic() { - return L"http://www.nexusmods.com/oblivion"; + return L"http://nmm.nexusmods.com/oblivion"; } diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index a8b9a433..38b2cf37 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -183,13 +183,13 @@ std::wstring SkyrimInfo::getSEName() std::wstring SkyrimInfo::getNexusPage() { - return L"http://www.nexusmods.com/skyrim"; + return L"http://nmm.nexusmods.com/skyrim"; } std::wstring SkyrimInfo::getNexusInfoUrlStatic() { - return L"http://www.nexusmods.com/skyrim"; + return L"http://nmm.nexusmods.com/skyrim"; } -- cgit v1.3.1 From 9ee90a35dc3ebdbc76b18cbeb72995345ee37052 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sun, 8 Dec 2013 14:16:25 +0100 Subject: - MO now applies a minimum to the nmm-compatibility field. - bugfix: "visit on nexus" directed the browser to the servers meant for nmm - bugfix: url for "check all for updates" and "enorse/unendorse" were not constructed correctly --- src/mainwindow.cpp | 2 +- src/nexusinterface.cpp | 5 ++++- src/settings.cpp | 13 +++++++++---- src/shared/fallout3info.cpp | 8 ++++++-- src/shared/fallout3info.h | 2 +- src/shared/falloutnvinfo.cpp | 8 ++++++-- src/shared/falloutnvinfo.h | 2 +- src/shared/gameinfo.h | 2 +- src/shared/oblivioninfo.cpp | 8 ++++++-- src/shared/oblivioninfo.h | 2 +- src/shared/skyriminfo.cpp | 8 ++++++-- src/shared/skyriminfo.h | 2 +- src/version.rc | 4 ++-- 13 files changed, 45 insertions(+), 21 deletions(-) (limited to 'src/shared/skyriminfo.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b435cda0..17f2de09 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3215,7 +3215,7 @@ void MainWindow::visitOnNexus_clicked() { int modID = m_ModList.data(m_ModList.index(m_ContextRow, 0), Qt::UserRole).toInt(); if (modID > 0) { - nexusLinkActivated(QString("%1/mods/%2").arg(ToQString(GameInfo::instance().getNexusPage())).arg(modID)); + nexusLinkActivated(QString("%1/mods/%2").arg(ToQString(GameInfo::instance().getNexusPage(false))).arg(modID)); } else { MessageDialog::showMessage(tr("Nexus ID for this Mod is unknown"), this); } diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 6b5cf19c..309915aa 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -381,6 +381,7 @@ void NexusInterface::nextRequest() QString url; if (!info.m_Reroute) { + bool hasParams = false; switch (info.m_Type) { case NXMRequestInfo::TYPE_DESCRIPTION: { url = QString("%1/Mods/%2/").arg(info.m_URL).arg(info.m_ModID); @@ -396,14 +397,16 @@ void NexusInterface::nextRequest() } break; case NXMRequestInfo::TYPE_TOGGLEENDORSEMENT: { url = QString("%1/Mods/toggleendorsement/%2?lvote=%3").arg(info.m_URL).arg(info.m_ModID).arg(!info.m_Endorse); + hasParams = true; } break; case NXMRequestInfo::TYPE_GETUPDATES: { QString modIDList = VectorJoin(info.m_ModIDList, ","); modIDList = "[" + modIDList + "]"; url = QString("%1/Mods/GetUpdates?ModList=%2").arg(info.m_URL).arg(modIDList); + hasParams = true; } break; } - url.append(QString("?game_id=%1").arg(GameInfo::instance().getNexusGameID())); + url.append(QString("%1game_id=%2").arg(hasParams ? '&' : '?').arg(GameInfo::instance().getNexusGameID())); } else { url = info.m_URL; } diff --git a/src/settings.cpp b/src/settings.cpp index 144049aa..8086672a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -214,7 +214,12 @@ QString Settings::getModDirectory() const QString Settings::getNMMVersion() const { - return m_Settings.value("Settings/nmm_version", "0.34.0").toString(); + static const QString MIN_NMM_VERSION = "0.46.0"; + QString result = m_Settings.value("Settings/nmm_version", MIN_NMM_VERSION).toString(); + if (VersionInfo(result) < VersionInfo(MIN_NMM_VERSION)) { + result = MIN_NMM_VERSION; + } + return result; } bool Settings::getNexusLogin(QString &username, QString &password) const @@ -583,9 +588,9 @@ void Settings::query(QWidget *parent) downloadDirEdit->setText(getDownloadDirectory()); modDirEdit->setText(getModDirectory()); cacheDirEdit->setText(getCacheDirectory()); - offlineBox->setChecked(m_Settings.value("Settings/offline_mode", false).toBool()); - proxyBox->setChecked(m_Settings.value("Settings/use_proxy", false).toBool()); - nmmVersionEdit->setText(m_Settings.value("Settings/nmm_version", "0.33.1").toString()); + offlineBox->setChecked(offlineMode()); + proxyBox->setChecked(useProxy()); + nmmVersionEdit->setText(getNMMVersion()); logLevelBox->setCurrentIndex(logLevel()); // display plugin settings diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index cc90d6fa..1808d1d9 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -153,9 +153,13 @@ std::wstring Fallout3Info::getSEName() } -std::wstring Fallout3Info::getNexusPage() +std::wstring Fallout3Info::getNexusPage(bool nmmScheme) { - return L"http://nmm.nexusmods.com/fallout3"; + if (nmmScheme) { + return L"http://nmm.nexusmods.com/fallout3"; + } else { + return L"http://www.nexusmods.com/fallout3"; + } } diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h index 4bcb9d37..9575103b 100644 --- a/src/shared/fallout3info.h +++ b/src/shared/fallout3info.h @@ -71,7 +71,7 @@ public: virtual std::wstring getSEName(); - virtual std::wstring getNexusPage(); + virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); virtual std::wstring getNexusInfoUrl() { return Fallout3Info::getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index abff9323..e7e747e2 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -207,9 +207,13 @@ std::wstring FalloutNVInfo::getSEName() } -std::wstring FalloutNVInfo::getNexusPage() +std::wstring FalloutNVInfo::getNexusPage(bool nmmScheme) { - return L"http://nmm.nexusmods.com/newvegas"; + if (nmmScheme) { + return L"http://nmm.nexusmods.com/newvegas"; + } else { + return L"http://www.nexusmods.com/newvegas"; + } } diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h index e25d2e02..8767fdb0 100644 --- a/src/shared/falloutnvinfo.h +++ b/src/shared/falloutnvinfo.h @@ -72,7 +72,7 @@ public: virtual std::wstring getSEName(); - virtual std::wstring getNexusPage(); + virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); virtual std::wstring getNexusInfoUrl() { return FalloutNVInfo::getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index d517fc1b..10775e6c 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -142,7 +142,7 @@ public: virtual std::wstring getSEName() = 0; - virtual std::wstring getNexusPage() = 0; + virtual std::wstring getNexusPage(bool nmmScheme = true) = 0; virtual std::wstring getNexusInfoUrl() = 0; virtual int getNexusModID() = 0; virtual int getNexusGameID() = 0; diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index c84f8b88..d8daa0f7 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -189,9 +189,13 @@ std::wstring OblivionInfo::getSEName() } -std::wstring OblivionInfo::getNexusPage() +std::wstring OblivionInfo::getNexusPage(bool nmmScheme) { - return L"http://nmm.nexusmods.com/oblivion"; + if (nmmScheme) { + return L"http://nmm.nexusmods.com/oblivion"; + } else { + return L"http://www.nexusmods.com/oblivion"; + } } diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h index dfa53575..ba27aa40 100644 --- a/src/shared/oblivioninfo.h +++ b/src/shared/oblivioninfo.h @@ -68,7 +68,7 @@ public: virtual std::wstring getSEName(); - virtual std::wstring getNexusPage(); + virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); virtual std::wstring getNexusInfoUrl() { return OblivionInfo::getNexusInfoUrlStatic(); } static int getNexusModIDStatic(); diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 38b2cf37..1620bcc3 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -181,9 +181,13 @@ std::wstring SkyrimInfo::getSEName() } -std::wstring SkyrimInfo::getNexusPage() +std::wstring SkyrimInfo::getNexusPage(bool nmmScheme) { - return L"http://nmm.nexusmods.com/skyrim"; + if (nmmScheme) { + return L"http://nmm.nexusmods.com/skyrim"; + } else { + return L"http://www.nexusmods.com/skyrim"; + } } diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h index 7da523a1..438beb41 100644 --- a/src/shared/skyriminfo.h +++ b/src/shared/skyriminfo.h @@ -75,7 +75,7 @@ public: virtual std::wstring getSEName(); - virtual std::wstring getNexusPage(); + virtual std::wstring getNexusPage(bool nmmScheme = true); static std::wstring getNexusInfoUrlStatic(); virtual std::wstring getNexusInfoUrl() { return SkyrimInfo::getNexusInfoUrlStatic(); } diff --git a/src/version.rc b/src/version.rc index b91dce85..811ee7ca 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 1,0,10,0 -#define VER_FILEVERSION_STR "1,0,10,0\0" +#define VER_FILEVERSION 1,0,11,0 +#define VER_FILEVERSION_STR "1,0,11,0\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION -- cgit v1.3.1