summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTannin <devnull@localhost>2013-03-13 19:35:20 +0100
committerTannin <devnull@localhost>2013-03-13 19:35:20 +0100
commitb3d0fcb2083143dc21c751adafd2523c3555e5d2 (patch)
tree6adeeedc26848af1d5dd96be17b4bca97ede085d /src
parent93e073c32445eefba5595123b28acf87d9f409e8 (diff)
- some more safety checks in the ini-limit removal code
- some code cleanup and minor bug fixes based on results from static code analysis - added naemfilter for the esp list - bsa changes are now stored automatically but delayed by up to 0.5 seconds (for performance reasons) - bugfix: buffer overrun when certain functions are called with empty file names - bugfix: reroute for the ini-limit fix was placed in the data segment - bugfix: mod list got mixed up when the mod directory was changed externally - bugfix: plugins.txt reroute on skyrim didn't work on winXP - bugfix: MO could become unresponsive if the tutorial script couldn't be interpreted
Diffstat (limited to 'src')
-rw-r--r--src/categoriesdialog.cpp3
-rw-r--r--src/loadmechanism.cpp3
-rw-r--r--src/mainwindow.cpp56
-rw-r--r--src/mainwindow.h7
-rw-r--r--src/mainwindow.ui21
-rw-r--r--src/modinfo.cpp3
-rw-r--r--src/modlist.cpp2
-rw-r--r--src/organizer.pro7
-rw-r--r--src/pluginlist.cpp2
-rw-r--r--src/pluginlistsortproxy.cpp262
-rw-r--r--src/pluginlistsortproxy.h101
-rw-r--r--src/profile.cpp21
-rw-r--r--src/profile.h584
-rw-r--r--src/profilesdialog.cpp2
-rw-r--r--src/shared/skyriminfo.cpp12
-rw-r--r--src/shared/skyriminfo.h4
-rw-r--r--src/shared/util.cpp7
-rw-r--r--src/shared/util.h3
-rw-r--r--src/version.rc4
19 files changed, 593 insertions, 511 deletions
diff --git a/src/categoriesdialog.cpp b/src/categoriesdialog.cpp
index 21e1a5aa..c7f03740 100644
--- a/src/categoriesdialog.cpp
+++ b/src/categoriesdialog.cpp
@@ -86,7 +86,8 @@ public:
{
QLineEdit *edit = qobject_cast<QLineEdit*>(editor);
int pos = 0;
- if (m_Validator->validate(edit->text(), pos) == QValidator::Acceptable) {
+ QString editText = edit->text();
+ if (m_Validator->validate(editText, pos) == QValidator::Acceptable) {
QItemDelegate::setModelData(editor, model, index);
}
}
diff --git a/src/loadmechanism.cpp b/src/loadmechanism.cpp
index 8eed460f..426b3763 100644
--- a/src/loadmechanism.cpp
+++ b/src/loadmechanism.cpp
@@ -169,7 +169,8 @@ void LoadMechanism::deactivateProxyDLL()
}
}
- removeHintFile(QDir(gameDirectory));
+ QDir dir(gameDirectory);
+ removeHintFile(dir);
} catch (const std::exception &e) {
QMessageBox::critical(NULL, QObject::tr("Failed to deactivate proxy-dll loading"), e.what());
}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 4e3fa296..2d00580e 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -202,6 +202,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(&m_ModList, SIGNAL(showMessage(QString)), this, SLOT(showMessage(QString)));
connect(&m_ModList, SIGNAL(modRenamed(QString,QString)), this, SLOT(modRenamed(QString,QString)));
connect(ui->modFilterEdit, SIGNAL(textChanged(QString)), m_ModListSortProxy, SLOT(updateFilter(QString)));
+ connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), m_PluginListSortProxy, SLOT(updateFilter(QString)));
connect(&m_ModList, SIGNAL(modlist_changed(int)), m_ModListSortProxy, SLOT(invalidate()));
connect(&m_ModList, SIGNAL(removeSelectedMods()), this, SLOT(removeMod_clicked()));
@@ -221,6 +222,9 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString)));
+ m_CheckBSATimer.setSingleShot(true);
+ connect(&m_CheckBSATimer, SIGNAL(timeout()), this, SLOT(checkBSAList()));
+
m_DirectoryRefresher.moveToThread(&m_RefresherThread);
m_RefresherThread.start();
@@ -478,6 +482,27 @@ void MainWindow::createHelpWidget()
}
+void MainWindow::saveArchiveList()
+{
+ if (m_ArchivesInit) {
+ QFile archiveFile(m_CurrentProfile->getArchivesFileName());
+ if (archiveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) {
+ QTreeWidgetItem *item = ui->bsaList->topLevelItem(i);
+ if ((item != NULL) && (item->checkState(0) == Qt::Checked)) {
+ archiveFile.write(item->text(0).toUtf8().append("\r\n"));
+ }
+ }
+ } else {
+ reportError(tr("failed to save archives order, do you have write access "
+ "to \"%1\"?").arg(m_CurrentProfile->getArchivesFileName()));
+ }
+ archiveFile.close();
+ } else {
+ qWarning("archive list not initialised");
+ }
+}
+
bool MainWindow::saveCurrentLists()
{
if (m_DirectoryUpdate) {
@@ -503,23 +528,6 @@ bool MainWindow::saveCurrentLists()
reportError(tr("failed to save load order: %1").arg(e.what()));
}
- if (m_ArchivesInit) {
- QFile archiveFile(m_CurrentProfile->getArchivesFileName());
- if (archiveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) {
- QTreeWidgetItem *item = ui->bsaList->topLevelItem(i);
- if ((item != NULL) && (item->checkState(0) == Qt::Checked)) {
- archiveFile.write(item->text(0).toUtf8().append("\r\n"));
- }
- }
- } else {
- reportError(tr("failed to save archives order, do you have write access "
- "to \"%1\"?").arg(m_CurrentProfile->getArchivesFileName()));
- }
- archiveFile.close();
- } else {
- qWarning("archive list not initialised");
- }
return true;
}
@@ -1059,6 +1067,9 @@ void MainWindow::startExeAction()
void MainWindow::refreshModList()
{
+ // don't lose changes!
+ m_CurrentProfile->writeModlistNow(true);
+
ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure);
m_CurrentProfile->refreshModStatus();
@@ -1102,7 +1113,7 @@ void MainWindow::setExecutableIndex(int index)
void MainWindow::activateSelectedProfile()
{
QString profileName = ui->profileBox->currentText();
- qDebug() << "activate profile " << profileName;
+ qDebug("activate profile \"%s\"", qPrintable(profileName));
QString profileDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()))
.append("/").append(profileName);
delete m_CurrentProfile;
@@ -1650,12 +1661,6 @@ void MainWindow::on_tabWidget_currentChanged(int index)
}
-static QString guessModName(const QString &fileName)
-{
- return QFileInfo(fileName).baseName();
-}
-
-
void MainWindow::installMod(const QString &fileName)
{
bool hasIniTweaks = false;
@@ -3831,7 +3836,8 @@ void MainWindow::on_bsaList_customContextMenuRequested(const QPoint &pos)
void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int)
{
- checkBSAList();
+ saveArchiveList();
+ m_CheckBSATimer.start(500);
}
void MainWindow::on_actionProblems_triggered()
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 26e4c9dc..075c6247 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -103,6 +103,7 @@ public:
void addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info);
+ void saveArchiveList();
public slots:
void displayColumnSelection(const QPoint &pos);
@@ -197,8 +198,6 @@ private:
bool extractProgress(QProgressDialog &extractProgress, int percentage, std::string fileName);
- void checkBSAList();
-
bool checkForProblems(QString &problemDescription);
int getBinaryExecuteInfo(const QFileInfo &targetInfo, QFileInfo &binaryInfo, QString &arguments);
@@ -277,6 +276,7 @@ private:
QStringList m_ActiveArchives;
bool m_DirectoryUpdate;
bool m_ArchivesInit;
+ QTimer m_CheckBSATimer;
QTime m_StartTime;
SaveGameInfoWidget *m_CurrentSaveView;
@@ -394,6 +394,8 @@ private slots:
void startExeAction();
+ void checkBSAList();
+
private slots: // ui slots
// actions
void on_actionAdd_Profile_triggered();
@@ -425,6 +427,7 @@ private slots: // ui slots
void on_espList_customContextMenuRequested(const QPoint &pos);
void on_displayCategoriesBtn_toggled(bool checked);
+
};
#endif // MAINWINDOW_H
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
index 3493b710..71f0e3c6 100644
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -596,9 +596,18 @@ p, li { white-space: pre-wrap; }
<string notr="true">ESPs</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
- <property name="margin">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
<number>6</number>
</property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
@@ -720,6 +729,16 @@ p, li { white-space: pre-wrap; }
</attribute>
</widget>
</item>
+ <item>
+ <widget class="MOBase::LineEditClear" name="espFilterEdit">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="placeholderText">
+ <string>Namefilter</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="bsaTab">
diff --git a/src/modinfo.cpp b/src/modinfo.cpp
index b8ef834f..80f27ccf 100644
--- a/src/modinfo.cpp
+++ b/src/modinfo.cpp
@@ -731,6 +731,9 @@ ModInfoRegular::EConflictType ModInfoRegular::isConflicted() const
overwritten = true;
break;
}
+ } else if (alternatives.size() == 1) {
+ // only alternative is data -> no conflict
+ regular = true;
}
}
}
diff --git a/src/modlist.cpp b/src/modlist.cpp
index e70e3c31..1f318e0d 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -145,7 +145,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
} else {
if ((m_Profile->getModPriority(modIndex) == 0) && (role == Qt::DisplayRole)) {
return tr("min");
- } else if ((m_Profile->getModPriority(modIndex) == m_Profile->numRegularMods() - 1) &&
+ } else if ((m_Profile->getModPriority(modIndex) == static_cast<int>(m_Profile->numRegularMods()) - 1) &&
(role == Qt::DisplayRole)) {
return tr("max");
} else {
diff --git a/src/organizer.pro b/src/organizer.pro
index 723c1e04..86e9c616 100644
--- a/src/organizer.pro
+++ b/src/organizer.pro
@@ -199,6 +199,11 @@ CONFIG(debug, debug|release) {
QMAKE_LFLAGS += /DEBUG
}
+QMAKE_CXXFLAGS_WARN_ON -= -W3
+QMAKE_CXXFLAGS_WARN_ON += -W4
+QMAKE_CXXFLAGS += -wd4127 -wd4512 -wd4189
+
+
CONFIG += embed_manifest_exe
# QMAKE_CXXFLAGS += /analyze
@@ -231,7 +236,7 @@ TRANSLATIONS = organizer_de.ts \
PRE_TARGETDEPS += compiler_TSQM_make_all
} else:message(No translation files in project)
-LIBS += -lmo_shared -luibase -lshell32 -lole32 -luser32 -ladvapi32 -lgdi32 -lPsapi -lVersion -lbsatk
+LIBS += -lmo_shared -luibase -lshell32 -lole32 -luser32 -ladvapi32 -lgdi32 -lPsapi -lVersion -lbsatk -lshlwapi
DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS NOMINMAX
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp
index 486d6f42..25dc69bd 100644
--- a/src/pluginlist.cpp
+++ b/src/pluginlist.cpp
@@ -602,7 +602,7 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const
case COL_PRIORITY: {
if (m_ESPs[index].m_Priority == 0) {
return tr("min");
- } else if (m_ESPs[index].m_Priority == m_ESPs.size() - 1) {
+ } else if (m_ESPs[index].m_Priority == static_cast<int>(m_ESPs.size()) - 1) {
return tr("max");
} else {
return QString::number(m_ESPs[index].m_Priority);
diff --git a/src/pluginlistsortproxy.cpp b/src/pluginlistsortproxy.cpp
index 788073ab..5bff24f5 100644
--- a/src/pluginlistsortproxy.cpp
+++ b/src/pluginlistsortproxy.cpp
@@ -17,127 +17,141 @@ You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "pluginlistsortproxy.h"
-#include "pluginlist.h"
-#include <QMenu>
-#include <QCheckBox>
-#include <QWidgetAction>
-
-
-PluginListSortProxy::PluginListSortProxy(QObject *parent)
- : QSortFilterProxyModel(parent),
- m_SortIndex(0), m_SortOrder(Qt::AscendingOrder)
-{
- m_EnabledColumns.set(PluginList::COL_NAME);
- m_EnabledColumns.set(PluginList::COL_PRIORITY);
- m_EnabledColumns.set(PluginList::COL_MODINDEX);
- this->setDynamicSortFilter(true);
-}
-
-
-unsigned int PluginListSortProxy::getEnabledColumns() const
-{
- unsigned int result = 0;
- for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) {
- if (m_EnabledColumns.test(i)) {
- result |= 1 << i;
- }
- }
- return result;
-}
-
-
-void PluginListSortProxy::setEnabledColumns(unsigned int columns)
-{
- emit layoutAboutToBeChanged();
- for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) {
- m_EnabledColumns.set(i, (columns & (1 << i)) != 0);
- }
- emit layoutChanged();
-}
-
-
-Qt::ItemFlags PluginListSortProxy::flags(const QModelIndex &modelIndex) const
-{
- Qt::ItemFlags flags = sourceModel()->flags(mapToSource(modelIndex));
- if (sortColumn() == PluginList::COL_NAME) {
- flags &= ~Qt::ItemIsDragEnabled;
- }
- return flags;
-}
-
-
-void PluginListSortProxy::displayColumnSelection(const QPoint &pos)
-{
- QMenu menu;
-
- for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) {
- QCheckBox *checkBox = new QCheckBox(&menu);
- checkBox->setText(PluginList::getColumnName(i));
- checkBox->setChecked(m_EnabledColumns.test(i) ? Qt::Checked : Qt::Unchecked);
- QWidgetAction *checkableAction = new QWidgetAction(&menu);
- checkableAction->setDefaultWidget(checkBox);
- menu.addAction(checkableAction);
- }
- menu.exec(pos);
- int i = 0;
-
- emit layoutAboutToBeChanged();
- m_EnabledColumns.reset();
- foreach (const QAction *action, menu.actions()) {
- const QWidgetAction *widgetAction = qobject_cast<const QWidgetAction*>(action);
- if (widgetAction != NULL) {
- const QCheckBox *checkBox = qobject_cast<const QCheckBox*>(widgetAction->defaultWidget());
- if (checkBox != NULL) {
- m_EnabledColumns.set(i, checkBox->checkState() == Qt::Checked);
- }
- }
- ++i;
- }
- emit layoutChanged();
-}
-
-
-bool PluginListSortProxy::lessThan(const QModelIndex &left,
- const QModelIndex &right) const
-{
- PluginList *plugins = qobject_cast<PluginList*>(sourceModel());
- switch (left.column()) {
- case PluginList::COL_NAME: {
- return QString::compare(plugins->getName(left.row()), plugins->getName(right.row()), Qt::CaseInsensitive) < 0;
- } break;
- case PluginList::COL_PRIORITY:
- case PluginList::COL_MODINDEX: {
- return plugins->getPriority(left.row()) < plugins->getPriority(right.row());
- } break;
- default: {
- static bool first = true;
- if (first) {
- qCritical("invalid sort column %d", left.column());
- first = false;
- }
- return plugins->getPriority(left.row()) < plugins->getPriority(right.row());
- } break;
- }
-}
-
-
-bool PluginListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action,
- int row, int column, const QModelIndex &parent)
-{
- if ((row == -1) && (column == -1)) {
- return this->sourceModel()->dropMimeData(data, action, -1, -1, mapToSource(parent));
- }
- // in the regular model, when dropping between rows, the row-value passed to
- // the sourceModel is inconsistent between ascending and descending ordering.
- // This should fix that
- if (sortOrder() == Qt::DescendingOrder) {
- --row;
- }
-
- QModelIndex proxyIndex = index(row, column, parent);
- QModelIndex sourceIndex = mapToSource(proxyIndex);
- return this->sourceModel()->dropMimeData(data, action, sourceIndex.row(), sourceIndex.column(),
- sourceIndex.parent());
-}
-
+#include "pluginlistsortproxy.h"
+#include "pluginlist.h"
+#include <QMenu>
+#include <QCheckBox>
+#include <QWidgetAction>
+
+
+PluginListSortProxy::PluginListSortProxy(QObject *parent)
+ : QSortFilterProxyModel(parent),
+ m_SortIndex(0), m_SortOrder(Qt::AscendingOrder)
+{
+ m_EnabledColumns.set(PluginList::COL_NAME);
+ m_EnabledColumns.set(PluginList::COL_PRIORITY);
+ m_EnabledColumns.set(PluginList::COL_MODINDEX);
+ this->setDynamicSortFilter(true);
+}
+
+
+unsigned int PluginListSortProxy::getEnabledColumns() const
+{
+ unsigned int result = 0;
+ for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) {
+ if (m_EnabledColumns.test(i)) {
+ result |= 1 << i;
+ }
+ }
+ return result;
+}
+
+
+void PluginListSortProxy::setEnabledColumns(unsigned int columns)
+{
+ emit layoutAboutToBeChanged();
+ for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) {
+ m_EnabledColumns.set(i, (columns & (1 << i)) != 0);
+ }
+ emit layoutChanged();
+}
+
+
+Qt::ItemFlags PluginListSortProxy::flags(const QModelIndex &modelIndex) const
+{
+ Qt::ItemFlags flags = sourceModel()->flags(mapToSource(modelIndex));
+ if (sortColumn() == PluginList::COL_NAME) {
+ flags &= ~Qt::ItemIsDragEnabled;
+ }
+ return flags;
+}
+
+
+void PluginListSortProxy::displayColumnSelection(const QPoint &pos)
+{
+ QMenu menu;
+
+ for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) {
+ QCheckBox *checkBox = new QCheckBox(&menu);
+ checkBox->setText(PluginList::getColumnName(i));
+ checkBox->setChecked(m_EnabledColumns.test(i) ? Qt::Checked : Qt::Unchecked);
+ QWidgetAction *checkableAction = new QWidgetAction(&menu);
+ checkableAction->setDefaultWidget(checkBox);
+ menu.addAction(checkableAction);
+ }
+ menu.exec(pos);
+ int i = 0;
+
+ emit layoutAboutToBeChanged();
+ m_EnabledColumns.reset();
+ foreach (const QAction *action, menu.actions()) {
+ const QWidgetAction *widgetAction = qobject_cast<const QWidgetAction*>(action);
+ if (widgetAction != NULL) {
+ const QCheckBox *checkBox = qobject_cast<const QCheckBox*>(widgetAction->defaultWidget());
+ if (checkBox != NULL) {
+ m_EnabledColumns.set(i, checkBox->checkState() == Qt::Checked);
+ }
+ }
+ ++i;
+ }
+ emit layoutChanged();
+}
+
+
+void PluginListSortProxy::updateFilter(const QString &filter)
+{
+ m_CurrentFilter = filter;
+ invalidateFilter();
+}
+
+
+bool PluginListSortProxy::filterAcceptsRow(int row, const QModelIndex&) const
+{
+ return m_CurrentFilter.isEmpty() ||
+ sourceModel()->data(sourceModel()->index(row, 0)).toString().contains(m_CurrentFilter, Qt::CaseInsensitive);
+}
+
+
+bool PluginListSortProxy::lessThan(const QModelIndex &left,
+ const QModelIndex &right) const
+{
+ PluginList *plugins = qobject_cast<PluginList*>(sourceModel());
+ switch (left.column()) {
+ case PluginList::COL_NAME: {
+ return QString::compare(plugins->getName(left.row()), plugins->getName(right.row()), Qt::CaseInsensitive) < 0;
+ } break;
+ case PluginList::COL_PRIORITY:
+ case PluginList::COL_MODINDEX: {
+ return plugins->getPriority(left.row()) < plugins->getPriority(right.row());
+ } break;
+ default: {
+ static bool first = true;
+ if (first) {
+ qCritical("invalid sort column %d", left.column());
+ first = false;
+ }
+ return plugins->getPriority(left.row()) < plugins->getPriority(right.row());
+ } break;
+ }
+}
+
+
+bool PluginListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent)
+{
+ if ((row == -1) && (column == -1)) {
+ return this->sourceModel()->dropMimeData(data, action, -1, -1, mapToSource(parent));
+ }
+ // in the regular model, when dropping between rows, the row-value passed to
+ // the sourceModel is inconsistent between ascending and descending ordering.
+ // This should fix that
+ if (sortOrder() == Qt::DescendingOrder) {
+ --row;
+ }
+
+ QModelIndex proxyIndex = index(row, column, parent);
+ QModelIndex sourceIndex = mapToSource(proxyIndex);
+ return this->sourceModel()->dropMimeData(data, action, sourceIndex.row(), sourceIndex.column(),
+ sourceIndex.parent());
+}
+
diff --git a/src/pluginlistsortproxy.h b/src/pluginlistsortproxy.h
index 7397ff7c..d5c71858 100644
--- a/src/pluginlistsortproxy.h
+++ b/src/pluginlistsortproxy.h
@@ -17,52 +17,55 @@ You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PLUGINLISTSORTPROXY_H
-#define PLUGINLISTSORTPROXY_H
-
-
-#include <bitset>
-#include <QSortFilterProxyModel>
-#include "pluginlist.h"
-
-
-class PluginListSortProxy : public QSortFilterProxyModel
-{
- Q_OBJECT
-public:
-
- enum ESorting {
- SORT_ASCENDING,
- SORT_DESCENDING
- };
-
-public:
-
- explicit PluginListSortProxy(QObject *parent = 0);
-
- unsigned int getEnabledColumns() const;
-
- void setEnabledColumns(unsigned int columns);
-
- virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const;
-
- virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
-
-public slots:
-
- void displayColumnSelection(const QPoint &pos);
-
-protected:
-
- virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
-
-private:
-
- int m_SortIndex;
- Qt::SortOrder m_SortOrder;
-
- std::bitset<PluginList::COL_LASTCOLUMN + 1> m_EnabledColumns;
-
-};
-
-#endif // PLUGINLISTSORTPROXY_H
+#ifndef PLUGINLISTSORTPROXY_H
+#define PLUGINLISTSORTPROXY_H
+
+
+#include <bitset>
+#include <QSortFilterProxyModel>
+#include "pluginlist.h"
+
+
+class PluginListSortProxy : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+
+ enum ESorting {
+ SORT_ASCENDING,
+ SORT_DESCENDING
+ };
+
+public:
+
+ explicit PluginListSortProxy(QObject *parent = 0);
+
+ unsigned int getEnabledColumns() const;
+
+ void setEnabledColumns(unsigned int columns);
+
+ virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const;
+
+ virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
+
+public slots:
+
+ void displayColumnSelection(const QPoint &pos);
+ void updateFilter(const QString &filter);
+
+protected:
+
+ virtual bool filterAcceptsRow(int row, const QModelIndex &parent) const;
+ virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
+
+private:
+
+ int m_SortIndex;
+ Qt::SortOrder m_SortOrder;
+
+ std::bitset<PluginList::COL_LASTCOLUMN + 1> m_EnabledColumns;
+ QString m_CurrentFilter;
+
+};
+
+#endif // PLUGINLISTSORTPROXY_H
diff --git a/src/profile.cpp b/src/profile.cpp
index 002c93c0..67fd1e13 100644
--- a/src/profile.cpp
+++ b/src/profile.cpp
@@ -130,8 +130,10 @@ void Profile::cancelWriteModlist() const
}
-void Profile::writeModlistNow() const
+void Profile::writeModlistNow(bool onlyOnTimer) const
{
+ if (onlyOnTimer && !m_SaveTimer->isActive()) return;
+
m_SaveTimer->stop();
#pragma message("right now, this is doing unnecessary saves. Need a flag that says that mod priority, enabled-state or name of a mod has changed")
@@ -194,11 +196,6 @@ void Profile::createTweakedIniFile()
void Profile::refreshModStatus()
{
- // don't lose changes!
- if (m_SaveTimer->isActive()) {
- writeModlistNow();
- }
-
QFile file(getModlistFileName());
if (!file.exists()) {
throw MyException(QObject::tr("failed to find \"%1\"").arg(getModlistFileName()));
@@ -571,11 +568,15 @@ void Profile::deactivateInvalidation() const
}
}
+ // just to be safe...
+ ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL);
+
if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) ||
!::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"0", iniFileName.c_str()) ||
!::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"ArchiveInvalidation.txt", iniFileName.c_str())) {
- throw windows_error("failed to modify ini file");
+ QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName));
+ throw windows_error(errorMessage.toUtf8().constData());
}
}
}
@@ -598,11 +599,15 @@ void Profile::activateInvalidation(const QString& dataDirectory) const
archives.insert(0, invalidationBSA);
}
+ // just to be safe...
+ ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL);
+
if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) ||
!::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", iniFileName.c_str()) ||
!::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"", iniFileName.c_str())) {
- throw windows_error("failed to modify ini file");
+ QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName));
+ throw windows_error(errorMessage.toUtf8().constData());
}
QString bsaFile = dataDirectory + "/" + invalidationBSA;
diff --git a/src/profile.h b/src/profile.h
index 0c25b8f1..32647591 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -17,295 +17,295 @@ You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PROFILE_H
-#define PROFILE_H
-
-
-#include "modinfo.h"
-
-#include <QString>
-#include <QDir>
-#include <QMetaType>
-#include <QSettings>
-#include <vector>
-#include <tuple>
-
-
-/**
- * @brief represents a profile
- **/
-class Profile : public QObject
-{
-
- Q_OBJECT
-
-public:
-
- /**
- * @brief default constructor
- * @todo This constructor initialised nothing, the resulting object is not usable
- **/
- Profile();
-
- /**
- * @brief constructor
- *
- * This constructor is used to create a new profile so it is to be assumed a profile
- * by this name does not yet exist
- * @param name name of the new profile
- * @param filter save game filter. Defaults to &lt;no filter&gt;.
- **/
- Profile(const QString &name, bool useDefaultSettings);
- /**
- * @brief constructor
- *
- * This constructor is used to open an existing profile though it will also try to repair
- * the profile if important files are missing (including the directory itself) so technically,
- * invoking this should always produce a working profile
- * @param directory directory to read the profile from
- **/
- Profile(const QDir &directory);
-
- Profile(const Profile &reference);
-
- ~Profile();
-
- /**
- * @return true if this profile (still) exists on disc
- */
- bool exists() const;
-
- /**
- * @brief copy constructor
- *
- * @param name of the new profile
- * @param reference profile to copy from
- **/
- static Profile createFrom(const QString &name, const Profile &reference);
-
- /**
- * @brief write out the modlist.txt
- **/
- void writeModlist() const;
-
- /**
- * cancel any potential request to write modlist.txt. This is used to ensure no invalid modlist is
- * saved while it's being modified
- */
- void cancelWriteModlist() const;
-
- /**
- * @brief test if this profile uses archive invalidation
- *
- * @param supported if this is not null, the parameter will be set to false if invalidation is not supported in this profile
- * @return true if archive invalidation is active
- * @note currently, invalidation is not supported if the relevant entry in the ini file does not exist
- **/
- bool invalidationActive(bool *supported) const;
-
- /**
- * @brief deactivate archive invalidation if it was active
- **/
- void deactivateInvalidation() const;
-
- /**
- * @brief activate archive invalidation
- *
- * @param dataDirectory data directory of the game
- * @todo passing the data directory as a parameter is useless, the function should
- * be able to query it from GameInfo
- **/
- void activateInvalidation(const QString &dataDirectory) const;
-
- /**
- * @return true if this profile uses local save games
- */
- bool localSavesEnabled() const;
-
- /**
- * @brief enables or disables the use of local save games for this profile
- * disabling this does not delete exising local saves but they will not be visible
- * in the game
- * @param enable if true, local saves are enabled, otherewise they are disabled
- */
- bool enableLocalSaves(bool enable);
-
- /**
- * @return name of the profile (this is identical to its directory name)
- **/
- QString getName() const { return m_Directory.dirName(); }
-
- /**
- * @return the path of the plugins file in this profile
- * @todo is this required? can the functionality using this function be moved to the Profile-class?
- **/
- QString getPluginsFileName() const;
-
- /**
- * @return the path of the loadorder file in this profile
- **/
- QString getLoadOrderFileName() const;
-
- /**
- * @return the path of the file containing locked mod indices
- */
- QString getLockedOrderFileName() const;
-
- /**
- * @return path of the archives file in this profile
- */
- QString getArchivesFileName() const;
-
- /**
- * @return the path of the delete file in this profile
- * @note the deleter file lists plugins that should be hidden from the game and other tools
- **/
- QString getDeleterFileName() const;
-
- /**
- * @return the path of the ini file in this profile
- * @todo since the game can contain multiple ini files (i.e. skyrim.ini skyrimprefs.ini)
- * the concept of this function is somewhat broken
- **/
- QString getIniFileName() const;
-
- /**
- * @return path to this profile
- **/
- QString getPath() const;
-
- /**
- * @brief create the ini file to be used by the game
- *
- * the tweaked ini file constructed by this file is a merger
- * of the game-ini of this profile with ini tweaks applied */
- void createTweakedIniFile();
-
- /**
- * @brief re-read the modlist.txt and update the mod status from it
- **/
- void refreshModStatus();
-
- /**
- * @brief retrieve a list of mods that are enabled in this profile
- *
- * @return list of active mods sorted by priority (ascending). "first" is the mod name, "second" is its path
- **/
- std::vector<std::tuple<QString, QString, int> > getActiveMods();
-
- /**
- * retrieve the number of mods for which this object has status information.
- * This is usually the same as ModInfo::getNumMods() except between
- * calls to ModInfo::updateFromDisc() and the Profile::refreshModStatus()
- *
- * @return number of mods for which the profile has status information
- **/
- unsigned int numMods() const { return m_ModStatus.size(); }
-
- /**
- * @return the number of mods that can be enabled and where the priority can be modified
- */
- unsigned int numRegularMods() const { return m_NumRegularMods; }
-
- /**
- * @brief retrieve the mod index based on the priority
- *
- * @param priority priority to look up
- * @return the index of the mod
- * @throw std::exception an exception is thrown if there is no mod with the specified priority
- **/
- unsigned int modIndexByPriority(unsigned int priority) const;
-
- /**
- * @brief enable or disable a mod
- *
- * @param index index of the mod to enable/disable
- * @param enabled true if the mod is to be enabled, false if it is to be disabled
- **/
- void setModEnabled(unsigned int index, bool enabled);
-
- /**
- * change the priority of a mod. Of course this also changes the priority of other mods.
- * The priority of the mods in the range ]old, new priority] are shifted so that no gaps
- * are possible.
- *
- * @param index index of the mod to change
- * @param newPriority the new priority value
- *
- * @todo what happens if the new priority is outside the range?
- **/
- void setModPriority(unsigned int index, int &newPriority);
-
- /**
- * @brief determine if a mod is enabled
- *
- * @param index index of the mod to look up
- * @return true if the mod is enabled, false otherwise
- **/
- bool modEnabled(unsigned int index) const;
-
- /**
- * @brief query the priority of a mod
- *
- * @param index index of the mod to look up
- * @return priority of the specified mod
- **/
- int getModPriority(unsigned int index) const;
-
- void dumpModStatus() const;
-
-signals:
-
- /**
- * @brief emitted whenever the status (enabled/disabled) of a mod changed
- *
- * @param index index of the mod that changed
- **/
- void modStatusChanged(unsigned int index);
-
-public slots:
-
- void writeModlistNow() const;
-
-private:
-
- class ModStatus {
- friend class Profile;
- public:
- ModStatus() : m_Overwrite(false), m_Enabled(false), m_Priority(-1) {}
- private:
- bool m_Overwrite;
- bool m_Enabled;
- int m_Priority;
- };
-
-private:
- Profile& operator=(const Profile &reference); // not implemented
-
- void initTimer();
-
- void updateIndices();
-
- QString getModlistFileName() const;
- void copyFilesTo(QString &target) const;
-
- std::vector<std::wstring> splitDZString(const wchar_t *buffer) const;
- void mergeTweak(const QString &tweakName, const QString &tweakedIni) const;
- void mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const;
-
-private:
-
- QDir m_Directory;
-
- std::vector<ModStatus> m_ModStatus;
- std::vector<unsigned int> m_ModIndexByPriority;
- unsigned int m_NumRegularMods;
-
- QTimer *m_SaveTimer;
-
-};
-
-Q_DECLARE_METATYPE(Profile)
-
-
-#endif // PROFILE_H
+#ifndef PROFILE_H
+#define PROFILE_H
+
+
+#include "modinfo.h"
+
+#include <QString>
+#include <QDir>
+#include <QMetaType>
+#include <QSettings>
+#include <vector>
+#include <tuple>
+
+
+/**
+ * @brief represents a profile
+ **/
+class Profile : public QObject
+{
+
+ Q_OBJECT
+
+public:
+
+ /**
+ * @brief default constructor
+ * @todo This constructor initialised nothing, the resulting object is not usable
+ **/
+ Profile();
+
+ /**
+ * @brief constructor
+ *
+ * This constructor is used to create a new profile so it is to be assumed a profile
+ * by this name does not yet exist
+ * @param name name of the new profile
+ * @param filter save game filter. Defaults to &lt;no filter&gt;.
+ **/
+ Profile(const QString &name, bool useDefaultSettings);
+ /**
+ * @brief constructor
+ *
+ * This constructor is used to open an existing profile though it will also try to repair
+ * the profile if important files are missing (including the directory itself) so technically,
+ * invoking this should always produce a working profile
+ * @param directory directory to read the profile from
+ **/
+ Profile(const QDir &directory);
+
+ Profile(const Profile &reference);
+
+ ~Profile();
+
+ /**
+ * @return true if this profile (still) exists on disc
+ */
+ bool exists() const;
+
+ /**
+ * @brief copy constructor
+ *
+ * @param name of the new profile
+ * @param reference profile to copy from
+ **/
+ static Profile createFrom(const QString &name, const Profile &reference);
+
+ /**
+ * @brief write out the modlist.txt
+ **/
+ void writeModlist() const;
+
+ /**
+ * cancel any potential request to write modlist.txt. This is used to ensure no invalid modlist is
+ * saved while it's being modified
+ */
+ void cancelWriteModlist() const;
+
+ /**
+ * @brief test if this profile uses archive invalidation
+ *
+ * @param supported if this is not null, the parameter will be set to false if invalidation is not supported in this profile
+ * @return true if archive invalidation is active
+ * @note currently, invalidation is not supported if the relevant entry in the ini file does not exist
+ **/
+ bool invalidationActive(bool *supported) const;
+
+ /**
+ * @brief deactivate archive invalidation if it was active
+ **/
+ void deactivateInvalidation() const;
+
+ /**
+ * @brief activate archive invalidation
+ *
+ * @param dataDirectory data directory of the game
+ * @todo passing the data directory as a parameter is useless, the function should
+ * be able to query it from GameInfo
+ **/
+ void activateInvalidation(const QString &dataDirectory) const;
+
+ /**
+ * @return true if this profile uses local save games
+ */
+ bool localSavesEnabled() const;
+
+ /**
+ * @brief enables or disables the use of local save games for this profile
+ * disabling this does not delete exising local saves but they will not be visible
+ * in the game
+ * @param enable if true, local saves are enabled, otherewise they are disabled
+ */
+ bool enableLocalSaves(bool enable);
+
+ /**
+ * @return name of the profile (this is identical to its directory name)
+ **/
+ QString getName() const { return m_Directory.dirName(); }
+
+ /**
+ * @return the path of the plugins file in this profile
+ * @todo is this required? can the functionality using this function be moved to the Profile-class?
+ **/
+ QString getPluginsFileName() const;
+
+ /**
+ * @return the path of the loadorder file in this profile
+ **/
+ QString getLoadOrderFileName() const;
+
+ /**
+ * @return the path of the file containing locked mod indices
+ */
+ QString getLockedOrderFileName() const;
+
+ /**
+ * @return path of the archives file in this profile
+ */
+ QString getArchivesFileName() const;
+
+ /**
+ * @return the path of the delete file in this profile
+ * @note the deleter file lists plugins that should be hidden from the game and other tools
+ **/
+ QString getDeleterFileName() const;
+
+ /**
+ * @return the path of the ini file in this profile
+ * @todo since the game can contain multiple ini files (i.e. skyrim.ini skyrimprefs.ini)
+ * the concept of this function is somewhat broken
+ **/
+ QString getIniFileName() const;
+
+ /**
+ * @return path to this profile
+ **/
+ QString getPath() const;
+
+ /**
+ * @brief create the ini file to be used by the game
+ *
+ * the tweaked ini file constructed by this file is a merger
+ * of the game-ini of this profile with ini tweaks applied */
+ void createTweakedIniFile();
+
+ /**
+ * @brief re-read the modlist.txt and update the mod status from it
+ **/
+ void refreshModStatus();
+
+ /**
+ * @brief retrieve a list of mods that are enabled in this profile
+ *
+ * @return list of active mods sorted by priority (ascending). "first" is the mod name, "second" is its path
+ **/
+ std::vector<std::tuple<QString, QString, int> > getActiveMods();
+
+ /**
+ * retrieve the number of mods for which this object has status information.
+ * This is usually the same as ModInfo::getNumMods() except between
+ * calls to ModInfo::updateFromDisc() and the Profile::refreshModStatus()
+ *
+ * @return number of mods for which the profile has status information
+ **/
+ unsigned int numMods() const { return m_ModStatus.size(); }
+
+ /**
+ * @return the number of mods that can be enabled and where the priority can be modified
+ */
+ unsigned int numRegularMods() const { return m_NumRegularMods; }
+
+ /**
+ * @brief retrieve the mod index based on the priority
+ *
+ * @param priority priority to look up
+ * @return the index of the mod
+ * @throw std::exception an exception is thrown if there is no mod with the specified priority
+ **/
+ unsigned int modIndexByPriority(unsigned int priority) const;
+
+ /**
+ * @brief enable or disable a mod
+ *
+ * @param index index of the mod to enable/disable
+ * @param enabled true if the mod is to be enabled, false if it is to be disabled
+ **/
+ void setModEnabled(unsigned int index, bool enabled);
+
+ /**
+ * change the priority of a mod. Of course this also changes the priority of other mods.
+ * The priority of the mods in the range ]old, new priority] are shifted so that no gaps
+ * are possible.
+ *
+ * @param index index of the mod to change
+ * @param newPriority the new priority value
+ *
+ * @todo what happens if the new priority is outside the range?
+ **/
+ void setModPriority(unsigned int index, int &newPriority);
+
+ /**
+ * @brief determine if a mod is enabled
+ *
+ * @param index index of the mod to look up
+ * @return true if the mod is enabled, false otherwise
+ **/
+ bool modEnabled(unsigned int index) const;
+
+ /**
+ * @brief query the priority of a mod
+ *
+ * @param index index of the mod to look up
+ * @return priority of the specified mod
+ **/
+ int getModPriority(unsigned int index) const;
+
+ void dumpModStatus() const;
+
+signals:
+
+ /**
+ * @brief emitted whenever the status (enabled/disabled) of a mod changed
+ *
+ * @param index index of the mod that changed
+ **/
+ void modStatusChanged(unsigned int index);
+
+public slots:
+
+ void writeModlistNow(bool onlyOnTimer = false) const;
+
+private:
+
+ class ModStatus {
+ friend class Profile;
+ public:
+ ModStatus() : m_Overwrite(false), m_Enabled(false), m_Priority(-1) {}
+ private:
+ bool m_Overwrite;
+ bool m_Enabled;
+ int m_Priority;
+ };
+
+private:
+ Profile& operator=(const Profile &reference); // not implemented
+
+ void initTimer();
+
+ void updateIndices();
+
+ QString getModlistFileName() const;
+ void copyFilesTo(QString &target) const;
+
+ std::vector<std::wstring> splitDZString(const wchar_t *buffer) const;
+ void mergeTweak(const QString &tweakName, const QString &tweakedIni) const;
+ void mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const;
+
+private:
+
+ QDir m_Directory;
+
+ std::vector<ModStatus> m_ModStatus;
+ std::vector<unsigned int> m_ModIndexByPriority;
+ unsigned int m_NumRegularMods;
+
+ QTimer *m_SaveTimer;
+
+};
+
+Q_DECLARE_METATYPE(Profile)
+
+
+#endif // PROFILE_H
diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp
index cbf50dfc..ae3d6660 100644
--- a/src/profilesdialog.cpp
+++ b/src/profilesdialog.cpp
@@ -261,7 +261,7 @@ void ProfilesDialog::on_profilesList_currentItemChanged(QListWidgetItem *current
void ProfilesDialog::on_localSavesBox_stateChanged(int state)
{
- Profile &currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile>();
+ Profile currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile>();
if (currentProfile.enableLocalSaves(state == Qt::Checked)) {
ui->transferButton->setEnabled(state == Qt::Checked);
} else {
diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp
index 6302cef3..26395e67 100644
--- a/src/shared/skyriminfo.cpp
+++ b/src/shared/skyriminfo.cpp
@@ -27,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "error_report.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
+#include <Shlwapi.h>
#include <boost/assign.hpp>
namespace MOShared {
@@ -36,6 +37,11 @@ SkyrimInfo::SkyrimInfo(const std::wstring &omoDirectory, const std::wstring &gam
: GameInfo(omoDirectory, gameDirectory)
{
identifyMyGamesDirectory(L"skyrim");
+
+ wchar_t appDataPath[MAX_PATH];
+ if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataPath))) {
+ m_AppData = appDataPath;
+ }
}
bool SkyrimInfo::identifyGame(const std::wstring &searchPath)
@@ -258,8 +264,12 @@ bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPa
}
}
+if (_wcsicmp(fileName, L"plugins.txt") == 0) {
+ log("plugins.txt expected in \"%ls\", got \"%ls\"", m_AppData.c_str(), fullPath);
+}
+
if ((_wcsicmp(fileName, L"plugins.txt") == 0) &&
- (wcsstr(fullPath, L"AppData") != NULL)){
+ (m_AppData.empty() || (StrStrIW(fullPath, m_AppData.c_str()) != NULL))) {
return true;
}
diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h
index b12a5d58..024d0395 100644
--- a/src/shared/skyriminfo.h
+++ b/src/shared/skyriminfo.h
@@ -97,6 +97,10 @@ private:
static bool identifyGame(const std::wstring &searchPath);
+private:
+
+ std::wstring m_AppData;
+
};
} // namespace MOShared
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index a6378a74..e61ae8a6 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -79,6 +79,13 @@ std::string &ToLower(std::string &text)
return text;
}
+std::string ToLower(const std::string &text)
+{
+ std::string temp = text;
+
+ std::transform(temp.begin(), temp.end(), temp.begin(), tolower);
+ return temp;
+}
std::wstring &ToLower(std::wstring &text)
{
diff --git a/src/shared/util.h b/src/shared/util.h
index 9c59f6db..80983cb0 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -39,8 +39,9 @@ std::string ToString(const std::wstring &source, bool utf8);
std::wstring ToWString(const std::string &source, bool utf8);
std::string &ToLower(std::string &text);
-std::wstring &ToLower(std::wstring &text);
+std::string ToLower(const std::string &text);
+std::wstring &ToLower(std::wstring &text);
std::wstring ToLower(const std::wstring &text);
VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName);
diff --git a/src/version.rc b/src/version.rc
index 23f7b6e3..9f083ed4 100644
--- a/src/version.rc
+++ b/src/version.rc
@@ -1,7 +1,7 @@
#include "Winver.h"
-#define VER_FILEVERSION 0,12,8,0
-#define VER_FILEVERSION_STR 0,12,8,0
+#define VER_FILEVERSION 0,12,9,0
+#define VER_FILEVERSION_STR 0,12,9,0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION